The root helper, its unit, the group-scoped polkit rule and the install-kind
marker all shipped with the HOST package. A Steam Deck or a handheld has no host
package, so the client half of one-tap updates had nothing to stand on.
The client packages now ship their own copies — deliberately under their own
paths (`pf-update-client`, `punktfunk-client-update.service`,
`49-punktfunk-client-update.rules`, `/usr/share/punktfunk-client/install-kind`)
rather than sharing the host's. Two packages owning one path is a hard conflict
in dpkg, rpm and pacman alike, and the client marker needs its own DIRECTORY
too because the host RPM claims `%{_datadir}/punktfunk/*` with a glob — a
sibling file there would break `dnf install punktfunk punktfunk-client`.
`pf-update` grows the matching `apply-client` verb. The verb comes from a
root-owned unit's fixed ExecStart, never from the caller, so the zero-parameter
invariant holds; both verbs sweep every installed punktfunk package, and what
the verb selects is which marker is read and which binary the run-the-binary
gate executes afterwards. A client sysext is refused with its reason: the signed
feed carries the host image, and installing that over a client-only box is not
an update.
The opt-in group is created by both packages' scriptlets, idempotently, so
whichever lands first wins.
Also fixes the host's own helper install: it read `$R/target/release/pf-update`,
which is the source checkout, while build() exports CARGO_TARGET_DIR="$srcdir/
target" and every other binary in the file installs from `$T`. Both now read
`$T`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>