# 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