47990 is the management API's port and also Sunshine's (and Apollo's, and Vibeshine's) web UI port. With the GameStream planes off it is the ONLY port the two still share, so moving it is the whole of what "run both on one box" needs — except moving it was barely possible: * `--mgmt-bind` was the sole route, and it lives in a unit file / service registration that a package upgrade rewrites. There was no `host.env` key, so the change did not survive. * The literal 47990 appeared in SIX places — mgmt::DEFAULT_PORT, the Windows service's console launch, scripts/punktfunk-web.service, the NixOS module, web/web-run.cmd, and the console's own default. Nothing downstream could learn a different port, so moving the listener silently left the console proxying to a port nothing was listening on. Now there is one source of truth. `PUNKTFUNK_MGMT_BIND` joins `host.env` (the `--gamestream` / PUNKTFUNK_GAMESTREAM shape: either source works, the CLI flag wins), and `serve` publishes the port it ACTUALLY bound to ~/.config/punktfunk/mgmt-endpoint, in the same KEY=VALUE form mgmt-token already uses so it is sourceable as a systemd EnvironmentFile and readable by the Windows service's existing read_env_file_value. Every consumer derives from that; the 47990 literals survive only as the fallback that keeps an OLD host working with a NEW console. The two unit files drop their hardcoded `Environment=PUNKTFUNK_MGMT_URL=` rather than layering a default beneath the file: whether Environment= or EnvironmentFile= wins is a directive-ordering question, and the hand-written unit and the Nix-generated one do not order the same way. No default, no precedence puzzle — the server's own built-in fallback covers a host that never wrote the file. Two robustness details worth naming, because both fail in the same direction: * mgmt-endpoint is written write-then-rename. A torn read would set PUNKTFUNK_MGMT_URL to EMPTY, which is worse than a missing file — a built-in default only rescues an *unset* variable. * mgmtUrl() now treats blank as unset, which `??` alone does not. The publish happens in parse_serve next to the token persistence, so both files appear together; the console's unit gates on mgmt-token, and its Restart=always picks up a lost race anyway. What this does NOT change: a lost 47990 bind is still fatal to the whole host (the bind sits in tokio::try_join! with the native plane), and running two Moonlight-compatible hosts at once is still unsupported — on Windows the exclusive display topology is a second, independent conflict. Both are documented rather than altered. Verified on Linux in punktfunk-rust-ci (amd64): cargo check --all-targets clean for punktfunk-host and pf-host-config with the "Checking punktfunk-host" marker confirmed present (a first run exited 0 having compiled nothing — the warm shared target dir judged it fresh), 40/40 mgmt tests pass including the new one pinning the published line against both parsers that consume it. Console: tsc --noEmit clean, bun test server/ 9/9. cargo fmt --all --check clean.
137 lines
10 KiB
Bash
137 lines
10 KiB
Bash
# punktfunk host configuration (~/.config/punktfunk/host.env) — consumed by punktfunk-host.service.
|
|
#
|
|
# YOU BARELY NEED THIS FILE. The host AUTO-DETECTS the live session per connect — which compositor
|
|
# is running (KWin / Mutter / sway / Hyprland / gamescope), its Wayland socket, session bus, and the
|
|
# matching input backend — and FOLLOWS the box when it switches between a desktop and Steam Gaming
|
|
# Mode, even mid-stream. EVERYTHING below is an optional override — the host runs with sane
|
|
# defaults if this file does not exist at all (its systemd unit no longer requires it).
|
|
#
|
|
# Two rules that save debugging sessions:
|
|
# * Keys are CASE-SENSITIVE. `punktfunk_gamescope_attach=1` sets nothing — use the exact
|
|
# uppercase names.
|
|
# * On a desktop you actually use, do NOT set PUNKTFUNK_COMPOSITOR / WAYLAND_DISPLAY /
|
|
# XDG_CURRENT_DESKTOP. Pinning the compositor DISABLES session-following (a switch to Game
|
|
# 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).
|
|
|
|
# GameStream/Moonlight compatibility — OPT-IN (the shipped unit runs the secure native-only
|
|
# host). Set =1 so stock Moonlight clients can pair and stream; TRUSTED LANs only — the compat
|
|
# planes carry plain-HTTP pairing + the legacy GCM-nonce path (security-review #5/#9), and need
|
|
# their extra firewall ports (TCP 47984/47989/48010, UDP 47998-48000/48002/48010; the packages
|
|
# ship a `punktfunk-gamestream` firewalld service / ufw profile for exactly this).
|
|
#PUNKTFUNK_GAMESTREAM=1
|
|
|
|
# Where the management API listens (default 0.0.0.0:47990). Two uses:
|
|
# * 127.0.0.1:47990 keeps it off the LAN — at the cost of paired clients browsing your library.
|
|
# * MOVING THE PORT is how you share a machine with Sunshine/Apollo/Vibeshine: 47990 is their web
|
|
# UI as well as our management API, and with PUNKTFUNK_GAMESTREAM off it is the ONLY port the
|
|
# two still share. Nothing else needs editing — clients learn the port from discovery and the
|
|
# web console reads it from ~/.config/punktfunk/mgmt-endpoint, which the host rewrites on start.
|
|
# Running two Moonlight-compatible hosts at once is still unsupported; see the troubleshooting
|
|
# page. On Windows also see PUNKTFUNK_NO_ISOLATE — the display topology is the second conflict.
|
|
#PUNKTFUNK_MGMT_BIND=0.0.0.0:47991
|
|
|
|
# Video source (GameStream/Moonlight sessions only): `virtual` creates a per-client virtual
|
|
# output at the client's exact resolution+refresh (the flagship mode, and the default);
|
|
# `portal` captures an existing monitor.
|
|
#PUNKTFUNK_VIDEO_SOURCE=virtual
|
|
|
|
# GPU zero-copy capture (dmabuf → CUDA → NVENC / VAAPI / Vulkan) is ON by default and falls back to
|
|
# CPU automatically. No need to set it. Set to 0 only to force the CPU path.
|
|
# PUNKTFUNK_ZEROCOPY=0
|
|
|
|
# The name this host shows up under in Moonlight and the Punktfunk clients. Defaults to the
|
|
# machine's hostname; set it to give the box a friendly name without renaming the machine.
|
|
#PUNKTFUNK_HOST_NAME=Living Room
|
|
|
|
# --- Session anchors (rarely needed) -------------------------------------------------------
|
|
# As a `systemctl --user` service the host inherits the correct XDG_RUNTIME_DIR from systemd and
|
|
# derives the bus (`unix:path=$XDG_RUNTIME_DIR/bus`) itself. Set these ONLY when running the host
|
|
# outside a user service (ssh, cron) — and with YOUR uid (`id -u`), never a copy-pasted 1000: a
|
|
# wrong uid points the host at another user's (nonexistent) PipeWire/D-Bus, and every session
|
|
# fails with errors like "pw audio connect … Creation failed".
|
|
#XDG_RUNTIME_DIR=/run/user/<uid>
|
|
#DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/<uid>/bus
|
|
|
|
# --- Steam Gaming Mode (Linux boxes with gamescope session infra: Bazzite/SteamOS/Nobara) ---
|
|
# Game Mode is auto-handled; two models decide WHERE it runs when a client streams:
|
|
# * MANAGED (the default where session infra is detected) — the host relaunches the gaming
|
|
# session HEADLESS at the CLIENT's exact mode ("game mode on the virtual screen"); physical
|
|
# displays drop out of it, and the box is restored on a debounced idle after disconnect.
|
|
# * ATTACH — the BOX owns its session: Game Mode stays on the physical screen and the host
|
|
# captures/follows it, never tearing it down. Reconnects land wherever the box is.
|
|
#PUNKTFUNK_GAMESCOPE_ATTACH=1 # pick the ATTACH model
|
|
#PUNKTFUNK_GAMESCOPE_MANAGED=1 # force MANAGED even where infra detection wouldn't pick it
|
|
#PUNKTFUNK_GAMESCOPE_SESSION=steam # host owns a gamescope-session-plus session at the client mode
|
|
#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=kwin # wlr | kwin | libei | gamescope — anything else logs "unknown
|
|
# PUNKTFUNK_INPUT_BACKEND" and auto-detects instead:
|
|
# PUNKTFUNK_COMPOSITOR above wins (except mutter), else
|
|
# XDG_CURRENT_DESKTOP — KDE -> kwin, GNOME -> libei, else wlr
|
|
#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):
|
|
#PUNKTFUNK_FEC_PCT=20 # video FEC overhead percent
|
|
#PUNKTFUNK_PERF=1 # per-stage timing logs
|
|
#PUNKTFUNK_MDNS=0 # disable the mDNS adverts (native + GameStream) — for multicast-
|
|
# dead networks/containers; clients add the host by address instead
|
|
# Display-management policy (keep-alive · topology · conflict · identity · layout · max) is set in the
|
|
# web console (Host → Virtual displays) → ~/.config/punktfunk/display-settings.json, NOT here; a
|
|
# settings file supersedes the legacy PUNKTFUNK_MONITOR_LINGER_MS / _NO_ISOLATE / *_VIRTUAL_PRIMARY
|
|
# knobs. One transport knob has no console equivalent:
|
|
#PUNKTFUNK_IDLE_TIMEOUT_MS=8000 # disconnect-detection latency: how long before a DROPPED client is
|
|
# declared gone (a kept display then starts its linger, or frees).
|
|
# Lower (e.g. 3000) to reclaim kept displays sooner after an
|
|
# ungraceful drop; clamped ≥1s, keep-alive ping scales with it so a
|
|
# live session never false-disconnects. A deliberate quit is instant.
|
|
# Session recovery hook: fired (debounced, ≥60 s apart) when a client connects while NO graphical
|
|
# session is live for this uid — e.g. a compositor crash dropped the box to the GDM greeter, whose
|
|
# auto-login only runs once per boot, so the box would otherwise need a walk-up login or a reboot.
|
|
# Restarting the display manager re-runs auto-login and the client's retry lands in the recovered
|
|
# desktop. Runs detached via `sh -c` as the host's user, so it needs passwordless privilege for
|
|
# exactly this action (sudoers drop-in: `youruser ALL=(root) NOPASSWD: /usr/bin/systemctl restart
|
|
# gdm`, or a polkit rule + plain `systemctl restart display-manager`). Unset = disabled.
|
|
#PUNKTFUNK_RECOVER_SESSION_CMD=sudo -n systemctl restart gdm
|
|
|
|
# Full-chroma 4:4:4 (HEVC Range Extensions) — sharper text/desktop, no chroma loss. Honored only on
|
|
# the punktfunk/1 native path when the client advertises 4:4:4 AND the GPU supports it (probed; else
|
|
# the session stays 4:2:0). HEVC-only; independent of 10-bit. NVENC (NVIDIA) is the validated path;
|
|
# VAAPI/AMF/QSV decline (4:2:0). GameStream/Moonlight always stays 4:2:0.
|
|
#PUNKTFUNK_444=1
|
|
#PUNKTFUNK_10BIT=1 # HEVC Main10 / HDR (when the client advertises 10-bit)
|
|
|
|
# Frame limiter for the GAME — how fast the compositor lets it render. Unset (or 0) = no limit,
|
|
# the default. It does NOT cap the stream: the client still negotiates and receives its full rate,
|
|
# because the encode loop re-encodes the held frame when the compositor produced no new one (an
|
|
# almost-empty P-frame). So a 60-capped game on a 120 Hz session still sends 120 frames a second,
|
|
# and the GPU time the game gives up goes to capture and encode instead — and to heat and battery
|
|
# on a laptop or handheld.
|
|
# gamescope only, today: it takes this as --nested-refresh, the rate it clamps the game to. That is
|
|
# the nested output's rate, so everything gamescope composites moves at it. Other compositors have
|
|
# no equivalent lever and ignore it.
|
|
#PUNKTFUNK_MAX_FPS=60
|
|
|
|
# Run the virtual display at a MULTIPLE of the session's frame rate, without sending a single
|
|
# extra frame. A compositor paints on its own vblank, so a frame finished just after the capture
|
|
# sampled waits nearly a whole interval to be picked up — the jittery part of the latency budget.
|
|
# At 2 that worst case halves. Costs the compositor and GPU the extra composites, so it's opt-in;
|
|
# 1 (default) is off, 4 is the ceiling. If the backend won't give the multiplied rate it reports
|
|
# what it achieved and the stream paces to that, exactly as it would for any other refusal.
|
|
#PUNKTFUNK_VDISPLAY_HZ_MULT=2
|
|
#RUST_LOG=info
|
|
# Management API bearer token. The mgmt API is HTTPS + token-authenticated ALWAYS (even on
|
|
# loopback); if unset it is auto-generated + persisted to ~/.config/punktfunk/mgmt-token (which the
|
|
# bundled web console sources). Set here only to pin a specific token.
|
|
#PUNKTFUNK_MGMT_TOKEN=
|