diff --git a/README.md b/README.md index 2b514ec..e7077e6 100644 --- a/README.md +++ b/README.md @@ -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 [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 ``` @@ -46,7 +63,9 @@ tools/{latency-probe,loss-harness}/ measurement (plan §10) 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 cargo build --workspace # green on Linux and macOS diff --git a/docs-site/content/docs/bazzite.md b/docs-site/content/docs/bazzite.md index d4eb806..2e7dbfa 100644 --- a/docs-site/content/docs/bazzite.md +++ b/docs-site/content/docs/bazzite.md @@ -13,10 +13,37 @@ the mode of the device you're streaming to, not the TV the box is plugged into. ## Install -The host installs from the punktfunk COPR repository (see `packaging/bazzite/` in the repo for the -exact COPR/RPM/bootc options). You can also build from source as on -[Fedora KDE](/docs/fedora-kde) — Bazzite is Fedora Atomic underneath, and its FFmpeg builds the host -fine. +The host ships as an RPM in punktfunk's **Gitea RPM registry** (public), so a Bazzite / Fedora +Atomic box layers and updates it with `rpm-ostree`. Add the repo, then layer the host plus the web +console and reboot: + +```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 @@ -32,7 +59,14 @@ permission, not a client problem.) ## 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 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 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://:3000: +systemctl --user enable --now punktfunk-web +journalctl --user -u punktfunk-web-init | sed -n 's/.*password generated: //p' +``` + ## Good to know - **gamescope 3.16.22 or newer is required.** Older versions can deadlock during capture. Bazzite's diff --git a/docs-site/content/docs/clients.md b/docs-site/content/docs/clients.md index 0b82395..1cfcbff 100644 --- a/docs-site/content/docs/clients.md +++ b/docs-site/content/docs/clients.md @@ -3,7 +3,8 @@ title: Clients 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) @@ -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 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 -testing and development. It connects, streams to a file, runs the speed test, and can discover hosts: +`punktfunk-client` is the native graphical Linux client — a GTK4 / libadwaita app that speaks +`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 -punktfunk-client-rs --discover # list hosts on the network -punktfunk-client-rs --connect :9777 --pin # connect to one +punktfunk-client --connect :9777 # skip the picker, start a session immediately ``` -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 :9777 --pin # connect to one +``` ## Which should I use? | You're streaming to… | Use | |---|---| | A Mac, iPhone, iPad, or Apple TV | The **Apple app** | -| Windows, Android, Steam Deck, a browser, a TV | **Moonlight** | -| Another Linux box (testing) | **`punktfunk-client-rs`** | +| A Linux desktop or laptop, or a Steam Deck | **`punktfunk-client`** (GTK4) | +| 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). diff --git a/docs-site/content/docs/configuration.md b/docs-site/content/docs/configuration.md index 50d0efc..eb55ec4 100644 --- a/docs-site/content/docs/configuration.md +++ b/docs-site/content/docs/configuration.md @@ -43,12 +43,13 @@ The client requests a bitrate; the host encodes to it. To find a good value for ## Multiple devices at once -A host can stream to several clients simultaneously — each gets its own virtual display at its own -resolution. This is the natural way to put your desktop on a laptop *and* a TV at the same time (both -see and control the same desktop). +Today the native `punktfunk/1` host (`serve --native`) streams **one session at a time** — additional +clients wait in the accept queue until the active session ends. Each session gets its own virtual +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 -`--max-concurrent N` on the host command line (default 4); extra clients wait until a slot frees. +(`m3-host`, the standalone test host, has a `--max-concurrent N` knob, default 4, bounded by your +GPU's encoder — see the [Host CLI](/docs/host-cli) reference — but `serve --native` does **not** take +that flag.) ## Codec and FEC diff --git a/docs-site/content/docs/fedora-kde.md b/docs-site/content/docs/fedora-kde.md index 23d6faf..13b5892 100644 --- a/docs-site/content/docs/fedora-kde.md +++ b/docs-site/content/docs/fedora-kde.md @@ -113,9 +113,10 @@ mDNS. It requires **PIN pairing** by default (secure on a LAN); pair once from y ## 4. Connect a client 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 -host with `--allow-pairing`). See [Clients](/docs/clients) and -[Running as a Service](/docs/running-as-a-service). +first connect, complete the PIN pairing — **arm it from the host's web console / mgmt API**, which +makes the host display a 4-digit PIN to type into the client. (Pairing is required by default; pass +`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 diff --git a/docs-site/content/docs/host-cli.md b/docs-site/content/docs/host-cli.md index bba2e55..ce173f5 100644 --- a/docs-site/content/docs/host-cli.md +++ b/docs-site/content/docs/host-cli.md @@ -21,10 +21,18 @@ punktfunk-host serve --native | `--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 loopback `127.0.0.1:47990`). | -| `--mgmt-token ` | Bearer token for the management API; required when `--mgmt-bind` isn't loopback. | +| `--mgmt-token ` | Override the bearer token for the management API. | -By default the host **requires pairing** — see [Pairing & Trust](/docs/pairing). Arm pairing from the -web console (or the `m3-host` flags below for a quick test). +These are the only flags `serve` accepts. + +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` @@ -38,12 +46,17 @@ punktfunk-host m3-host --source virtual | Flag | Meaning | |---|---| | `--port ` | 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 ` / `--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-pairing` | Accept PIN pairing; the host prints a PIN when a client pairs. | | `--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 hosts from another machine with `punktfunk-client-rs --discover`. diff --git a/docs-site/content/docs/install.md b/docs-site/content/docs/install.md new file mode 100644 index 0000000..e008e7c --- /dev/null +++ b/docs-site/content/docs/install.md @@ -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://: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. diff --git a/docs-site/content/docs/meta.json b/docs-site/content/docs/meta.json index cad4758..352c33b 100644 --- a/docs-site/content/docs/meta.json +++ b/docs-site/content/docs/meta.json @@ -4,6 +4,7 @@ "index", "how-it-works", "quickstart", + "install", "---Host Setup---", "requirements", "ubuntu-gnome", diff --git a/docs-site/content/docs/pairing.md b/docs-site/content/docs/pairing.md index 2122b51..799401c 100644 --- a/docs-site/content/docs/pairing.md +++ b/docs-site/content/docs/pairing.md @@ -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. 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 - shows the PIN and the list of paired devices. This is the easiest way and works on a headless host - 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. +(The standalone headless test host, `m3-host`, takes `--allow-pairing`/`--require-pairing` on its +command line instead; the production `serve --native` host arms pairing from the console.) 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 connect. -If you're on a fully trusted single-user network and want to skip pairing, the host can be run open — -but requiring pairing is strongly recommended. +If you're on a fully trusted single-user network and want to skip pairing, start the host with +`serve --native --open` — but requiring pairing is strongly recommended. ## Trust-on-first-use diff --git a/docs-site/content/docs/quickstart.md b/docs-site/content/docs/quickstart.md index 8b96052..dcc5c77 100644 --- a/docs-site/content/docs/quickstart.md +++ b/docs-site/content/docs/quickstart.md @@ -37,9 +37,9 @@ On the device you want to stream to: network*. Tap it, and when prompted, **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 -`--allow-pairing` on the command line) — the host displays a 4-digit PIN, you type it into the client, -and they trust each other from then on. Full details: [Pairing & Trust](/docs/pairing). +To pair, the host needs to show a PIN. Arm pairing from the host's web console — the host displays a +4-digit PIN, you type it into the client, and they trust each other from then on. Pairing is required +by default. Full details: [Pairing & Trust](/docs/pairing). ## 4. Stream diff --git a/docs-site/content/docs/troubleshooting.md b/docs-site/content/docs/troubleshooting.md index 423f415..7852c52 100644 --- a/docs-site/content/docs/troubleshooting.md +++ b/docs-site/content/docs/troubleshooting.md @@ -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 routed subnets or most VPNs-without-multicast. As a fallback, add the host by **IP address** in your client. -- A firewall on the host can block it. The native protocol uses UDP port **9777** (plus the data - port); GameStream/Moonlight uses its standard ports. Allow them on the host's firewall. +- A firewall on the host can block it. The native protocol's control plane uses UDP port **9777**. The + 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 **47998–48010** + ENet UDP **47999**. Allow them on the host's + firewall. ## `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 -- The host **requires pairing** by default. Arm pairing (web console, or `--allow-pairing`), then - enter the PIN on the client. See [Pairing & Trust](/docs/pairing). +- The host **requires pairing** by default. Arm pairing from the web console, then enter the PIN on + the client. See [Pairing & Trust](/docs/pairing). - If you re-installed the host, its identity changed — re-pair the client. ## 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 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. -- Streaming to **many devices at once** shares the GPU encoder; cap concurrency with - `--max-concurrent`. +- Streaming to **many devices at once** shares the GPU encoder. The production host + (`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? diff --git a/docs-site/content/docs/ubuntu-gnome.md b/docs-site/content/docs/ubuntu-gnome.md index 786d049..36b7417 100644 --- a/docs-site/content/docs/ubuntu-gnome.md +++ b/docs-site/content/docs/ubuntu-gnome.md @@ -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 > 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 -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 +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 ``` -Install Rust if you don't have it: - -```sh -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -## 3. Build - -```sh -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: +`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 +installed on a host. The NVIDIA driver is **not** a dependency — you installed it out of band in +step 1. Later updates are just `sudo apt update && sudo apt upgrade`. + +## 3. Configure + +The package ships the systemd **user** unit, the `/dev/uinput` udev rule, the socket-buffer sysctl +tuning, and an example config. As the desktop user, grant gamepad access and write the GNOME config: ```sh +sudo usermod -aG input "$USER" # /dev/uinput for virtual gamepads (re-login to apply) mkdir -p ~/.config/punktfunk cat > ~/.config/punktfunk/host.env <<'ENV' WAYLAND_DISPLAY=wayland-0 @@ -88,18 +84,30 @@ ENV 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 -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 -[connect a client](/docs/clients). +The host listens on UDP `9777` (native punktfunk/1) plus the GameStream ports, and advertises itself +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://: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). ## 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 [Running as a Service](/docs/running-as-a-service). - 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 +``` diff --git a/docs-site/content/docs/ubuntu-kde.md b/docs-site/content/docs/ubuntu-kde.md index ad80bed..4eae49d 100644 --- a/docs-site/content/docs/ubuntu-kde.md +++ b/docs-site/content/docs/ubuntu-kde.md @@ -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. -## NVIDIA driver, dependencies, and build +## 1. NVIDIA driver -These steps are identical to the GNOME guide — follow **steps 1–3** of -[Ubuntu — GNOME](/docs/ubuntu-gnome#1-nvidia-driver): +Identical to the GNOME guide — follow **step 1** of +[Ubuntu — GNOME](/docs/ubuntu-gnome#1-nvidia-driver): install the NVIDIA driver **and** the +`libnvidia-gl-` userspace, enable `nvidia-drm modeset=1`, reboot, and verify with +`nvidia-smi`. -1. Install the NVIDIA driver **and** the `libnvidia-gl-` userspace; enable `nvidia-drm - 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`. +## 2. Install the host (apt) -## Configure - -The host reads `~/.config/punktfunk/host.env`. For KDE Plasma: +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: ```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 cat > ~/.config/punktfunk/host.env <<'ENV' WAYLAND_DISPLAY=wayland-0 @@ -38,15 +59,26 @@ ENV > virtual-display path is Wayland-only. See the [Configuration reference](/docs/configuration) for > 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 -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://: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 — 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`. - **No picture / capture fails:** confirm you're on a Wayland session and the NVIDIA GL userspace is installed (`libnvidia-gl-`). 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 +``` diff --git a/packaging/arch/README.md b/packaging/arch/README.md index 53c0e8d..efb5487 100644 --- a/packaging/arch/README.md +++ b/packaging/arch/README.md @@ -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), 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 | +| 47998–48010 | 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 - `PKGBUILD` — split package: `punktfunk-host` + `punktfunk-client` (builds the working tree via `PF_SRCDIR`, or a git tag for AUR). diff --git a/packaging/bootc/Containerfile b/packaging/bootc/Containerfile index bde97da..086d4ef 100644 --- a/packaging/bootc/Containerfile +++ b/packaging/bootc/Containerfile @@ -1,8 +1,9 @@ # 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 -# stack), so we layer punktfunk on top: enable RPM Fusion (for the NVENC ffmpeg) and our -# COPR, install the package, and pre-enable the udev rule. Build + push this image, then +# stack), so we layer punktfunk on top: enable RPM Fusion (for the NVENC ffmpeg) and unom's +# 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 — # no per-host `rpm-ostree install` drift. # diff --git a/packaging/copr/README.md b/packaging/copr/README.md index 26b5515..64be5e5 100644 --- a/packaging/copr/README.md +++ b/packaging/copr/README.md @@ -11,7 +11,7 @@ once in the COPR web UI (or with `copr-cli`): - Source build method: `rpkg` (or `make_srpm`) **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. - External repositories (so `ffmpeg-devel` resolves at build time): `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: ```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/nonfree/fedora/rpmfusion-nonfree-release-$releasever.noarch.rpm' copr-cli buildscm punktfunk \ diff --git a/packaging/debian/README.md b/packaging/debian/README.md index d491835..2b86f30 100644 --- a/packaging/debian/README.md +++ b/packaging/debian/README.md @@ -2,8 +2,16 @@ `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 -and publishes on every push to `main` (a rolling `0.0.1~ciN.` build) and on `v*` tags -(a clean `X.Y.Z`). +and publishes on every push to `main` (a rolling `0.2.0~ciN.g` build) and on `host-v*` tags +(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` 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 cp /usr/share/punktfunk-host/host.env.example ~/.config/punktfunk/host.env # then edit systemctl --user enable --now punktfunk-host +# Web console — enable it and read the auto-generated login password (then open http://: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 | +| 47998–48010 | 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