ADR-09: API keys for REST automationยถ

Contextยถ

Authentication is delegated to an OIDC provider (ADR-07: OIDC for authentication), which works well for interactive users: the browser-based SSO flow establishes a session cookie.

Automation cannot follow that flow. A CI pipeline or a script has no browser to complete an authorization-code exchange, yet it still needs to call the ๐Ÿ”Œ REST API โ€” to generate a document, commit it, or download a bucket.

A credential was needed that a non-interactive client can put in a request header.

Decisionยถ

TINO issues static API keys โ€” long-lived bearer tokens for programmatic access to the REST API.

  • Keys are generated by administrators (via the web UI or the API).

  • Keys are presented as Authorization: Bearer tino_โ€ฆ.

  • Each key carries an explicit per-bucket access map (bucket slug โ†’ role).

  • Keys are stored in a single api_keys.yml file in the data directory, consistent with the filesystem-only state model (ADR-02: No database).

  • Only a hash of the token is stored and the raw token is shown once at creation and never again.

Consequencesยถ

Positiveยถ

  • Automation without a browser
    CI pipelines and scripts authenticate with a single header, no interactive login required.
  • No database
    Keys live in api_keys.yml alongside the rest of TINOโ€™s state, backed up and inspected with the same filesystem tools (ADR-02: No database).
  • Explicit scoping
    A key grants exactly the per-bucket roles it was given โ€” least privilege by construction, and revocation is deleting one entry.
  • Hashed at rest
    Only token hashes are stored, so a leaked api_keys.yml exposes no usable credentials.

Negativeยถ

  • Long-lived secrets
    A key stays valid until explicitly revoked โ€” there is no automatic expiry. Leaked keys must be revoked by hand, which argues for narrow scoping.
  • Manual lifecycle
    Creating, rotating, and revoking keys is an administratorโ€™s responsibility rather than something the identity provider manages.
  • Snapshotted access
    A keyโ€™s bucket roles are fixed at creation and do not follow OIDC group changes.
    Access must be updated on the key itself.