Files
punktfunk/clients/decky/README.md
T
enricobuehler d6596ff81b
windows-drivers / probe-and-proto (push) Successful in 24s
windows-drivers / driver-build (push) Successful in 1m18s
apple / swift (push) Successful in 1m5s
android / android (push) Successful in 4m21s
ci / rust (push) Successful in 5m3s
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m2s
deb / build-publish (push) Successful in 2m48s
windows-host / package (push) Successful in 7m10s
decky / build-publish (push) Successful in 24s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 6s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
ci / bench (push) Successful in 4m38s
release / apple (push) Successful in 9m1s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, aarch64-pc-windows-msvc, C:\t-a64) (push) Successful in 1m13s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 51s
windows-msix / package (x64, C:\Users\Public\ffmpeg, x86_64-pc-windows-msvc, C:\t) (push) Successful in 1m10s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m42s
windows / build (aarch64-pc-windows-msvc) (push) Successful in 1m0s
windows / build (x86_64-pc-windows-msvc) (push) Successful in 1m0s
apple / screenshots (push) Successful in 5m32s
flatpak / build-publish (push) Successful in 4m59s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 9m7s
docker / deploy-docs (push) Successful in 25s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m49s
docs: rework client/crate READMEs, add missing ones
Rework the client READMEs to be accurate and inviting to first-time
visitors, and fill in the gaps where crates and tools had none.

- Rewrite clients/{apple,android,decky} READMEs (features-first, trim
  dense internal narrative; drop the stale "one session at a time" /
  "renegotiation not implemented" section from the Apple README).
- Add READMEs for clients/{linux,windows,probe}, which had none.
- Add crate READMEs for punktfunk-host, punktfunk-core, pf-driver-proto.
- Add brief READMEs for tools/{loss-harness,latency-probe}.
- Fix packaging/README duplicate "Option B" heading (bootc -> Option C).
- Fix docs-site/README stale docs/ -> design/ reference.
- De-stale packaging/windows/drivers/pf-dualsense README (drop "M0 spike"
  / external-checkout framing; reflect in-tree workspace + shipped +
  installer-bundled + multi-pad), keeping the driver-authoring lore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 19:31:06 +00:00

4.3 KiB

punktfunk — Steam Deck plugin (Decky)

Stream to your Steam Deck without ever leaving Gaming Mode. This Decky Loader plugin adds a punktfunk panel to the Quick Access Menu (the button): discover hosts on your network, pair with a PIN, tweak stream settings, and launch a fullscreen, gamescope-focused stream — all from the couch, gamepad-navigable.

The video itself is the native GTK4 Linux client (the io.unom.Punktfunk flatpak); the plugin discovers, pairs, configures, and launches it the right way so gamescope fullscreens it — the same Steam-shortcut trick MoonDeck uses. Because it's built from real Steam UI primitives (@decky/ui), the panel looks and feels native to Gaming Mode.

What it does

  1. Discover — browses the LAN over mDNS for punktfunk hosts, in both the QAM panel and a fullscreen page.
  2. Pair — for a host that requires it, a gamepad-navigable PIN keypad runs the SPAKE2 pairing ceremony headlessly, then remembers the host so future streams connect silently.
  3. Stream — launches fullscreen via a hidden Steam shortcut so gamescope focuses it.
  4. Settings — resolution / refresh / bitrate / gamepad / mic, written to the client's config.

To leave a stream: the in-client controller chord (L1 + R1 + Start + Select), or close the "game" from the Steam overlay — either returns you to Gaming Mode.

Install on the Deck

You need Decky Loader and the io.unom.Punktfunk flatpak (packaging/flatpak) installed on the Deck — SteamOS /usr is read-only, so the flatpak (which bundles libadwaita/SDL3) is the canonical client. Discovery uses avahi-browse, which ships on SteamOS/Bazzite.

Recommended — install from URL (published by CI): in Decky → Settings → Developer ModeInstall Plugin from URL, paste:

https://git.unom.io/api/packages/unom/generic/punktfunk-decky/latest/punktfunk.zip

(or a pinned .../punktfunk-decky/<version>/punktfunk.zip). The plugin then self-updates without the Decky store — when a newer build exists, an Update to vX button appears and drives Decky Loader's own (SHA-256-verified) install.

Build & sideload (development)

cd clients/decky
pnpm install
pnpm build                             # rollup → dist/index.js
pnpm run package                       # → out/punktfunk/ + out/punktfunk-v<ver>.zip
DECK=deck@<deck-ip> pnpm run deploy    # rsync → /tmp, sudo-install into the root-owned plugins dir, restart loader

~/homebrew/plugins/ is root-owned (the loader runs as root), so deploy.sh stages to a temp dir then sudo-installs and restarts the loader — set DECKPASS=… to run it non-interactively. A loader restart is required for an out-of-band install to appear.

Architecture

File Role
src/index.tsx Frontend: QAM panel + the /punktfunk fullscreen page (host list, PIN keypad, settings).
src/steam.ts Steam-shortcut launch (AddShortcut / SetAppLaunchOptions / RunGame) — the focus-correct stream start.
src/backend.ts Typed callable bridges to main.py.
bin/punktfunkrun.sh The launch wrapper the Steam shortcut targets (so the window is focusable).
main.py Backend: discover (via avahi-browse) / pair / settings / kill_stream / check_update.
plugin.json · update.json Decky manifest; CI-baked update channel.

The client binary is resolved PATH/usr/bin/usr/local/bin~/.local/bin → a flatpak run io.unom.Punktfunk fallback, so the flatpak install always works.

Limitations / next steps

  • Needs on-Deck validation in Gaming Mode — the Steam-shortcut launch and headless pairing follow MoonDeck's proven pattern but are verified only at build time here.
  • No manual "add host by IP" entry yet (discovery is mDNS-only).
  • No in-stream overlay inside the plugin — the client owns the session once launched.
  • Pairing needs the operator to arm pairing on the host so it shows the PIN; the plugin can't arm it remotely.