feat(host): GameStream/Moonlight compat is now opt-in (--gamestream) — secure native-only by default
apple / swift (push) Successful in 55s
windows-host / package (push) Successful in 2m31s
android / android (push) Successful in 4m40s
ci / rust (push) Successful in 4m43s
ci / web (push) Successful in 30s
ci / docs-site (push) Successful in 34s
deb / build-publish (push) Successful in 2m9s
decky / build-publish (push) Successful in 11s
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 14s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 21s
ci / bench (push) Successful in 4m44s
docker / deploy-docs (push) Successful in 19s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m6s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m19s

Follows the security audit (#5/#9): the GameStream-compat plane carries inherent on-path weaknesses
that can't be fixed on the wire without breaking stock Moonlight — its pairing runs over plain HTTP
(#9, MITM-able during the pairing window) and its legacy control encryption can reuse GCM nonces (#5,
a passive eavesdropper can recover/forge input). The native punktfunk/1 plane (SPAKE2 PIN pairing +
per-direction AEAD nonces) has neither. So flip the default to secure-by-default:

- `serve`              → native punktfunk/1 plane + management API ONLY (no GameStream surface).
- `serve --gamestream` → ALSO the GameStream/Moonlight-compat planes (nvhttp pairing, RTSP, ENet
  control, _nvstream mDNS). Opt-in, logged with a trusted-LAN caveat. `--moonlight` is an alias.
- The native plane is now ALWAYS on in `serve` (`--native` is a kept-for-compat no-op); the unified
  GameStream+native host is `serve --gamestream`.

`gamestream::serve` gates the GameStream spawns (nvhttp/rtsp/control/mdns) on the flag; the native
plane + mgmt + native-pairing handle always run.

To avoid silently regressing validated Moonlight deployments, the explicit deployment configs PRESERVE
Moonlight via `--gamestream` (each documents dropping it for a secure native-only host): the Linux
systemd unit, the Steam Deck installer, and the Windows service default (DEFAULT_HOST_CMD). The bare
`serve` default (new/manual use) is secure.

Docs swept to match (host-cli, moonlight, quickstart, install, packaging READMEs, CLAUDE.md, README,
…): Moonlight setup now instructs `--gamestream`; native/console refs use bare `serve`. OpenAPI
regenerated (a stale "run `serve --native`" string). fmt + clippy clean; 94 host tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-21 10:19:40 +00:00
parent 3c55ec37fa
commit 54b75c9be4
30 changed files with 226 additions and 141 deletions
+1 -1
View File
@@ -621,7 +621,7 @@
}
},
"503": {
"description": "Native host not enabled (run `serve --native`)",
"description": "Native host not available in this process",
"content": {
"application/json": {
"schema": {
+2 -2
View File
@@ -45,7 +45,7 @@ settings, in-app SPAKE2 PIN pairing) + the video on a **`SwapChainPanel`**, all
## What we're building
A native Windows client that connects to a punktfunk/1 host (`serve --native` / `punktfunk1-host`), decodes
A native Windows client that connects to a punktfunk/1 host (`serve` / `punktfunk1-host`), decodes
HEVC, presents it low-latency, plays Opus audio, and captures local mouse/keyboard/gamepad to send
back — i.e. the Windows analogue of the **GTK4 Linux client** (`clients/linux`),
which is the architectural template. The Windows client is close to a 1:1 port of the Linux client
@@ -149,7 +149,7 @@ Windows client should mirror it:
`punktfunk-core { path, features=["quic"] }`, `windows`, the Reactor crate, `ffmpeg-next`, `opus`,
`sdl3`, `mdns-sd`, `anyhow`, `tracing`. Mirror `clients/linux/Cargo.toml`.
3. **Connect + control plane.** Port `session.rs` + `trust.rs`; validate headless against the 4090
box (`punktfunk1-host`/`serve --native`) — handshake, PIN/TOFU, plane counters — before any UI/decode.
box (`punktfunk1-host`/`serve`) — handshake, PIN/TOFU, plane counters — before any UI/decode.
4. **Decode + present.** FFmpeg D3D11VA → `SwapChainPanel`. SDR (8-bit BGRA) first, then **P010 +
HDR colorspace** (see the HDR section).
5. **Audio.** WASAPI render + Opus decode (port `audio.rs`).
+2 -2
View File
@@ -15,7 +15,7 @@ plan + dev box + SudoVDA protocol + no-GPU strategy added 2026-06-14 (12-agent r
## Status (2026-06-15) — full pipeline live-validated on an RTX 4090
Every OS-touching backend is implemented behind the existing traits and **builds clean on
`x86_64-pc-windows-msvc`** (and Linux unaffected). `serve --native` / `punktfunk1-host` **run on Windows**
`x86_64-pc-windows-msvc`** (and Linux unaffected). `serve` / `punktfunk1-host` **run on Windows**
(identity in `%APPDATA%`, QUIC bound, mDNS advertising, accepting sessions). The **full native
pipeline is validated live on a real RTX 4090** (Windows 11): SudoVDA virtual display → DXGI
Desktop Duplication (D3D11 zero-copy) → **NVENC HEVC** → punktfunk/1 → Rust reference client, at
@@ -146,7 +146,7 @@ rustc 1.96 clippy is stricter than the Linux CI image on shared code, e.g. `need
3. `cargo build -p punktfunk-host --features nvenc` (needs NASM + CMake for aws-lc-rs; libclang for
any ffmpeg-using client). Default build (no feature) uses the openh264 software encoder.
4. Run in the **interactive session** (not a Session-0 service / not over SSH — SendInput + DXGI
Desktop Duplication need a desktop): `serve --native` or `punktfunk1-host --source virtual`. Set
Desktop Duplication need a desktop): `serve` or `punktfunk1-host --source virtual`. Set
`PUNKTFUNK_ENCODER=nvenc` to select NVENC (the DXGI capturer switches to zero-copy D3D11 output to
match). The SudoVDA monitor activates once a real GPU drives WDDM, so capture + NVENC then work.
+1 -1
View File
@@ -74,7 +74,7 @@ PUNKTFUNK_ENCODER=nvenc
PUNKTFUNK_VIDEO_SOURCE=virtual
PUNKTFUNK_SECURE_DDA=1
RUST_LOG=info
# PUNKTFUNK_HOST_CMD=serve --native # the host subcommand the service launches (default)
# PUNKTFUNK_HOST_CMD=serve --gamestream # the host subcommand the service launches (default: native + Moonlight)
```
The service loads these into its environment and carries `PUNKTFUNK_*` + `RUST_LOG` to the host child