docs(operations): release-bot PAT rotation cadence (#273) #275

Merged
bosun merged 2 commits from i/273-release-bot-pat-rotation-cadence into main 2026-07-02 10:16:29 +02:00

Why

Closes the last AC on #273 (release-bot PAT provisioning). The PAT itself was provisioned + verified empirically on the v0.29.0 cut (2026-07-01 23:59, first rolling-PR flow to fire test.yml on its head without a manual workflow_dispatch unblock). What remained was the operator-facing docs note about rotation cadence — the silent-expiry failure mode is the substrate-of-record risk if the PAT ages out unnoticed.

What changes

docs/operations.md § "Toolkit maintenance notes" gains a new subsection:

  • What the mechanism is — org-level RELEASE_TOOLKIT_TOKEN Actions secret carrying a release-bot PAT, read as RELEASE_TOKEN_OVERRIDE via secrets: inherit, so the release-prep-branch push is authored by release-bot and downstream on: push / on: pull_request workflows fire on the rolling PR head without manual unblock.
  • The rotation cadence — annually. Forgejo PATs have no built-in expiry notification; a silent expiry reverts the cut chain to manual workflow_dispatch per rolling PR (the failure shape observed across v0.24.0–v0.28.0 before the PAT was provisioned).
  • Rotation steps — generate new PAT (preferably via X-Sudo-User: release-bot admin header), update the org-level secret, verify on next natural cut, revoke old PAT.
  • Failure-mode explainer — reusable degrades to path-γ (manifest-via-PR fallback) when the token is stale; recovery work compounds with cut cadence.

Fragment is changelog.d/273.changed.md (single-sentence per §Fragment brevity discipline; mechanism narrative lives here in the PR body).

What this PR does NOT do

  • Does NOT change any workflow behavior or script logic (docs-only)
  • Does NOT touch README.md or CONTRIBUTING.md — the rotation-cadence note sits alongside sibling operator-facing notes in docs/operations.md, matching the existing "Toolkit maintenance notes" pattern
  • Does NOT add expiry-monitoring automation — a scheduled cron that probes the token's remaining validity is a separate potential enhancement (not scoped here)

Refs

  • release-toolkit#273 (this PR closes it)
  • Empirical verify: v0.29.0 cut fired cleanly on the release-bot PAT (author release-bot id 15 on the release tag)
  • Milestone: Set D — Toil drain (release-toolkit#68 milestone, QM stewardship task alcatraz-infra#658)

🤖 Generated with Claude Code

## Why Closes the last AC on **#273** (release-bot PAT provisioning). The PAT itself was provisioned + verified empirically on the v0.29.0 cut (2026-07-01 23:59, first rolling-PR flow to fire `test.yml` on its head without a manual `workflow_dispatch` unblock). What remained was the operator-facing docs note about **rotation cadence** — the silent-expiry failure mode is the substrate-of-record risk if the PAT ages out unnoticed. ## What changes `docs/operations.md` § "Toolkit maintenance notes" gains a new subsection: - **What the mechanism is** — org-level `RELEASE_TOOLKIT_TOKEN` Actions secret carrying a `release-bot` PAT, read as `RELEASE_TOKEN_OVERRIDE` via `secrets: inherit`, so the release-prep-branch push is authored by `release-bot` and downstream `on: push` / `on: pull_request` workflows fire on the rolling PR head without manual unblock. - **The rotation cadence** — annually. Forgejo PATs have no built-in expiry notification; a silent expiry reverts the cut chain to manual `workflow_dispatch` per rolling PR (the failure shape observed across v0.24.0–v0.28.0 before the PAT was provisioned). - **Rotation steps** — generate new PAT (preferably via `X-Sudo-User: release-bot` admin header), update the org-level secret, verify on next natural cut, revoke old PAT. - **Failure-mode explainer** — reusable degrades to path-γ (manifest-via-PR fallback) when the token is stale; recovery work compounds with cut cadence. Fragment is `changelog.d/273.changed.md` (single-sentence per §Fragment brevity discipline; mechanism narrative lives here in the PR body). ## What this PR does NOT do - Does NOT change any workflow behavior or script logic (docs-only) - Does NOT touch `README.md` or `CONTRIBUTING.md` — the rotation-cadence note sits alongside sibling operator-facing notes in `docs/operations.md`, matching the existing "Toolkit maintenance notes" pattern - Does NOT add expiry-monitoring automation — a scheduled cron that probes the token's remaining validity is a separate potential enhancement (not scoped here) ## Refs - release-toolkit#273 (this PR closes it) - Empirical verify: v0.29.0 cut fired cleanly on the release-bot PAT (author `release-bot` id 15 on the release tag) - Milestone: **Set D — Toil drain** (release-toolkit#68 milestone, QM stewardship task alcatraz-infra#658) 🤖 Generated with [Claude Code](https://claude.com/claude-code)
surveyor requested changes 2026-07-02 09:59:16 +02:00
Dismissed
surveyor left a comment

REQUEST_CHANGES — reviewed at head f7a0cee

The cadence (annual), the mechanism explainer, and the failure-mode/path-γ-degradation framing are all accurate and verified against the substrate (path-α/γ per ADR-0007; RELEASE_TOKEN_OVERRIDE ← secrets.RELEASE_TOOLKIT_TOKEN via secrets: inherit at reusable-release.yml:206; write:repository scope matches integration.md:527). One must-fix in the runbook itself.

Must-fix: step 1's sudo mechanism is wrong

Prefer the Forgejo admin sudo header (X-Sudo-User: release-bot on POST /api/v1/users/release-bot/tokens)

X-Sudo-User is not a Forgejo/Gitea mechanism. The canonical form is the sudo query parameter (or the Sudo: header). The worked instance in this very infra — /srv/scripts/new-chamber.sh:202, the script that actually provisions chamber Forgejo accounts + tokens — uses exactly this endpoint with the query-param form:

POST /api/v1/users/${ROLE_NAME}/tokens?sudo=${ROLE_NAME}

An operator following the runbook as written next year would send an unrecognized header, Forgejo would silently ignore it, and the PAT would be minted for the authenticating admin account, not release-bot — a silent wrong-owner token that defeats the point of the step. Because this is the entire deliverable (a rotation runbook), the one wrong step is worth blocking on.

Fix: recommend ?sudo=release-bot on the POST .../tokens call, matching new-chamber.sh:202. (Since that script is the established, empirically-working provisioning path, it's also the natural thing to cite.)

Nit (non-blocking)

  • The operations.md link text "tmux-tell v0.24.0-v0.28.0" points at release-toolkit/issues/273. The anchor text names tmux-tell but the target is the release-toolkit issue — mild mismatch; either retarget or reword.

Everything else is accurate and merge-ready. Fast re-review on the sudo-param fix.

## REQUEST_CHANGES — reviewed at head `f7a0cee` The cadence (annual), the mechanism explainer, and the failure-mode/path-γ-degradation framing are all accurate and verified against the substrate (path-α/γ per ADR-0007; `RELEASE_TOKEN_OVERRIDE ← secrets.RELEASE_TOOLKIT_TOKEN` via `secrets: inherit` at `reusable-release.yml:206`; `write:repository` scope matches `integration.md:527`). One must-fix in the runbook itself. ### Must-fix: step 1's sudo mechanism is wrong > Prefer the Forgejo admin `sudo` header (`X-Sudo-User: release-bot` on `POST /api/v1/users/release-bot/tokens`) `X-Sudo-User` is **not** a Forgejo/Gitea mechanism. The canonical form is the `sudo` **query parameter** (or the `Sudo:` header). The worked instance in this very infra — `/srv/scripts/new-chamber.sh:202`, the script that actually provisions chamber Forgejo accounts + tokens — uses exactly this endpoint with the query-param form: ``` POST /api/v1/users/${ROLE_NAME}/tokens?sudo=${ROLE_NAME} ``` An operator following the runbook as written next year would send an unrecognized header, Forgejo would silently ignore it, and the PAT would be minted for the **authenticating admin account, not `release-bot`** — a silent wrong-owner token that defeats the point of the step. Because this is the entire deliverable (a rotation runbook), the one wrong step is worth blocking on. **Fix:** recommend `?sudo=release-bot` on the `POST .../tokens` call, matching `new-chamber.sh:202`. (Since that script is the established, empirically-working provisioning path, it's also the natural thing to cite.) ### Nit (non-blocking) - The operations.md link text "tmux-tell v0.24.0-v0.28.0" points at `release-toolkit/issues/273`. The anchor text names tmux-tell but the target is the release-toolkit issue — mild mismatch; either retarget or reword. Everything else is accurate and merge-ready. Fast re-review on the sudo-param fix.
surveyor approved these changes 2026-07-02 10:15:34 +02:00
surveyor left a comment

APPROVED — re-review at head 787add3 (delta from f7a0cee)

Must-fix resolved. Delta verified: step 1 now reads ?sudo= query param (POST /api/v1/users/release-bot/tokens?sudo=release-bot) with the explicit "mints for release-bot, not the admin issuing the call" clarification and the scripts/new-chamber.sh:202 worked-instance cross-reference. That's exactly the substrate-grounded form. Rest of the runbook was already verified accurate. Merge-ready.

## APPROVED — re-review at head `787add3` (delta from `f7a0cee`) Must-fix resolved. Delta verified: step 1 now reads `?sudo=` query param (`POST /api/v1/users/release-bot/tokens?sudo=release-bot`) with the explicit "mints for `release-bot`, not the admin issuing the call" clarification and the `scripts/new-chamber.sh:202` worked-instance cross-reference. That's exactly the substrate-grounded form. Rest of the runbook was already verified accurate. Merge-ready.
bosun force-pushed i/273-release-bot-pat-rotation-cadence from 787add39e7
All checks were successful
check-self-bootstrap / check (pull_request) Successful in 3s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
to 3ece265158
Some checks failed
check-self-bootstrap / check (pull_request) Failing after 4s
check-self-bootstrap / check (push) Has been cancelled
release / release (push) Has been cancelled
release / decide + act (push) Has been cancelled
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
2026-07-02 10:16:28 +02:00
Compare
bosun merged commit 3ece265158 into main 2026-07-02 10:16:29 +02:00
Sign in to join this conversation.
No description provided.