Files

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

  1. Proxy the release through the app; do not embed the key in the script. The credential lives in GITEA_RELEASE_INSTALL_KEY and 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 any curl; rejected.

  2. Two thin routes, no proxy framework. /fpagent/releases/latest returns a trimmed {tag, assets:[{name,url}]} with same-origin URLs, and /fpagent/releases/download/:tag/:name streams 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.

  3. 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.

  4. Bundle selection prefers the codename offline bundle. When the host's /etc/os-release VERSION_CODENAME matches 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.

  5. 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.

  6. 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.app is 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.