docs(dist): end-user install front door + serve/pairing/firewall accuracy fixes

Make the host docs match the real distribution path and the actual CLI. Reviewed by a
multi-agent pass (6 editors against one verified fact sheet + an accuracy reviewer); its
findings (a wrong client-Recommends claim, a native-concurrency overstatement) folded in.

- Install front door: new README "Install (host)" method-picker + docs-site/install.md
  (+ nav), routing each distro to its package registry; source build demoted to a fallback.
- Registry-first install: ubuntu-gnome/ubuntu-kde now lead with the apt registry (not a
  cargo build); bazzite leads with the Gitea RPM registry (was COPR/source). Source builds
  moved to an appendix.
- CLI accuracy: serve --native arms pairing from the web console (NOT --allow-pairing, which
  with --require-pairing/--max-concurrent is m3-host-only); --open disables mandatory pairing.
  host-cli/configuration/pairing/quickstart/troubleshooting corrected; mgmt API documented as
  always HTTPS+token. Native host serves one session at a time (extras queue) — not multi.
- Firewall: real ports documented (native UDP 9777 + the ephemeral data port caveat +
  GameStream ports) for Debian + Arch (ufw + nftables), not just Bazzite.
- Sync/accuracy: punktfunk-client (GTK4) presented as a shipping client (not "roadmap"),
  punktfunk-client-rs as the headless tool; host Recommends punktfunk-web only (not the
  client); COPR chroots f43/44; bootc header says Gitea registry not COPR.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 10:43:12 +00:00
