The lock prune (a5b99b2) stopped flatpak-builder full-cloning windows-rs
(disk-fill), but exposed the next layer: `cargo --offline --locked -p
punktfunk-client-linux` resolves the WHOLE workspace, so it still tried
to load the now-un-vendored windows-rs source for the
punktfunk-client-windows member (its windows-rs git deps are
cfg(windows)-gated, but cargo resolves all targets regardless) and
failed: "can't checkout ... you are in the offline mode".
Drop the Windows client from the workspace members inside the sandbox
build (sed on the copied Cargo.toml — the flatpak never compiles it) and
remove --locked (the lock no longer matches the reduced member set;
--offline still pins every crate to the vendored cargo-sources.json, so
the build stays reproducible). android stays — it has no git deps.
Verified locally: removing the member, `cargo build -p
punktfunk-client-linux --offline` Finishes with zero windows-rs
involvement; manifest YAML still valid.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
punktfunk client — Flatpak (Steam Deck / SteamOS, and any flatpak distro)
The native Linux client (crate punktfunk-client-linux, binary punktfunk-client) is
published as a single-file .flatpak bundle to Gitea's generic package registry in
the public unom org. CI (.gitea/workflows/flatpak.yml) builds and publishes on every push
to main (a rolling 0.0.1-ciN.<sha> build) and on v* tags (a clean X.Y.Z), and on tags
also attaches the bundle to the Gitea release.
The host is NOT a flatpak (it needs unsandboxed
/dev/uinput+ zero-copy NVENC — see../README.md"Why not Flatpak"). Only the client is sandbox-friendly.
Why flatpak for the Steam Deck
SteamOS /usr is read-only and image-based, and the system is missing libadwaita and
libSDL3 — so a bare punktfunk-client binary dropped into ~/.local/bin won't run. Flatpak
is the Deck's native, update-survivable app path (the user already runs Moonlight and chiaki-ng
as flatpaks), and the bundle carries libadwaita (from org.gnome.Platform//50) + a bundled SDL3,
with HEVC-capable FFmpeg supplied automatically by the runtime's codecs-extra extension.
App id: io.unom.Punktfunk (matches the Apple bundle id family and the Decky plugin's
flatpak fallback).
Install on the Deck (one-time)
The generic registry is a plain HTTP file store, so just download the bundle and install it per-user (no root, survives SteamOS updates):
# Pick a version: a tag like 1.2.3, or the newest main build's 0.0.1-ciN.gSHA.
VER=1.2.3
URL="https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/$VER/punktfunk-client-$VER.flatpak"
# Flathub must be enabled (it is on the Deck) so the GNOME runtime + ffmpeg-full pull in:
flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
curl -fL -o /tmp/punktfunk-client.flatpak "$URL"
flatpak install --user --bundle /tmp/punktfunk-client.flatpak
Run it:
flatpak run io.unom.Punktfunk # GUI host list (mDNS)
flatpak run io.unom.Punktfunk --connect HOST:PORT
The Decky plugin launches exactly this (flatpak run io.unom.Punktfunk --connect …) once
installed — see ../../clients/decky/README.md.
Updates
A bundle has no remote to track, so updates are "download the newer bundle and reinstall":
flatpak install --user --bundle /tmp/punktfunk-client.flatpak # same command, newer file
(If you want flatpak update to track new builds automatically you'd need a hosted OSTree
repo, which Gitea cannot serve — see "Alternatives" below. The bundle is the simplest path.)
Build locally / the CI fallback
CI builds this in a --privileged Fedora container, because flatpak-builder runs
bubblewrap, which needs user namespaces the default Docker executor denies. If the Gitea
runner can't grant --privileged (the job fails at flatpak-builder with
"Creating new namespace failed: Operation not permitted"), build it out-of-band and upload
by hand. The easiest place is on the Deck itself (it can run org.flatpak.Builder
user-scope, no root):
# On the Deck (or any flatpak box), one-time:
flatpak install --user -y flathub org.flatpak.Builder
# build-flatpak.sh auto-detects org.flatpak.Builder, generates cargo-sources.json (or reuses an
# existing one — see below), builds, and exports dist/punktfunk-client-<version>.flatpak:
bash packaging/flatpak/build-flatpak.sh
# Upload to the generic registry (PAT with write:package):
curl -fsS --user "enricobuehler:$REGISTRY_TOKEN" \
--upload-file dist/punktfunk-client-*.flatpak \
"https://git.unom.io/api/packages/unom/generic/punktfunk-client-flatpak/0.0.1-manual/punktfunk-client.flatpak"
cargo-sources.jsongeneration needspython3+aiohttp+tomlkit, which the Deck lacks. Generate it on a dev box (build-flatpak.shdoes it, or run the upstreamflatpak-cargo-generator.py Cargo.lock -o packaging/flatpak/cargo-sources.json), rsync it next to the manifest, andbuild-flatpak.shreuses it (it only regenerates when the file is absent orFORCE_GEN=1).
The Mac build host cannot build a Linux flatpak (no flatpak-builder for macOS), and home-worker-2 has no flatpak and no passwordless sudo to install it — so the Deck or the privileged CI container are the only two viable build sites.
Manifest
io.unom.Punktfunk.yml. Runtime org.gnome.Platform//50
(GTK 4.20 + libadwaita 1.8 ≥ the crate floors of v4_16 / v1_5), built on freedesktop-sdk 25.08,
with two build-time SDK extensions: org.freedesktop.Sdk.Extension.rust-stable (→ //25.08,
rustc 1.96 — the GTK4 dep chain, e.g. pango-sys 0.22, needs ≥ 1.92, which the EOL GNOME-48 /
24.08 rust-stable at 1.89 could not provide) and org.freedesktop.Sdk.Extension.llvm20 (libclang,
needed by bindgen in ffmpeg-sys-next / sdl3-sys). HEVC-capable libavcodec (soname 61, accepted by
ffmpeg-next 8.x) is supplied automatically at runtime by the freedesktop codecs-extra
extension point (auto-downloaded with the runtime; no app-side codec declaration). A bundled
SDL3 3.4.10 module (pinned to match sdl3-sys 0.6.6+SDL-3.4.10), and finish-args for Wayland +
--device=all (GPU/VAAPI render node + evdev + the hidraw char-devices SDL3 needs for DualSense)
--socket=pulseaudio(PipeWire-pulse: playback + mic) +--share=network. Alongside it:io.unom.Punktfunk.desktop,io.unom.Punktfunk.metainfo.xml,io.unom.Punktfunk.svg(all installed by the manifest).cargo-sources.json(the offline crate cache) is a pure function ofCargo.lock; CI regenerates it each build and it is gitignored — generate it on any box with network +python3/aiohttp/tomlkit(build-flatpak.shdoes this automatically) and, for a build host that lacks those (the Deck), rsync the generated file in alongside the manifest.
Alternatives considered (and why the bundle wins)
- Generic registry bundle (chosen): one curl to publish, one
flatpak install --bundleto consume; mirrors the existing deb/rpm curl-upload pattern exactly. No auto-update. - Release attachment: also done on tags (the bundle is attached to the Gitea release), good for a human-facing download page; the generic registry gives the stable per-version URL the Decky fallback and scripts use.
- Self-hosted OSTree repo (rejected): would enable
flatpak update, but Gitea has no flatpak/ostree registry, so it would mean serving a static OSTree tree over Gitea raw/Pages — more moving parts than the appliance needs today.