ADR 0014: Secrets via environment variables and keyring¶
Status¶
Accepted — 2026-09-19
Context¶
Future providers (an HTTP image API) and the optional YouTube Data API source (decision D7) need API keys. The config TOML (ADR 0003) is meant to be shared and committed to dotfiles; the SQLite database (ADR 0004) is copied around with data_dir; provider_profile.params_json is snapshotted verbatim into every run. None of these may ever contain a secret. The Antigravity CLI needs no key at all — it uses its own cached login.
Decision¶
- Lookup order for a provider secret: environment variable
THUMBFORGE_PROVIDERS__<KEY>__API_KEY(uppercase provider key), thenkeyring(uv add keyring) with servicethumbforgeand username<provider_key>. thumbforge provider set-key KEYprompts (no echo) and writes to keyring; nothing else writes secrets. There is no--api-keyflag.- The settings loader rejects any TOML key matching
*_key,*_token,*_secretwithSettingsErrorand a hint pointing at env/keyring. - The DB schema has no secret columns;
provider_profile.params_jsonis validated against a deny-list of the same patterns before insert. - Secrets are never logged: structlog processors drop keys matching the deny-list (ADR 0015), and provider subprocess environments receive only the variables the adapter whitelists.
provider listshows auth state asok | missing | n/a, never the value.
Consequences¶
- CI supplies secrets purely through environment variables; no keyring backend is required on runners.
- On Windows,
keyringuses Windows Credential Manager; on Linux, Secret Service or a file backend —provider check KEYreports which backend is active. - A
config.tomlcan be committed publicly without review for leaked keys. - Contributors adding a provider must put its secret name in the adapter's
secret_namestuple so the deny-list andset-keyknow about it.
Alternatives considered¶
- Secrets in
config.toml— rejected: the file is user-editable, often synced to dotfiles repos, and printed byconfig show. - A
.envfile loaded automatically — rejected: silently picks up files from the working directory; explicit environment variables are predictable. - Encrypting secrets inside SQLite — rejected: the key would need to live somewhere, which is exactly what keyring already solves.