--- title: Host CLI description: The punktfunk-host commands and the flags you'll actually use — plus punktfunk, the command on the client machine. --- The host is one binary, `punktfunk-host`. Most of the time you'll run a single command; the rest reads its settings from [`host.env`](/docs/configuration). On the machine you stream *to*, there's a second command — [`punktfunk`](#punktfunk-on-the-client-machine), which ships with the client. | Command | What it does | Platform | |---|---|---| | [`serve`](#serve) | Run the host. | all | | [`punktfunk1-host`](#punktfunk1-host) | Standalone native-only test host. | all | | [`service`](#service-windows) | Register, start, stop and remove the Windows service. | Windows | | [`tray`](#tray-windows) | Start, stop or query the status-tray icon. | Windows | | [`driver`](#driver-windows) | Install or remove the bundled virtual-display / virtual-gamepad drivers. | Windows | | [`plugins`](#plugins) | Install, remove and list plugins, and switch the runner on. | all | | [`list-monitors`](#list-monitors) | List the physical monitors, by connector name. | Linux | | `mirror-test` | Prove capture works from one of them — see [`list-monitors`](#list-monitors). | Linux | | [`hdr-probe`](#hdr-probe-and-probe-compositor) | Report whether this box can deliver a 10-bit HDR stream, and what's missing. | Linux | | [`hdr-p010-selftest`](#hdr-probe-and-probe-compositor) | Check the GPU's HDR capture colour conversion, with no display or session. | Windows | | [`probe-compositor`](#hdr-probe-and-probe-compositor) | Exit 0 when the compositor is up and can create a virtual output. | Linux | | [`detect-conflicts`](#detect-conflicts) | Report other Moonlight-compatible hosts on this machine. | all | | `library` | Print [the resolved game library](/docs/game-library) as JSON — "does the host see my games?". | all | | `openapi` | Print the management API's OpenAPI document. | all | | `--version` | Print the host version. | all | `punktfunk-host --help` prints the most-used of these. `plugins`, `service`, `driver` and `tray` print their own usage when you run them with no arguments. ## `serve` The normal way to run a host. By default `serve` starts the **secure native host**: the native `punktfunk/1` server (QUIC, SPAKE2 PIN pairing, per-direction AEAD) plus the management API/web console — all in one process. The native plane is **always on**; there is no flag to turn it off. ```sh punktfunk-host serve ``` Add `--gamestream` (alias `--moonlight`) to **also** run the GameStream/Moonlight-compatible planes (nvhttp pairing, RTSP, ENet control, `_nvstream` mDNS) — required for stock [Moonlight](/docs/moonlight) clients. This is **opt-in** because GameStream carries inherent on-path weaknesses (pairing over plain HTTP; its legacy control encryption can reuse GCM nonces), so enable it **only on a trusted LAN**. The native plane is immune to those issues. ```sh punktfunk-host serve --gamestream ``` | Flag | Meaning | |---|---| | `--gamestream` / `--moonlight` | Also run the GameStream/Moonlight-compat planes (for stock Moonlight clients). Opt-in, trusted-LAN only — see above. | | `--native` | No-op. The native `punktfunk/1` server always runs in `serve`; kept only for backward compatibility. | | `--native-port ` | Native QUIC port (default `9777`). | | `--open` | Don't require pairing — serve any device on the network. Off by default; only for trusted single-user setups. | | `--mgmt-bind ` | Management API address (default `0.0.0.0:47990` — all interfaces, so paired clients can browse the game library over mTLS; pass `127.0.0.1:47990` to keep it loopback-only). | | `--mgmt-token ` | Override the bearer token for the management API. | | `--no-mdns` | Skip the mDNS adverts (native + GameStream) — for networks/containers where multicast doesn't work. Clients connect via a manually added host instead. Same as `PUNKTFUNK_MDNS=0`. | | `--data-port ` | Pin the per-session video data plane to this fixed UDP port and stream direct (no hole-punch) — open exactly that port in the host firewall. Same as `PUNKTFUNK_DATA_PORT`; default is a random port + hole-punch. | These are the only flags `serve` accepts. The management API is **always HTTPS**. It binds all interfaces by default so a **paired client** can fetch the game library over its mTLS certificate — but off loopback that certificate reaches only the read-only status + library endpoints. The **admin surface** (arming pairing, removing devices, session control, library edits) authenticates with a **bearer token** and is honored **from loopback only**, so it is never LAN-exposed even under the default wide bind. If you don't pass `--mgmt-token`, a token is auto-generated and persisted to `~/.config/punktfunk/mgmt-token` (the bundled web console reads the same file); `--mgmt-token` only overrides it. Pass `--mgmt-bind 127.0.0.1:47990` to keep 47990 loopback-only. Every endpoint is documented in the interactive [**API Reference**](/api). By default the host **requires pairing** — see [Pairing & Trust](/docs/pairing). On `serve` you **arm pairing from the web console** (or mgmt API); the host then displays a 4-digit PIN. Pass `--open` to turn off the mandatory-pairing default and serve any device on the network (trusted single-user setups only). `punktfunk1-host` (below) requires pairing by default too; its `--allow-tofu` flag is the test-host equivalent of `--open`. ## `punktfunk1-host` A standalone native-only host, mainly for testing the `punktfunk/1` path without the GameStream server or web console. ```sh punktfunk-host punktfunk1-host --source virtual ``` | Flag | Meaning | |---|---| | `--port ` | QUIC listen port (default `9777`). | | `--source synthetic` · `virtual` | `virtual` uses a real virtual display + NVENC; `synthetic` emits test frames. | | `--seconds ` / `--frames ` | Bound each session by wall-clock seconds or frame count. | | `--max-concurrent ` | Stream at most N sessions at once (default 4); overflow waits in the queue. | | `--max-sessions ` | Exit after N sessions (0 = serve forever). | | `--allow-tofu` | Also accept **unpaired** clients (trust-on-first-use) and advertise pairing as optional. Pairing is required by default; trusted LANs only. (`--allow-pairing`/`--require-pairing` are the old names for the default behaviour and are accepted as no-ops.) | | `--pairing-pin ` | Use a fixed pairing PIN instead of a fresh random one per ceremony. For test harnesses/CI only — a guessable PIN defeats the ceremony's rate limit. | | `--data-port ` | Pin the video data plane to this fixed UDP port and stream direct (no hole-punch). Same as `PUNKTFUNK_DATA_PORT`. | | `--idle-timeout-ms ` | Disconnect-detection latency — the QUIC control-connection idle timeout (default 8000). | | `--no-mdns` | Skip the `_punktfunk._udp` advert; clients use `--connect HOST:PORT`. Same as `PUNKTFUNK_MDNS=0`. | `--max-concurrent` and `--allow-tofu` are **`punktfunk1-host`-only** — `serve` does not accept them. On `serve` you arm pairing from the web console instead (`--open` is its serve-any-device switch), and concurrency is fixed at the built-in default (4 sessions) rather than settable from the command line. Both `serve` and `punktfunk1-host` advertise the host on the network so clients can discover it. The graphical client browses the LAN for you, so it needs no command; from a terminal on the client machine, [`punktfunk hosts list --probe`](/docs/clients#scripting-the-punktfunk-cli) re-checks the hosts you have already saved by asking each one directly — which is how you confirm a routed or VPN host that mDNS never reaches. (`punktfunk-probe --discover` also browses the LAN, but it is a developer tool built from the repo, `cargo run -p punktfunk-probe -- --discover`, and no package installs it.) Where multicast doesn't work (some Docker/VLAN setups), pass `--no-mdns` (or set `PUNKTFUNK_MDNS=0`) and add the host in the client by address instead. ## `service` (Windows) The Windows lifecycle surface. The installer runs `service install` for you, so you only need these when you change something or when the service needs a nudge. Run them from an **Administrator** prompt. ```powershell punktfunk-host service install [--gamestream=on|off] [--allow-public-network] punktfunk-host service uninstall punktfunk-host service start | stop | restart | status ``` | Subcommand | What it does | |---|---| | `install` | Registers the auto-start `PunktfunkHost` service, adds the firewall rules, and writes a default `%ProgramData%\punktfunk\host.env` if there isn't one. Safe to re-run: it's also how you change the two options below. | | `--gamestream=on\|off` | Sets `PUNKTFUNK_HOST_CMD` in `host.env` — `on` adds the GameStream/Moonlight planes, `off` goes back to the native-only host. A command line you edited by hand is left alone. | | `--allow-public-network` | Also opens the ports on networks Windows classifies **Public**. By default only Private and Domain are opened. | | `uninstall` | Stops and deletes the service and removes its firewall rules. It does **not** remove the host itself — see [Uninstalling](/docs/uninstall) for that. | | `start` / `stop` / `restart` | Service control. `restart` waits for the old process to exit first — this is what picks up a `host.env` edit. | | `status` | Queries the service (the same thing `sc query PunktfunkHost` prints). | See [Running as a Service](/docs/running-as-a-service) and [Windows Host](/docs/windows-host). ## `tray` (Windows) The status tray is a per-user program started at sign-in, so an update or a crash otherwise leaves you without the icon until the next logon. This is how you get it back without signing out: ```powershell punktfunk-host tray start punktfunk-host tray status punktfunk-host tray stop ``` `start` reports the process it started, or says the tray is already running; `status` says whether the tray is installed at all (it's an optional component at install time) and whether it's running. ## `driver` (Windows) The installer installs and removes the bundled drivers, so you rarely touch this. It exists for removing one without uninstalling the host: ```powershell punktfunk-host driver uninstall # the pf-vdisplay virtual display driver punktfunk-host driver uninstall --gamepad # the virtual-gamepad driver instead ``` `driver install --dir [--gamepad]` is the install half; it takes the staged driver files the installer lays down, which is why it isn't something you run by hand. ## `plugins` `punktfunk-host plugins add|remove|list|enable|disable|status` installs plugins and switches the plugin/scripting runner on — the same thing the web console's **Plugins** page does. Plugins run with the host's privileges, so read [Plugins](/docs/plugins) before installing one. ## `list-monitors` `punktfunk-host list-monitors` prints the **physical** monitors this host's compositor has, by connector name — which is how you name one for [Streamed screen](/docs/virtual-displays#stream-a-real-monitor-instead) (in the console, or as `PUNKTFUNK_CAPTURE_MONITOR`). ```sh punktfunk-host list-monitors ``` ``` Kwin: HDMI-A-1 1920x1080@60 at +0,+0 scale 1 Dell U2412M [primary] DP-2 2560x1440@144 at +1920,+0 scale 1 ACME 27 [PINNED] ``` Tags flag what's worth knowing before you pick: `primary`, `disabled` (nothing to stream), `punktfunk virtual display` (one of ours, not a real head), and `PINNED` for the one currently selected. Linux only — it reads the live compositor, so run it in (or with the environment of) the session you want to stream. `punktfunk-host mirror-test --monitor [--seconds N] [--cpu]` then proves the whole path — mirror, capture, frames — with no client involved. It reports the first frame, the frame count and the negotiated size. Screen recording is damage-driven, so move the mouse on the host while it runs; an idle desktop legitimately yields almost nothing. ## `hdr-probe` and `probe-compositor` Two Linux readiness checks that need no client and no session of their own. ```sh punktfunk-host hdr-probe punktfunk-host probe-compositor ``` `hdr-probe` answers "why isn't my stream HDR?" — it reports, for both Linux HDR routes, whether the box can deliver 10-bit PQ right now: is a monitor in HDR colour mode (the GNOME monitor-mirror route), is the resolved gamescope the `punktfunk-gamescope` build with the knob on, and does the encoder probe Main10 for HEVC/AV1. Run it with the same environment the host service has, or the answers describe your shell rather than the host — [HDR → Check it](/docs/hdr#check-it) has that one-liner and reads the output line by line. See [HDR on gamescope](/docs/gamescope#hdr-on-gamescope) for the gamescope half. `probe-compositor` exits **0** only when the compositor is up and can create a virtual output now — what a session-bringup script should gate on instead of a blind `sleep`. There is no `hdr-probe` on Windows. The Windows equivalent is a GPU colour self-test of the HDR capture conversion — it needs no display and no session, and prints PASS or FAIL with the largest error it saw: ```powershell punktfunk-host hdr-p010-selftest 1920x1080 nvidia ``` Both arguments are optional: your real capture size (heights like 1080 aren't 16-aligned and take a different driver path — the default is a token `64x64`) and, on a dual-GPU box, the vendor that encodes: `intel`, `nvidia` or `amd`. ## `detect-conflicts` `punktfunk-host detect-conflicts` reports other Moonlight-compatible hosts (Sunshine, Apollo, and forks) installed or running on this machine. Running one alongside Punktfunk is **unsupported** — they fight over the same ports and virtual-display driver. Prints what it found and exits **1** if any conflict exists, **0** if clean (so installers and scripts can gate on it). The host also runs this check at `serve` startup and reports it in the logs and in the management API's status summary; on Windows the installer warns you before it installs. (The tray stays quiet about it on purpose — an installed-but-idle Sunshine isn't a conflict until it runs.) See [Troubleshooting → another streaming host is installed](/docs/troubleshooting#another-streaming-host-sunshine-apollo--is-installed). ## `punktfunk` on the client machine The client half has its own command, `punktfunk` — the same core the graphical apps use, with no window, so a script gets what a click gets, including waking a sleeping host and waiting for it. Its verbs, where it ships, the `` grammar and the stable exit codes are on [Clients → the `punktfunk` CLI](/docs/clients#scripting-the-punktfunk-cli); `punktfunk help ` prints one verb's flags. `punktfunk wake` has its own exit codes, on [Wake on LAN → From the command line](/docs/wake-on-lan#from-the-command-line). ## Environment Most behaviour (compositor, video source, input backend, zero-copy) is set in [`host.env`](/docs/configuration), not on the command line. When running as a [service](/docs/running-as-a-service), the unit loads `host.env` for you.