docs(env): stop teaching the compositor pin + uid-1000 anchors in starters
ci / docs-site (push) Successful in 54s
ci / web (push) Successful in 59s
apple / swift (push) Successful in 1m21s
decky / build-publish (push) Successful in 28s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 25s
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 10s
ci / bench (push) Successful in 6m23s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 4m43s
apple / screenshots (push) Successful in 6m18s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m9s
arch / build-publish (push) Successful in 13m44s
deb / build-publish (push) Successful in 13m48s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 7m39s
deb / build-publish-host (push) Successful in 13m11s
docker / deploy-docs (push) Successful in 32s
android / android (push) Successful in 16m23s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 15m15s
ci / rust (push) Successful in 24m12s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 14m55s

Field triage (Nobara, Discord): the kde.md starter host.env told desktop
users to set PUNKTFUNK_COMPOSITOR=kwin, which PINS the backend — detect()
short-circuits and the capture-loss rebuild never re-detects — so a
mid-stream switch to Game Mode killed the stream instead of following it.
A follow-up hardcoded XDG_RUNTIME_DIR=/run/user/1000 anchor broke PipeWire
for any non-1000 uid (pw audio connect: Creation failed).

Revamp across every starter/example/reference:
- Desktop starters (kde/gnome/hyprland/sway) shrink to
  PUNKTFUNK_VIDEO_SOURCE=virtual + an explicit warning that pinning
  disables session-following; forcing a backend is CI/appliance-only.
- host.env.example: rewritten around auto-detection; anchors demoted to a
  commented ssh/cron-only block with the uid trap spelled out; the
  gamescope ATTACH/MANAGED knobs documented (previously missing);
  case-sensitivity called out.
- packaging/bazzite/host.env + README: drop the uid-1000 anchors (a
  systemctl --user service inherits/derives them); README's stale
  PUNKTFUNK_COMPOSITOR=gamescope-era template synced to the real one.
- packaging/kde/host.env: loud APPLIANCE-ONLY header (it pins on purpose).
- configuration.md: session-anchors section inverted to "leave unset",
  compositor row states the pin consequence, case-sensitivity note.
- troubleshooting.md: new "session fails right after editing host.env"
  section (case, wrong-uid anchors, stale pin, restart-to-apply).
