docs: refresh README/CLAUDE status; roadmap pairing-hardening + SudoVDA Windows
ci / rust (push) Has been cancelled

- README: replace the stale M0/M2-in-flight status with reality — M1 hardened, M2
  GameStream host live to stock Moonlight, M3 punktfunk/1 validated, M4 Apple first
  light, web console + unified host; FFmpeg 7/8; Bazzite-deployed. Layout adds
  web/, packaging/, native_pairing, dualsense.
- CLAUDE: protocol-growth item now reflects the unified host + web-console native
  pairing (done) and flags the next steps; layout updated.
- roadmap §7 Windows: de-risked via SudoVDA (the Sunshine Virtual Display Adapter) —
  no self-signed kernel IDD needed; the virtual-display backend drops XL→M.
- roadmap §8 (new) Pairing & trust hardening: mandatory PIN pairing by default
  (TOFU-open is insecure on a LAN) + delegated pairing approval (an already-paired
  device approves a new one, no out-of-band PIN).
- windows-host.md: SudoVDA path throughout (status, table, phasing, effort M not L).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 09:54:55 +00:00
parent 19666ba57e
commit 12cf2e4e16
4 changed files with 104 additions and 54 deletions
+51 -14
View File
@@ -1,13 +1,19 @@
# punktfunk roadmap — next goals
Decided 2026-06-10 (research-grounded; see commit history). Sequence:
**KDE reliability → client compositor options → mic passthrough → Bazzite COPR RPM (then
bootc) → touch → full UHID DualSense → iOS** (+ Windows host, scoped & deferred).
Decided 2026-06-10 (research-grounded; see commit history), extended since.
**Done (2026-06-10):** #1 KDE reliability (Phase 1 + 2), #2 compositor options (full stack incl.
macOS client), #4 mic passthrough — all on `main`, live-validated. #3 Bazzite packaging written
(`packaging/`); the COPR/bootc build is operator-run. Remaining: #5 touch → UHID DualSense, #6 iOS,
and a Windows host (`docs/windows-host.md`).
**Done & live (on `main`):** #1 KDE reliability (Phase 1+2), #2 client compositor options (full
stack incl. the macOS client), #4 mic passthrough, #5 touch (host path) + **rich UHID DualSense**
— input + adaptive-trigger/LED feedback over the new `0xCC`/`0xCD` planes + C ABI, Phase C/D/E
live-validated. #3 Bazzite packaging (`packaging/`) **deployed live** on a Bazzite F43 box (builds
against FFmpeg 7 **or** 8; gamescope capture → zero-copy NVENC, sub-ms latency; Sunshine replaced).
**Unified host:** `serve --native` runs the GameStream host + the punktfunk/1 QUIC host in one
process, with native pairing driven from the **web console** (arm → show PIN), not the service log.
Advanced DualSense (audio-driven voice-coil) haptics **scoped NO-GO** (`docs/dualsense-haptics.md`).
**Next:** **§8 pairing & trust hardening** (mandatory PIN by default + delegated approval), the M4
client presenter + iOS (§6), and a Windows host (§7 — now **de-risked via SudoVDA**, no custom
signed driver needed).
## 1. Reliable headless KDE/compositor spawning ✅ *(done — Phase 1 + 2)*
@@ -111,14 +117,45 @@ select = a `pw_stream` with `Direction::Output` + `media.class=Audio/Source`.
PunktfunkKit is already platform-shared; iOS needs the `UIViewRepresentable` presenter twin
+ touch capture (#5) + UI. tvOS later.
## 7. Windows as a host *(scoped & deferred — `docs/windows-host.md`)*
## 7. Windows as a host *(scoped — `docs/windows-host.md`; de-risked via SudoVDA)*
Architecturally an "add a backend" job, not a parallel port: `punktfunk-core` (protocol/FEC/
crypto/C-ABI) + QUIC + GameStream + mgmt + the `m3`/pipeline orchestration are all platform-agnostic
and already `cfg`-isolated (~95% reuse). New `#[cfg(windows)]` backends behind the existing traits:
capture (DXGI Desktop Duplication), encode (Media Foundation / NVENC-SDK with a D3D11 context),
input (SendInput + ViGEm), audio (WASAPI loopback + a virtual mic). **The blocker** is the
virtual-display feature — no user-mode Windows API; it needs a signed kernel-mode **IDD** driver
(XL). Recommended start: **Phase 0** — a "basic Windows host" capturing an existing monitor (no
virtual display), proving the whole stack with the smallest surface. Deferred because it's large and
unbuildable on the Linux dev box; the trait boundaries are already in the right places.
capture (DXGI Desktop Duplication / Windows.Graphics.Capture), encode (Media Foundation / NVENC-SDK
with a D3D11 context), input (SendInput + ViGEm), audio (WASAPI loopback + a virtual mic).
**The old blocker is gone.** Rather than author + sign our own kernel IDD for the per-client virtual
display, use **SudoVDA** (the Sunshine Virtual Display Adapter) — a pre-built, signed Indirect
Display Driver that creates virtual displays at arbitrary WxH@Hz on demand. The `VirtualDisplay`
backend becomes *"install + drive SudoVDA's control API"* (M effort), not *"write + WHQL-sign a
kernel driver"* (XL). That removes the only hard blocker — the Windows host is now a medium,
mostly-mechanical port. Recommended start: **Phase 0** — capture an existing monitor to prove the
stack end to end; **Phase 1** wires SudoVDA for the native-resolution output. Deferred only because
it's unbuildable on the Linux dev box; the trait boundaries are already in the right places.
## 8. Pairing & trust hardening *(next)*
The unified host + web-console pairing (arm a window → display the host PIN → user enters it on the
client) is built and live. Two changes harden it from "works" to "secure by default":
- **Mandatory PIN pairing by default.** Today the punktfunk/1 host can run open (trust-on-first-use)
— *not* acceptable on a shared LAN, where any reachable device could connect. The unified host
should `require_pairing` out of the box: a client must complete the SPAKE2 PIN ceremony (one online
guess, no offline attack) before any session. The operator arms a window and reads the PIN from the
web console (already built); an explicit `--open` escape hatch covers trusted single-user setups.
The wire is already in place (`M3Options.require_pairing` + the `serve_session` gate); this flips
the default and threads it through `serve --native` and the mgmt arm endpoint.
- **Delegated pairing approval** — the ergonomic enabler for "mandatory" (pair a new device without
fetching the host PIN out of band):
1. Device A is already paired (authenticated) to Host X.
2. The user tries to connect Device B to Host X.
3. Host X pushes a request to the authenticated Device A: *"Allow Device B to pair with Host X?"*
4. The user approves/denies on Device A; on approve, Host X admits Device B — binding B's
certificate fingerprint — with no PIN typed.
Needs: a host→client *pairing-approval-request* (B's fingerprint + a human label) delivered to A's
live connection (a QUIC side-plane message) or polled via the mgmt API; an approve/deny round-trip
carrying an approval token; the host gating B's admission on it. The web console **and** the Apple
client render the approval prompt. PIN pairing stays the bootstrap (the first device, or when no
paired device is online to approve).