ci / rust (push) Failing after 2m31s
ci / docs-site (push) Successful in 1m22s
ci / web (push) Successful in 1m48s
docker / builders (--build-arg FEDORA_VERSION=44, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm, -f44) (push) Successful in 1m1s
ci / rust-arm64 (push) Successful in 2m2s
docker / builders (ci/android-ci.Dockerfile, punktfunk-android-ci) (push) Successful in 11s
docker / builders (ci/arch-ci.Dockerfile, punktfunk-arch-ci) (push) Successful in 9s
docker / builders (ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / builders (ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 9s
docker / builders (ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 9s
docker / builders-arm64cross (push) Successful in 20s
docker / apps (., web/Dockerfile, punktfunk-web) (push) Canceled after 36s
docker / apps (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 36s
docker / deploy-docs (push) Canceled after 0s
~1150 feat/fix commits landed since v0.19 and the docs drifted badly. This is a full sweep of every page against the code as shipped: ~280 verified corrections, nine new pages, and one deletion. The worst of what was wrong: the quickstart's five-minute path could not work (`serve` never started the web console, so step 3 had no PIN to read); every packaged Linux host runs `serve --gamestream` while security.md told readers to leave GameStream off; HDR was documented as Windows-only; `PUNKTFUNK_SECURE_DDA` was documented as a working knob that nothing reads; `PUNKTFUNK_INPUT_BACKEND` listed a `uinput` value that does not exist and named libei for KDE instead of kwin; README linked three pages deleted on 2026-07-05; and the rpm-ostree update command pointed at a script no package installs. Completeness: about half of what shipped since v0.19 had no page at all. New: support-matrix (what works where, from 217 verified capability cells), input (mouse/touch/pen — and the in-stream chords, so the docs finally say how to get your mouse back), client-settings, profiles-and-links, game-library, clipboard, wake-on-lan, hdr, uninstall. Updating existed but had zero inbound links. status.md is gone: its facts moved into the support matrix, its shell stays as a redirect so the public URL does not 404. roadmap.md is themes now, not a feature checklist — checkboxes are what rotted. Debian is no longer claimed. The .deb's Depends resolve against Ubuntu images, nothing in CI builds or tests Debian, and Debian 12 is below the glibc 2.39 floor. The `debian` in the repo URL is the package format. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
257 lines
15 KiB
Markdown
257 lines
15 KiB
Markdown
---
|
|
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 <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 <IP:PORT>` | 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 <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 <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 <N>` | QUIC listen port (default `9777`). |
|
|
| `--source synthetic` · `virtual` | `virtual` uses a real virtual display + NVENC; `synthetic` emits test frames. |
|
|
| `--seconds <N>` / `--frames <N>` | Bound each session by wall-clock seconds or frame count. |
|
|
| `--max-concurrent <N>` | Stream at most N sessions at once (default 4); overflow waits in the queue. |
|
|
| `--max-sessions <N>` | 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 <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 <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 <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 <stage> [--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 <CONNECTOR> [--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 `<host-ref>` grammar and the stable exit codes are on [Clients → the
|
|
`punktfunk` CLI](/docs/clients#scripting-the-punktfunk-cli); `punktfunk help <command>` 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.
|