rotate/gitea: multi-reference blast-and-cutover (LIVE-VM)

Rotate one Gitea PAT and rewrite it in EVERY on-disk reference in lockstep,
so a real in-use token can rotate without breaking git auth. The blob carries
one ref=<file> per reference; Rotate mints the new token, proves it
authenticates, then rewrites the old->new token literal in each ref file
(atomic temp+rename, minGiteaTokenLen-guarded), Verify asserts the new token
is present in each ref, and the spine revokes the old token last. A dead token
is never written into a git config; any failure leaves every ref on a live token.

Adds internal/rotate/gitea_refs.go (read-only reference enumerator) + driver and
enumerator unit tests. Proven end-to-end against real Gitea 1.25.0 in the sandbox
VM via lab/lab-gitea-blast-vm.sh (GITEA_BLAST_VM_OK): one rotation rewrote a fake
.git-credentials, an embedded git-remote URL, and a tea config; new->200, old->401.

Two documented follow-ons before any in-use host PAT: gopass-entry-sync (PAT
duplicated across other gopass entries) and token-scope-cloning (preserve the old
token's repo scopes on mint, like sendgrid).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-07-19 13:25:52 -07:00
parent 9f227bb05f
commit 8dbb2f21fd
7 changed files with 622 additions and 18 deletions
+30
View File
@@ -59,6 +59,36 @@ multipass exec incredigo-sbx -- bash -lc '
| `lab-provision-browserrot.sh` | Phase A-tier-1 site-side rotation engine (`internal/browserrot`) — real headless Chromium against a throwaway local change-password form (fake cred) |
| `tui-probe.py` | drives the `guide` Bubble Tea TUI under a pty |
## Multi-reference blast-and-cutover (gitea)
| Script | Proves | Depends on |
|---|---|---|
| `lab-gitea-blast-vm.sh` | the gitea **blast-and-cutover** — one rotation rewrites the token in **every** on-disk reference in lockstep | `lab-provision-gitea.sh` (real local Gitea) |
`lab-gitea-blast-vm.sh` is the LIVE-VM proof for the cutover a real *in-use* Gitea PAT
needs: it embeds one seed token verbatim in three real-world reference shapes (a fake
`~/.git-credentials` line, an embedded git-remote URL in a repo `.git/config`, and a
`tea` config), stages a driver-ready blob carrying one `ref=<file>` per reference, runs
`rotate --execute`, then asserts **every** reference was rewritten old→new, the new token
is alive (`200`), the old is revoked (`401`), and the rewritten `.git-credentials` still
authenticates at the git transport (not `401`). Ordering is the point: incredigo mints
the new token, proves it authenticates, **then** rewrites the refs, and revokes the old
one **last** — so a dead token is never written into a git config.
Run it after the provisioner:
```sh
multipass exec incredigo-sbx -- bash /home/ubuntu/lab/lab-provision-gitea.sh
multipass exec incredigo-sbx -- bash /home/ubuntu/lab/lab-gitea-blast-vm.sh # -> GITEA_BLAST_VM_OK
```
> **Two documented follow-ons** (surfaced by this proof, not yet built):
> 1. **gopass-entry-sync** — the same PAT also lives in *other* gopass entries; the driver
> rewrites only files named in `ref=`, so a spine hook is needed to pass old+new token
> to a gopass-sweep step. Surface in the blast report; do not auto-rewrite yet.
> 2. **token-scope-cloning** — the driver mints the new token with fixed
> `write:user`/`read:user` scopes; a token used for git operations needs its repo scopes
> preserved (clone the old token's scopes on create, like the `sendgrid` driver).
## Custody / smoke
- `lab-rung1-appsecret-host.sh`**safe-candidate ladder rung 1**, the host dress