WP4 of the docs-and-onboarding overhaul (punktfunk-planning design/docs-and-onboarding-overhaul.md). scripts/install.sh — plain POSIX sh (dash-clean), ~380 lines, `curl -fsSL https://punktfunk.unom.io/install.sh | sh`: detect the distro from os-release (apt / dnf / pacman / rpm-ostree→sysext; NixOS, SteamOS, Windows and unknown distros get a one-line pointer and stop; Debian 12 / Ubuntu 24.04 / Mint 22 / Fedora 45 hit the documented floors with the right docs link) → install with the platforms.json lines VERBATIM (channel and the Fedora group are edited into the string at run time; `--yes` rewrites them non-interactive, a tty hands the package manager its own prompt; stdin is never read, because under `curl | sh` stdin is the script) → `punktfunk-host detect-conflicts` (exit 1 = active Sunshine-family host) → offer to keep both by moving the management API port (PUNKTFUNK_MGMT_BIND, default 47991, the firewall step opens it) → input group (ujust on Bazzite; no-op if already in) → optional punktfunk group, GameStream compat, shared clipboard (all default no) → firewalld/ufw profiles → enable host + console (+ the plugin runner where it isn't) → optional linger → verify (unit active, UDP 9777 bound) and print the console URL, the password command and the pairing steps. `--dry-run` prints every command and changes nothing; every prompt has a PUNKTFUNK_INSTALL_* environment twin; re-running is safe (install skipped when the binary exists). Running under sudo is refused (host.env and the units belong to the user); root without sudo gets a shim so the verbatim lines still work. Decisions: the canonical URL is punktfunk.unom.io/install.sh, a 302 on the website to the script at raw/branch/main (versioned with the code it installs; precedent: the Bazzite sysext bootstrap) — the website half is punktfunk-website PR #4. GPU drivers stay the docs pages' job; the one silent failure (Fedora + NVIDIA without RPM Fusion's ffmpeg-libs → no NVENC) is called out at the end. Gates: check-docs-drift.sh gate 6 — every apt/pacman/dnf/sysext install line in data/platforms.json must appear verbatim in the script, and the script must parse (shown to fail on a planted drift). New path-filtered workflow installer-smoke.yml runs the script unattended in debian:trixie, fedora:44 and archlinux:base against the real registry, then `punktfunk-host --version`, `detect-conflicts`, and a re-run that must say "already installed". Docs: install hub gains "Guided install (preview)" rendered from platforms.json's new `installer` block via an <Installer/> component (one-liner + inspect-first form + flags); CONTRIBUTING names the new gate. Verified locally: sh/dash -n, both docs gates, docs-site build + lint, and a --dry-run matrix over 16 faked os-release files (all four families, every floor, canary, every option, piped stdin). The container run itself is the CI job's to report — Docker on this machine was wedged under another session's emulated build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.7 KiB
Contributing to Punktfunk
Thanks for your interest in contributing!
Licensing of contributions (inbound = outbound)
Punktfunk is dual-licensed under MIT OR Apache-2.0.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as MIT OR Apache-2.0, without any additional terms or conditions.
By opening a pull request you agree to license your contribution under these terms. This is the standard Rust-ecosystem "inbound = outbound" model; it keeps the project's licensing unambiguous (including the Apache-2.0 §5 contributor patent grant) and any future relicensing clean. You retain the copyright to your contributions.
Do not paste copyleft (or otherwise incompatibly-licensed) code
The single thing that could poison the permissive license is copied source from a copyleft project. Several adjacent projects (Sunshine, Apollo, Moonlight) are GPL-3.0. You may study them and reimplement a technique, protocol, or wire format — those are not copyrightable — but never paste their code, and do not translate a GPL implementation line-by-line. When a comment credits prior art, make clear it is an independent reimplementation, not a copy. The same applies to any third party's code under a license incompatible with MIT/Apache.
If you add a new third-party dependency, it must be permissive (MIT / Apache-2.0 / BSD / ISC / Zlib /
Unicode-3.0 / etc.). about.toml holds the accepted-license allow-list; regenerate the attribution
file with scripts/gen-third-party-notices.sh when the dependency tree changes.
Prerequisites
The Rust toolchain is pinned exactly in rust-toolchain.toml; rustup installs it for you the
first time you build, so don't override it — a different rustc reformats files nobody touched.
The workspace links real system libraries, so a bare cargo build --workspace fails on a stock
machine. The authoritative list is what CI installs, in ci/rust-ci.Dockerfile — on Ubuntu 26.04,
which is what gets you FFmpeg 8:
sudo apt install build-essential clang libclang-dev pkg-config cmake \
libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavfilter-dev libavdevice-dev \
libpipewire-0.3-dev libopus-dev libwayland-dev libxkbcommon-dev \
libgl-dev libegl-dev libgbm-dev \
libgtk-4-dev libadwaita-1-dev libsdl3-dev \
libvulkan-dev
(The last two groups are the Linux client shell and the Vulkan session presenter; skip them only
if you never build those crates. libvulkan-dev is for the LOADER's pkg-config/soname — ash
dlopens it, and the client links no FFmpeg at all, so no libav*-dev appears here.
scripts/bootstrap-ubuntu.sh sets up an Ubuntu capture-test host — NVIDIA, Sway, PipeWire —
and is not a substitute for the list above.)
Before you push
Enable the repo git hooks once per clone — they run the exact rustfmt gates CI runs (main workspace + the UMDF driver workspace) on every commit and push, so a push can never fail CI on formatting alone:
git config core.hooksPath scripts/git-hooks
Then the usual full pass. Use --locked as CI does — otherwise a silent Cargo.lock update can pass
locally and fail CI:
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
Two more gates that only apply to some changes:
- Touched
web/ordocs-site/? CI builds and typechecks both. Run, in that directory:Build first — it generates the API client / MDX typegen that the typecheck imports.bun install && bun run build && bun run lint - Touched Windows- or Linux-gated code from another OS?
scripts/xcheck.sh windows(orlinux) type-checks and lints that platform's#[cfg(target_os = …)]code in about a second, instead of waiting for the CI job that compiles it.
Generated artifacts are checked in. include/punktfunk_core.h (cbindgen) is regenerated by the build
and CI fails if the committed copy drifts. api/openapi.json is gated the same way: the rust job
regenerates the spec and diffs it against the committed file, and the docs-drift job checks that
docs-site/public/openapi.json — the snapshot the docs site serves — is a byte-for-byte copy of it.
Touch the management API and CI stays red until you regenerate and re-copy:
cargo run -p punktfunk-host -- openapi > api/openapi.json
cp api/openapi.json docs-site/public/openapi.json
Where facts live (docs vs READMEs vs website)
Every user-facing fact has exactly one canonical home; everything else links to it. Duplicated walkthroughs are how the docs drifted before — don't add new ones.
| Surface | Owns | Never contains |
|---|---|---|
docs-site (docs-site/content/) |
All user-facing facts: install, config, features, troubleshooting | Design rationale |
READMEs (root, packaging/*, scripts/*) |
Dev/packager rationale and pointers into the docs | User walkthroughs duplicated from docs-site |
| punktfunk.unom.io (separate repo) | Marketing, downloads, blog | Instructions — it deep-links the docs instead |
| punktfunk-planning (private) | Design rationale, RFCs, plans | Anything user-facing |
Docs pages are written for one of two audiences, not both at once: the get-started track (quickstart, install, pairing — short, one task per page, happy path only) assumes no Linux expertise; the reference track (configuration, CLI, API, per-compositor pages) is allowed to be dense. When a change touches a user-facing fact, update the docs-site page that owns it in the same PR.
CI enforces the cheap half of this (scripts/ci/check-docs-drift.sh and check-docs-links.sh):
the OpenAPI snapshot must match api/openapi.json, the docs-site copy of data/platforms.json must
match the canonical one, scripts/install.sh must carry the file's install lines verbatim, every PUNKTFUNK_* variable the docs mention
must still exist in the tree, the counts of undocumented PUNKTFUNK_* variables and undocumented
punktfunk-host subcommands may never grow (document the new knob, or consciously raise the
baseline in the script), and internal docs links must resolve.
Match the surrounding code's comment density and naming. Commit messages end with the
Co-Authored-By trailer (see git log).
See the README's Build & test section for the extra dev commands (the FEC loss harness, the standalone C-ABI proof) and Design invariants for the rules a change is expected to hold to, and the docs site for architecture and per-platform guides.