Skip to content

Amends the shred and mint cache-drain steps - #123

Merged
johnnyt merged 1 commit into
mainfrom
enc-880r-shred-amendment-and-mint-cache
Sep 30, 2026
Merged

johnnyt merged 1 commit into
mainfrom
enc-880r-shred-amendment-and-mint-cache

Conversation

@johnnyt

@johnnyt johnnyt commented Sep 30, 2026

Copy link
Copy Markdown
Member

What this changes

A write on a warm encryption-materials cache after a mint still carries the old version's data key. The engine's encryption cache id (aws_encryption_sdk 1.0.0, Cmm.Caching.compute_encryption_cache_id/3) hashes the partition id, the suite and the request context, not the key, and the vault's partition id is the vault and the selector (Encryptor.Vault.Encrypt's maybe_caching/3). So after P2 step 1 a write whose context finds a warm entry from before the mint is wrapped under version n while encryption_key/2 answers n+1.

  • Test first. test/encryptor/vault/shred_drain_test.exs, describe "P2 step 1, the mint, on a warm encryption cache": the write after the mint names version 1 in its header; after a vault restart the write names version 2. The gap is real.
  • The fix is to the procedure, not the code. No lib/ behaviour changes. The example fix, dropping the scope's encryption entry at the mint, cannot be built without a new public option: Encryptor.Envelope.provision/3 takes the root vault, holds no handle on the scope's vault, and a drop there would bind one node. P2 step 1 now carries a drain before step 2 (wait max_age from when the row is visible to every node's provider, or restart the vaults), and P4's second precondition is restated on that drain.
  • ADR-0005 Amendment B, proposed. B1: P3 answers {:unknown_key, selector} at once for a provider that reads its store on every call, so P3 step 3, the blast-radius P3 row, the "Cache drainage is now a runbook step" consequence and Amendment A's A5 contrast paragraph are superseded for such a provider. B2: P4 waits on the drain. B3: P2 step 1 needs a drain before step 2. B4: P4's second precondition holds from the end of that drain. It cites the 2026-09-29 Note "P4 depends on the cache drain and P3 does not" by heading and shred_drain_test by test name. The decision to amend was ruled by the operator, 2026-09-30.
  • The suspend/2 doc and the three guides (guides/rotation-runbook.md, guides/choosing-the-scope.md, guides/getting-started.md): each site now says that P3 answers unknown_key at once for a provider that reads its store on every call and that P4 is the procedure that waits on the drain. The runbook also gains the P2 step 1 drain and a blast-radius row for it.
  • ADR-0007 foot Note: its two P3 step 3 passages (decision 8's mapping row and the offboarding walk's step 3) are read as residency-only on this provider, citing ADR-0005's 2026-09-29 Note by heading.

No changelog fragment: the change is documentation, records and a test, which changelog.d/README.md excludes.

Review (in-turn, gate tier)

I re-read the diff against the three beads' acceptance fields. I checked every claim in the record text against the code by anchor at 9a399a0 and in the engine at 1.0.0. The checks: decrypt/7 and Resolve.decryption_keys/3 come before the client is built; maybe_caching/3 passes Partition.id(vault, selector); rekey/2's write half builds its client through Encrypt.client/3; GcpKms's rows/2 calls the store closure and answers unknown_key on an empty result; compute_encryption_cache_id/3 takes no key input; and CacheEntry.new/2 sets expiry from creation. I also re-located each of ADR-0005's self-cites (:132, :419-422, :455-456, :463, :602, :694, :796, :1095) at its anchor. The rekey claim is marked in the record as read from the code, not tested. Direction check of the record text: every claim verified on main, cites by anchor, zero removed lines under docs/adr/. The pinned guide passages (test/guides_test.exs: the GCP subsection headings and the per-shape table) are untouched.

Sabotage: maybe_caching/3 returning the uncached CMM makes the new test fail on the after-mint assertion (the header names version 2). Restored byte-equal, then recompiled.

Provenance

  • The runbook's P2 step 1 drain paragraph and its blast-radius row are not in the bead's file map. The fix forces them: they are the host-facing half of B3.
  • Rebased over the merged GCP Decrypt change. ADR-0007 took a keep-both resolution: its new Amendment B first, this Note after it. The GcpKms rows/2 cites were re-located to 9a399a0 (the lines moved), and the full gate ran green on the rebased head.
  • The gate ran green on the committed tree before the rebase, with the gate lock and a slot held (the commit is that tree), and again on the rebased head.

Refs: enc-880r
Refs: enc-w0i1
Refs: enc-t6b0

A write on a warm encryption cache after a mint still wraps its data
key under the old version: the engine's encryption cache id does not
include the key. A new test in shred_drain_test pins it. ADR-0005
Amendment B records that P3 answers unknown_key at once for a
provider that reads its store on every call, that P4 waits on the
drain, and that P2 step 1 now needs a drain before the rewrite. The
suspend/2 doc, the three guides and an ADR-0007 foot Note follow.

Refs: enc-880r
Refs: enc-w0i1
Refs: enc-t6b0
@johnnyt
johnnyt merged commit a594fe3 into main Sep 30, 2026
1 check passed
@johnnyt
johnnyt deleted the enc-880r-shred-amendment-and-mint-cache branch September 30, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant