feat(packaging): ship punktfunk everywhere, and deprecate the session's stray pairing flag
ci / web (push) Successful in 57s
ci / docs-site (push) Successful in 1m5s
android / android (push) Canceled after 3m17s
apple / swift (push) Canceled after 3m18s
apple / screenshots (push) Canceled after 0s
arch / build-publish (push) Canceled after 3m18s
ci / rust (push) Canceled after 3m19s
ci / rust-arm64 (push) Canceled after 3m20s
ci / bench (push) Canceled after 3m18s
deb / build-publish (push) Canceled after 2m23s
deb / build-publish-host (push) Canceled after 2m13s
deb / build-publish-client-arm64 (push) Canceled after 0s
decky / build-publish (push) Canceled after 0s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Canceled after 0s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Canceled after 0s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Canceled after 0s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Canceled after 0s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Canceled after 0s
docker / build-push-arm64cross (push) Canceled after 0s
docker / deploy-docs (push) Canceled after 0s
flatpak / build-publish (push) Canceled after 2s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Canceled after 0s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Canceled after 0s
windows-host / package (push) Canceled after 3m25s
windows-host / winget-source (push) Canceled after 0s
windows-msix / package (arm64, C:\Users\Public\ffmpeg-arm64, --no-default-features, aarch64-pc-windows-msvc, C:\t-a64) (push) Canceled after 0s
windows-msix / package (x64, C:\Users\Public\ffmpeg, , x86_64-pc-windows-msvc, C:\t) (push) Canceled after 0s
windows / build (aarch64-pc-windows-msvc) (push) Canceled after 0s
windows / build (x86_64-pc-windows-msvc) (push) Canceled after 0s

The rest of C1. A CLI that only exists in a dev tree is not a door anyone can use, so it
now builds and installs in deb, rpm, arch, nix, flatpak and MSIX alongside the client and
the session. On Windows it gets its own `AppListEntry="none"` application with the alias
`punktfunk.exe` — a command, not a Start-menu tile — which is what the Playnite importer
will shell to for `punktfunk library <host> --json`.

`punktfunk-session --pair` prints a deprecation notice and forwards. Pairing is a trust
ceremony and belongs to the brain, fronted by `punktfunk pair` or a shell; a renderer
owning one is precisely the mixing of concerns this split exists to undo. It keeps working
for a release, because someone's provisioning script is calling it today.

