Files
tools-legacy/openspec/specs/fpagent-installer/spec.md
T
adebisi-fa 35d895574f docs(openspec): archive fpagent-install-upgrade and sync specs
Documents release pinning (--tag), the pinned-tag metadata route,
installed-version tracking with upgrade reporting, and restart of a
managed running service on upgrade; syncs the four requirements into the
fpagent-installer main spec.
2026-10-02 11:41:50 +01:00

8.2 KiB

fpagent-installer Specification

Purpose

Lets a field laptop install the latest private fpagent release with a single public command, without ever handling the forge credential — the tools app proxies the private release and serves the installer.

Requirements

Requirement: Public installer endpoint

The app SHALL serve an installer script at /fpagent/install.sh that is reachable without authentication. When run, the script SHALL fetch the latest release metadata, choose a bundle for the host, verify the bundle's published sha256 checksum, extract it, and hand off to the extracted bundle's own installer. The script SHALL be served uncached.

Scenario: Installer is fetched anonymously

  • WHEN a client requests /fpagent/install.sh without a session
  • THEN the app returns the installer script and does not redirect to login

Scenario: Host-appropriate bundle is chosen

  • WHEN the latest release offers both a bundle for the host's Ubuntu codename and a generic bundle
  • THEN the installer uses the codename bundle

Scenario: Generic bundle is the fallback

  • WHEN the latest release offers no bundle matching the host's Ubuntu codename
  • THEN the installer uses the generic bundle

Scenario: Checksum is verified before extraction

  • WHEN the installer downloads a bundle and its published checksum
  • THEN it verifies the checksum before extracting and refuses to install when the checksum does not match

Scenario: Options are forwarded to the bundled installer

  • WHEN the installer is invoked with options such as --enable-service or --check
  • THEN it passes those options through to the extracted bundle's installer

Scenario: No compatible bundle

  • WHEN the latest release contains no bundle for the host architecture
  • THEN the installer exits with a failure message and installs nothing

Requirement: Release metadata endpoint

The app SHALL expose a same-origin endpoint that returns the latest release's tag and the names of its downloadable assets as same-origin URLs. The app SHALL authenticate to the private forge using the configured release key, SHALL omit asset names that are not valid asset names, and SHALL NOT reveal the release key or the forge host to clients.

Scenario: Latest release is returned

  • WHEN a client requests the latest-release endpoint and the forge returns a release
  • THEN the response carries the release tag and each asset's name and a same-origin URL under /fpagent/releases/download/

Scenario: Unsafe asset names are omitted

  • WHEN the forge release lists an asset whose name is not a valid asset name
  • THEN that asset does not appear in the response

Scenario: Release key is not exposed

  • WHEN any client fetches the latest-release endpoint
  • THEN the response body contains neither the release key nor the forge host

Scenario: Installer not configured

  • WHEN the release key is not configured
  • THEN the endpoint answers that the installer is not configured

Scenario: Forge unavailable

  • WHEN the forge cannot be reached or returns an error for the latest release
  • THEN the endpoint answers with a gateway error

Requirement: Release asset download proxy

The app SHALL stream a requested release asset to the client through a same-origin route, authenticating the upstream request with the release key so the client sends no credentials. It SHALL reject asset names that are not valid asset names and SHALL report a missing asset as not found.

Scenario: Asset is streamed without client credentials

  • WHEN a client requests a release asset by tag and name without any credential
  • THEN the app fetches it from the forge with the release key and streams its bytes to the client

Scenario: Invalid asset name is rejected

  • WHEN a client requests an asset whose tag or name is not a valid asset name
  • THEN the app rejects the request without contacting the forge

Scenario: Missing asset

  • WHEN the forge reports the requested asset as not found
  • THEN the app answers with not found

Scenario: Installer not configured

  • WHEN the release key is not configured
  • THEN the endpoint answers that the installer is not configured

Requirement: Installer credential stays server-side

The app SHALL read the private-forge release key from the GITEA_RELEASE_INSTALL_KEY environment variable. The key SHALL NOT appear in the installer script, in any release metadata response, or in any streamed asset.

Scenario: Served installer carries no credential

  • WHEN a client fetches /fpagent/install.sh
  • THEN the response contains no release key

Scenario: Credential supplied by the environment

  • WHEN the release key is supplied in the environment
  • THEN the installer and proxy endpoints use it without any change to the hardcoded configuration

Requirement: Pinned release selection

The installer SHALL accept a release tag and install that release instead of the latest. Without a tag it SHALL install the latest release.

Scenario: A pinned release is installed

  • WHEN the installer is invoked with a release tag
  • THEN it fetches that tag's metadata and installs its bundle

Scenario: Default is the latest release

  • WHEN the installer is invoked without a release tag
  • THEN it installs the latest release

Scenario: Unknown pinned release

  • WHEN the pinned tag does not exist
  • THEN the installer exits with a failure message and installs nothing

Requirement: Pinned release metadata endpoint

The app SHALL expose a same-origin endpoint that returns a specific release's tag and asset names as same-origin URLs, with the same authentication, filtering and non-disclosure guarantees as the latest-release endpoint. It SHALL reject a tag that is not a valid release tag without contacting the forge.

Scenario: Tagged release is returned

  • WHEN a client requests the metadata for an existing tag
  • THEN the response carries that release's tag and its assets as same-origin URLs

Scenario: Invalid tag is rejected

  • WHEN a client requests metadata for a tag that is not a valid release tag
  • THEN the app rejects the request without contacting the forge

Requirement: Installed version is tracked and upgrades are reported

The installer SHALL treat the git hash in the bundle's asset name as the version. It SHALL hand that version to the bundle installer, which SHALL record it beside the installed binary and SHALL report an upgrade with the previous and new versions on the next run. Uninstalling SHALL remove the recorded version.

Scenario: Version is derived from the asset name

  • WHEN the installer selects a bundle named fpagent-<hash>-<platform>.tar.gz
  • THEN it uses <hash> as the version

Scenario: Upgrade is reported

  • WHEN the recorded version differs from the version being installed
  • THEN the bundle installer reports the change from the old to the new version

Scenario: First install records the version

  • WHEN no version has been recorded yet
  • THEN the bundle installer records the new version and reports no upgrade

Scenario: Uninstall removes the recorded version

  • WHEN the installer is asked to uninstall
  • THEN the recorded version is removed

Requirement: A managed running service is restarted on upgrade

When the installer is asked to manage the systemd service and a service is already running, the installer SHALL restart it after installing the new binary so the new binary takes effect. A fresh install SHALL NOT perform a redundant restart, and the installer SHALL NOT restart the service when service management is not requested.

Scenario: Upgrade restarts the running service

  • WHEN the running service is managed and a new binary is installed
  • THEN the service is restarted on the new binary

Scenario: Fresh install starts the service

  • WHEN the service is not running and service management is requested
  • THEN the service is started and no separate restart is performed

Scenario: Service management not requested

  • WHEN service management is not requested
  • THEN the installer does not restart the service