parent 5b3d5689bf
commit 9e015304ee
17 changed files with 438 additions and 96 deletions
+20 -1
View File
@@ -31,6 +31,23 @@ roadmap, setup guides & progress: the docs site ([`docs-site/`](docs-site) — F
`bun run dev`), with the canonical [roadmap](docs-site/content/docs/roadmap.md) and `bun run dev`), with the canonical [roadmap](docs-site/content/docs/roadmap.md) and
[status](docs-site/content/docs/status.md) there. Design notes stay in [`docs/`](docs). [status](docs-site/content/docs/status.md) there. Design notes stay in [`docs/`](docs).
## Install (host)
The package registries are the real distribution channel — pick your distro and run one command.
Per-distro setup (add the repo, first-run, web console) lives in the linked READMEs.
| Distro | One-command happy path | Details |
|--------|------------------------|---------|
| **Ubuntu / Debian** (apt) | `sudo apt install punktfunk-host` *(after adding the repo)* | [`packaging/debian/README.md`](packaging/debian/README.md) |
| **Fedora / Bazzite** (rpm-ostree) | `rpm-ostree install punktfunk punktfunk-web` *(after adding the repo; or the bootc image)* | [`packaging/rpm/README.md`](packaging/rpm/README.md) |
| **Arch / Steam Deck** (PKGBUILD / sysext) | `makepkg -si` *(Arch)* · sysext `.raw` *(SteamOS/Deck)* | [`packaging/arch/README.md`](packaging/arch/README.md) |
`punktfunk-host` is the streaming host; `punktfunk-web` is the browser console (pairing + status);
`punktfunk-client` is the GTK4 desktop client (also shipped via apt/RPM/Arch/Flatpak). After install,
run `punktfunk-host serve --native` inside your desktop session, then pair from the web console.
Building from source (below) is a fallback.
## Layout ## Layout
``` ```
@@ -46,7 +63,9 @@ tools/{latency-probe,loss-harness}/ measurement (plan §10)
docs/{implementation-plan,roadmap,windows-host,dualsense-haptics}.md docs/{implementation-plan,roadmap,windows-host,dualsense-haptics}.md
``` ```
## Build & test ## Build & test (from source)
For development, or as an install fallback where no package is available:
```sh ```sh
cargo build --workspace # green on Linux and macOS cargo build --workspace # green on Linux and macOS
+47 -5
View File
@@ -13,10 +13,37 @@ the mode of the device you're streaming to, not the TV the box is plugged into.
## Install ## Install
The host installs from the punktfunk COPR repository (see `packaging/bazzite/` in the repo for the The host ships as an RPM in punktfunk's **Gitea RPM registry** (public), so a Bazzite / Fedora
exact COPR/RPM/bootc options). You can also build from source as on Atomic box layers and updates it with `rpm-ostree`. Add the repo, then layer the host plus the web
[Fedora KDE](/docs/fedora-kde) — Bazzite is Fedora Atomic underneath, and its FFmpeg builds the host console and reboot:
fine.
```sh
# Add the repo. Our RPMs are unsigned, but Gitea GPG-signs the repo METADATA — verify that
# (repo_gpgcheck=1) and skip the per-package signature check (gpgcheck=0). The signed metadata
# carries each package's SHA256, so authenticity still holds. (Don't curl Gitea's served
# bazzite.repo verbatim — it sets gpgcheck=1, which fails on unsigned packages.)
sudo tee /etc/yum.repos.d/punktfunk.repo >/dev/null <<'REPO'
[gitea-unom-bazzite]
name=punktfunk (unom, Bazzite)
baseurl=https://git.unom.io/api/packages/unom/rpm/bazzite
enabled=1
gpgcheck=0
repo_gpgcheck=1
gpgkey=https://git.unom.io/api/packages/unom/rpm/repository.key
REPO
# Layer the host + the web console, then reboot into the new deployment.
# (punktfunk Recommends punktfunk-web; list it explicitly so it's pulled regardless of weak-dep
# settings — the Gitea registry carries punktfunk-web, which COPR can't build.)
rpm-ostree install punktfunk punktfunk-web
systemctl reboot
```
`rpm-ostree upgrade` then tracks new builds automatically (Bazzite's auto-update timer does this
for you). For a fully baked appliance image there's also a **bootc** Containerfile that installs
the same RPMs from this registry — see `packaging/bootc/` and `packaging/rpm/README.md` in the repo.
Building from source works too (Bazzite is Fedora Atomic underneath, and its FFmpeg builds the host
fine — same steps as [Fedora KDE](/docs/fedora-kde)), but the registry is the supported path.
## Allow controller input ## Allow controller input
@@ -32,7 +59,14 @@ permission, not a client problem.)
## Configure ## Configure
Point the host at the gamescope backend in `~/.config/punktfunk/host.env`: The RPM ships a gamescope-ready config you can copy as your starting point:
```sh
mkdir -p ~/.config/punktfunk
cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env
```
The key settings in `~/.config/punktfunk/host.env` point the host at the gamescope backend:
```sh ```sh
PUNKTFUNK_COMPOSITOR=gamescope PUNKTFUNK_COMPOSITOR=gamescope
@@ -51,6 +85,14 @@ Bazzite hosts are typically headless. Enable the host service and linger so it s
[Running as a Service](/docs/running-as-a-service). Because the host launches its own gamescope [Running as a Service](/docs/running-as-a-service). Because the host launches its own gamescope
session per client, you don't need a separate desktop-session unit. session per client, you don't need a separate desktop-session unit.
```sh
systemctl --user enable --now punktfunk-host
# Web console (pairing + status) — enable it and read the auto-generated login password,
# then open http://<host-ip>:3000:
systemctl --user enable --now punktfunk-web
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p'
```
## Good to know ## Good to know
- **gamescope 3.16.22 or newer is required.** Older versions can deadlock during capture. Bazzite's - **gamescope 3.16.22 or newer is required.** Older versions can deadlock during capture. Bazzite's
+35 -9
View File
@@ -3,7 +3,8 @@ title: Clients
description: The ways to connect to a punktfunk host — the Apple app, Moonlight, or the Linux client. description: The ways to connect to a punktfunk host — the Apple app, Moonlight, or the Linux client.
--- ---
A punktfunk host accepts two kinds of client. Pick whichever fits the device you're streaming *to*. A punktfunk host accepts clients over its own `punktfunk/1` protocol (the Apple and Linux apps) and
over GameStream (Moonlight). Pick whichever fits the device you're streaming *to*.
## Apple app (Mac, iPhone, iPad, Apple TV) ## Apple app (Mac, iPhone, iPad, Apple TV)
@@ -28,24 +29,49 @@ software. See [Connect with Moonlight](/docs/moonlight).
This is the broadest-compatibility option and great for couch gaming. It doesn't use the native This is the broadest-compatibility option and great for couch gaming. It doesn't use the native
protocol's FEC/encryption extensions, but for a healthy LAN that rarely matters. protocol's FEC/encryption extensions, but for a healthy LAN that rarely matters.
## Linux reference client ## Linux desktop client (GTK4)
`punktfunk-client-rs` (in the repo) is a command-line client for the native protocol, mainly for `punktfunk-client` is the native graphical Linux client — a GTK4 / libadwaita app that speaks
testing and development. It connects, streams to a file, runs the speed test, and can discover hosts: `punktfunk/1` directly, with hardware decode (VAAPI → dmabuf on Intel/AMD, software fallback),
PipeWire audio, and SDL3 controllers (rumble, lightbar, DualSense touchpad/motion). Like the Apple
app it discovers hosts on your network automatically, does PIN pairing, and pins reconnects.
It ships as a real package, not just a source build:
- **Ubuntu / Debian** — `apt install punktfunk-client` from the punktfunk apt registry
(see `packaging/debian/README.md`).
- **Fedora / Bazzite** — `rpm-ostree install punktfunk-client` from the Gitea RPM registry
(see `packaging/rpm/README.md`).
- **Arch / SteamOS** — the `punktfunk-client` split package from the `PKGBUILD`
(see `packaging/arch/README.md`).
- **Steam Deck / any Flatpak distro** — the `io.unom.Punktfunk` Flatpak bundle
(see `packaging/flatpak/README.md`); this is what the Decky plugin launches.
Launch it, pick your host from the list, and stream. For scripting you can skip the host list and
connect straight away:
```sh ```sh
punktfunk-client-rs --discover # list hosts on the network punktfunk-client --connect <host>:9777 # skip the picker, start a session immediately
punktfunk-client-rs --connect <host>:9777 --pin <fp> # connect to one
``` ```
A full graphical Linux client (hardware decode + present) is on the [roadmap](/docs/roadmap). ## Linux reference client (headless)
`punktfunk-client-rs` (in the repo) is a command-line client for the native protocol, used for
testing, development, and latency measurement — not an everyday client. It connects, streams to a
file, runs the speed test, and can discover hosts:
```sh
punktfunk-client-rs --discover # list hosts on the network
punktfunk-client-rs --connect <host>:9777 --pin <fp> # connect to one
```
## Which should I use? ## Which should I use?
| You're streaming to… | Use | | You're streaming to… | Use |
|---|---| |---|---|
| A Mac, iPhone, iPad, or Apple TV | The **Apple app** | | A Mac, iPhone, iPad, or Apple TV | The **Apple app** |
| Windows, Android, Steam Deck, a browser, a TV | **Moonlight** | | A Linux desktop or laptop, or a Steam Deck | **`punktfunk-client`** (GTK4) |
| Another Linux box (testing) | **`punktfunk-client-rs`** | | Windows, Android, a browser, a TV | **Moonlight** |
| Automated tests / latency measurement | **`punktfunk-client-rs`** (headless) |
Whichever you choose, the first connection needs a one-time [pairing](/docs/pairing). Whichever you choose, the first connection needs a one-time [pairing](/docs/pairing).
+6 -5
View File
@@ -43,12 +43,13 @@ The client requests a bitrate; the host encodes to it. To find a good value for
## Multiple devices at once ## Multiple devices at once
A host can stream to several clients simultaneously — each gets its own virtual display at its own Today the native `punktfunk/1` host (`serve --native`) streams **one session at a time** — additional
resolution. This is the natural way to put your desktop on a laptop *and* a TV at the same time (both clients wait in the accept queue until the active session ends. Each session gets its own virtual
see and control the same desktop). display at the client's exact resolution; concurrent native sessions are on the roadmap.
The number of simultaneous streams is bounded by your GPU's encoder. Cap it with (`m3-host`, the standalone test host, has a `--max-concurrent N` knob, default 4, bounded by your
`--max-concurrent N` on the host command line (default 4); extra clients wait until a slot frees. GPU's encoder — see the [Host CLI](/docs/host-cli) reference — but `serve --native` does **not** take
that flag.)
## Codec and FEC ## Codec and FEC
+4 -3
View File
@@ -113,9 +113,10 @@ mDNS. It requires **PIN pairing** by default (secure on a LAN); pair once from y
## 4. Connect a client ## 4. Connect a client
From any [client](/docs/clients) — `punktfunk-client --discover` finds the host on the LAN. On From any [client](/docs/clients) — `punktfunk-client --discover` finds the host on the LAN. On
first connect, complete the PIN pairing (arm it from the host's web console / mgmt API, or run the first connect, complete the PIN pairing **arm it from the host's web console / mgmt API**, which
host with `--allow-pairing`). See [Clients](/docs/clients) and makes the host display a 4-digit PIN to type into the client. (Pairing is required by default; pass
[Running as a Service](/docs/running-as-a-service). `serve --native --open` only if you deliberately want to disable the requirement.) See
[Clients](/docs/clients) and [Running as a Service](/docs/running-as-a-service).
## Appendix — build from source ## Appendix — build from source
+17 -4
View File
@@ -21,10 +21,18 @@ punktfunk-host serve --native
| `--native-port <PORT>` | Native QUIC port (default `9777`). | | `--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. | | `--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 loopback `127.0.0.1:47990`). | | `--mgmt-bind <IP:PORT>` | Management API address (default loopback `127.0.0.1:47990`). |
| `--mgmt-token <TOKEN>` | Bearer token for the management API; required when `--mgmt-bind` isn't loopback. | | `--mgmt-token <TOKEN>` | Override the bearer token for the management API. |
By default the host **requires pairing** — see [Pairing & Trust](/docs/pairing). Arm pairing from the These are the only flags `serve` accepts.
web console (or the `m3-host` flags below for a quick test).
The management API is **always HTTPS with bearer-token auth**. If you don't pass `--mgmt-token`, a token
is auto-generated and persisted to `~/.config/punktfunk/mgmt-token`; `--mgmt-token` only overrides it. A
token is **required** when you bind the API off loopback with `--mgmt-bind`.
By default the host **requires pairing** — see [Pairing & Trust](/docs/pairing). On `serve --native` 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). The pairing flags below are `m3-host`-only and do **not** apply to `serve`.
## `m3-host` ## `m3-host`
@@ -38,12 +46,17 @@ punktfunk-host m3-host --source virtual
| Flag | Meaning | | Flag | Meaning |
|---|---| |---|---|
| `--port <N>` | QUIC listen port (default `9777`). | | `--port <N>` | QUIC listen port (default `9777`). |
| `--source virtual` | Use a real virtual display + NVENC (vs. `synthetic` test frames). | | `--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-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). | | `--max-sessions <N>` | Exit after N sessions (0 = serve forever). |
| `--allow-pairing` | Accept PIN pairing; the host prints a PIN when a client pairs. | | `--allow-pairing` | Accept PIN pairing; the host prints a PIN when a client pairs. |
| `--require-pairing` | Only serve paired devices (implies `--allow-pairing`). | | `--require-pairing` | Only serve paired devices (implies `--allow-pairing`). |
`--max-concurrent`, `--allow-pairing`, and `--require-pairing` are **`m3-host`-only** — `serve` does not
accept them. On `serve --native` you arm pairing from the web console instead, and concurrency is not
yet capped from the command line.
Both `serve --native` and `m3-host` advertise the host on the network so clients can discover it. List Both `serve --native` and `m3-host` advertise the host on the network so clients can discover it. List
hosts from another machine with `punktfunk-client-rs --discover`. hosts from another machine with `punktfunk-client-rs --discover`.
+54
View File
@@ -0,0 +1,54 @@
---
title: Install the Host
description: Pick your distro and install the punktfunk host from its package registry.
---
The package registries are the real distribution channel. Pick your distro, add the repo, and install
with your native package manager. Each row links to the full per-distro guide (add the repo, first-run
steps, the web console) — those are the source of truth, so this page doesn't duplicate them.
## Pick your distro
| Distro | Package manager | One-command happy path | Guide |
|--------|-----------------|------------------------|-------|
| **Ubuntu / Debian** | apt | `sudo apt install punktfunk-host` | [Ubuntu — GNOME](/docs/ubuntu-gnome) · [Ubuntu — KDE](/docs/ubuntu-kde) · [packaging/debian](https://git.unom.io/unom/punktfunk/src/branch/main/packaging/debian/README.md) |
| **Fedora / Bazzite** | rpm-ostree | `rpm-ostree install punktfunk punktfunk-web` | [Fedora — KDE](/docs/fedora-kde) · [Bazzite](/docs/bazzite) · [packaging/rpm](https://git.unom.io/unom/punktfunk/src/branch/main/packaging/rpm/README.md) |
| **Arch / Steam Deck** | PKGBUILD / sysext | `makepkg -si` (Arch) · sysext `.raw` (SteamOS/Deck) | [packaging/arch](https://git.unom.io/unom/punktfunk/src/branch/main/packaging/arch/README.md) |
Each registry is public — no auth, you just trust the repo's signing key. Adding the repo is a
one-time step covered in the linked guide; after that, normal `apt upgrade` / `rpm-ostree upgrade`
tracks new builds automatically.
## What the packages are
- **`punktfunk-host`** — the streaming host. Install this on your Linux + NVIDIA gaming machine.
- **`punktfunk-web`** — the browser management console (pairing + status). Recommended alongside the
host; on RPM list it explicitly (`rpm-ostree install punktfunk punktfunk-web`).
- **`punktfunk-client`** — the GTK4 desktop client, for streaming *to* a Linux box (also shipped via
apt / RPM / Arch / Flatpak). On a Steam Deck, this is the package you want.
## After installing
1. Add yourself to the `input` group (virtual gamepads need `/dev/uinput`), then re-login. The exact
command differs per distro — see your guide (`usermod -aG input "$USER"`, or `ujust
add-user-to-input-group` on Bazzite).
2. Start the host inside your desktop session:
```sh
punktfunk-host serve --native
```
3. Enable the web console and read its login password, then open `http://<host-ip>:3000`:
```sh
systemctl --user enable --now punktfunk-web
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p'
```
From there, follow the [Quick Start](/docs/quickstart) to pair your first client. To run the host
automatically at boot, see [Running as a Service](/docs/running-as-a-service).
## Building from source
If no package exists for your platform, you can build from source — see the repository README. Source
builds are a fallback; the registries are the supported path.
+1
View File
@@ -4,6 +4,7 @@
"index", "index",
"how-it-works", "how-it-works",
"quickstart", "quickstart",
"install",
"---Host Setup---", "---Host Setup---",
"requirements", "requirements",
"ubuntu-gnome", "ubuntu-gnome",
+8 -8
View File
@@ -38,13 +38,13 @@ The PIN ceremony is the other path — useful for the *first* device (before the
anything) or when you're at the client and the console isn't handy. anything) or when you're at the client and the console isn't handy.
Pairing has to be **armed** on the host before a client can pair (so a random device can't pair Pairing has to be **armed** on the host before a client can pair (so a random device can't pair
itself). Two ways: itself). On the production host (`serve --native`), this is done from the **web console**: open the
host's management console, click to arm pairing, and the host displays a 4-digit PIN along with the
list of paired devices. This works on a headless host over the network — there is no command-line flag
to arm pairing on `serve`.
- **Web console** *(recommended)* — open the host's management console, click to arm pairing, and it (The standalone headless test host, `m3-host`, takes `--allow-pairing`/`--require-pairing` on its
shows the PIN and the list of paired devices. This is the easiest way and works on a headless host command line instead; the production `serve --native` host arms pairing from the console.)
over the network.
- **Command line** — start the host with `--allow-pairing` (or `--require-pairing`); it prints a PIN
in its log when a client begins pairing.
Then, on the client: Then, on the client:
@@ -57,8 +57,8 @@ By default, the native host **requires** pairing — only devices that have pair
the right setting on a shared network: a device has to complete the PIN ceremony once before it can the right setting on a shared network: a device has to complete the PIN ceremony once before it can
connect. connect.
If you're on a fully trusted single-user network and want to skip pairing, the host can be run open — If you're on a fully trusted single-user network and want to skip pairing, start the host with
but requiring pairing is strongly recommended. `serve --native --open`but requiring pairing is strongly recommended.
## Trust-on-first-use ## Trust-on-first-use
+3 -3
View File
@@ -37,9 +37,9 @@ On the device you want to stream to:
network*. Tap it, and when prompted, **pair**. network*. Tap it, and when prompted, **pair**.
- **Anything with Moonlight:** add the host (it should be discovered automatically), then pair. - **Anything with Moonlight:** add the host (it should be discovered automatically), then pair.
To pair, the host needs to show a PIN. Arm pairing from the host's web console (or with To pair, the host needs to show a PIN. Arm pairing from the host's web console — the host displays a
`--allow-pairing` on the command line) — the host displays a 4-digit PIN, you type it into the client, 4-digit PIN, you type it into the client, and they trust each other from then on. Pairing is required
and they trust each other from then on. Full details: [Pairing & Trust](/docs/pairing). by default. Full details: [Pairing & Trust](/docs/pairing).
## 4. Stream ## 4. Stream
+10 -6
View File
@@ -10,8 +10,11 @@ description: Common problems setting up or using a punktfunk host, and how to fi
- Host and client must be on the **same network/subnet**. Discovery uses mDNS, which doesn't cross - Host and client must be on the **same network/subnet**. Discovery uses mDNS, which doesn't cross
routed subnets or most VPNs-without-multicast. As a fallback, add the host by **IP address** in your routed subnets or most VPNs-without-multicast. As a fallback, add the host by **IP address** in your
client. client.
- A firewall on the host can block it. The native protocol uses UDP port **9777** (plus the data - A firewall on the host can block it. The native protocol's control plane uses UDP port **9777**. The
port); GameStream/Moonlight uses its standard ports. Allow them on the host's firewall. per-session **data plane** uses an *ephemeral* UDP port negotiated at connect time (currently
random) — for a strict firewall, open a UDP range or move the data port. GameStream/Moonlight uses
TCP **47984/47989/48010** + UDP **4799848010** + ENet UDP **47999**. Allow them on the host's
firewall.
## `nvidia-smi` says it can't communicate with the driver ## `nvidia-smi` says it can't communicate with the driver
@@ -56,8 +59,8 @@ Then log out and back in. On other distros this is `sudo usermod -aG input $USER
## Pairing is rejected / the client can't connect ## Pairing is rejected / the client can't connect
- The host **requires pairing** by default. Arm pairing (web console, or `--allow-pairing`), then - The host **requires pairing** by default. Arm pairing from the web console, then enter the PIN on
enter the PIN on the client. See [Pairing & Trust](/docs/pairing). the client. See [Pairing & Trust](/docs/pairing).
- If you re-installed the host, its identity changed — re-pair the client. - If you re-installed the host, its identity changed — re-pair the client.
## Stutter, drops, or high latency ## Stutter, drops, or high latency
@@ -65,8 +68,9 @@ Then log out and back in. On other distros this is `sudo usermod -aG input $USER
- Lower the **bitrate**. On a busy or Wi-Fi link, the requested bitrate may be too high — the Apple - Lower the **bitrate**. On a busy or Wi-Fi link, the requested bitrate may be too high — the Apple
app's [speed test](/docs/configuration#bitrate) picks a safe value; with Moonlight, set it manually. app's [speed test](/docs/configuration#bitrate) picks a safe value; with Moonlight, set it manually.
- Prefer a **wired** connection or 5 GHz Wi-Fi between host and client. - Prefer a **wired** connection or 5 GHz Wi-Fi between host and client.
- Streaming to **many devices at once** shares the GPU encoder; cap concurrency with - Streaming to **many devices at once** shares the GPU encoder. The production host
`--max-concurrent`. (`serve --native`) handles one native session at a time, with extra clients queued; heavy load is
usually bitrate-bound, so lower the bitrate first.
## Still stuck? ## Still stuck?
+71 -32
View File
@@ -42,39 +42,35 @@ sudo update-initramfs -u && sudo reboot
> `sudo mokutil --import /var/lib/shim-signed/mok/MOK.der` (set a one-time password), reboot, and > `sudo mokutil --import /var/lib/shim-signed/mok/MOK.der` (set a one-time password), reboot, and
> choose **Enrol MOK** at the blue screen. Or disable Secure Boot in firmware. > choose **Enrol MOK** at the blue screen. Or disable Secure Boot in firmware.
## 2. Dependencies ## 2. Install the host (apt)
Install the build toolchain and runtime libraries: `punktfunk-host` is published as a `.deb` to the public Gitea apt registry, so the box installs and
updates with plain `apt`. The registry is public — no auth needed, just trust its signing key:
```sh ```sh
sudo apt install build-essential pkg-config cmake clang libclang-dev nasm git curl \ sudo install -d -m 0755 /etc/apt/keyrings
pipewire pipewire-pulse wireplumber libpipewire-0.3-dev libspa-0.2-dev \ curl -fsSL https://git.unom.io/api/packages/unom/debian/repository.key \
libwayland-dev wayland-protocols libxkbcommon-dev libopus-dev \ | sudo tee /etc/apt/keyrings/punktfunk.asc >/dev/null
libdrm-dev libgbm-dev libegl-dev libgles-dev mesa-common-dev libva-dev \
ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev libavdevice-dev \ echo "deb [signed-by=/etc/apt/keyrings/punktfunk.asc] https://git.unom.io/api/packages/unom/debian stable main" \
libnvidia-egl-wayland1 libnvidia-egl-gbm1 libei-dev | sudo tee /etc/apt/sources.list.d/punktfunk.list
sudo apt update
sudo apt install punktfunk-host
``` ```
Install Rust if you don't have it: `punktfunk-host` `Recommends` the browser console (`punktfunk-web`), so apt pulls it in by default.
The desktop *client* (`punktfunk-client`) is a separate package for the machine you stream *to* — not
```sh installed on a host. The NVIDIA driver is **not** a dependency — you installed it out of band in
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh step 1. Later updates are just `sudo apt update && sudo apt upgrade`.
```
## 3. Configure
## 3. Build
The package ships the systemd **user** unit, the `/dev/uinput` udev rule, the socket-buffer sysctl
```sh tuning, and an example config. As the desktop user, grant gamepad access and write the GNOME config:
git clone https://git.unom.io/unom/punktfunk.git && cd punktfunk
cargo build --release -p punktfunk-host
```
The host binary is at `target/release/punktfunk-host`.
## 4. Configure
The host reads its settings from `~/.config/punktfunk/host.env`. For GNOME:
```sh ```sh
sudo usermod -aG input "$USER" # /dev/uinput for virtual gamepads (re-login to apply)
mkdir -p ~/.config/punktfunk mkdir -p ~/.config/punktfunk
cat > ~/.config/punktfunk/host.env <<'ENV' cat > ~/.config/punktfunk/host.env <<'ENV'
WAYLAND_DISPLAY=wayland-0 WAYLAND_DISPLAY=wayland-0
@@ -88,18 +84,30 @@ ENV
See the [Configuration reference](/docs/configuration) for every option. See the [Configuration reference](/docs/configuration) for every option.
## 5. Run ## 4. Run
From a terminal **inside your GNOME session** (so the host can reach Mutter): Start the host as a user service from **inside your GNOME session** (so it can reach Mutter):
```sh ```sh
cargo run --release -p punktfunk-host -- serve --native systemctl --user enable --now punktfunk-host
journalctl --user -u punktfunk-host -f # watch it come up + print its fingerprint
``` ```
The host starts listening, prints its fingerprint, and advertises itself on the network. Now The host listens on UDP `9777` (native punktfunk/1) plus the GameStream ports, and advertises itself
[connect a client](/docs/clients). over mDNS. It requires **PIN pairing** by default (secure on a LAN) — arm pairing from the web
console (next step) and pair once from your [client](/docs/clients).
To run it automatically at boot — including on a **headless** machine with no monitor — see ### Web console
The console (status, paired devices, arm pairing) ships as `punktfunk-web`:
```sh
systemctl --user enable --now punktfunk-web
# read the auto-generated login password, then open http://<host-ip>:3000
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p'
```
To run the host automatically at boot — including on a **headless** machine with no monitor — see
[Running as a Service](/docs/running-as-a-service). [Running as a Service](/docs/running-as-a-service).
## Troubleshooting ## Troubleshooting
@@ -111,3 +119,34 @@ To run it automatically at boot — including on a **headless** machine with no
capture. On a headless/always-on host, disable the lock — see capture. On a headless/always-on host, disable the lock — see
[Running as a Service](/docs/running-as-a-service). [Running as a Service](/docs/running-as-a-service).
- More in [Troubleshooting](/docs/troubleshooting). - More in [Troubleshooting](/docs/troubleshooting).
## Appendix — build from source
If the apt registry doesn't have a build for your release, or you want to track `main` directly,
compile the host yourself (no clean updates / no packaged units — you wire those up by hand).
Install the build toolchain and runtime libraries:
```sh
sudo apt install build-essential pkg-config cmake clang libclang-dev nasm git curl \
pipewire pipewire-pulse wireplumber libpipewire-0.3-dev libspa-0.2-dev \
libwayland-dev wayland-protocols libxkbcommon-dev libopus-dev \
libdrm-dev libgbm-dev libegl-dev libgles-dev mesa-common-dev libva-dev \
ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev libavdevice-dev \
libnvidia-egl-wayland1 libnvidia-egl-gbm1 libei-dev
```
Install Rust if you don't have it, then build:
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://git.unom.io/unom/punktfunk.git && cd punktfunk
cargo build --release -p punktfunk-host
```
The host binary lands at `target/release/punktfunk-host`. Write `~/.config/punktfunk/host.env` as in
step 3, then run it inside your GNOME session:
```sh
cargo run --release -p punktfunk-host -- serve --native
```
+64 -14
View File
@@ -8,21 +8,42 @@ create a per-client virtual display. Needs **KWin 6.5.6 or newer**.
> New to this? Skim [Requirements](/docs/requirements) first. > New to this? Skim [Requirements](/docs/requirements) first.
## NVIDIA driver, dependencies, and build ## 1. NVIDIA driver
These steps are identical to the GNOME guide — follow **steps 13** of Identical to the GNOME guide — follow **step 1** of
[Ubuntu — GNOME](/docs/ubuntu-gnome#1-nvidia-driver): [Ubuntu — GNOME](/docs/ubuntu-gnome#1-nvidia-driver): install the NVIDIA driver **and** the
`libnvidia-gl-<version>` userspace, enable `nvidia-drm modeset=1`, reboot, and verify with
`nvidia-smi`.
1. Install the NVIDIA driver **and** the `libnvidia-gl-<version>` userspace; enable `nvidia-drm ## 2. Install the host (apt)
modeset=1`; reboot and verify with `nvidia-smi`.
2. Install the build toolchain and runtime libraries (the same `apt` line).
3. Clone and `cargo build --release -p punktfunk-host`.
## Configure The host is published as a `.deb` to the public Gitea apt registry — install and update with plain
`apt`. Trust the repo's signing key, add the repo, and install:
The host reads `~/.config/punktfunk/host.env`. For KDE Plasma:
```sh ```sh
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL https://git.unom.io/api/packages/unom/debian/repository.key \
| sudo tee /etc/apt/keyrings/punktfunk.asc >/dev/null
echo "deb [signed-by=/etc/apt/keyrings/punktfunk.asc] https://git.unom.io/api/packages/unom/debian stable main" \
| sudo tee /etc/apt/sources.list.d/punktfunk.list
sudo apt update
sudo apt install punktfunk-host
```
This also pulls the web console (`punktfunk-web`) via `Recommends` (the pairing/status UI). The
desktop *client*`punktfunk-client`, for the machine you stream *to* — is a separate package, not
needed on a host. The NVIDIA driver stays out of band (step 1). Updates later are just
`sudo apt update && sudo apt upgrade`.
## 3. Configure
The package ships the systemd **user** unit, the udev rule, and the sysctl tuning. As the desktop
user, grant gamepad access and write the KDE config:
```sh
sudo usermod -aG input "$USER" # /dev/uinput for virtual gamepads (re-login to apply)
mkdir -p ~/.config/punktfunk mkdir -p ~/.config/punktfunk
cat > ~/.config/punktfunk/host.env <<'ENV' cat > ~/.config/punktfunk/host.env <<'ENV'
WAYLAND_DISPLAY=wayland-0 WAYLAND_DISPLAY=wayland-0
@@ -38,15 +59,26 @@ ENV
> virtual-display path is Wayland-only. See the [Configuration reference](/docs/configuration) for > virtual-display path is Wayland-only. See the [Configuration reference](/docs/configuration) for
> every option. > every option.
## Run ## 4. Run
From a terminal **inside your Plasma session**: Start the host as a user service from **inside your Plasma session**:
```sh ```sh
cargo run --release -p punktfunk-host -- serve --native systemctl --user enable --now punktfunk-host
journalctl --user -u punktfunk-host -f # watch it come up + print its fingerprint
``` ```
The host starts listening and advertises itself on the network. Now [connect a client](/docs/clients). The host listens on UDP `9777` (native punktfunk/1) plus the GameStream ports and advertises over
mDNS. It requires **PIN pairing** by default — arm pairing from the web console and pair once from
your [client](/docs/clients).
### Web console
```sh
systemctl --user enable --now punktfunk-web
# read the auto-generated login password, then open http://<host-ip>:3000
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p'
```
To run it at boot — including fully **headless**, with KWin brought up automatically and no login — To run it at boot — including fully **headless**, with KWin brought up automatically and no login —
see [Running as a Service](/docs/running-as-a-service); the headless appliance is built around KDE. see [Running as a Service](/docs/running-as-a-service); the headless appliance is built around KDE.
@@ -56,3 +88,21 @@ see [Running as a Service](/docs/running-as-a-service); the headless appliance i
- **KWin too old:** virtual outputs need KWin **≥ 6.5.6**. Check with `kwin_wayland --version`. - **KWin too old:** virtual outputs need KWin **≥ 6.5.6**. Check with `kwin_wayland --version`.
- **No picture / capture fails:** confirm you're on a Wayland session and the NVIDIA GL userspace is - **No picture / capture fails:** confirm you're on a Wayland session and the NVIDIA GL userspace is
installed (`libnvidia-gl-<version>`). More in [Troubleshooting](/docs/troubleshooting). installed (`libnvidia-gl-<version>`). More in [Troubleshooting](/docs/troubleshooting).
## Appendix — build from source
If the apt registry has no build for your release, compile the host yourself (no clean updates / no
packaged units). Install the build toolchain and runtime libraries — the same `apt` line as the
[GNOME build-from-source appendix](/docs/ubuntu-gnome#appendix--build-from-source) — then:
```sh
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
git clone https://git.unom.io/unom/punktfunk.git && cd punktfunk
cargo build --release -p punktfunk-host
```
Write `~/.config/punktfunk/host.env` as in step 3, then run it inside your Plasma session:
```sh
cargo run --release -p punktfunk-host -- serve --native
```
+40
View File
@@ -95,6 +95,46 @@ launches it (`punktfunk-client --connect host:port`) — gamescope composites it
The client needs no `/dev/uinput` or compositor-spawning rights (it captures input and decodes), The client needs no `/dev/uinput` or compositor-spawning rights (it captures input and decodes),
so it's a much lighter sysext than the host. so it's a much lighter sysext than the host.
## Firewall
If the host box runs a firewall, open the ports it listens on. The **native `punktfunk/1`** plane:
- **QUIC control plane: UDP 9777** (`serve --native --native-port N` to change).
- **Data plane: an *ephemeral* UDP port** — negotiated per session, so there is no fixed port to
open. For a restrictive firewall you'd need to allow a UDP range (the repo does not pin one).
And the **GameStream / Moonlight** ports (fixed):
| Port | Proto | Purpose |
|---|---|---|
| 47984 | TCP | HTTPS nvhttp (paired, mutual-TLS) |
| 47989 | TCP | HTTP nvhttp (`/serverinfo`, `/pair` PIN flow) |
| 48010 | TCP | RTSP handshake |
| 4799848010 | UDP | Video RTP (+ FEC), ENet control (47999), audio (48000) |
| 5353 | UDP | mDNS auto-discovery |
The mgmt API (TCP 47990) binds to loopback by default — leave it closed unless you move it off
loopback with `--mgmt-bind IP:PORT` (which then requires `--mgmt-token`).
With `ufw`:
```sh
sudo ufw allow 9777/udp # punktfunk/1 control plane
sudo ufw allow 47984/tcp && sudo ufw allow 47989/tcp && sudo ufw allow 48010/tcp
sudo ufw allow 47998:48010/udp
sudo ufw allow 5353/udp
# plus the ephemeral punktfunk/1 data port — open a UDP range you reserve for it.
```
With raw `nftables` (add to your `inet filter input` chain):
```
udp dport 9777 accept # punktfunk/1 control plane
tcp dport { 47984, 47989, 48010 } accept
udp dport { 47998-48010, 5353 } accept
# plus the ephemeral punktfunk/1 data port (a reserved UDP range).
```
## Files ## Files
- `PKGBUILD` — split package: `punktfunk-host` + `punktfunk-client` (builds the working tree via - `PKGBUILD` — split package: `punktfunk-host` + `punktfunk-client` (builds the working tree via
`PF_SRCDIR`, or a git tag for AUR). `PF_SRCDIR`, or a git tag for AUR).
+3 -2
View File
@@ -1,8 +1,9 @@
# bootc / OCI image layer that bakes punktfunk into a Bazzite-based atomic image. # bootc / OCI image layer that bakes punktfunk into a Bazzite-based atomic image.
# #
# Bazzite is already a bootc image (Fedora Atomic + gamescope + PipeWire + the NVIDIA # Bazzite is already a bootc image (Fedora Atomic + gamescope + PipeWire + the NVIDIA
# stack), so we layer punktfunk on top: enable RPM Fusion (for the NVENC ffmpeg) and our # stack), so we layer punktfunk on top: enable RPM Fusion (for the NVENC ffmpeg) and unom's
# COPR, install the package, and pre-enable the udev rule. Build + push this image, then # Gitea RPM registry (NOT COPR — only the registry carries the punktfunk-web subpackage),
# install the packages, and pre-enable the udev rule. Build + push this image, then
# `bootc switch` (or rebase) a Bazzite host onto it for an image-based, atomic install — # `bootc switch` (or rebase) a Bazzite host onto it for an image-based, atomic install —
# no per-host `rpm-ostree install` drift. # no per-host `rpm-ostree install` drift.
# #
+2 -2
View File
@@ -11,7 +11,7 @@ once in the COPR web UI (or with `copr-cli`):
- Source build method: `rpkg` (or `make_srpm`) - Source build method: `rpkg` (or `make_srpm`)
**Project settings** **Project settings**
- Chroots: `fedora-41-x86_64`, `fedora-42-x86_64` (match your Bazzite Fedora base; - Chroots: `fedora-43-x86_64`, `fedora-44-x86_64` (match your Bazzite Fedora base;
`rpm -E %fedora` on the host tells you which). Add `aarch64` if needed. `rpm -E %fedora` on the host tells you which). Add `aarch64` if needed.
- External repositories (so `ffmpeg-devel` resolves at build time): - External repositories (so `ffmpeg-devel` resolves at build time):
`https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$releasever.noarch.rpm` `https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$releasever.noarch.rpm`
@@ -22,7 +22,7 @@ once in the COPR web UI (or with `copr-cli`):
`copr-cli` equivalent: `copr-cli` equivalent:
```sh ```sh
copr-cli create punktfunk --chroot fedora-42-x86_64 \ copr-cli create punktfunk --chroot fedora-44-x86_64 \
--repo 'https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$releasever.noarch.rpm' \ --repo 'https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$releasever.noarch.rpm' \
--repo 'https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$releasever.noarch.rpm' --repo 'https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$releasever.noarch.rpm'
copr-cli buildscm punktfunk \ copr-cli buildscm punktfunk \
+53 -2
View File
@@ -2,8 +2,16 @@
`punktfunk-host` is published as a `.deb` to **Gitea's Debian package registry** in the public `punktfunk-host` is published as a `.deb` to **Gitea's Debian package registry** in the public
`unom` org, so the Ubuntu hosts update with plain `apt`. CI (`.gitea/workflows/deb.yml`) builds `unom` org, so the Ubuntu hosts update with plain `apt`. CI (`.gitea/workflows/deb.yml`) builds
and publishes on every push to `main` (a rolling `0.0.1~ciN.<sha>` build) and on `v*` tags and publishes on every push to `main` (a rolling `0.2.0~ciN.g<sha>` build) and on `host-v*` tags
(a clean `X.Y.Z`). (a clean `X.Y.Z`) — the rolling builds outrank the stray `0.1.1`, so plain `apt upgrade` always
gets the latest (no version pin needed).
The same workflow also publishes **`punktfunk-web`** (the browser management console — pairing +
status) and **`punktfunk-client`** (the GTK4 couch/Deck client). `punktfunk-host` **Recommends**
`punktfunk-web`, so a default `apt install punktfunk-host` pulls the console too (alongside the
udev/sysctl bits) unless you've disabled weak deps; `punktfunk-client` is independent — install it
on the box you stream *to*. (`punktfunk-client-rs` is the headless reference/test tool, not packaged
here.)
Package layout mirrors the Fedora RPM (`../rpm/punktfunk.spec`): the host binary, the `/dev/uinput` Package layout mirrors the Fedora RPM (`../rpm/punktfunk.spec`): the host binary, the `/dev/uinput`
udev rule, the systemd **user** unit, headless session helpers, the example config, and the OpenAPI udev rule, the systemd **user** unit, headless session helpers, the example config, and the OpenAPI
@@ -35,6 +43,49 @@ sudo usermod -aG input "$USER" # virtual gamepads (re-login to take eff
mkdir -p ~/.config/punktfunk mkdir -p ~/.config/punktfunk
cp /usr/share/punktfunk-host/host.env.example ~/.config/punktfunk/host.env # then edit cp /usr/share/punktfunk-host/host.env.example ~/.config/punktfunk/host.env # then edit
systemctl --user enable --now punktfunk-host systemctl --user enable --now punktfunk-host
# Web console — enable it and read the auto-generated login password (then open http://<host-ip>:3000):
systemctl --user enable --now punktfunk-web
journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p'
```
## Firewall
Open the ports the host listens on. The **native `punktfunk/1`** plane:
- **QUIC control plane: UDP 9777** (`serve --native --native-port N` to change).
- **Data plane: an *ephemeral* UDP port** — negotiated per session, so there is no fixed port to
open. For a restrictive firewall you'd need to allow a UDP range (the repo does not pin one).
And the **GameStream / Moonlight** ports (fixed):
| Port | Proto | Purpose |
|---|---|---|
| 47984 | TCP | HTTPS nvhttp (paired, mutual-TLS) |
| 47989 | TCP | HTTP nvhttp (`/serverinfo`, `/pair` PIN flow) |
| 48010 | TCP | RTSP handshake |
| 4799848010 | UDP | Video RTP (+ FEC), ENet control (47999), audio (48000) |
| 5353 | UDP | mDNS auto-discovery |
The mgmt API (TCP 47990) binds to loopback by default — leave it closed unless you move it off
loopback with `--mgmt-bind IP:PORT` (which then requires `--mgmt-token`).
With `ufw`:
```sh
sudo ufw allow 9777/udp # punktfunk/1 control plane
sudo ufw allow 47984/tcp && sudo ufw allow 47989/tcp && sudo ufw allow 48010/tcp
sudo ufw allow 47998:48010/udp
sudo ufw allow 5353/udp
# plus the ephemeral punktfunk/1 data port — open a UDP range you reserve for it.
```
With raw `nftables` (add to your `inet filter input` chain):
```
udp dport 9777 accept # punktfunk/1 control plane
tcp dport { 47984, 47989, 48010 } accept
udp dport { 47998-48010, 5353 } accept
# plus the ephemeral punktfunk/1 data port (a reserved UDP range).
``` ```
## Updates ## Updates