f36d13e371
ci / web (push) Successful in 54s
ci / docs-site (push) Successful in 1m5s
apple / swift (push) Successful in 1m19s
decky / build-publish (push) Successful in 31s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 16s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 14s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 9s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 8s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 11s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 51s
ci / bench (push) Successful in 6m5s
apple / screenshots (push) Successful in 6m34s
deb / build-publish (push) Successful in 9m43s
docker / deploy-docs (push) Successful in 48s
deb / build-publish-host (push) Successful in 10m9s
android / android (push) Successful in 18m18s
arch / build-publish (push) Successful in 19m59s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 15m2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 22m16s
ci / rust (push) Successful in 28m27s
The system-tuning block (UDP buffers, gamepad udev rule, vhci-hcd autoload, input group) was gated on `sudo -n true` — passwordless sudo — so on a stock SteamOS box (the deck account needs a password) the whole block was silently skipped: /dev/uhid stayed root-only so the gamepad degraded to an Xbox 360 pad, and the UDP buffers stayed at the 416 KB default. Video was unaffected because Game Mode/gamescope needs none of it, which masked the skip on-glass. - install.sh + update.sh: acquire sudo interactively (`sudo -v` prompt on a TTY) instead of requiring passwordless; skip only when there's no TTY or auth fails, and then print the exact manual block plus a `passwd` hint — a stock SteamOS 'deck' account has no password, so sudo can't work until one is set. - update.sh: also retrofit the UDP-buffer sysctl for older/skipped installs, and loudly nag to reboot when the input group was just added (a --user restart does not pick up the new group; only a fresh login does). - steamos-host.md: note this step prompts for the sudo password. Root is unavoidable: the udev rule, input group, and vhci-hcd module are all kernel operations with no user-space alternative. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
152 lines
8.1 KiB
Markdown
152 lines
8.1 KiB
Markdown
---
|
||
title: "SteamOS (Host)"
|
||
description: "Run a punktfunk host on SteamOS — stream its Game Mode (or desktop) to your other devices. One script, built on-device and ABI-matched to SteamOS."
|
||
---
|
||
|
||
This is for using a **SteamOS device as the host** — streaming *from* it to a laptop, TV, phone, or
|
||
another device. (For the usual case — streaming *to* a Steam Deck — see [Install a Client](/docs/install-client),
|
||
which uses the Flatpak + Decky plugin.)
|
||
|
||
We support SteamOS as a host mainly with an eye to the **upcoming Steam Machine** — a living-room,
|
||
desktop-class SteamOS box is a natural always-on streaming host. The **Steam Deck** is the SteamOS
|
||
device we can test on today, so it's what these instructions are validated against; the same
|
||
on-device build works on any SteamOS 3 system.
|
||
|
||
> New here? Read [Security & Safe Use](/docs/security) first — a streaming host is remote control of
|
||
> the machine, so keep it on a trusted LAN or VPN and require pairing.
|
||
|
||
SteamOS is an immutable, read-only Arch base, so the host isn't a system package. Instead a single
|
||
script builds the host **natively inside a Debian-trixie distrobox** (ABI-matched to SteamOS's
|
||
FFmpeg/glibc — the binary then runs natively on SteamOS) and wires it up as systemd user services.
|
||
Building on-device means a rebuild always matches the running OS, so a SteamOS update can't leave you
|
||
with a binary linked against the wrong libraries. Encode is **VAAPI** on the AMD GPU (auto-detected;
|
||
NVENC on NVIDIA).
|
||
|
||
> **Heads up:** in our testing the Steam Deck's WiFi *tx* topped out around ~250 Mbps of goodput
|
||
> regardless of band — enough for 1080p/1440p60, not 4K. This looked like a hardware/driver
|
||
> packet-rate limit rather than a bandwidth ceiling, but it's one device measured on one network:
|
||
> other SteamOS hardware, newer drivers, or a less congested band may do better. A wired dock
|
||
> sidesteps it entirely. See [Configuration](/docs/configuration) for bitrate guidance.
|
||
|
||
## Prerequisites
|
||
|
||
- A SteamOS device on **SteamOS 3** (e.g. a Steam Deck, LCD or OLED). Steady WiFi or, better, a wired dock.
|
||
- **distrobox** installed (no root needed). If `distrobox` isn't found:
|
||
```sh
|
||
curl -sfL https://raw.githubusercontent.com/89luca89/distrobox/main/install | sh -s -- --prefix ~/.local
|
||
```
|
||
Make sure `~/.local/bin` is on your `PATH` (re-open the terminal).
|
||
- The first build downloads a container image + toolchain (~1 GB) and takes ~10–15 minutes. Later
|
||
rebuilds are incremental.
|
||
|
||
## 1. Get the source
|
||
|
||
In Desktop Mode open **Konsole** (or ssh in), then:
|
||
|
||
```sh
|
||
git clone https://git.unom.io/unom/punktfunk ~/punktfunk
|
||
```
|
||
|
||
## 2. Run the installer
|
||
|
||
```sh
|
||
bash ~/punktfunk/scripts/steamdeck/install.sh
|
||
```
|
||
|
||
It is idempotent — safe to re-run. In one pass it:
|
||
|
||
1. creates the `pf2` Debian-trixie distrobox and installs the build toolchain,
|
||
2. builds `punktfunk-host` (and the web console),
|
||
3. writes config to `~/.config/punktfunk/` (a generated web-console login password),
|
||
4. raises the UDP socket buffers to 32 MB, installs the gamepad udev rule + the `vhci-hcd` autoload
|
||
and adds you to the `input` group (virtual gamepads / **native Steam Deck controller passthrough**),
|
||
and seeds the KDE RemoteDesktop grant for Desktop-mode input — this step **prompts for your `sudo`
|
||
password** (a stock Steam Deck requires one; without it gamepad passthrough and the UDP tuning are skipped),
|
||
5. installs + starts the `punktfunk-host` and `punktfunk-web` **systemd user services** (with linger,
|
||
so they run without a login session).
|
||
|
||
Useful flags:
|
||
|
||
| Flag | Effect |
|
||
|------|--------|
|
||
| `--open` | Accept **unpaired** clients (trust-on-first-use) — convenient on a fully trusted LAN. Default is PIN pairing required. |
|
||
| `--no-gamestream` | Run a **secure native-only** host — skip the GameStream/Moonlight-compat planes (see below). Default keeps them on so stock Moonlight works. |
|
||
| `--no-web` | Skip the management web console. |
|
||
| `--src=DIR` | Build from source at `DIR` instead of `~/punktfunk`. |
|
||
|
||
When it finishes it prints the web-console URL and how to pair.
|
||
|
||
> **GameStream/Moonlight compat is on by default.** The native `punktfunk/1` plane (used by
|
||
> punktfunk's own clients — SPAKE2 PIN pairing, per-direction AEAD) is **always on** and is the secure
|
||
> path. The installer also enables the **GameStream/Moonlight-compat planes** so stock
|
||
> [Moonlight](/docs/moonlight) works — but those carry inherent on-path weaknesses (pairing over plain
|
||
> HTTP; legacy control encryption that can reuse GCM nonces), so enable them only on a **trusted LAN**.
|
||
> If you only ever use native clients, install with `--no-gamestream` for a host with no GameStream
|
||
> surface at all.
|
||
|
||
> **First install — reboot once before streaming.** KWin only authorizes Desktop-mode screen capture
|
||
> on a fresh session, and the new `input` group (native Steam Deck controller passthrough) only takes
|
||
> effect on a new login — so after the **first** install, **reboot the Deck** (a re-run that changes
|
||
> nothing doesn't need it). Streaming **Game Mode** with a generic Xbox pad works right away; **Desktop
|
||
> capture and the native Steam Deck controller need the reboot.** If a client connects and every
|
||
> session ends with `KWin does not expose zkde_screencast_unstable_v1` or the pad shows up as an Xbox
|
||
> 360 controller, you haven't rebooted yet.
|
||
|
||
## 3. Pair a device
|
||
|
||
By default the host **requires PIN pairing** (secure). Two ways to pair:
|
||
|
||
- **Web console** (printed at the end of step 2): open `https://<device-ip>:47992` (self-signed host
|
||
cert — your browser warns once; trust it and continue),
|
||
[arm pairing](/docs/web-console#arm-pairing), and enter the PIN on your client.
|
||
- **From the client directly**: pick this host (it advertises over mDNS as `_punktfunk._udp`) and
|
||
enter the PIN the host shows.
|
||
|
||
On a trusted home LAN you can instead install with `--open` and skip pairing entirely.
|
||
|
||
### Console login password
|
||
|
||
The installer generates a random console login password (printed at the end of step 2) and writes it
|
||
to `~/.config/punktfunk/web.env`. To read it back or set your own, see
|
||
[The Web Console](/docs/web-console#login-password).
|
||
|
||
## 4. Verify
|
||
|
||
```sh
|
||
systemctl --user status punktfunk-host # active (running)
|
||
journalctl --user -u punktfunk-host -f # watch a client connect
|
||
```
|
||
|
||
Connect from a [native client](/docs/clients), or from [Moonlight](/docs/moonlight) (unless you
|
||
installed with `--no-gamestream`). In Game Mode the host attaches to the running gamescope session and
|
||
streams it at your client's resolution; in Desktop Mode it streams the KDE desktop. The host
|
||
auto-detects which session is live per connection. See [Steam / gamescope](/docs/gamescope) for the
|
||
attach-vs-managed detail and known limits.
|
||
|
||
## Updating
|
||
|
||
After pulling new source, rebuild and restart in one step (config + pairings persist):
|
||
|
||
```sh
|
||
git -C ~/punktfunk pull # or rsync new source in
|
||
bash ~/punktfunk/scripts/steamdeck/update.sh
|
||
```
|
||
|
||
## Notes & limits
|
||
|
||
- **Single session at a time** at custom resolutions — two clients requesting different modes will
|
||
thrash the managed session. Pick one mode per session.
|
||
- **Keep the device awake.** On handhelds, Game Mode auto-suspends on idle, which drops the host off
|
||
the network mid stream — disable auto-suspend (Settings → Power) for a headless host.
|
||
- **Native Steam Deck controller passthrough** presents the client's pad as a real Steam Deck
|
||
controller (paddles, trackpads, gyro) via a virtual USB device — that needs the `input` group and the
|
||
`vhci-hcd` module live, so it only works **after the first-install reboot** above; until then the pad
|
||
degrades to a generic Xbox 360 controller (still fully playable). If you're streaming *to* another
|
||
Steam Deck, also set Steam Input to **Off** for Punktfunk on that Deck — see
|
||
[Stream to a Steam Deck](/docs/steam-deck).
|
||
- **It survives OS updates**, but a major SteamOS bump can move library versions; if the host fails to
|
||
start after an update, just re-run `update.sh` to rebuild against the new base.
|
||
- Deeper reference (services, container, manual steps): [`scripts/steamdeck/README.md`](https://git.unom.io/unom/punktfunk/src/branch/main/scripts/steamdeck/README.md).
|
||
|
||
Trouble? See [Troubleshooting](/docs/troubleshooting) and [Pairing](/docs/pairing).
|