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>
5.0 KiB
Windows service (deployment)
The PunktfunkHost Windows service is the end-user way to run the host on Windows. It replaces the
manual bring-up chain (a scheduled task → PsExec64 -s -i 1 → wscript launch.vbs → host-run.cmd)
with one command, auto-start on boot, and supervision.
Install (installer — recommended)
Download the signed installer from the package registry
(punktfunk-host-windows, https://git.unom.io/unom/-/packages) and run it (it elevates itself):
punktfunk-host-setup-<ver>.exe # wizard
punktfunk-host-setup-<ver>.exe /VERYSILENT # unattended
It lays the host into C:\Program Files\punktfunk, optionally installs the bundled SudoVDA
virtual-display driver, then runs service install + service start for you. Upgrades stop the
service first and re-point it; uninstall (Add/Remove Programs) runs service uninstall. Packaging
details: packaging/windows/README.md. A self-signed CI build also
publishes a .cer — import it once (Import-Certificate -FilePath punktfunk-host-windows.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher) so Windows trusts the signed setup.
Install (manual / CLI)
From an elevated (Administrator) prompt:
punktfunk-host service install # register auto-start LocalSystem service + firewall rules + default host.env
punktfunk-host service start # start it now (also starts automatically on every boot)
service install is idempotent — run it again after upgrading the exe to re-point the service at the
new binary. Register whatever location you keep the exe in (e.g. C:\Program Files\punktfunk\); the
service records the current exe path.
Other subcommands:
punktfunk-host service stop
punktfunk-host service status
punktfunk-host service uninstall # stop + delete the service + remove its firewall rules
How it works
The host must run as SYSTEM in the interactive session (Session 1+): Desktop Duplication of the
secure desktop (UAC / lock / login) and SendInput need SYSTEM, and capture/injection need the
interactive session, which a plain Session-0 service is not in.
So the service (itself in Session 0) never captures. On start, and whenever the active console session changes, it:
- resolves the active console session (
WTSGetActiveConsoleSessionId), - duplicates its own LocalSystem token and retargets it to that session (
SetTokenInformationTokenSessionId), - launches the host there with
CreateProcessAsUserW(lpDesktop = winsta0\default), - supervises it: relaunches on exit/crash (with backoff) and on a console connect/disconnect.
A kill-on-close job object ensures a service crash never orphans the SYSTEM host. The host in turn
spawns the WGC helper into the user session (see windows-secure-desktop.md)
— two nested launches. Lock/unlock are handled inside the host (the DesktopWatcher DDA↔WGC mux), so
the service deliberately does not relaunch on lock/unlock — only on a real session switch.
This is the same model Sunshine/Apollo use.
Configuration
Config lives in %ProgramData%\punktfunk\host.env (KEY=VALUE lines, # comments). service install writes a default if none exists. Template: scripts/windows/host.env.example.
PUNKTFUNK_ENCODER=nvenc
PUNKTFUNK_VIDEO_SOURCE=virtual
PUNKTFUNK_SECURE_DDA=1
RUST_LOG=info
# 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
(the same env-merge the WGC helper uses). Restart the service after editing:
punktfunk-host service stop; punktfunk-host service start
The host's identity (cert/pairing/mgmt token/library) also lives under %ProgramData%\punktfunk — a
machine-wide dir the SYSTEM service and the interactive user share, surviving user logout.
PUNKTFUNK_CONFIG_DIR overrides the location (both platforms; handy for tests).
Logs
%ProgramData%\punktfunk\logs\service.log— the service's own supervision log (spawn/exit/session switches).%ProgramData%\punktfunk\logs\host.log— the host child's stdout/stderr.
Prerequisites
- The host built with
--features nvencfor NVENC (the driver shipsnvEncodeAPI64.dll; no SDK needed at runtime). Software encode otherwise. - The SudoVDA indirect display driver installed (for
PUNKTFUNK_VIDEO_SOURCE=virtual). - ViGEmBus for virtual gamepads (optional).
Gotchas
service install/uninstallneed an elevated prompt (the SCM rejects non-admin).service runis the SCM entry point — don't run it by hand (it errors with a hint).- A graceful stop currently
TerminateProcesses the host, so its RAII teardown (SudoVDA monitor REMOVE) doesn't run; a stale virtual monitor can linger until the next start. A cooperative-stop signal is a follow-up.