#411: managed re-encryption (drain) — job, lock deferral, ledger, boot tripwire, Rotation UI #412

Merged
Cordy merged 14 commits from drain-411 into main 2026-09-03 20:05:08 +00:00
Owner

Implements the drain half of ADR 0002 (closes #411).

Backend

  • internal/keydrain: throttled, pausable, idempotent re-encryption pass. Walks the bare base driver, classifies by trial unwrap, rewrites retired-only objects through the encrypt decorator (custody re-resolution + PQ upgrade; below ownermeta/filelock/quota, so ownership and accounting are untouched by construction). Live-locked files are deferred with path/type/holder; state objects re-seal via the statestore (no double-wrap).
  • Rotation ledger (key-rotation.json, statestore-backed) + boot tripwire: removing an undrained retired key from config refuses to start; CAIRN_KEYDRAIN_FORCE=1 is the explicit escape hatch. The coverage scan (keyscan.OnComplete) marks entries drained at proven zero.
  • POST/GET/DELETE /api/v1/admin/keydrain; one key-drain audit event per pass.
  • Tests: rewrite + owner-sidecar content preservation (the #411 ownership regression test), lock deferral, state re-seal path, userForPath, ledger lifecycle.

Frontend (per the approved drain-locked-files mockup)

  • Rotation tab: Re-encrypt / Re-encrypt again / Pause buttons, progress bar, amber locked-files notice with expandable holder list, end-of-pass summary (moved / deferred / failed), honest "nothing is stuck — do not break locks" copy.
  • Recovery ↔ Rotation cross-links; "1 object skipped" singular; root-level scan group labelled "/ (top level)".

Verified in CI on the branch: go vet/build, keydrain/keyscan/api/webcheck tests, node web tests.

Implements the drain half of ADR 0002 (closes #411). **Backend** - `internal/keydrain`: throttled, pausable, idempotent re-encryption pass. Walks the bare base driver, classifies by trial unwrap, rewrites retired-only objects through the encrypt decorator (custody re-resolution + PQ upgrade; below ownermeta/filelock/quota, so ownership and accounting are untouched by construction). Live-locked files are deferred with path/type/holder; state objects re-seal via the statestore (no double-wrap). - Rotation ledger (`key-rotation.json`, statestore-backed) + boot tripwire: removing an undrained retired key from config refuses to start; `CAIRN_KEYDRAIN_FORCE=1` is the explicit escape hatch. The coverage scan (`keyscan.OnComplete`) marks entries drained at proven zero. - `POST/GET/DELETE /api/v1/admin/keydrain`; one `key-drain` audit event per pass. - Tests: rewrite + owner-sidecar content preservation (the #411 ownership regression test), lock deferral, state re-seal path, userForPath, ledger lifecycle. **Frontend** (per the approved drain-locked-files mockup) - Rotation tab: Re-encrypt / Re-encrypt again / Pause buttons, progress bar, amber locked-files notice with expandable holder list, end-of-pass summary (moved / deferred / failed), honest "nothing is stuck — do not break locks" copy. - Recovery ↔ Rotation cross-links; "1 object skipped" singular; root-level scan group labelled "/ (top level)". Verified in CI on the branch: go vet/build, keydrain/keyscan/api/webcheck tests, node web tests.
Cordy merged commit b78e63366f into main 2026-09-03 20:05:08 +00:00
Cordy deleted branch drain-411 2026-09-03 20:05:11 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#412
No description provided.