- gamescope.md/bazzite.md: attach/managed descriptions match current
  behavior (managed is the infra-detected default; attach re-modes a
  box-owned session to the client's resolution).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-23 22:42:59 +02:00
co-authored by Claude Fable 5
parent bda015b101
commit 6617275387
14 changed files with 189 additions and 137 deletions
+9 -9
View File
@@ -85,11 +85,10 @@ cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env
The template is deliberately minimal — it does **not** force a compositor, because the host The template is deliberately minimal — it does **not** force a compositor, because the host
auto-detects Gaming Mode (gamescope) vs Desktop (KWin) on every connect and follows the switch auto-detects Gaming Mode (gamescope) vs Desktop (KWin) on every connect and follows the switch
mid-stream. The only settings that matter are the session anchors (GPU zero-copy is on by default): mid-stream. No session anchors are needed either (a user service inherits the right runtime dir).
The only settings that matter (GPU zero-copy is on by default):
```sh ```sh
XDG_RUNTIME_DIR=/run/user/1000
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. # GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU.
PUNKTFUNK_GAMESCOPE_ATTACH=1 # Gaming Mode = attach to the box's own session (see below) PUNKTFUNK_GAMESCOPE_ATTACH=1 # Gaming Mode = attach to the box's own session (see below)
@@ -99,12 +98,13 @@ PUNKTFUNK_GAMESCOPE_ATTACH=1 # Gaming Mode = attach to the box's own session
For Gaming Mode there are two models (pick one; the shipped default is **attach**): For Gaming Mode there are two models (pick one; the shipped default is **attach**):
- **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`, the default) — the **box** owns its gamescope session, - **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`, the template's default) — the **box** owns its
the host attaches to whatever's live and never tears it down, and the streamed game-mode resolution gamescope session on its own display, and the host attaches to whatever's live without ever
is the box's own gamescope mode. Switching Desktop ↔ Game is rock-solid. tearing it down (a box-owned autologin session is restarted at the client's resolution on a
- **Managed** (`PUNKTFUNK_GAMESCOPE_MANAGED=1`, and remove the attach line) — the host launches its mismatch). Switching Desktop ↔ Game is rock-solid.
**own** gamescope at the *client's* exact resolution and refresh. Client-mode-following, but there - **Managed** (`PUNKTFUNK_GAMESCOPE_MANAGED=1`, and remove the attach line) — the host takes the
must be no physical gaming session already running. box's gamescope over and relaunches it **headless** at the *client's* exact resolution and
refresh — Game Mode on the virtual screen — restoring the box on idle.
Full treatment: [Steam / gamescope → Attach vs managed](/docs/gamescope#attach-vs-managed). Full treatment: [Steam / gamescope → Attach vs managed](/docs/gamescope#attach-vs-managed).
+15 -15
View File
@@ -4,7 +4,8 @@ description: Every host.env setting and PUNKTFUNK_* environment variable — com
--- ---
The host reads its settings from **`~/.config/punktfunk/host.env`** (a simple `KEY=value` file, `#` The host reads its settings from **`~/.config/punktfunk/host.env`** (a simple `KEY=value` file, `#`
starts a comment). On Windows the service reads **`%ProgramData%\punktfunk\host.env`** instead. Your starts a comment; keys are **case-sensitive**`punktfunk_compositor` sets nothing, use the exact
uppercase names). On Windows the service reads **`%ProgramData%\punktfunk\host.env`** instead. Your
[setup guide](/docs/requirements) gives you a starting `host.env` for your desktop; this page is the [setup guide](/docs/requirements) gives you a starting `host.env` for your desktop; this page is the
full reference for every setting. full reference for every setting.
@@ -16,25 +17,24 @@ full reference for every setting.
## Session anchors ## Session anchors
These tell the host which desktop session to attach to. Your setup guide sets them for you; they're **Leave these unset on a normal setup.** Running as a `systemctl --user` service the host inherits
required when the host runs outside your interactive session (e.g. as a service). the correct `XDG_RUNTIME_DIR` from systemd, derives the session bus from it, and **rewrites
`WAYLAND_DISPLAY` / `XDG_CURRENT_DESKTOP` / `XDG_RUNTIME_DIR` / `DBUS_SESSION_BUS_ADDRESS` on every
connect** to follow the active session (Gaming ↔ Desktop) — a value written here can only be
redundant or stale.
| Setting | What it does | | Setting | When to set it |
|---|---| |---|---|
| `XDG_RUNTIME_DIR` | Your session's runtime dir (e.g. `/run/user/1000`). Always needed for a service. | | `XDG_RUNTIME_DIR` | Only when the host runs **outside** a user service (ssh, cron): `/run/user/<your uid>` — check `id -u`. A copy-pasted `1000` on a box where that isn't your uid points the host at another user's (nonexistent) PipeWire/D-Bus, and **everything** fails (audio `Creation failed`, no capture, clients report the host unreachable). |
| `DBUS_SESSION_BUS_ADDRESS` | Your session bus (e.g. `unix:path=/run/user/1000/bus`). Always needed for a service. | | `DBUS_SESSION_BUS_ADDRESS` | Same cases only: `unix:path=/run/user/<your uid>/bus`. Otherwise derived automatically. |
| `WAYLAND_DISPLAY` | The Wayland socket of your session (`wayland-0` for a normal desktop, `wayland-kde` for the headless-KDE unit). | | `WAYLAND_DISPLAY` | Only the dedicated [headless-KDE appliance](/docs/kde#headless-session) (`wayland-kde`, set by its shipped `host.env.kde`). |
| `XDG_CURRENT_DESKTOP` | Your desktop (`GNOME`, `KDE`). | | `XDG_CURRENT_DESKTOP` | Same — appliance-only. |
On Linux the host **rewrites `WAYLAND_DISPLAY` / `XDG_CURRENT_DESKTOP` / `XDG_RUNTIME_DIR` /
`DBUS_SESSION_BUS_ADDRESS` on every connect** to follow the active session (Gaming ↔ Desktop). Only
`XDG_RUNTIME_DIR` and `DBUS_SESSION_BUS_ADDRESS` need to be pinned as trustworthy anchors.
## Core ## Core
| Setting | Values | Meaning | | Setting | Values | Meaning |
|---|---|---| |---|---|---|
| `PUNKTFUNK_COMPOSITOR` | `kwin` · `mutter` · `gamescope` · `wlroots` · `hyprland` (aliases: `kde`/`plasma`, `gnome`, `sway`/`wlr`) | Which backend creates the virtual display. `wlroots` is sway/River; `hyprland` is its own backend. **Leave unset to auto-detect;** set only to force one. | | `PUNKTFUNK_COMPOSITOR` | `kwin` · `mutter` · `gamescope` · `wlroots` · `hyprland` (aliases: `kde`/`plasma`, `gnome`, `sway`/`wlr`) | Which backend creates the virtual display. `wlroots` is sway/River; `hyprland` is its own backend. **Leave unset.** Setting it **pins** the backend and turns session-following **off** — per connect *and* mid-stream, so a Desktop ↔ Gaming switch kills the stream instead of being followed. For CI/tests and dedicated single-session appliances only. |
| `PUNKTFUNK_VIDEO_SOURCE` | `virtual` · `portal` | `virtual` creates a per-client display at the client's exact mode (the normal choice). `portal` captures an existing monitor instead. | | `PUNKTFUNK_VIDEO_SOURCE` | `virtual` · `portal` | `virtual` creates a per-client display at the client's exact mode (the normal choice). `portal` captures an existing monitor instead. |
| `PUNKTFUNK_ZEROCOPY` | `1` · `0` *(default on)* | GPU zero-copy capture→encode (dmabuf → CUDA → NVENC, or D3D11 on Windows). **On by default** — no need to set it; it falls back to a CPU path automatically. Set `0` to force the CPU path. One exception: Windows **Intel/QSV** keeps the CPU path by default until zero-copy is validated on Intel hardware — set `1` to try it there. | | `PUNKTFUNK_ZEROCOPY` | `1` · `0` *(default on)* | GPU zero-copy capture→encode (dmabuf → CUDA → NVENC, or D3D11 on Windows). **On by default** — no need to set it; it falls back to a CPU path automatically. Set `0` to force the CPU path. One exception: Windows **Intel/QSV** keeps the CPU path by default until zero-copy is validated on Intel hardware — set `1` to try it there. |
| `PUNKTFUNK_INPUT_BACKEND` | `libei` · `gamescope` · `wlr` · `uinput` | How input is injected. `libei` for GNOME/KDE, `gamescope` for Bazzite/gamescope, `wlr` for Sway/wlroots **and Hyprland**. Auto-detected with the compositor. | | `PUNKTFUNK_INPUT_BACKEND` | `libei` · `gamescope` · `wlr` · `uinput` | How input is injected. `libei` for GNOME/KDE, `gamescope` for Bazzite/gamescope, `wlr` for Sway/wlroots **and Hyprland**. Auto-detected with the compositor. |
@@ -53,8 +53,8 @@ the full picture (and [Bazzite](/docs/bazzite) for that distro's specifics).
| Setting | Values | Meaning | | Setting | Values | Meaning |
|---|---|---| |---|---|---|
| `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | **Attach** model: the box owns its gamescope session (you switch Gaming ↔ Desktop with the Steam UI); the host just captures whatever's live and never tears it down. Rock-solid; streamed resolution is the box's gamescope mode. | | `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | **Attach** model: the box owns its gamescope session on its own display (you switch Gaming ↔ Desktop with the Steam UI); the host just captures whatever's live and never tears it down. A box-owned autologin session is restarted at the client's resolution on a mismatch; a foreign/bare gamescope streams at its own mode. |
| `PUNKTFUNK_GAMESCOPE_MANAGED` | `1` | **Managed** model: the host tears the box's gamescope down on connect and launches its **own** at the *client's* exact resolution, restoring on idle. Client-mode-following, but doesn't coexist with a box-owned game-mode session. | | `PUNKTFUNK_GAMESCOPE_MANAGED` | `1` | **Managed** model (the default where session infra is detected): the host takes the box's gamescope over and relaunches it **headless** at the *client's* exact resolution — Game Mode on the virtual screen — restoring the box on idle. |
| `PUNKTFUNK_GAMESCOPE_SESSION` | `steam` | The host owns a `gamescope-session-plus` (Steam) session at the client's mode (headless appliance; no physical session running). | | `PUNKTFUNK_GAMESCOPE_SESSION` | `steam` | The host owns a `gamescope-session-plus` (Steam) session at the client's mode (headless appliance; no physical session running). |
| `PUNKTFUNK_GAMESCOPE_NODE` | `auto` · node id | Discover + capture a **running** gamescope's PipeWire node at a fixed mode. Do **not** combine with `SESSION`. | | `PUNKTFUNK_GAMESCOPE_NODE` | `auto` · node id | Discover + capture a **running** gamescope's PipeWire node at a fixed mode. Do **not** combine with `SESSION`. |
| `PUNKTFUNK_GAMESCOPE_APP` | command | For an ad-hoc bare-gamescope session, the nested command to run (e.g. `vkcube`). | | `PUNKTFUNK_GAMESCOPE_APP` | command | For an ad-hoc bare-gamescope session, the nested command to run (e.g. `vkcube`). |
+17 -15
View File
@@ -17,18 +17,20 @@ from the install guide for your OS: [Bazzite](/docs/bazzite) or [SteamOS (Host)]
## Attach vs managed ## Attach vs managed
There are two mutually-exclusive models for a gamescope box; pick one. The shipped default is There are two mutually-exclusive models for a gamescope box; pick one. With **nothing set**, a box
**attach**. that has gamescope session infrastructure (Bazzite, SteamOS, Nobara) gets **managed**; the
[Bazzite template](/docs/bazzite) ships with **attach** chosen instead.
- **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`, the default) — the **box** owns its gamescope session - **Attach** (`PUNKTFUNK_GAMESCOPE_ATTACH=1`) — the **box** owns its gamescope session and decides
and decides Gaming vs Desktop via the normal Steam UI. The host just attaches to whatever's live Gaming vs Desktop via the normal Steam UI. Game Mode stays on the box's own (physical) display;
and never tears it down, so switching Desktop ↔ Game is rock-solid and disconnecting leaves the box the host attaches to whatever's live and never tears it down, so switching Desktop ↔ Game is
where it was. The streamed game-mode resolution is the box's gamescope mode rock-solid and disconnecting leaves the box where it was. When the session is the box's own
(`SCREEN_WIDTH/HEIGHT` in `/etc/gamescope-session-plus/sessions.d/steam`), not the client's. autologin unit, the host restarts it at the **client's** resolution on a mismatch; a foreign or
- **Managed** (`PUNKTFUNK_GAMESCOPE_MANAGED=1`, and remove the attach line) — the host tears the bare gamescope is streamed at its own mode.
box's gamescope down on connect and launches its **own** at the *client's* exact resolution and - **Managed** (the infra-detected default; force with `PUNKTFUNK_GAMESCOPE_MANAGED=1`) — the host
refresh, restoring on idle. Client-mode-following, but it can't coexist with a box-owned game-mode takes the box's gamescope session over and relaunches it **headless** at the *client's* exact
session, and there must be **no physical gaming session already running**. resolution and refresh — Game Mode runs on the virtual screen, physical displays drop out of it —
restoring the box on idle after disconnect.
## Session following ## Session following
@@ -40,8 +42,8 @@ over its own compositor, and re-targets whichever is live on each switch.
## Start the host ## Start the host
On an appliance box (Bazzite, SteamOS) the install guide already enables the host service for you. On On an appliance box (Bazzite, SteamOS) the install guide already enables the host service for you. On
any other distro running a gamescope session, start it from your session — the default attach model any other distro running a gamescope session, just start it — the host auto-detects the live
just latches onto whatever gamescope session is live: gamescope session and picks the model for it:
```sh ```sh
systemctl --user enable --now punktfunk-host systemctl --user enable --now punktfunk-host
@@ -56,8 +58,8 @@ a model. See the full [Configuration reference](/docs/configuration) for every o
| Setting | Values | Meaning | | Setting | Values | Meaning |
|---|---|---| |---|---|---|
| `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | **Attach** model: the box owns its gamescope session; the host captures whatever's live and never tears it down. Streamed resolution is the box's gamescope mode. The default. | | `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | **Attach** model: the box owns its gamescope session (on its own display); the host captures whatever's live and never tears it down. A box-owned autologin session is restarted at the client's resolution on a mismatch; a foreign/bare gamescope streams at its own mode. |
| `PUNKTFUNK_GAMESCOPE_MANAGED` | `1` | **Managed** model: the host tears the box's gamescope down on connect and launches its own at the client's exact mode, restoring on idle. Doesn't coexist with a box-owned game-mode session. | | `PUNKTFUNK_GAMESCOPE_MANAGED` | `1` | **Managed** model (the default where session infra is detected): the host takes the box's gamescope over and relaunches it headless at the client's exact mode, restoring on idle. |
| `PUNKTFUNK_GAMESCOPE_SESSION` | `steam` | The host owns a `gamescope-session-plus` (Steam) session at the client's mode — a headless appliance with no physical session running. | | `PUNKTFUNK_GAMESCOPE_SESSION` | `steam` | The host owns a `gamescope-session-plus` (Steam) session at the client's mode — a headless appliance with no physical session running. |
| `PUNKTFUNK_GAMESCOPE_NODE` | `auto` · node id | Discover and capture a **running** gamescope's PipeWire node at a fixed mode. Do **not** combine with `SESSION`. | | `PUNKTFUNK_GAMESCOPE_NODE` | `auto` · node id | Discover and capture a **running** gamescope's PipeWire node at a fixed mode. Do **not** combine with `SESSION`. |
| `PUNKTFUNK_GAMESCOPE_APP` | command | For an ad-hoc bare-gamescope session, the nested command to run (e.g. `vkcube`). | | `PUNKTFUNK_GAMESCOPE_APP` | command | For an ad-hoc bare-gamescope session, the nested command to run (e.g. `vkcube`). |
+8 -7
View File
@@ -12,19 +12,20 @@ installed — see [Ubuntu](/docs/ubuntu), [Fedora](/docs/fedora), or [Arch](/doc
## host.env ## host.env
Write `~/.config/punktfunk/host.env` with the GNOME settings. The host auto-detects the compositor The host auto-detects the compositor from your live session on every connect, so the starter
from your session, so the explicit `PUNKTFUNK_COMPOSITOR` is belt-and-braces: `~/.config/punktfunk/host.env` is one line:
```ini ```ini
# ~/.config/punktfunk/host.env # ~/.config/punktfunk/host.env (keys are case-sensitive)
WAYLAND_DISPLAY=wayland-0
XDG_CURRENT_DESKTOP=GNOME
PUNKTFUNK_COMPOSITOR=mutter
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. # GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU.
PUNKTFUNK_INPUT_BACKEND=libei
``` ```
> **Don't set `PUNKTFUNK_COMPOSITOR`, `WAYLAND_DISPLAY`, or `XDG_CURRENT_DESKTOP` here.** Pinning
> the compositor turns auto-detection **off** — per connect *and* mid-stream — so the host stops
> following session switches, and stale session values point it at dead sockets. Forcing a backend
> is a CI / dedicated-appliance posture, not desktop configuration.
You must be on a **Wayland** session (not X11), and Mutter must be **≥ 48**. See the You must be on a **Wayland** session (not X11), and Mutter must be **≥ 48**. See the
[Configuration reference](/docs/configuration) for every option. [Configuration reference](/docs/configuration) for every option.
+9 -4
View File
@@ -19,14 +19,19 @@ or [Fedora](/docs/fedora).
## host.env ## host.env
The host auto-detects a Hyprland session, so you usually need nothing here. To force the backend, set The host auto-detects a Hyprland session, so the starter `~/.config/punktfunk/host.env` is one line:
these in `~/.config/punktfunk/host.env`:
```ini
PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy capture→encode is ON by default; auto-falls back to CPU. Set PUNKTFUNK_ZEROCOPY=0 to force CPU.
```
To force the backend (CI/testing — note that pinning turns live-session auto-detection **off**, so
the host stops following session switches):
```ini ```ini
PUNKTFUNK_COMPOSITOR=hyprland PUNKTFUNK_COMPOSITOR=hyprland
PUNKTFUNK_INPUT_BACKEND=wlr PUNKTFUNK_INPUT_BACKEND=wlr
PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy capture→encode is ON by default; auto-falls back to CPU. Set PUNKTFUNK_ZEROCOPY=0 to force CPU.
``` ```
See [Configuration](/docs/configuration) for the full reference. See [Configuration](/docs/configuration) for the full reference.
+18 -8
View File
@@ -13,20 +13,30 @@ installed — see [Ubuntu](/docs/ubuntu), [Fedora](/docs/fedora), [Arch](/docs/a
## host.env ## host.env
A KDE starter `~/.config/punktfunk/host.env`: The host auto-detects your KWin session on every connect — including a box that switches between
the Plasma desktop and Steam Game Mode — so the starter `~/.config/punktfunk/host.env` is one line:
```ini ```ini
WAYLAND_DISPLAY=wayland-0 # ~/.config/punktfunk/host.env (keys are case-sensitive)
XDG_CURRENT_DESKTOP=KDE
PUNKTFUNK_COMPOSITOR=kwin
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU. # GPU zero-copy (dmabuf → CUDA → NVENC) is ON by default; auto-falls back to CPU. Set =0 to force CPU.
PUNKTFUNK_INPUT_BACKEND=libei
``` ```
The host auto-detects the running compositor on every connect, so most of this is optional — the > **Don't set `PUNKTFUNK_COMPOSITOR`, `WAYLAND_DISPLAY`, or `XDG_CURRENT_DESKTOP` here.** Pinning
values above are just what it resolves to on a KWin session. See the > the compositor turns auto-detection **off** — per connect *and* mid-stream — so a switch to Game
[Configuration reference](/docs/configuration) for every option. > Mode then kills the stream instead of being followed, and stale session values point the host at
> dead sockets. Forcing a backend is for CI and dedicated appliances (the
> [headless session](#headless-session) below ships a `host.env.kde` that pins on purpose).
If the box switches between the desktop and Game Mode, also enable lingering — the host is a user
service, and without linger the logout moment of a session switch tears it (and PipeWire) down
mid-stream:
```sh
sudo loginctl enable-linger "$USER"
```
See the [Configuration reference](/docs/configuration) for every option.
## Use a Wayland session ## Use a Wayland session
+9 -4
View File
@@ -23,16 +23,21 @@ or [Fedora](/docs/fedora).
## host.env ## host.env
The host auto-detects a wlroots session, so you usually need nothing here. To force the backend, set The host auto-detects a wlroots session, so the starter `~/.config/punktfunk/host.env` is one line:
these in `~/.config/punktfunk/host.env`:
```ini ```ini
PUNKTFUNK_COMPOSITOR=wlroots # aliases: sway, wlr, hyprland (all the wlroots family; the exact backend is auto-detected)
PUNKTFUNK_INPUT_BACKEND=wlr
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
# GPU zero-copy capture→encode is ON by default; auto-falls back to CPU. Set PUNKTFUNK_ZEROCOPY=0 to force CPU. # GPU zero-copy capture→encode is ON by default; auto-falls back to CPU. Set PUNKTFUNK_ZEROCOPY=0 to force CPU.
``` ```
To force the backend (CI/testing — note that pinning turns live-session auto-detection **off**, so
the host stops following session switches):
```ini
PUNKTFUNK_COMPOSITOR=wlroots # aliases: sway, wlr (the wlroots-proper family)
PUNKTFUNK_INPUT_BACKEND=wlr
```
See [Configuration](/docs/configuration) for the full reference. See [Configuration](/docs/configuration) for the full reference.
## How it works ## How it works
+16 -1
View File
@@ -103,7 +103,22 @@ See [GNOME](/docs/gnome) for the GL/EGL userspace details.
- KWin must be **≥ 6.5.6** (`kwin_wayland --version`); GNOME **≥ 48**; gamescope **≥ 3.16.22**. See - KWin must be **≥ 6.5.6** (`kwin_wayland --version`); GNOME **≥ 48**; gamescope **≥ 3.16.22**. See
[KDE](/docs/kde) for the KWin/Wayland requirement and [gamescope](/docs/gamescope) for the [KDE](/docs/kde) for the KWin/Wayland requirement and [gamescope](/docs/gamescope) for the
gamescope one. gamescope one.
- Confirm `PUNKTFUNK_COMPOSITOR` in [`host.env`](/docs/configuration) matches your desktop. - If [`host.env`](/docs/configuration) sets `PUNKTFUNK_COMPOSITOR`, **remove it** — the host
auto-detects the live compositor, and the pin points it at one backend even when a different
session is live (it also disables Gaming ↔ Desktop following).
## Session fails right after editing host.env
- Keys are **case-sensitive**: `punktfunk_gamescope_attach=1` sets nothing — use the exact
uppercase names.
- Hardcoded session anchors with the wrong uid (`XDG_RUNTIME_DIR=/run/user/1000` when `id -u`
isn't 1000) point the host at another user's PipeWire/D-Bus: audio errors like
`pw audio connect … Creation failed`, no capture, and clients reporting the host as
unreachable or asleep. **Delete both anchor lines** — a `systemctl --user` service doesn't need
them — or fix the uid.
- `PUNKTFUNK_COMPOSITOR` pins the backend and disables Gaming ↔ Desktop following — remove it on
any box that switches sessions.
- The env file is read at service start: `systemctl --user restart punktfunk-host` after edits.
## Capture fails: "Session creation inhibited" (GNOME) ## Capture fails: "Session creation inhibited" (GNOME)
+17 -24
View File
@@ -234,33 +234,25 @@ cp /usr/share/punktfunk/host.env.bazzite ~/.config/punktfunk/host.env
The Bazzite template (`packaging/bazzite/host.env`) contains: The Bazzite template (`packaging/bazzite/host.env`) contains:
```sh ```sh
XDG_RUNTIME_DIR=/run/user/1000
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
# gamescope backend: spawned per session, no compositor login required.
PUNKTFUNK_COMPOSITOR=gamescope
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
PUNKTFUNK_GAMESCOPE_APP=steam -gamepadui
# gamescope hosts its own EIS input socket — input lands in the nested session.
PUNKTFUNK_INPUT_BACKEND=gamescope
# GPU zero-copy capture (dmabuf -> CUDA -> NVENC) is ON by default and auto-falls back to CPU if # GPU zero-copy capture (dmabuf -> CUDA -> NVENC) is ON by default and auto-falls back to CPU if
# unavailable. No need to set it. Set to 0 only to force the CPU path. # unavailable. No need to set it. Set to 0 only to force the CPU path.
# PUNKTFUNK_ZEROCOPY=0 # PUNKTFUNK_ZEROCOPY=0
#RUST_LOG=info #RUST_LOG=info
# Gaming Mode = ATTACH: the box owns its gamescope session; the host captures + follows it.
PUNKTFUNK_GAMESCOPE_ATTACH=1
``` ```
**What each knob means and why these are the Bazzite defaults:** **What each knob means and why these are the Bazzite defaults:**
| Knob | Value | Meaning | | Knob | Value | Meaning |
|---|---|---| |---|---|---|
| `XDG_RUNTIME_DIR` / `DBUS_SESSION_BUS_ADDRESS` | `…/user/1000` | Session bus / runtime dir. **`1000` assumes your user is UID 1000** — change both if `id -u` says otherwise. | | *(no compositor / no anchors)* | — | The host **auto-detects** the live session per connect (Gaming Mode gamescope vs the KDE desktop) and follows switches mid-stream; a `systemctl --user` service inherits the right `XDG_RUNTIME_DIR` and the host derives the bus itself. Pinning `PUNKTFUNK_COMPOSITOR` or hardcoding uid-1000 anchors only breaks this — leave them out. |
| `PUNKTFUNK_COMPOSITOR` | `gamescope` | **The Bazzite default.** The host spawns a **headless gamescope per session** at the client's exact resolution/refresh and captures its PipeWire node — so you need **no graphical desktop login** to stream. Bazzite ships gamescope, so this "just works." |
| `PUNKTFUNK_VIDEO_SOURCE` | `virtual` | Create a per-client virtual output at the client's exact WxH@Hz (the flagship "native resolution, no scaling" mode), vs. `portal` which captures an existing monitor. | | `PUNKTFUNK_VIDEO_SOURCE` | `virtual` | Create a per-client virtual output at the client's exact WxH@Hz (the flagship "native resolution, no scaling" mode), vs. `portal` which captures an existing monitor. |
| `PUNKTFUNK_GAMESCOPE_APP` | `steam -gamepadui` | The command launched **inside** the nested gamescope — here, a SteamOS-style couch UI. Set it to whatever you want the session to run. | | `PUNKTFUNK_GAMESCOPE_ATTACH` | `1` | Gaming Mode model: the **box** owns its gamescope session; the host attaches to whatever's live and never tears it down. Swap for `PUNKTFUNK_GAMESCOPE_MANAGED=1` to have the host relaunch the gaming session headless at the **client's** exact mode instead (see the template's comments). |
| `PUNKTFUNK_INPUT_BACKEND` | `gamescope` | Inject mouse/keyboard/gamepad into the nested gamescope via its own EIS socket. |
| `PUNKTFUNK_ZEROCOPY` | `on` *(default)* | GPU zero-copy capture (dmabuf → CUDA → NVENC), on by default. Falls back to CPU automatically if unavailable; set `0` to force the CPU path. | | `PUNKTFUNK_ZEROCOPY` | `on` *(default)* | GPU zero-copy capture (dmabuf → CUDA → NVENC), on by default. Falls back to CPU automatically if unavailable; set `0` to force the CPU path. |
| `RUST_LOG` | (commented) | Uncomment `RUST_LOG=info` for verbose logs while debugging. | | `RUST_LOG` | (commented) | Uncomment `RUST_LOG=info` for verbose logs while debugging. |
@@ -268,16 +260,14 @@ PUNKTFUNK_INPUT_BACKEND=gamescope
games a virtual Sony DualSense (lightbar, adaptive triggers, touchpad, motion) instead of the games a virtual Sony DualSense (lightbar, adaptive triggers, touchpad, motion) instead of the
default X-Box-360 pad. The feedback flows back to a real DualSense on the client. default X-Box-360 pad. The feedback flows back to a real DualSense on the client.
**Alternative — drive the full Plasma/GNOME desktop** instead of a nested gamescope (per the **The Plasma desktop needs no extra config:** the same auto-detection streams the KDE Desktop
template's footer comment): switch to `PUNKTFUNK_COMPOSITOR=kwin` and session whenever that's what's live — no compositor pin, no `WAYLAND_DISPLAY` /
`PUNKTFUNK_INPUT_BACKEND=libei`, and run the host **inside** a KDE session with `WAYLAND_DISPLAY` / `XDG_CURRENT_DESKTOP` (the host retargets those per connect). The full knob list (FEC %, per-stage
`XDG_CURRENT_DESKTOP` set. The full knob list (FEC %, per-stage timing, etc.) is in timing, etc.) is in `scripts/host.env.example` / `/usr/share/punktfunk/host.env.example`.
`scripts/host.env.example` / `/usr/share/punktfunk/host.env.example`.
> The gamescope default is what makes Bazzite the easy path: it's a **headless, per-session** > Auto-detection is what makes Bazzite the easy path: the host follows the box between Gaming Mode
> compositor — no desktop login, no display manager, no `--drm` scanout. You don't need any of the > and the Desktop — even mid-stream — with a one-line config. You don't need any of the
> headless-KDE bring-up scripts (`scripts/headless/run-headless-kde.sh`) on Bazzite unless you > headless-KDE bring-up scripts (`scripts/headless/run-headless-kde.sh`) on Bazzite.
> deliberately switch to the KWin backend.
--- ---
@@ -474,8 +464,11 @@ desktop viewer.
NVIDIA driver. The code falls back to CPU automatically; check the log for the fallback line and NVIDIA driver. The code falls back to CPU automatically; check the log for the fallback line and
verify the `-nvidia` image / driver is healthy. verify the `-nvidia` image / driver is healthy.
- **Wrong UID in `host.env`.** `XDG_RUNTIME_DIR=/run/user/1000` and the bus path assume UID 1000. Run - **Session anchors in `host.env`.** The template no longer sets `XDG_RUNTIME_DIR` /
`id -u`; if it's different, fix both lines or the host can't reach your session's PipeWire/D-Bus. `DBUS_SESSION_BUS_ADDRESS` — a `systemctl --user` service inherits the right values. If an older
config hardcodes them with the wrong uid (`/run/user/1000` when `id -u` isn't 1000), the host
points at another user's PipeWire/D-Bus and everything fails (`pw audio connect … Creation
failed`, no capture). Delete both lines, or fix the uid.
- **Service `ExecStart` points at a missing path in `$HOME`.** The dev unit references - **Service `ExecStart` points at a missing path in `$HOME`.** The dev unit references
`%h/punktfunk/target/release/...`. The RPM binary is `/usr/bin/punktfunk-host`. Override `%h/punktfunk/target/release/...`. The RPM binary is `/usr/bin/punktfunk-host`. Override
+8 -8
View File
@@ -3,10 +3,9 @@
# The compositor + input backend are AUTO-DETECTED per connect from the ACTIVE session: the host # The compositor + input backend are AUTO-DETECTED per connect from the ACTIVE session: the host
# follows the box as you flip between Steam Gaming Mode (gamescope — a managed session at the # follows the box as you flip between Steam Gaming Mode (gamescope — a managed session at the
# CLIENT's resolution) and a KDE/GNOME Desktop (KWin/Mutter virtual output at the client's mode). # CLIENT's resolution) and a KDE/GNOME Desktop (KWin/Mutter virtual output at the client's mode).
# So nothing here forces a backend — only the trustworthy anchors stay. # So nothing here forces a backend, and no session anchors are needed: a `systemctl --user`
# service inherits the correct XDG_RUNTIME_DIR and the host derives the bus from it. (Keys are
XDG_RUNTIME_DIR=/run/user/1000 # CASE-SENSITIVE — use the exact uppercase names.)
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus
PUNKTFUNK_VIDEO_SOURCE=virtual PUNKTFUNK_VIDEO_SOURCE=virtual
@@ -23,10 +22,11 @@ PUNKTFUNK_VIDEO_SOURCE=virtual
# #
# GAME MODE = ATTACH (the box owns its session; the host follows). The box decides whether it's in # GAME MODE = ATTACH (the box owns its session; the host follows). The box decides whether it's in
# Steam Gaming Mode or a Desktop — you switch with the normal Steam UI / "Switch to Desktop". The # Steam Gaming Mode or a Desktop — you switch with the normal Steam UI / "Switch to Desktop". The
# host just ATTACHES to whatever's live and captures it; it never tears the session down or relaunches # host just ATTACHES to whatever's live and captures it; it never tears the session down. So
# it. So switching Desktop<->Game is rock-solid, and when you disconnect the box STAYS in its current # switching Desktop<->Game is rock-solid, and when you disconnect the box STAYS in its current
# mode — reconnecting drops you right back where you were. The streamed resolution in game mode is the # mode — reconnecting drops you right back where you were. On a resolution mismatch the host
# box's gamescope mode (see SCREEN_WIDTH/HEIGHT in /etc/gamescope-session-plus/sessions.d/steam). # restarts the box's own game-mode session at the CLIENT's resolution (a foreign/bare gamescope
# instead streams at its own mode).
PUNKTFUNK_GAMESCOPE_ATTACH=1 PUNKTFUNK_GAMESCOPE_ATTACH=1
# #
# Opt OUT to the MANAGED model instead (host tears the box's gamescope down on connect and launches # Opt OUT to the MANAGED model instead (host tears the box's gamescope down on connect and launches
+7
View File
@@ -1,4 +1,11 @@
# punktfunk host config for a Fedora/Ubuntu KDE Plasma appliance (kwin backend). # punktfunk host config for a Fedora/Ubuntu KDE Plasma appliance (kwin backend).
#
# APPLIANCE-ONLY: this file deliberately PINS the backend (PUNKTFUNK_COMPOSITOR) and the session
# env (WAYLAND_DISPLAY/XDG_CURRENT_DESKTOP) at the dedicated headless KWin session — which also
# turns OFF the host's live-session auto-detection and Desktop<->Game following. On a normal
# desktop (or any box that switches to Steam Game Mode) do NOT use this file; start from
# host.env.example instead, whose defaults auto-detect and follow the live session.
#
# Copy to ~/.config/punktfunk/host.env. Pairs with punktfunk-kde-session.service, which brings # Copy to ~/.config/punktfunk/host.env. Pairs with punktfunk-kde-session.service, which brings
# up a headless `kwin --virtual` on wayland-kde (with KWIN_WAYLAND_NO_PERMISSION_CHECKS=1 so the # up a headless `kwin --virtual` on wayland-kde (with KWIN_WAYLAND_NO_PERMISSION_CHECKS=1 so the
# host can bind KWin's privileged zkde_screencast protocol — an interactive Plasma session will # host can bind KWin's privileged zkde_screencast protocol — an interactive Plasma session will
+4 -2
View File
@@ -168,14 +168,16 @@ Everything the RPM's `%install` + `%post` do, declaratively:
### Headless / appliance ### Headless / appliance
Set `autoStart = true`, enable lingering, and pick a backend in `settings`: Set `autoStart = true`, enable lingering, and — for a **dedicated single-session appliance**
pin a backend in `settings` (pinning `PUNKTFUNK_COMPOSITOR` disables live-session auto-detection,
so leave it out on any box that switches between a desktop and Game Mode):
```nix ```nix
services.punktfunk.host = { services.punktfunk.host = {
enable = true; enable = true;
autoStart = true; autoStart = true;
users = [ "streamer" ]; users = [ "streamer" ];
settings = { PUNKTFUNK_COMPOSITOR = "gamescope"; }; # or kwin/mutter/wlroots settings = { PUNKTFUNK_COMPOSITOR = "gamescope"; }; # appliance-only; omit to auto-detect
}; };
users.users.streamer.linger = true; users.users.streamer.linger = true;
# For the gamescope/KWin backends extend the service PATH, e.g.: # For the gamescope/KWin backends extend the service PATH, e.g.:
+44 -35
View File
@@ -1,16 +1,17 @@
# punktfunk host configuration (~/.config/punktfunk/host.env) — consumed by punktfunk-host.service. # punktfunk host configuration (~/.config/punktfunk/host.env) — consumed by punktfunk-host.service.
# #
# The compositor + input backend are AUTO-DETECTED per connect from the live session (the host # YOU BARELY NEED THIS FILE. The host AUTO-DETECTS the live session per connect — which compositor
# probes which compositor is actually running and retargets WAYLAND_DISPLAY/XDG_CURRENT_DESKTOP/ # is running (KWin / Mutter / sway / Hyprland / gamescope), its Wayland socket, session bus, and the
# DBUS at it), so a box that flips between Steam Gaming Mode and a KDE/GNOME desktop is followed # matching input backend — and FOLLOWS the box when it switches between a desktop and Steam Gaming
# automatically. The blocks below are OPTIONAL OVERRIDES — uncomment one only to force a backend # Mode, even mid-stream. Everything below except PUNKTFUNK_VIDEO_SOURCE is an optional override.
# (this also skips the per-connect env retargeting). The anchors XDG_RUNTIME_DIR + DBUS stay. #
# Two rules that save debugging sessions:
# Session / compositor environment (headless KWin example). # * Keys are CASE-SENSITIVE. `punktfunk_gamescope_attach=1` sets nothing — use the exact
XDG_RUNTIME_DIR=/run/user/1000 # uppercase names.
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus # * On a desktop you actually use, do NOT set PUNKTFUNK_COMPOSITOR / WAYLAND_DISPLAY /
WAYLAND_DISPLAY=wayland-kde # XDG_CURRENT_DESKTOP. Pinning the compositor DISABLES session-following (a switch to Game
XDG_CURRENT_DESKTOP=KDE # Mode mid-stream then kills the stream instead of being followed), and stale session vars
# point detection at dead sockets. Those knobs are for CI and dedicated appliances (below).
# Video source: `virtual` creates a per-client virtual output at the client's exact # Video source: `virtual` creates a per-client virtual output at the client's exact
# resolution+refresh (the flagship mode); `portal` captures an existing monitor. # resolution+refresh (the flagship mode); `portal` captures an existing monitor.
@@ -20,33 +21,41 @@ PUNKTFUNK_VIDEO_SOURCE=virtual
# CPU automatically. No need to set it. Set to 0 only to force the CPU path. # CPU automatically. No need to set it. Set to 0 only to force the CPU path.
# PUNKTFUNK_ZEROCOPY=0 # PUNKTFUNK_ZEROCOPY=0
# --- Bazzite / SteamOS-like host: host-managed Steam-Deck-UI session ----------------------- # --- Session anchors (rarely needed) -------------------------------------------------------
# The host LAUNCHES gamescope-session-plus headless AT THE CLIENT'S mode (so games see the # As a `systemctl --user` service the host inherits the correct XDG_RUNTIME_DIR from systemd and
# client's exact resolution + refresh, not the box's TV), and relaunches it when the mode # derives the bus (`unix:path=$XDG_RUNTIME_DIR/bus`) itself. Set these ONLY when running the host
# changes. Requires the headless-appliance prereqs (linger + multi-user.target — see # outside a user service (ssh, cron) — and with YOUR uid (`id -u`), never a copy-pasted 1000: a
# punktfunk-steam-session.service header) and NO physical gaming session running. # wrong uid points the host at another user's (nonexistent) PipeWire/D-Bus, and every session
#PUNKTFUNK_COMPOSITOR=gamescope # fails with errors like "pw audio connect … Creation failed".
#PUNKTFUNK_GAMESCOPE_SESSION=steam # host owns a gamescope-session-plus session at the client mode #XDG_RUNTIME_DIR=/run/user/<uid>
#PUNKTFUNK_INPUT_BACKEND=gamescope #DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<uid>/bus
# Mutually exclusive with the above: ATTACH to a gamescope session something ELSE owns (fixed mode):
#PUNKTFUNK_GAMESCOPE_NODE=auto # discover + capture a running gamescope (do NOT combine with SESSION)
# --- GNOME / Mutter host (e.g. an Ubuntu desktop) ----------------------------------------- # --- Steam Gaming Mode (Linux boxes with gamescope session infra: Bazzite/SteamOS/Nobara) ---
# Attach to a running GNOME (Wayland) session — its default socket is wayland-0, not wayland-kde. # Game Mode is auto-handled; two models decide WHERE it runs when a client streams:
# Mutter creates the per-client virtual output via its `RecordVirtual` D-Bus API (a virtual # * MANAGED (the default where session infra is detected) — the host relaunches the gaming
# monitor alongside any real one), and input goes through the RemoteDesktop portal (libei). On a # session HEADLESS at the CLIENT's exact mode ("game mode on the virtual screen"); physical
# real desktop the host runs as the logged-in user; headless GNOME also works (gnome-shell # displays drop out of it, and the box is restored on a debounced idle after disconnect.
# --headless). Needs GNOME ≥ 48 for the zero-copy RecordVirtual path. # * ATTACH — the BOX owns its session: Game Mode stays on the physical screen and the host
#WAYLAND_DISPLAY=wayland-0 # captures/follows it, never tearing it down. Reconnects land wherever the box is.
#XDG_CURRENT_DESKTOP=GNOME #PUNKTFUNK_GAMESCOPE_ATTACH=1 # pick the ATTACH model
#PUNKTFUNK_COMPOSITOR=mutter #PUNKTFUNK_GAMESCOPE_MANAGED=1 # force MANAGED even where infra detection wouldn't pick it
#PUNKTFUNK_VIDEO_SOURCE=virtual #PUNKTFUNK_GAMESCOPE_SESSION=steam # host owns a gamescope-session-plus session at the client mode
#PUNKTFUNK_INPUT_BACKEND=libei #PUNKTFUNK_GAMESCOPE_NODE=auto # raw attach: discover + capture a running gamescope's node
# # (do NOT combine with SESSION)
#PUNKTFUNK_GAMESCOPE_APP=vkcube # nested command for ad-hoc bare-gamescope sessions
#PUNKTFUNK_SESSION_WATCH=0 # disable mid-stream Desktop<->Game following (on by default
# # on gamescope-infra boxes)
# --- Force a backend (CI / tests / dedicated single-session appliances ONLY) ---------------
# PINS the backend: the host stops following the live session entirely — per connect AND
# mid-stream. Fine for a dedicated headless appliance (punktfunk-kde-session.service, a pure
# gamescope box) or a CI run; wrong for any box that switches sessions.
#PUNKTFUNK_COMPOSITOR=kwin # kwin | mutter | gamescope | wlroots | hyprland
#PUNKTFUNK_INPUT_BACKEND=libei # wlr | libei | gamescope | uinput (auto-routed per connect)
#WAYLAND_DISPLAY=wayland-kde # headless-KDE appliance socket; retargeted per connect otherwise
#XDG_CURRENT_DESKTOP=KDE
# Optional overrides (apps.json is the primary mechanism for per-app settings): # Optional overrides (apps.json is the primary mechanism for per-app settings):
#PUNKTFUNK_COMPOSITOR=kwin # kwin | mutter | gamescope | wlroots
#PUNKTFUNK_GAMESCOPE_APP=vkcube # nested command for ad-hoc bare-gamescope sessions
#PUNKTFUNK_INPUT_BACKEND=libei # wlr | libei | gamescope | uinput
#PUNKTFUNK_FEC_PCT=20 # video FEC overhead percent #PUNKTFUNK_FEC_PCT=20 # video FEC overhead percent
#PUNKTFUNK_PERF=1 # per-stage timing logs #PUNKTFUNK_PERF=1 # per-stage timing logs
#PUNKTFUNK_MDNS=0 # disable the mDNS adverts (native + GameStream) — for multicast- #PUNKTFUNK_MDNS=0 # disable the mDNS adverts (native + GameStream) — for multicast-
+8 -5
View File
@@ -2,15 +2,18 @@
# GameStream/Moonlight-compat planes). For a SECURE native-only host (no plain-HTTP pairing / legacy # GameStream/Moonlight-compat planes). For a SECURE native-only host (no plain-HTTP pairing / legacy
# GCM nonce reuse — security-review #5/#9; native clients only), drop `--gamestream` from ExecStart. # GCM nonce reuse — security-review #5/#9; native clients only), drop `--gamestream` from ExecStart.
# #
# Install (against an already-running compositor session): # Install (against an already-running compositor session — the host auto-detects and follows it,
# so host.env needs no backend config):
# mkdir -p ~/.config/systemd/user && cp scripts/punktfunk-host.service ~/.config/systemd/user/ # mkdir -p ~/.config/systemd/user && cp scripts/punktfunk-host.service ~/.config/systemd/user/
# cp scripts/host.env.example ~/.config/punktfunk/host.env # then edit for your backend # cp scripts/host.env.example ~/.config/punktfunk/host.env # defaults are right for a desktop
# systemctl --user daemon-reload && systemctl --user enable --now punktfunk-host # systemctl --user daemon-reload && systemctl --user enable --now punktfunk-host
# #
# Self-contained boot appliance (no login, no manual steps after boot): # Self-contained boot appliance (no login, no manual steps after boot). These routes PIN the
# backend via PUNKTFUNK_COMPOSITOR — correct for a dedicated single-session box, but it turns off
# live-session auto-detection, so never do it on a desktop that switches sessions (Game Mode etc.):
# - kwin backend (stream the Plasma desktop): also install + enable # - kwin backend (stream the Plasma desktop): also install + enable
# punktfunk-kde-session.service (it brings up the headless KWin session this After=s), and set # punktfunk-kde-session.service (it brings up the headless KWin session this After=s), and use
# PUNKTFUNK_COMPOSITOR=kwin + WAYLAND_DISPLAY=wayland-kde in host.env. # the shipped packaging/kde/host.env (pins kwin + WAYLAND_DISPLAY=wayland-kde on purpose).
# - gamescope backend (stream a nested app, no desktop): set PUNKTFUNK_COMPOSITOR=gamescope in # - gamescope backend (stream a nested app, no desktop): set PUNKTFUNK_COMPOSITOR=gamescope in
# host.env — the host spawns gamescope per session, so no kde-session unit is needed. # host.env — the host spawns gamescope per session, so no kde-session unit is needed.
# Then `sudo loginctl enable-linger "$USER"` so user units start at boot, and reboot. # Then `sudo loginctl enable-linger "$USER"` so user units start at boot, and reboot.