Support zero-downtime rotation of synchronized Draft encryption keys #1100

Closed
opened 2026-08-18 22:51:40 +00:00 by rockachopa · 0 comments
Member

Product problem

Synchronized unfiled Drafts are encrypted at rest with one deployment-wide AES-GCM key. The documented configuration has no online key rotation: replacing the key makes existing private Drafts unreadable, while retaining a compromised key indefinitely weakens incident response.

Vertical slice

Add a bounded named keyring with one active write key. New ciphertext records identify their key; reads accept legacy v1 records and configured older keys, then rewrap successfully read collections under the active key without changing their logical revision. Provide an idempotent migration/status CLI so operators can prove an old key has zero remaining dependencies before removal.

Acceptance

  • Validated keyring configuration rejects duplicate, malformed, oversized, unknown-active, or missing keys without logging key material.
  • New writes use the active named key and authenticate the account, revision, envelope version, and key ID.
  • Legacy v1 and inactive-key records remain readable while their key is configured and are rewrapped under the active key without changing collection revision or conflict semantics.
  • Unknown key IDs and tampered envelopes fail closed.
  • A restart-safe CLI reports content-free aggregate counts, migrates all readable rows, and exits nonzero if any row is unreadable.
  • Existing plaintext migration, account isolation, bounds, API behavior, and full suite stay green.

Evidence / non-duplication

README.md explicitly states there is no online key rotation; src/unfiled_draft_store.py constructs one AESGCM and stores v1: envelopes without key IDs. All 552 historical issue titles and recent commit subjects were checked: #1098/#1099 introduced encryption but no title or commit implements rotation/keyrings. No open eligible issue is assigned to timmy or unassigned, so this issue owns the coherent follow-up.

## Product problem Synchronized unfiled Drafts are encrypted at rest with one deployment-wide AES-GCM key. The documented configuration has no online key rotation: replacing the key makes existing private Drafts unreadable, while retaining a compromised key indefinitely weakens incident response. ## Vertical slice Add a bounded named keyring with one active write key. New ciphertext records identify their key; reads accept legacy v1 records and configured older keys, then rewrap successfully read collections under the active key without changing their logical revision. Provide an idempotent migration/status CLI so operators can prove an old key has zero remaining dependencies before removal. ## Acceptance - Validated keyring configuration rejects duplicate, malformed, oversized, unknown-active, or missing keys without logging key material. - New writes use the active named key and authenticate the account, revision, envelope version, and key ID. - Legacy v1 and inactive-key records remain readable while their key is configured and are rewrapped under the active key without changing collection revision or conflict semantics. - Unknown key IDs and tampered envelopes fail closed. - A restart-safe CLI reports content-free aggregate counts, migrates all readable rows, and exits nonzero if any row is unreadable. - Existing plaintext migration, account isolation, bounds, API behavior, and full suite stay green. ## Evidence / non-duplication `README.md` explicitly states there is no online key rotation; `src/unfiled_draft_store.py` constructs one AESGCM and stores `v1:` envelopes without key IDs. All 552 historical issue titles and recent commit subjects were checked: #1098/#1099 introduced encryption but no title or commit implements rotation/keyrings. No open eligible issue is assigned to timmy or unassigned, so this issue owns the coherent follow-up.
timmy was assigned by rockachopa 2026-08-18 22:51:40 +00:00
Sign in to join this conversation.
No Milestone
No project
No Assignees
1 Participants
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: stackchain/stackchain-dashboard#1100
No description provided.