Keys: legacy-key coverage scan + rotation lifecycle tab (#348, ADR 0002) #409

Merged
Cordy merged 8 commits from keyscan into main 2026-09-03 18:09:38 +00:00
Owner

The verify half of ADR 0002's rotate → drain → verify → discard cycle, per the approved variant-B mockups.

Backend: internal/keyscan — background scan over the bare base driver (below reserved/encrypt, so .cairn-state objects are included). Classification by trial-unwrap: the first 64 KiB goes to age.Decrypt per identity set; NoIdentityMatchError = not covered, payload never read. Report: active / retired-only / neither counts, PQ-flavour split (mlkem stanza sniff), retired-only paths grouped by two-segment prefix (max 50 rows), neither-paths capped at 100. Generic over retained identities — deploymentIdentityLegacy rides the same scanner. GET/POST/DELETE /api/v1/admin/keyscan, wired in main.go only when a *Legacy identity parses.

Frontend: Encryption & keys gains a Rotation tab (hidden unless a retired key is configured) with the four-step lifecycle stepper, key rows (public halves + flavour chips), scan/rescan/cancel, 3s polling while running, the amber report grid, and the green "covers nothing — remove recoveryIdentityLegacy" verdict. The Re-encrypt action's home is reserved for the drain follow-up. i18n ×4.

Docs: handbook retirement section now points at the scan; CHANGELOG v0.6.115.

Tests: scanner classification (active/retired/neither/skipped + flavour + neither-paths) and prefix grouping; full gate green on the runner (vet, build, keyscan/api/webcheck tests, web extract + tests).

The verify half of ADR 0002's rotate → drain → verify → discard cycle, per the approved variant-B mockups. **Backend:** `internal/keyscan` — background scan over the **bare base driver** (below `reserved`/`encrypt`, so `.cairn-state` objects are included). Classification by trial-unwrap: the first 64 KiB goes to `age.Decrypt` per identity set; `NoIdentityMatchError` = not covered, payload never read. Report: active / retired-only / neither counts, PQ-flavour split (mlkem stanza sniff), retired-only paths grouped by two-segment prefix (max 50 rows), neither-paths capped at 100. Generic over retained identities — `deploymentIdentityLegacy` rides the same scanner. `GET/POST/DELETE /api/v1/admin/keyscan`, wired in main.go only when a `*Legacy` identity parses. **Frontend:** Encryption & keys gains a **Rotation** tab (hidden unless a retired key is configured) with the four-step lifecycle stepper, key rows (public halves + flavour chips), scan/rescan/cancel, 3s polling while running, the amber report grid, and the green "covers nothing — remove `recoveryIdentityLegacy`" verdict. The Re-encrypt action's home is reserved for the drain follow-up. i18n ×4. **Docs:** handbook retirement section now points at the scan; CHANGELOG v0.6.115. Tests: scanner classification (active/retired/neither/skipped + flavour + neither-paths) and prefix grouping; full gate green on the runner (vet, build, keyscan/api/webcheck tests, web extract + tests).
Cordy merged commit fba9642dc7 into main 2026-09-03 18:09:38 +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#409
No description provided.