Files
incredigo/docs/ROTATION-PROOFS.md
T
leetcrypt b415c43bff rotate: 4 SaaS-token/app-signing drivers + data-separated proof tracking
Add gitlab, cloudflare, ghactions (MOCK-ONLY) and appsecret (LIVE-VM) one-file
Rotators, each with a table-driven cutover-proof + leak-check test. gitlab/
cloudflare/ghactions are in-place SaaS-token rolls (self/rotate, value-roll,
sealed-secret overwrite) so RevokeOld is a no-op; ghactions seals via
nacl/box.SealAnonymous (no new go.mod dep). appsecret regenerates a local app
signing secret and rewrites it in place across every target file (atomic, mode-
preserving, redacted errors), discoverable via exact-match app-signing key names
in env.go.

Keep real-rotation code distinct from mock code: how each driver's cutover was
validated lives as DATA in internal/rotate/proofs.go (single source of truth) +
docs/ROTATION-PROOFS.md, surfaced as a PROOF column in `incredigo rotate` so a
MOCK-ONLY driver can never be mistaken for a LIVE-VM one. appsecret proven LIVE-VM
against real local files in the sandbox VM.

82 tests green, -race clean on rotate/sink/vault.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-18 15:28:05 -07:00

3.9 KiB

Rotation cutover proofs

This file is the human-facing mirror of internal/rotate/proofs.go. That Go table is the single source of truth (incredigo rotate surfaces it as the PROOF column); this document explains what each level means and records the basis for every driver's level. Keep the two in sync — if you change one, change the other.

Why this exists

Every Rotator in internal/rotate contains only real rotation code: it talks to a real service (HTTP API, DB client, SSH, local files). None of them contain simulation branches or test awareness — the only thing a test injects is an endpoint / HTTPClient / binary path, which a real deployment also configures.

What differs between drivers is how their real-cutover path has been validated. That is data, not behaviour, so it lives in one table (here + proofs.go) instead of as prose scattered through — and easily confused with — the driver code. This is the guardrail that stops a mock-only driver from ever being mistaken for a live one.

Levels

Level Meaning
LIVE-VM Cutover proven against the real target software running in the sandbox VM (real PostgreSQL / MariaDB / Redis / wg / Gitea / local files).
MOCK-ONLY Cutover proven only against an emulator — our own httptest / in-process SSH server, or a third-party mock (e.g. moto for AWS). NOT proven against the real target service.
UNPROVEN No cutover proof recorded (e.g. the dry-run noop helper).

Honesty note: running in the VM does not automatically make a driver LIVE-VM. AWS ran in the VM but only against moto (a mock AWS), so it is MOCK-ONLY. LIVE-VM means the real target software validated the cutover.

Current status

LIVE-VM — proven against real target software in the sandbox VM

Driver Real target Provisioner
postgres real PostgreSQL lab-provision-pg.sh
mysql real MariaDB lab-provision-dbclones.sh
redis real redis-server lab-provision-dbclones.sh
wireguard real wg lab-provision-wg.sh
gitea real Gitea lab-provision-gitea.sh
appsecret real local files lab-provision-appsec.sh

MOCK-ONLY — proven only against an emulator / mock

Driver Emulator Why not (yet) LIVE-VM
aws moto mock AWS in VM real AWS never hit
sshkey in-process SSH server live VM POC not run
openwrt in-process SSH server live QEMU deferred
mullvad httptest emulator needs a paid account
cloudflare httptest emulator no self-host
ghactions httptest emulator (holds the libsodium box keypair, decrypts the sealed PUT) no self-host
gitlab httptest emulator GitLab CE too heavy for the VM

What the MOCK-ONLY emulators actually prove

The emulators are not stubs that rubber-stamp success — each enforces the real protocol so the test exercises the driver's true cutover logic:

  • gitlabself/rotate mints a new PAT and revokes the caller's old one; the test asserts Verify(old) fails after Rotate, proving the revoke really happened.
  • cloudflarePUT .../value rolls the value for the same token id and invalidates the previous value; the test asserts the id is unchanged and Verify(old) fails.
  • ghactions — the emulator owns a real NaCl box keypair, serves the public key, and on PUT decrypts the sealed value and stores the plaintext; the test asserts the decrypted value equals the new value the driver put in the rebuilt blob — proving the sealed-box encryption is correct, not simulated.

Promoting any of these to LIVE-VM only requires standing up the real service (a self-hosted GitLab CE / a real Cloudflare token / a throwaway GitHub repo) and re-running the same driver against it — no driver code changes.