Repository navigation
docs(adr): pool the authority to mint keys, never a data key (CIP-5062) - #491
Open
freshtonic wants to merge 1 commit into
Open
freshtonic wants to merge 1 commit into
freshtonic wants to merge 1 commit into
Conversation
`pooled_key_generate` mints one data key and clones it across the batch. That is the only reason the pooled sources are `Unbound` — one key cannot carry a batch's several bindings — so making binding mandatory (CIP-4981) deletes that form of pooling and the round-trip saving with it. This records how to keep the saving. One backend call fetches an authority key the adapter caches; each value then gets its own data key, derived locally from that key and a per-value salt, with the binding as an input to the derivation. Nothing is shared between values except the authority to mint keys. It follows AWS's Hierarchical keyring, with one tier fewer: AWS derives a wrapping key and uses it to encrypt a separately generated data key, which an ESDK data key needs because several keyrings wrap the same one. A data key source returns material to one caller, so we derive the data key itself. Two consequences the ADR argues rather than assumes. `KeyIsolation` is the wrong axis — such a source is `PerValue`, and what differs is round trips and what sits in memory, so the capability belongs on its own trait and `Pooled` should go once nothing implements it. And `CachingKeyProvider`'s stance that skipping per-retrieval auditing must be opt-in and never a default is inverted here, so it is restated instead of dropped: a cached authority key means the backend authorizes once per refresh, and its TTL becomes a security control rather than a memory one. Left open on purpose, because the implementation should answer them: where the authority-key store lives, how rotation works, and whether an authority key is per backend key, per tenant or per keyset. The glossary term lands with the code, so CONTEXT.md keeps describing what the crate does today. Refs CIP-5062
freshtonic
force-pushed
the
claude/cip-5062-pooling-adr
branch
from
October 20, 2026 23:05
6356598 to
d673153
Compare
freshtonic
added this pull request to stack #356
October 21, 2026 04:53
4 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First deliverable for CIP-5062. Stacked on #451.
Docs only — one ADR, no code. It records how pooling survives mandatory
binding, because as built it does not.
pooled_key_generatemints one data key and clones it across the batch. Thatis the sole reason
PooledDataKeySourceand the three vendor pooled sourcesare
Unbound: one key cannot carry a batch's several bindings. So CIP-4981deletes that form of pooling, and the round-trip saving goes with it. The
saving is worth keeping, and this is how.
What it decides
Share the authority to mint keys, never a key. One backend call fetches an
authority key the adapter caches; each value then gets its own data key,
derived locally from that key and a fresh per-value salt, with the binding as
an input to the derivation:
Per-value isolation is intact, binding is intact — in fact stronger, since a
wrong binding cannot derive the key rather than being refused by the backend —
and the backend is called once per cache refresh instead of once per value.
It follows AWS's Hierarchical keyring with one tier fewer. AWS derives a
wrapping key and uses it to encrypt a separately generated data key, which an
ESDK data key needs because several keyrings wrap the same one. A data key
source returns material to one caller, so we derive the data key itself rather
than adding an indirection nothing uses and a second piece of key material in
memory.
Two things it argues rather than assumes
KeyIsolationis the wrong axis to hang strategies off. A source builtthis way reports
PerValue; what differs is round trips and what sits inmemory. Today's constant bundles keys-per-batch, round-trips and
can-it-bind together only because clone-one-key makes them move together. So
the capability belongs on its own trait, and
Pooledshould go once nothingimplements it.
It inverts
CachingKeyProvider's documented stance, so the ADR restates itrather than dropping it quietly. That type says skipping per-retrieval
auditing must be opt-in and never a default; a cached authority key makes it
the normal path, authorizing once per refresh. And plaintext key material now
lives for a TTL instead of fetch-use-drop, which makes the TTL a security
control rather than a memory bound — the opposite of how
CachingKeyProviderdocuments its own limits.
Left open on purpose
Where the authority-key store lives, how versioning and rotation work, and
whether an authority key is per backend key, per tenant or per keyset. Those
want the implementation to answer them honestly. The
CONTEXT.mdterm landswith the code too, so the glossary keeps describing what the crate does today.
Test plan
pooled_key_generateexists,KeyIsolationis{PerValue, Pooled},KeyCustodyis{ClientHeld, BackendHeld}.CachingKeyProviderpassage is quoted verbatim fromprovider/caching.rs.🤖 Generated with Claude Code