Files
punktfunk/docs/windows-service.md
T
enricobuehler 54b75c9be4
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
feat(host): GameStream/Moonlight compat is now opt-in (--gamestream) — secure native-only by default
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>
2026-06-21 10:19:40 +00:00

111 lines
5.0 KiB
Markdown

# 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`](../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:
```powershell
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:
```powershell
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:
1. resolves the active console session (`WTSGetActiveConsoleSessionId`),
2. duplicates its own LocalSystem token and retargets it to that session (`SetTokenInformation`
`TokenSessionId`),
3. launches the host there with `CreateProcessAsUserW` (`lpDesktop = winsta0\default`),
4. 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`](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`](../scripts/windows/host.env.example).
```ini
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:
```powershell
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 nvenc` for NVENC (the driver ships `nvEncodeAPI64.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`/`uninstall` need an **elevated** prompt (the SCM rejects non-admin).
- `service run` is the SCM entry point — don't run it by hand (it errors with a hint).
- A **graceful** stop currently `TerminateProcess`es 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.