Encryption & cryptographic erasure
ScaiLog encrypts each PI field under a key that belongs to one data subject, so you can erase that subject's data everywhere at once by destroying a single key. This page explains the key hierarchy, how a field is encrypted with AES-256-GCM, where the keys live, and how Article 17 erasure works as cryptographic shredding.
The key hierarchy#
Keys form a three-level tree. Each level wraps (encrypts) the level below it, and only the root ever lives outside the key store.
1 2 3 | |
- KRoot is the deployment root key, supplied by the passphrase-protected keystore (optionally custodied by ScaiVault as a plugin, but never as a runtime dependency).
- A tenant KEK (key-encryption key) is minted on first use for each tenant,
wrapped under KRoot, and stored in
keymap.db. - A subject DEK (data-encryption key) is minted the first time a given data
subject appears, wrapped under its tenant's KEK, and stored in
keymap.db.
Unwrapped KEKs and DEKs are cached only in memory — they are never written to disk in the clear. Actual PI field values are encrypted directly under the subject DEK.
AES-256-GCM field encryption#
Every wrap and every field encryption is AES-256-GCM with a fresh random 96-bit nonce and a 16-byte authentication tag appended to the ciphertext. Each encryption also binds a canonical AAD (additional authenticated data) string to the ciphertext, so a value can only ever be decrypted in the exact context it was encrypted in. Change any part of the context and decryption fails.
The AAD strings are fixed and versioned:
| Operation | AAD |
|---|---|
| Wrap a tenant KEK under KRoot | scailog.kekwrap.v1|tenant=<tenant> |
| Wrap a subject DEK under the tenant KEK | scailog.dekwrap.v1|tenant=<tenant>|subject=<subject> |
| Encrypt a PI field | scailog.field.v1|tenant=<tenant>|subject=<subject>|site=<call_site_id>|field=<name> |
Because the field AAD includes the tenant, subject, call site, and field name, a
ciphertext cannot be silently moved to another field or subject — the tag will
not verify. Each stored field is packed as {"n": <b64 nonce>, "c": <b64 ciphertext‖tag>}, and all encrypted fields for an entry are collected into the
opaque pi_fields blob. These AAD strings are normative test vectors; they do
not change.
keymap.db is separate#
Wrapped key material lives in its own SQLite file, keymap.db, kept separate
from the main scailog.db store. That separation is deliberate: the store holds
ciphertext and the keymap holds the keys that could decrypt it, and the keymap
gets short backup retention so destroyed keys do not linger in backups. The
crypto-shred marker (a subject's destroyed_at) lives in this shared keymap, so
every worker sees an erased subject authoritatively.
Cryptographic erasure (Article 17)#
Erasure in ScaiLog is cryptographic: to fulfil a subject's right to be forgotten, ScaiLog destroys the subject's DEK. Once the DEK is gone, every PI field ever encrypted under it — across every host, every backup, every replica — is permanently unrecoverable, because the ciphertext is meaningless without the key. There is no need to hunt down and overwrite individual rows; the key was the single point of decryptability.
Requesting a new DEK for an erased subject is refused, so no key can be re-minted. If new PI arrives for an already-erased subject, the pipeline stores it PI-free (redacted) rather than mint a fresh key.
The erasure job does three things:
- Crypto-shred — zero and mark the subject DEK in
keymap.db, recording adestroyed_attimestamp. - Tombstone affected entries — in tombstone mode the entries are marked erased (and their now-undecryptable ciphertext scrubbed); a delete mode removes them outright.
- Sign a receipt — write an Ed25519-signed erasure receipt recording the subject, timestamp, and how many entries were tombstoned.
Erasure is idempotent: re-running against an already-erased subject returns the original receipt rather than issuing a fresh zero-count one. Crypto-shredding ships in Phase 1 — this is design invariant P5.
Signed receipts and the rotating keyring#
DSAR reports and erasure receipts are signed with Ed25519 so an offline verifier
can confirm they are genuine. Signing keys rotate without breaking old
documents: the report keyring keeps one active key for new signatures plus
any number of retired keys kept only for verification. Every signature
carries the signing key's key_id (a fingerprint of its public key), and the
full set of public keys — active and retired — stays published (served at
GET /v1/report-keys) so a document signed by a since-retired key remains
verifiable forever.