Add ProofLiveReal (highest level, no driver yet) and enforce it at
execute time: rotate --execute only runs drivers with proof >= LIVE-VM
(MinExecuteProof). MOCK-ONLY/UNPROVEN drivers are skipped before any
provider call unless --allow-mock-proven is passed. ExecuteResult gains
Skipped/SkipReason; the CLI reports and audit-logs skips separately from
failures. Closes the last structural gap where an emulator-only driver
could touch a real credential unattended. Covered by execute_gate_test.go.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two more provider-API rotation drivers for the vibe-coder credential surface,
both following the create-new → verify → revoke-old pattern with the
self-contained blob form <provider>://<host>/?id=&secret=.
- vercel: POST /v3/user/tokens mints a new bearer; Verify lists tokens and
asserts the new id is present; RevokeOld DELETEs the old token by id.
- sendgrid: clones the old key's scopes (GET /v3/api_keys/<id>) onto the new
key so rotation is a faithful replacement, not a privilege downgrade; Verify
is a GET /v3/scopes auth proof; RevokeOld DELETEs the old key by id.
Both ship Bearer-enforcing httptest emulators that prove a real cutover (old
credential dead, new alive after RevokeOld) plus a leak check. Recorded as
MOCK-ONLY in proofs.go + docs/ROTATION-PROOFS.md (no self-host). go build/vet
clean; 77 rotate tests pass, -race clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
First of the vibe-coder API-rotation batch. Resend mints a new API key
authenticated by the old one (POST /api-keys), verifies it by listing keys
with the new Bearer token and asserting its id is present, then deletes the
old key by id (DELETE /api-keys/<id>). Self-contained blob
resend://host/?id=&secret= keeps the token off Identity/Meta/logs/argv.
httptest emulator enforces Bearer validity (key authenticates only while it
exists) so the test proves a real cutover: old dead, new alive, plus a leak
check. Recorded MOCK-ONLY in proofs.go + ROTATION-PROOFS.md (no self-host).
Note: GitHub PAT is intentionally NOT an API Rotator — GitHub has no
create-token API (classic Authorizations API removed 2020; fine-grained PATs
are UI-only), so a PAT belongs in the guided/worklist path, not here.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ran live cutover POCs in the sandbox VM against the real target software and
promoted both drivers from MOCK-ONLY to LIVE-VM in the proof table (data, not
driver behaviour):
- k8s: proven against real k3s v1.35 kube-apiserver + token controller
(lab-provision-k8s.sh). The real apiserver serves a self-signed cert, so the
driver gained CA-pinned TLS — blob params ca= (base64 PEM, pinned as the only
trusted root) and insecure=1 (lab-only escape hatch), routed through a new
clientFor(cr). Neither is test-awareness; a real deployment configures the same
CA. Added TestK8sTLSTrust covering CA-pinned / insecure / untrusted-rejection
and the blob round-trip.
- mongo: proven against real mongod 8.0 (lab-provision-mongo.sh).
npm stays MOCK-ONLY by decision (registry.npmjs.org not self-hostable; real
create-token requires the account password in the body) — documented as a
contract gap rather than faking a LIVE-VM. gcp/twilio/flyio remain MOCK-ONLY.
Live POCs surfaced real-target-only behaviour now recorded in the docs: the
apiserver's ~10s successful-auth cache (old token kept working ~7s after Secret
deletion) and real mongosh's stdout prompt. Full suite 104 green, -race clean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each driver is real-service code only (no test awareness; tests inject just an
HTTPClient/Bin), following the established patterns:
- mongo: in-place changeUserPassword over the old connection; no secret on argv
(host/db on the URI, both passwords on stdin, output scrubbed).
- npm: create token -> /-/whoami -> delete-by-key; blob records the key.
- gcp: service-account KEY rotation; mints an RS256 JWT signed with the SA's
own private key (crypto/rsa, stdlib) to self-auth the IAM create/verify/delete.
- twilio: API Key create -> get -> delete, Basic auth (KeySid/secret).
- flyio: GraphQL — a managing token mints/deletes the rotated deploy token.
- k8s: create a service-account-token Secret, await the populated token,
verify against the SA, delete the old Secret (invalidates the old token).
All 6 register their honest proof level (MOCK-ONLY) in proofs.go +
ROTATION-PROOFS.md; mongo/npm/k8s are LIVE-VM candidates pending real-target VM
POCs. Emulators enforce credential validity (gcp verifies the JWT signature),
tests assert Verify(old) fails after revoke + a secret-leak canary.
Full suite: 103 green, -race clean on rotate/sink/vault.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>