Decky's invocations are untouched and verified still working (`--list-hosts` JSON shape
unchanged, `--reachable` exit codes unchanged): those flags are a frozen compat contract
until Decky migrates to the verbs at its own pace.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-29 01:16:17 +02:00
co-authored by Claude Opus 5
parent f32c3aaa71
commit bf98102714
8 changed files with 50 additions and 10 deletions
+9 -3
View File
@@ -400,10 +400,16 @@ mod session_main {
}; };
} }
// `--pair <PIN>`: enrol this machine against a host and exit. Sits with the other // `--pair <PIN>`: enrol this machine against a host and exit. DEPRECATED — pairing is
// non-streaming subcommands, above every graphics call — the box doing this may have // a trust ceremony and belongs to the brain, fronted by `punktfunk pair` or a shell
// no display at all. // (design/client-architecture-split.md §5). It still works, with a notice, for the one
// release this needs; a renderer owning a trust ceremony is exactly the mixing of
// concerns the split exists to undo.
if let Some(pin) = arg_value("--pair") { if let Some(pin) = arg_value("--pair") {
eprintln!(
"note: punktfunk-session --pair is deprecated \u{2014} use `punktfunk pair \
<host[:port]>` instead (same store, same result)."
);
return headless_pair(&pin); return headless_pair(&pin);
} }
@@ -106,6 +106,27 @@
<uap:DefaultTile Square71x71Logo="Assets\Square71x71Logo.png" /> <uap:DefaultTile Square71x71Logo="Assets\Square71x71Logo.png" />
</uap:VisualElements> </uap:VisualElements>
</Application> </Application>
<!--
The headless CLI (design/client-architecture-split.md §4). AppListEntry="none" keeps it
out of the Start menu: it is a command, not a tile. Its alias is what a plugin or a
script types, and what the Playnite importer shells to for `punktfunk library`.
-->
<Application Id="PunktfunkCli" Executable="punktfunk.exe"
EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements AppListEntry="none"
DisplayName="Punktfunk CLI"
Description="Headless Punktfunk client commands"
BackgroundColor="transparent"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png" />
<Extensions>
<uap5:Extension Category="windows.appExecutionAlias">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="punktfunk.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
</Extensions>
</Application>
</Applications> </Applications>
<Capabilities> <Capabilities>
+1 -1
View File
@@ -72,7 +72,7 @@ New-Item -ItemType Directory -Force -Path (Join-Path $layout 'Assets') | Out-Nul
# session client the shell spawns for every stream (sibling resolution — see clients/windows/ # session client the shell spawns for every stream (sibling resolution — see clients/windows/
# src/spawn.rs); Skia links statically and vulkan-1.dll is a GPU-driver component, so the session # src/spawn.rs); Skia links statically and vulkan-1.dll is a GPU-driver component, so the session
# adds no DLLs of its own. # adds no DLLs of its own.
$required = @('punktfunk-client.exe', 'punktfunk-session.exe', 'punktfunk-console.exe', 'Microsoft.WindowsAppRuntime.Bootstrap.dll', 'SDL3.dll', 'resources.pri') $required = @('punktfunk-client.exe', 'punktfunk-session.exe', 'punktfunk-console.exe', 'punktfunk.exe', 'Microsoft.WindowsAppRuntime.Bootstrap.dll', 'SDL3.dll', 'resources.pri')
foreach ($f in $required) { foreach ($f in $required) {
$src = Join-Path $TargetDir $f $src = Join-Path $TargetDir $f
if (-not (Test-Path $src)) { throw "missing build artifact '$f' in $TargetDir (did 'cargo build --release' run?)" } if (-not (Test-Path $src)) { throw "missing build artifact '$f' in $TargetDir (did 'cargo build --release' run?)" }
+5 -2
View File
@@ -96,10 +96,11 @@ build() {
# opts back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices are safe. # opts back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices are safe.
if [ "$CARCH" = 'aarch64' ]; then if [ "$CARCH" = 'aarch64' ]; then
# Client-only: no host crate, so none of the encode features below apply. # Client-only: no host crate, so none of the encode features below apply.
cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session \
-p punktfunk-cli
else else
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \ cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-cli
# The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features # The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features
# across everything in one build, so co-building the tray with the host pulls the host's # across everything in one build, so co-building the tray with the host pulls the host's
# ashpd -> zbus/tokio onto the tray's shared zbus; the tray (ksni async-io + blocking, no tokio # ashpd -> zbus/tokio onto the tray's shared zbus; the tray (ksni async-io + blocking, no tokio
@@ -250,6 +251,8 @@ package_punktfunk-client() {
install -Dm0755 "$T/punktfunk-client" "$pkgdir/usr/bin/punktfunk-client" install -Dm0755 "$T/punktfunk-client" "$pkgdir/usr/bin/punktfunk-client"
# The session streamer the shell execs for a connect (resolved as its sibling in /usr/bin). # The session streamer the shell execs for a connect (resolved as its sibling in /usr/bin).
install -Dm0755 "$T/punktfunk-session" "$pkgdir/usr/bin/punktfunk-session" install -Dm0755 "$T/punktfunk-session" "$pkgdir/usr/bin/punktfunk-session"
# The headless CLI (design/client-architecture-split.md §4).
install -Dm0755 "$T/punktfunk" "$pkgdir/usr/bin/punktfunk"
install -Dm0644 "$R/packaging/linux/io.unom.Punktfunk.desktop" \ install -Dm0644 "$R/packaging/linux/io.unom.Punktfunk.desktop" \
"$pkgdir/usr/share/applications/io.unom.Punktfunk.desktop" "$pkgdir/usr/share/applications/io.unom.Punktfunk.desktop"
# DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver). # DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver).
+4 -1
View File
@@ -38,9 +38,11 @@ BIN="$OUTDIR/$PKG"
# The Vulkan/Skia session streamer the shell execs for a connect — shipped alongside the shell # The Vulkan/Skia session streamer the shell execs for a connect — shipped alongside the shell
# (the shell resolves it as its /usr/bin sibling), or desktop streaming breaks. # (the shell resolves it as its /usr/bin sibling), or desktop streaming breaks.
SESSION_BIN="$OUTDIR/punktfunk-session" SESSION_BIN="$OUTDIR/punktfunk-session"
# The headless CLI (design/client-architecture-split.md §4) ships with every client.
CLI_BIN="$OUTDIR/punktfunk"
if [ ! -x "$BIN" ] || [ ! -x "$SESSION_BIN" ]; then if [ ! -x "$BIN" ] || [ ! -x "$SESSION_BIN" ]; then
echo "==> building $CRATE + punktfunk-client-session (release${TARGET:+ for $TARGET})" echo "==> building $CRATE + punktfunk-client-session (release${TARGET:+ for $TARGET})"
cargo build --release --locked "${CARGO_TARGET_ARGS[@]}" -p "$CRATE" -p punktfunk-client-session cargo build --release --locked "${CARGO_TARGET_ARGS[@]}" -p "$CRATE" -p punktfunk-client-session -p punktfunk-cli
fi fi
STAGE="$(mktemp -d)" STAGE="$(mktemp -d)"
@@ -50,6 +52,7 @@ DOCDIR="$STAGE/usr/share/doc/$PKG"
# --- file layout -------------------------------------------------------------- # --- file layout --------------------------------------------------------------
install -Dm0755 "$BIN" "$STAGE/usr/bin/$PKG" install -Dm0755 "$BIN" "$STAGE/usr/bin/$PKG"
install -Dm0755 "$SESSION_BIN" "$STAGE/usr/bin/punktfunk-session" install -Dm0755 "$SESSION_BIN" "$STAGE/usr/bin/punktfunk-session"
install -Dm0755 "$CLI_BIN" "$STAGE/usr/bin/punktfunk"
install -Dm0644 packaging/linux/io.unom.Punktfunk.desktop \ install -Dm0644 packaging/linux/io.unom.Punktfunk.desktop \
"$STAGE/usr/share/applications/io.unom.Punktfunk.desktop" "$STAGE/usr/share/applications/io.unom.Punktfunk.desktop"
# DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver). # DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver).
+4
View File
@@ -222,8 +222,12 @@ modules:
# unification identical to a workspace build (the pinned skia-binaries archive's # unification identical to a workspace build (the pinned skia-binaries archive's
# `pdf-textlayout-vulkan` key must match the resolved feature set). # `pdf-textlayout-vulkan` key must match the resolved feature set).
- cargo --offline build --release -p punktfunk-client-linux -p punktfunk-client-session - cargo --offline build --release -p punktfunk-client-linux -p punktfunk-client-session
-p punktfunk-cli
- install -Dm0755 target/release/punktfunk-client ${FLATPAK_DEST}/bin/punktfunk-client - install -Dm0755 target/release/punktfunk-client ${FLATPAK_DEST}/bin/punktfunk-client
- install -Dm0755 target/release/punktfunk-session ${FLATPAK_DEST}/bin/punktfunk-session - install -Dm0755 target/release/punktfunk-session ${FLATPAK_DEST}/bin/punktfunk-session
# The headless CLI (design/client-architecture-split.md §4) — reachable as
# `flatpak run --command=punktfunk io.unom.Punktfunk …`.
- install -Dm0755 target/release/punktfunk ${FLATPAK_DEST}/bin/punktfunk
# Desktop entry (renamed to the app id; Exec is the in-sandbox binary). # Desktop entry (renamed to the app id; Exec is the in-sandbox binary).
- install -Dm0644 packaging/flatpak/io.unom.Punktfunk.desktop - install -Dm0644 packaging/flatpak/io.unom.Punktfunk.desktop
${FLATPAK_DEST}/share/applications/io.unom.Punktfunk.desktop ${FLATPAK_DEST}/share/applications/io.unom.Punktfunk.desktop
+1 -1
View File
@@ -224,7 +224,7 @@ in
# so build the session without it. The GTK shell (punktfunk-client-linux) is skia-free and full. # so build the session without it. The GTK shell (punktfunk-client-linux) is skia-free and full.
# Re-adding the Skia OSD under Nix is tracked in packaging/nix/README.md. # Re-adding the Skia OSD under Nix is tracked in packaging/nix/README.md.
cargoExtraArgs = cargoExtraArgs =
"--locked -p punktfunk-client-linux -p punktfunk-client-session " "--locked -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-cli "
+ "--no-default-features --features punktfunk-client-session/pyrowave"; + "--no-default-features --features punktfunk-client-session/pyrowave";
# pf-ffvk runs bindgen over libavutil/hwcontext_vulkan.h, which `#include <vulkan/vulkan.h>`. # pf-ffvk runs bindgen over libavutil/hwcontext_vulkan.h, which `#include <vulkan/vulkan.h>`.
+5 -2
View File
@@ -226,10 +226,10 @@ export PUNKTFUNK_BUILD_VERSION="%{version}-%{release}"
# back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices degrade gracefully. # back to libav VAAPI), and a failed open falls back to VAAPI so unsupported devices degrade gracefully.
%if %{with host} %if %{with host}
cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \ cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \
-p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-cli
%else %else
# Client-only (aarch64): no host crate, so none of the encode features apply. # Client-only (aarch64): no host crate, so none of the encode features apply.
cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session -p punktfunk-cli
%endif %endif
# The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features # The status tray in its OWN cargo invocation — load-bearing, not tidiness. Cargo unifies features
# across everything in one build, so co-building the tray with the host pulls the host's # across everything in one build, so co-building the tray with the host pulls the host's
@@ -326,6 +326,9 @@ done
install -Dm0755 target/release/punktfunk-client %{buildroot}%{_bindir}/punktfunk-client install -Dm0755 target/release/punktfunk-client %{buildroot}%{_bindir}/punktfunk-client
# The session streamer the shell execs for a connect (resolved as its sibling in %{_bindir}). # The session streamer the shell execs for a connect (resolved as its sibling in %{_bindir}).
install -Dm0755 target/release/punktfunk-session %{buildroot}%{_bindir}/punktfunk-session install -Dm0755 target/release/punktfunk-session %{buildroot}%{_bindir}/punktfunk-session
%{_bindir}/punktfunk
# The headless CLI (design/client-architecture-split.md §4).
install -Dm0755 target/release/punktfunk %{buildroot}%{_bindir}/punktfunk
install -Dm0644 packaging/linux/io.unom.Punktfunk.desktop \ install -Dm0644 packaging/linux/io.unom.Punktfunk.desktop \
%{buildroot}%{_datadir}/applications/io.unom.Punktfunk.desktop %{buildroot}%{_datadir}/applications/io.unom.Punktfunk.desktop
# DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver). # DualSense hidraw access (full pad fidelity through SDL's HIDAPI driver).