The forge seals app variables, origin tokens and subscriptions, but an operator credential the forge itself needs — a Cloudflare API token to spawn forges, a bucket key for tofu state — has no home: today it would sit in plaintext process env, or in one app's environment where no run can reach it.
Intent: one secret table keyed by name and scope (forge, org, repo), sealed with seal() under AAD secret:<scope>:<name>, written by admins, never read back.
[ ] admin-only write routes (PUT /api/v1/secrets/:scope/:name, delete), values write-only: list shows names and timestamps only, like app_env
[ ] a resolver resolveSecrets(names, {org, repo}) where the narrowest scope wins; it is the ONLY read path and appends an event row (actor, run, name — never the value) per open
[ ] Settings → Secrets (forge) and the same card under an org and a repo, in the studio's words
Files: src/core/secret_store.ts (new), src/kernel/secret.ts (reuse), src/http/api.ts, src/web/pages/settings.ts, migrations. Risks: a name shadowing an app_env var — keep the namespaces apart; the key-in-snapshot gap (#separate issue) applies to these rows too. One or two runs; split if it grows.
The forge seals app variables, origin tokens and subscriptions, but an operator credential the forge itself needs — a Cloudflare API token to spawn forges, a bucket key for tofu state — has no home: today it would sit in plaintext process env, or in one app's environment where no run can reach it.
Intent: one
secrettable keyed by name and scope (forge, org, repo), sealed withseal()under AADsecret:<scope>:<name>, written by admins, never read back.Acceptance:
secrettable (name, scope, scope_id, value_enc, updated_at, last_read_at) with migrationPUT /api/v1/secrets/:scope/:name, delete), values write-only: list shows names and timestamps only, like app_envresolveSecrets(names, {org, repo})where the narrowest scope wins; it is the ONLY read path and appends an event row (actor, run, name — never the value) per openFiles: src/core/secret_store.ts (new), src/kernel/secret.ts (reuse), src/http/api.ts, src/web/pages/settings.ts, migrations. Risks: a name shadowing an app_env var — keep the namespaces apart; the key-in-snapshot gap (#separate issue) applies to these rows too. One or two runs; split if it grows.