The re-architected Linux client is now two binaries: the punktfunk-client shell
execs its sibling punktfunk-session (ash/Vulkan presenter + Skia console UI) for
--connect/--browse. The Decky plugin's stream and browse paths launch the shell
with exactly those flags, but the flatpak built and installed only the shell, so
streaming and the gamepad library from the Deck failed at exec with
"punktfunk-session: No such file" (pair/wake/library still worked — the shell
handles them in-process).
Build and install both binaries. The session binary pulls in Skia (skia-safe),
whose build script downloads a prebuilt libskia — dead in the offline sandbox —
so point skia-bindings at a pinned, vendored archive via SKIA_BINARIES_URL=file://
(read directly, no curl); the tarball rides along as a sha256-pinned flatpak
source. Widen the flatpak CI path filters to the session binary's crates
(linux-session, pf-presenter, pf-console-ui, pf-client-core) and fix the moved
library.rs path in the Decky error-classifier comment.
All other plugin↔client contracts (flags, pairing/library output, config files,
env vars, exit codes, the 47990 mgmt port) already match — no changes needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The v0.7.2 flatpak build failed: `error: Unknown socket type pipewire` — this
flatpak-builder toolchain (and the Deck's flatpak 1.16 override CLI) don't
accept --socket=pipewire. --filesystem=xdg-run/pipewire-0 binds the same native
socket and is the portable form already validated on-Deck (pipewire-0 appears
in the sandbox, client audio node registers, no pw-connect error). Keep only
that + --socket=pulseaudio.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Linux client speaks the native PipeWire protocol (audio.rs `pw connect`),
but the manifest granted only --socket=pulseaudio, so the sandbox had just
`pulse/native` and no `pipewire-0`. Playback + mic both died with
"pw connect (is PipeWire running in this session?)" — reproduced live on a
Steam Deck in Gaming Mode (no client audio node ever appeared).
Add --socket=pipewire (canonical) + --filesystem=xdg-run/pipewire-0 (portable
bind of the same socket). Validated on-Deck via a `flatpak override
--filesystem=xdg-run/pipewire-0`: pipewire-0 then appears in the sandbox and
the client registers its "punktfunk-client" PipeWire node with no pw-connect
error.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CI added --default-branch=stable, so the repo ref is
app/io.unom.Punktfunk/x86_64/stable. build-bundle defaults to `master` when no
branch is given → "Refspec app/io.unom.Punktfunk/x86_64/master not found". Pass
`stable` explicitly in both flatpak.yml and the local build-flatpak.sh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The CI only shipped a single-file .flatpak bundle, which has no remote — users
couldn't `flatpak update`. Keep the bundle (Decky fallback) but also sign the
OSTree repo flatpak-builder already produces and publish it to a shared,
reusable unom-wide remote.
- flatpak.yml: pin --default-branch=stable; import the signing key and
build-update-repo --gpg-sign; generate unom.flatpakrepo + the app .flatpakref
+ index.html; rsync the repo to unom-1 and bring up a static Caddy container.
The step no-ops until FLATPAK_GPG_PRIVATE_KEY/DEPLOY_* exist (build stays green).
- packaging/flatpak/server/: compose.production.yml + Caddyfile (static file
server on :3230, mirrors docker.yml deploy-docs).
- unom-flatpak.gpg: committed public signing key (base64 -> GPGKey= in the descriptors).
- README: hosted repo is now the recommended install; documents the one-time
infra (edge Caddy vhost, infra port 3230, DNS, the GPG secret).
Edge Caddy vhost + infra port allowlist + the secret are applied out-of-band.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two bodies of work in one commit (the rename moved files the fixes also touched).
Naming/structure cleanup (pre-launch):
- Host modules m3.rs->punktfunk1.rs, m0.rs->spike.rs; CLI m3-host->punktfunk1-host,
m0->spike; bare `punktfunk-host` now prints help. Types M3Options/M3Source->
Punktfunk1Options/Punktfunk1Source.
- Clients consolidated out of crates/ into clients/: punktfunk-client-rs->
clients/probe (crate punktfunk-probe), client-linux->clients/linux,
client-windows->clients/windows, punktfunk-android->clients/android/native
(crate punktfunk-client-android; kept [lib] name=punktfunk_android so the JNI
contract is unchanged). crates/ now holds only core + host.
- Milestone codes M0-M4 purged from code/CLI/CLAUDE.md/README/docs/docs-site,
kept only in docs/implementation-plan.md. docs/m2-plan.md->
docs/gamestream-host-plan.md. CI/gradle/flatpak paths updated.
Client loss-recovery (video froze and never recovered after a brief drop):
- Export punktfunk_connection_frames_dropped through the C ABI (the core already
tracked it for the client keyframe-recovery loop; it was never reachable from
the ABI clients). Regenerated punktfunk_core.h.
- Apple (StreamPump + Stage2Pipeline) and Android (decode.rs) now poll
frames_dropped and request a keyframe when it climbs -- the same loss-driven
recovery Linux/Windows already had. Under infinite GOP the decoder silently
conceals reference-missing frames, so the decode-error trigger rarely fires.
Apple rumble robustness (worked then went spotty -- DualSense + Xbox):
- Add CHHapticEngine stopped/reset handlers (rebuild on app background / audio
interruption / server reset) and drop the permanent `broken` latch on a
transient drive failure; latch only when the controller truly has no haptics.
- Surface swallowed SDL set_rumble errors on Linux/Windows + diagnostic logging.
Verified: cargo build/clippy/fmt --workspace, C-ABI harness, header drift.
Not runnable on this box (verify in CI): Gitea workflows, gradle/Android,
flatpak, Swift/decky.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The lock prune (4df35bd) 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>
The flatpak CI was failing at "Downloading sources" with "No space left
on device": flatpak-cargo-generator walks the whole workspace Cargo.lock
and emits a `type: git` source for the windows-rs crates (windows +
windows-reactor + ~12 sub-crates, pinned by punktfunk-client-windows),
and flatpak-builder then FULL-clones that multi-GB repo — for a bundle
that only ever compiles `-p punktfunk-client-linux` and never touches a
windows-* crate.
New packaging/flatpak/prune-windows-lock.py writes a copy of Cargo.lock
with the windows-rs git packages stripped (matches on the `source =`
line, so a crate that merely lists a windows dependency is kept;
dependency-free so it also runs on the Deck's stock python). Both the CI
and build-flatpak.sh feed that pruned lock to the generator. The
committed Cargo.lock is untouched — cargo --offline only needs vendored
sources for the crates it actually builds, and the windows-rs crates are
not in the Linux client's dependency closure.
Verified locally: 14 crates pruned (507 -> 493 packages), zero windows-rs
`source =` lines remain, output parses as TOML, all Linux-client deps
(gtk4/ffmpeg-sys-next/sdl3/pipewire) intact.
This unblocks the flatpak build carrying the VAAPI green-screen fix
(a89b19a) for the Steam Deck.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
flatpak-cargo-generator.py (master) imports `tomlkit` + `aiohttp`; the workflow
installed `python3-toml`, so the "Generate offline cargo sources" step would fail
with ModuleNotFoundError. Install python3-tomlkit instead, and correct the same
note in build-flatpak.sh.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ship the punktfunk Linux client to the Steam Deck as a Flatpak — the only viable
SteamOS install path, since /usr is read-only and lacks libadwaita/SDL3 — and
publish both it and the Decky plugin through Gitea. Built and validated live on a
Steam Deck (SteamOS 3.7): bundle installs user-scope, all libs resolve, libavcodec
resolves to the codecs-extra HEVC build, devices=all for DualSense hidraw.
packaging/flatpak (new):
- io.unom.Punktfunk.yml on GNOME 50 / freedesktop-sdk 25.08. rust-stable//25.08
(rustc 1.96 — the GTK4 chain needs >=1.92; the EOL GNOME-48/24.08 rust-stable at
1.89 could not build it) + llvm20 (libclang for bindgen in ffmpeg-sys-next/sdl3-sys).
HEVC libavcodec comes from the runtime's auto codecs-extra extension point (no
app-side codec declaration). Bundled SDL3 3.4.10 (matches sdl3-sys 0.6.6+SDL-3.4.10).
finish-args: wayland/fallback-x11, --device=all (GPU/VAAPI + evdev + hidraw — flatpak
cannot bind /dev/hidrawN char devices via --filesystem), pulseaudio, network,
~/.config/punktfunk.
- metainfo.xml, desktop, square SVG icon, build-flatpak.sh (offline cargo-sources;
on-Deck org.flatpak.Builder or CI), README.
clients/decky:
- add LICENSE (MIT), fix package.json license (BSD-3-Clause -> Apache-2.0 OR MIT),
add scripts/{package.sh,deploy.sh} (the plugins dir is root-owned: stage to /tmp,
sudo install, restart plugin_loader), align the launcher fallback to the real
flatpak app id io.unom.Punktfunk, rewrite the install section.
.gitea/workflows:
- flatpak.yml: privileged Fedora container builds the bundle and publishes to the
Gitea generic registry (+ release attachment on tags).
- decky.yml: pnpm build -> store-layout zip -> registry (stable latest/ URL for
Decky "install from URL").
docs: packaging/README + packaging/flatpak/README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>