Phase 2. The lease, the settings, the events and the status surface were already platform-neutral; what Windows lacked was a way to see processes and a way to ask one to close. `procscan` splits per-OS behind one contract. The Windows matcher is a Toolhelp snapshot plus each process's full image path (`QueryFullProcessImageNameW`), with creation times from `GetProcessTimes` enforcing the two rules the module exists for: never adopt a process that predates the launch, never trust a bare pid. Both matter more here than on Linux — the host is SYSTEM, so it can see and signal everything, and Windows recycles pids briskly. Path matching is case-insensitive and separator-aware, and normalizes the `\\?\` prefix `canonicalize` adds, without which a store-derived path would never compare equal to a live image path. There is no reaper argv and no readable environment on Windows, so a spec carrying only a Steam appid or an env marker matches nothing rather than falling through to an empty predicate. Steam's appid instead feeds a **veto**: its per-app `Running` flag in the user's hive can't be trusted to say a game IS running (Steam sets it around updates too, and leaves it stale after a crash), but it is exactly right for refusing to declare one gone. If the matcher can't see a game whose exe sits outside its manifest's install dir, ending the session would be a false positive the player feels immediately; honoring the veto only ever leaves a stream up. Termination asks before it insists, which on Windows needs a detail that fails silently if missed: `EnumWindows` only sees the calling thread's desktop, and the host's is session 0, which holds none of the user's windows. So the terminating thread binds to the input desktop first (the pattern `pf-inject`'s `sendinput.rs` uses), posts `WM_CLOSE` to the game's visible top-level windows, waits, and only then calls `TerminateProcess` on freshly re-verified pids. Without the desktop bind the polite pass finds nothing and every game dies unsaved. Job Objects, planned as WP2.3, are deferred with the reasoning in the design doc: every Windows launch either hands off through a launcher or the shell — where a job would wrap a shim that exits immediately — or is a direct exe whose path we already know, and wiring one in would touch the `CreateProcessAsUserW` token path the UAC and secure-desktop work depends on. Also: the watcher's steady-state poll now re-verifies known pids and only re-scans the whole table once they all vanish (which still catches a game that re-execs). On Windows a full scan is an `OpenProcess` per process on the box, so this is the difference between a negligible and a noticeable poll. Gates: Linux .21 check/clippy/fmt/293 tests green; Windows .133 check + clippy --all-targets clean, and the 4 procscan tests pass — including the live one that finds the test process in the real process table and rejects a wrong creation time. On-glass Windows (Steam/Epic/GOG/Xbox titles, the unsaved-progress check) still owed; it needs a box with games on it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
punktfunk-host
The streaming host — the program you run on the machine whose desktop or games you want to stream. For each client that connects, it spins up a virtual display sized to that device, captures it on the GPU, encodes with hardware NVENC/VAAPI/AMF/QSV, and sends it out over a low-latency transport — no physical monitor, no letterboxing, no rearranging your real screens.
It speaks two protocols from one process:
- GameStream — so any Moonlight / Artemis client works day one.
punktfunk/1— punktfunk's own faster protocol (QUIC control plane, GF(2¹⁶) FEC + AES-GCM data plane) that the native clients use.
Runs on Linux (the primary, most battle-tested path) and Windows (x64). The shared protocol,
FEC, and crypto live in punktfunk-core; this crate is everything
platform-facing around it.
What it does
- Per-client virtual displays at the exact WxH@Hz. Linux uses per-compositor backends — KWin, gamescope, Mutter, and Sway/wlroots; Windows uses its own all-Rust IddCx virtual display, even on the secure desktop (UAC / lock screen).
- GPU zero-copy capture → encode. dmabuf → CUDA/Vulkan → NVENC on Linux; on Windows the host pushes frames straight into its own IDD (sealed IDD-push, no screen-scraping) → GPU encode. Encoders auto-select by GPU vendor: NVENC (NVIDIA), VAAPI (Linux AMD/Intel), AMF/QSV (Windows AMD/Intel), or software H.264 as a floor. HDR/10-bit and HEVC 4:4:4 supported.
- Input injection. Mouse/keyboard (libei / gamescope EIS / wlr / Windows SendInput) and virtual gamepads — Xbox 360/One, DualSense, DualShock 4 — with rumble and HID feedback back-channels.
- Audio both ways. Opus audio host→client, plus a virtual microphone the client can talk into.
- Trust & discovery. A persistent host identity, SPAKE2 PIN pairing (default) or TOFU, and mDNS auto-advertisement so clients find the host without typing an IP.
- Management API + web console. A REST API (
mgmt.rs, OpenAPI atapi/openapi.json) drives status, paired devices, and on-demand pairing; the browser UI is inweb/.
Run it
punktfunk-host serve runs inside your desktop session. Bare serve is the secure native-only
default (punktfunk/1 + the management API); add --gamestream on a trusted LAN to also accept
stock Moonlight clients.
# Linux, from the repo root (see the repo README "Running on this box" for the headless recipe):
cargo run -rp punktfunk-host -- serve # native-only (secure default)
cargo run -rp punktfunk-host -- serve --gamestream # + Moonlight compatibility
Then pair from the web console (https://<host-ip>:47992) or the client app.
Most people should install a package rather than run from source — see
packaging/ (apt · rpm/COPR/bootc · Arch/sysext · Windows installer) and
the per-platform guides at docs.punktfunk.unom.io/docs/install.
Subcommands
| Command | Purpose |
|---|---|
serve |
The host (native punktfunk/1 + mgmt API; --gamestream adds Moonlight). |
punktfunk1-host |
Standalone native-protocol listener for testing/measurement (--source virtual, --max-sessions). |
openapi |
Print the management-API OpenAPI spec (regenerates api/openapi.json). |
library |
Inspect the multi-store game library. |
service · driver · web |
Windows: SCM service, driver install, bundled web console. |
*-test / *-selftest / *-probe |
Diagnostics (input, zero-copy, HDR, compositor, gamepads). |
--help lists them all.
Layout
src/
main.rs CLI + subcommand dispatch
config.rs · session_plan.rs · session_tuning.rs · pipeline.rs session setup + the frame pipeline
vdisplay/ per-compositor virtual outputs (kwin · gamescope · mutter · wlroots)
capture/ · capture.rs screen/dmabuf capture (+ Windows IDD-push)
encode/ · encode.rs per-GPU encoders (nvenc · vaapi · ffmpeg_win (AMF/QSV) · sw)
linux/zerocopy/ dmabuf → CUDA → NVENC bridges (EGL/GL tiled, Vulkan LINEAR)
inject/ · inject.rs input backends (libei · wlr · uinput gamepads · UHID DualSense/DS4)
audio/ · audio.rs Opus out + virtual mic (PipeWire / WASAPI)
gamestream/ Moonlight compat: nvhttp · pairing · rtsp · control · stream · gamepad · apps
native.rs the native punktfunk/1 host (QUIC control + native-thread UDP data plane)
mgmt.rs · native_pairing.rs · stats_recorder.rs management API, pairing, perf capture
hdr.rs · library.rs HDR metadata; multi-store game library
linux/ · windows/ platform-confined backends
Related
punktfunk-core— the shared protocol · FEC · crypto core- Clients — the apps that connect (Apple · Linux · Windows · Android · probe)
- Packaging & docs — install & operate
- punktfunk-planning (internal planning repo) — architecture rationale and deep-dive plans