docs(adr-0024): reconcile the token inventory with the estate #74

Sammanfogat
supernaut sammanfogade 1 incheckning från docs/adr-0024-token-inventory in i main 2026-08-02 19:23:16 +00:00
Ägare

Closes #73. Follow-up from bitborg-infra#348, which could not do it — the decision record lives
here.

Reconciled against forgejo_service_accounts, group_vars/vault.example.yml and the live instance
on 2026-08-02.

Added

  • gitborg-webhook-admin — admin, read:admin, vault. One read-only call,
    GET /api/v1/admin/hooks, verifying the reconcile-trigger system webhook (ADR 0037).
  • gitborg-token-audit — admin, read:admin, vault. It was described in the Consequences prose
    but absent from the table, which is the part anyone reads as the inventory.

Corrected

  • gitborg-ci said "not vault". It holds two write:package tokens, one in each store: the
    vault copy drives the host-side registry mirror and the retention sweep, the Actions copy drives
    bitborg-web's deploy. Rotation must cover both — replacing one leaves the other working, so the
    miss surfaces later and somewhere else. The old wording actively suggested there was nothing in
    the vault to rotate.
  • gitborg-bot was "read-only MCP token + future narrow site-wide tokens". The future arrived:
    it carries read:package as the org Actions secret REGISTRY_READ_TOKEN, used by bitborg-infra's
    own CI to pull mirrored images.
  • gitborg-runner-controller said only "runner-controller admin PAT". The scope is
    write:admin, plus write:repository if the run-cancel path is exercised.

Structural change

Where a token lives is now a column, not a footnote, because it is what decides how the token is
rotated: a vault token is a vault edit plus an apply, an Actions-store token is a
PUT …/actions/secrets/<NAME>. Reading the store off the same row as the scope is what makes
"rotate this" an unambiguous instruction.

Also recorded, as part of the Decision rather than a caveat: the audited account list is derived
from forgejo_service_accounts rather than hand-kept (bitborg-infra#314). That is what makes
adding an account to the provisioning list sufficient to bring its PAT under
ForgejoTokenRotationDue, and the duplication it replaced is exactly how gitborg-webhook-admin
came to hold an admin-scoped PAT whose age nothing reported.

One thing worth reading before the next rotation

The table now states the expected token counts — gitborg-ci 2 and gitborg-bot 2 — and says a
count above what the table explains is a stale token that was never revoked. That is not
hypothetical: the audit currently reports 2 on gitborg-reconciler and 2 on gitborg-webhook-admin,
neither of which this table explains. The rotation procedure mints the replacement before revoking
the old one, and the revoke step is the one that gets skipped.

markdownlint and Prettier clean.

Closes #73. Follow-up from `bitborg-infra#348`, which could not do it — the decision record lives here. Reconciled against `forgejo_service_accounts`, `group_vars/vault.example.yml` and the live instance on 2026-08-02. ## Added - **`gitborg-webhook-admin`** — admin, `read:admin`, vault. One read-only call, `GET /api/v1/admin/hooks`, verifying the reconcile-trigger system webhook (ADR 0037). - **`gitborg-token-audit`** — admin, `read:admin`, vault. It was described in the Consequences prose but absent from the table, which is the part anyone reads as the inventory. ## Corrected - **`gitborg-ci` said "not vault".** It holds **two** `write:package` tokens, one in each store: the vault copy drives the host-side registry mirror and the retention sweep, the Actions copy drives bitborg-web's deploy. Rotation must cover both — replacing one leaves the other working, so the miss surfaces later and somewhere else. The old wording actively suggested there was nothing in the vault to rotate. - **`gitborg-bot`** was "read-only MCP token + future narrow site-wide tokens". The future arrived: it carries `read:package` as the org Actions secret `REGISTRY_READ_TOKEN`, used by bitborg-infra's own CI to pull mirrored images. - **`gitborg-runner-controller`** said only "runner-controller admin PAT". The scope is `write:admin`, plus `write:repository` if the run-cancel path is exercised. ## Structural change **Where a token lives is now a column, not a footnote**, because it is what decides how the token is rotated: a vault token is a vault edit plus an apply, an Actions-store token is a `PUT …/actions/secrets/<NAME>`. Reading the store off the same row as the scope is what makes "rotate this" an unambiguous instruction. Also recorded, as part of the Decision rather than a caveat: the audited account list is **derived** from `forgejo_service_accounts` rather than hand-kept (`bitborg-infra#314`). That is what makes adding an account to the provisioning list sufficient to bring its PAT under `ForgejoTokenRotationDue`, and the duplication it replaced is exactly how `gitborg-webhook-admin` came to hold an admin-scoped PAT whose age nothing reported. ## One thing worth reading before the next rotation The table now states the expected token *counts* — `gitborg-ci` 2 and `gitborg-bot` 2 — and says a count above what the table explains is a stale token that was never revoked. That is not hypothetical: the audit currently reports 2 on `gitborg-reconciler` and 2 on `gitborg-webhook-admin`, neither of which this table explains. The rotation procedure mints the replacement before revoking the old one, and the revoke step is the one that gets skipped. markdownlint and Prettier clean.
supernaut lade till 1 incheckning 2026-08-02 19:16:58 +00:00
docs(adr-0024): reconcile the token inventory with the estate
Alla kontroller lyckades
ci / ci (pull_request) Successful in 12s
7a494916c9
Two accounts were missing from the table, two rows were wrong, and the store a
token lives in — which is what decides how it is rotated — was a footnote
rather than a column.

Added gitborg-webhook-admin and gitborg-token-audit. The latter was described
in the prose but absent from the table, which is the part read as the
inventory.

Corrected gitborg-ci: it holds two write:package tokens, one per store, and the
table said 'not vault'. The vault copy drives the host-side registry mirror and
the retention sweep, the Actions copy drives the web deploy, and rotation has to
cover both — replacing one leaves the other working, so the miss surfaces later
and somewhere else.

Corrected gitborg-bot: it is no longer only an MCP token, carrying read:package
as REGISTRY_READ_TOKEN for CI image pulls. Gave the runner-controller row its
actual scope rather than 'admin PAT'.

Recorded that the audited account list is now derived from the provisioning
list rather than hand-kept: that duplication is how gitborg-webhook-admin came
to hold an admin-scoped PAT whose age nothing reported.

Reconciled against forgejo_service_accounts, vault.example.yml and the live
instance on 2026-08-02.

Closes #73
supernaut sammanfogade incheckning 6d325b8444 till main 2026-08-02 19:23:16 +00:00
supernaut tog bort grenen docs/adr-0024-token-inventory 2026-08-02 19:23:16 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-docs!74
Ingen beskrivning angiven.