Envelope versioning
Every ciphertext, every signed audit row, every persisted session ships inside a versioned envelope. The version byte is the framework's lever for rolling crypto algorithms forward without breaking a single piece of legacy data on disk.
Every primitive that emits crypto bytes (b.crypto.encryptEnvelope, b.vault.seal, b.audit.sign, b.session.encrypt, …) writes the same envelope shape: { v, alg, kid, fixedInfo, ct, aad }. Decrypt paths route on v + alg — old envelopes keep working when the framework rolls forward.
Key rotation without re-encryption #
The kid field references the key by id, not by value. Operators add a new key (vault-2026-06); new envelopes get the new kid; old envelopes keep their old kid and decrypt against the historical key in the keyring. Background re-encryption is a deliberate operator action, not a hidden migration.
Algorithm rollover #
When a new KEM/cipher pair is added (e.g. x-wing+xchacha alongside ml-kem-1024+xchacha), the new pair becomes the default for fresh envelopes. Decrypt paths still route on alg — old envelopes resolve to the old pair via the keyring. The framework refuses to drop a registered algorithm until every on-disk envelope has been re-encrypted to a newer version.
Why AAD matters #
Every envelope binds AAD (additional authenticated data) to the ciphertext. The AAD is the row id, the user id, the table name, the request method — whatever context determines whether this ciphertext was meant to be read here. An attacker who copies a ciphertext from row A to row B can't decrypt at row B because the AAD doesn't match. This defends the entire class of replay- style attacks where one user's ciphertext is reused as another's.
Last updated 2026-08-08T16:39:49.652Z by seeder.