From 8c58afa2ac823e92d87ad4bacbb249b2f3b0623e Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 11 Jun 2026 21:16:12 +0000 Subject: [PATCH] feat(headless): boot-appliance systemd units (KDE session + host, no login) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make a headless box a self-contained streaming appliance: after boot, with no display manager / login / manual script, the headless KWin Plasma session and the punktfunk host both come up so a client can just connect and stream the desktop. - New scripts/punktfunk-kde-session.service: a Type=simple user unit that runs run-headless-kde.sh (kwin --virtual on wayland-kde + Plasma + portals + a supervised plasmashell). The script foregrounds on `wait $KWIN_PID`, so Restart=always keeps the desktop alive across a KWin crash. - scripts/punktfunk-host.service: ExecStart now `serve --native` (the unified GameStream + punktfunk/1 host, matching how it's actually run), After= the kde-session unit (soft ordering — the host listens immediately and only needs the compositor per session, so a missing unit on the gamescope backend is harmless), and appliance install docs (kwin vs gamescope backend). Boot still requires `sudo loginctl enable-linger $USER` (the one thing that starts user units without a login) — documented in both unit headers. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/punktfunk-host.service | 26 ++++++++++++++------- scripts/punktfunk-kde-session.service | 33 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 scripts/punktfunk-kde-session.service diff --git a/scripts/punktfunk-host.service b/scripts/punktfunk-host.service index f168d3d..d95f5f6 100644 --- a/scripts/punktfunk-host.service +++ b/scripts/punktfunk-host.service @@ -1,19 +1,29 @@ -# punktfunk streaming host — systemd USER unit. +# punktfunk streaming host — systemd USER unit (`serve --native` = GameStream + punktfunk/1). # -# Install: +# Install (against an already-running compositor session): # mkdir -p ~/.config/systemd/user && cp scripts/punktfunk-host.service ~/.config/systemd/user/ -# cp scripts/host.env.example ~/.config/punktfunk/host.env # then edit +# cp scripts/host.env.example ~/.config/punktfunk/host.env # then edit for your backend # systemctl --user daemon-reload && systemctl --user enable --now punktfunk-host # -# The unit assumes the compositor session (e.g. headless KWin on wayland-kde) is already up; -# for a fully self-contained appliance, pair it with a kwin_wayland user unit it can After=. +# Self-contained boot appliance (no login, no manual steps after boot): +# - 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_COMPOSITOR=kwin + WAYLAND_DISPLAY=wayland-kde in host.env. +# - 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. +# Then `sudo loginctl enable-linger "$USER"` so user units start at boot, and reboot. +# +# The host LISTENS as soon as it starts and only touches the compositor per session (on a client +# connect), so the After= below is a soft ordering, not a hard readiness gate — the kde-session +# unit (when present) just needs to be up by the time a client streams (seconds later, user-driven). +# A missing After= unit (e.g. gamescope backend, no kde-session installed) is simply ignored. [Unit] -Description=punktfunk GameStream host -After=pipewire.service +Description=punktfunk GameStream + punktfunk/1 host +After=pipewire.service punktfunk-kde-session.service [Service] EnvironmentFile=%h/.config/punktfunk/host.env -ExecStart=%h/punktfunk/target/release/punktfunk-host serve +ExecStart=%h/punktfunk/target/release/punktfunk-host serve --native Restart=on-failure RestartSec=2 diff --git a/scripts/punktfunk-kde-session.service b/scripts/punktfunk-kde-session.service new file mode 100644 index 0000000..fa9cc6e --- /dev/null +++ b/scripts/punktfunk-kde-session.service @@ -0,0 +1,33 @@ +# punktfunk headless KDE Plasma session — systemd USER unit. +# +# Brings up `kwin --virtual` on WAYLAND_DISPLAY=wayland-kde plus the full Plasma desktop +# (portals, polkit agent, a supervised plasmashell) via run-headless-kde.sh, so the punktfunk +# host's *kwin* backend can stream the live desktop. This is what makes a headless box a +# self-contained streaming appliance: no display manager, no login, no manual script — the +# session comes up at boot. Pair it with punktfunk-host.service (kwin backend) + linger. +# +# The script foregrounds on `wait $KWIN_PID` and re-spawns plasmashell for KWin's lifetime, so +# Type=simple + Restart=always keeps the desktop alive (a KWin crash brings the whole session +# back). It sets its own env (XDG_CURRENT_DESKTOP=KDE, WAYLAND_DISPLAY=wayland-kde, +# XDG_MENU_PREFIX=plasma-, …) — see run-headless-kde.sh. +# +# Appliance install (boot with no interaction): +# cp scripts/punktfunk-kde-session.service scripts/punktfunk-host.service ~/.config/systemd/user/ +# cp scripts/host.env.example ~/.config/punktfunk/host.env # edit for the kwin backend +# systemctl --user daemon-reload +# systemctl --user enable punktfunk-kde-session punktfunk-host +# sudo loginctl enable-linger "$USER" # <-- starts user units at boot WITHOUT a login +# reboot # (or: systemctl --user start … to bring up now) +[Unit] +Description=punktfunk headless KDE Plasma session (kwin --virtual on wayland-kde) +After=pipewire.service pipewire-pulse.service dbus.service +Wants=pipewire.service + +[Service] +Type=simple +ExecStart=/usr/bin/bash %h/punktfunk/scripts/headless/run-headless-kde.sh 1920x1080 +Restart=always +RestartSec=3 + +[Install] +WantedBy=default.target