3.9 KiB
Design
Context
See proposal.md — Why. The fingerprint-agent repository and its releases are
private: an unauthenticated request to the forge's releases API answers 404.
Releases already publish, per build, a generic bundle
(fpagent-<version>-linux-x86_64.tar.gz), per-Ubuntu offline bundles
(fpagent-<version>-ubuntu-<codename>-x86_64.tar.gz) with the libfprint .deb
closure vendored, and a .sha256 beside each. Every bundle already contains
the fpagent binary, the udev rule, and its own install.sh that handles
libfprint, udev, plugdev and the systemd service. The tools app is an Express
server with a hardcoded-configuration policy whose only sanctioned environment
variables were the Image Cropper credentials.
Goals / Non-Goals
Goals:
- A field laptop installs or updates fpagent with one public command.
- The forge credential never reaches the field laptop.
- Reuse the bundle's installer rather than reimplementing hardware setup.
Non-Goals:
- Replacing the bundle installer, or the forge's own release tooling.
- Authenticating the installer: it is intentionally public.
- Supporting architectures other than x86_64 (the bundle installer already requires it).
Decisions
-
Proxy the release through the app; do not embed the key in the script. The credential lives in
GITEA_RELEASE_INSTALL_KEYand the app fetches the private release server-side. The alternative — rendering the key into the served script so the client could call the forge directly — publishes a repository credential to every installer and anycurl; rejected. -
Two thin routes, no proxy framework.
/fpagent/releases/latestreturns a trimmed{tag, assets:[{name,url}]}with same-origin URLs, and/fpagent/releases/download/:tag/:namestreams one asset. Both are registered before the session middleware so they stay public and do no per-request PocketBase work. A single combined "give me the right tarball" route was considered but needs the server to guess the client's OS; the client knows its own codename, so selection happens in the script. -
The installer delegates to the bundle's own
install.sh. All hardware and dependency logic already exists and is versioned with the binary; reimplementing it in the hosted script would duplicate it and drift. The hosted script only fetches, verifies, extracts, and forwards flags. -
Bundle selection prefers the codename offline bundle. When the host's
/etc/os-releaseVERSION_CODENAMEmatches a published offline bundle, use it (no network needed for the dependency closure); otherwise fall back to the generic online bundle, whose installer apt-gets libfprint. -
Checksum verification, not signature verification. The release publishes sha256 only, so the installer verifies sha256 over TLS from the same origin it trusts for the script itself. GPG would require release signing that does not exist yet; deferred.
-
Asset names are validated server-side against a conservative pattern, so a crafted tag or name cannot walk the forge path or reach another host.
Risks / Trade-offs
- [A compromised tools app serves a malicious installer] → the app is already
trusted to serve the tools pages and holds the operator session; the
installer is not a new trust boundary. TLS to
tools.yrka.appis the same trust as the app. - [The forge changes its bundle naming] → selection falls back to the generic bundle, and a release with no matching asset fails loudly rather than installing something wrong.
- [The app buffers an asset in memory] → bundles are tens of MB; acceptable, and requests are rare. Streaming can replace it if that changes.
Migration Plan
Add GITEA_RELEASE_INSTALL_KEY to .env/.env.production (already supplied)
and deploy the app; the installer and proxy are new routes and change no
existing behavior. Rollback = remove the routes; nothing else depends on them.
Open Questions
None.