Erasing a subject
A right-to-erasure request arrives naming one person, and Chronicle gives you exactly one call to make. This page covers what that call reaches, what happens to that person afterwards, and what you still have to do yourself before you can certify the erasure as complete.
The one call
Section titled “The one call”Erasure in Chronicle is the deletion of an encryption key. Every [PII] value is encrypted at append time under a key held for the event’s subject, so deleting that key makes every PII value for that subject unreadable at once, without touching the append-only log:
public static class ComplianceErasureDeleteKey{ public static async Task Delete(ChronicleClient chronicleClient) { var eventStore = await chronicleClient.GetEventStore("Sales"); await eventStore.PII.DeleteEncryptionKeyFor("person-42"); }}import io.cratis.chronicle.ChronicleClient
object ComplianceErasureDeleteKey { suspend fun delete(chronicleClient: ChronicleClient) { val eventStore = chronicleClient.getEventStore("Sales") eventStore.compliance.deleteEncryptionKey("person-42") }}import io.cratis.chronicle.java.BlockingChronicleClient;import io.cratis.chronicle.java.BlockingEventStore;import io.cratis.chronicle.java.ComplianceServiceJavaBridge;
class ComplianceErasureDeleteKey { static void delete(BlockingChronicleClient chronicleClient) { BlockingEventStore eventStore = chronicleClient.getEventStore("Sales"); ComplianceServiceJavaBridge.deleteEncryptionKey(eventStore.unwrap().getCompliance(), "person-42"); }}defmodule MyApp.Compliance.Erasure.DeleteKey do def delete do Chronicle.Compliance.delete_encryption_key("person-42") endendimport { ChronicleClient } from '@cratis/chronicle';
async function deletePersonEncryptionKey(chronicleClient: ChronicleClient): Promise<void> { const eventStore = await chronicleClient.getEventStore('Sales'); await eventStore.pii.deleteEncryptionKey('person-42');}IEventStore.PII is an IPIIManager. DeleteEncryptionKeyFor removes every revision of the key, evicts it from every silo’s cache, and records the erasure so that nothing puts the key back afterwards.
Read one of that subject’s events afterwards and nothing breaks. The event is still there, its sequence number is still there, and every field that was not marked [PII] still holds its value — only the PII properties lose their values. What a caller gets for an erased property depends on its schema type:
| Property type | Released value after erasure |
|---|---|
| String | An empty string |
| Non-nullable number or boolean | 0 or false |
| Nullable number or boolean | null |
| Collection | An empty collection |
| Object or value object | An empty object |
That is the whole point of crypto-shredding: the log stays immutable and the personal data stops existing. The consequence is that an erased 0, false or empty string is indistinguishable from a value that was genuinely recorded as 0, false or empty. Whether Chronicle should let a caller tell the two apart is an open decision, tracked in issue 4620.
Causation property values are not encrypted under the subject’s key, so deleting the key does not erase them. Set events.causationPropertyRetention to Omit to prevent new causation property values from being retained, including on forwarded events, revisions, and redactions. Omit does not remove values already stored in historical causation chains; handle those separately when assessing an erasure.
What one call reaches
Section titled “What one call reaches”The erasure covers every event store in the namespace you erased in, not only the event store you resolved PII from.
That is not generosity, it is arithmetic. When an event store subscription forwards a subject’s events from one event store to another, Chronicle copies that subject’s encryption key into the target event store — and it never copies across namespaces. So the set of places the key can have reached is exactly every event store, in this namespace, and that is the set the erasure covers:
The erasure runs in two phases across that set: it records the erasure in every event store first, and only then destroys the key material. Fencing everything before destroying anything is what closes the window a per-store fan-out could not — between the first delete and the last, one event store still held the key and another did not, and an event forwarded in that interval copied the survivor into a store that had just been cleared.
Every phase attempts every event store even when one of them fails, and the failures are reported together as EncryptionKeyErasureIncomplete. A partial erasure is not an erasure — repeat the call once the failing store is reachable. A call that returns without throwing reached everything.
The subject is fenced, not banned
Section titled “The subject is fenced, not banned”Deleting the key is only half of an erasure; the other half is making sure nothing puts it back. Chronicle records the erasure beside the keys, and from then on that store refuses to provision a key for the subject, refuses to accept the destroyed key material back, and refuses to let a subscription copy a key in.
The practical consequence is worth knowing before you erase:
Appending a [PII] value for an erased subject fails. It does not quietly mint a new key, and it does not quietly blank the value — both of those would be a silent surprise, one restarting protection for a person who asked to be forgotten and the other losing data with no signal. It fails with EncryptionKeyErased instead.
If the same person later has a lawful basis to be protected again, say so:
public static class ComplianceErasureAllowNewKey{ public static async Task Allow(ChronicleClient chronicleClient) { var eventStore = await chronicleClient.GetEventStore("Sales"); await eventStore.PII.AllowNewEncryptionKeyFor("person-42"); }}Kotlin does not support this workflow yet.Java does not support this workflow yet.Elixir does not support this workflow yet.import { ChronicleClient } from '@cratis/chronicle';
async function allowNewEncryptionKeyForPerson(chronicleClient: ChronicleClient): Promise<void> { const eventStore = await chronicleClient.getEventStore('Sales'); await eventStore.pii.allowNewEncryptionKeyFor('person-42');}That creates no key. It authorizes the next [PII] value written for the subject to provision a fresh, independent one — which protects data written from then on and can decrypt nothing that came before. The erased key itself never comes back. Like the erasure, the authorization covers every event store in the namespace.
The mechanics of the fence, and what it cannot protect you against, are in The encryption key lifecycle.
What you still have to do
Section titled “What you still have to do”Chronicle erases keys. It does not know what else your system did with the data.
- Erase in every namespace the person appears in. One call per namespace; there is no cross-namespace erasure, by design.
- Deal with the failed partition, if there is one. An event carrying
[PII]for a subject you erased cannot be appended, so a forwarding subscription or a reactor that keeps producing them will report a failed partition for that event source. That is the signal that something is still writing the person’s data — either stop it, or authorize a new key. - Expect the same refusal when you replay. Rebuilding a stored read model re-writes the protected values it holds, so a replay that covers an erased subject is refused for that subject’s partition too. Authorize a new key for anyone you intend to keep protecting before a rebuild that has to cover them.
- Record the who. Chronicle logs that an erasure completed, which event stores it reached, and the subject as a one-way binding rather than by name — see what Chronicle records. What it cannot know, and therefore cannot record, is who asked for the erasure and under what legal basis. That part is yours.
- Chase the copies outside Chronicle. Read models exported to a warehouse, search indexes, and anything a reactor sent to a third party are outside the key store and outside the erasure.
- Keep erasures from coming back with a restore. A backup of the key store taken before the erasure still holds the key, and restoring it brings the key back without any error. Plan a restore-and-re-erase step, and restore the key store at the same point in time as the rest of the storage — see what the fence cannot protect against and backup and restore ordering.
See also
Section titled “See also”| Topic | Description |
|---|---|
| The encryption key lifecycle | How the fence works, and what it does not protect against |
| Compliance | How Chronicle protects personal data in an immutable log |
| Subject | The identity a PII encryption key is held under |
| Compliance and PII in subscriptions | What forwarding preserves, and what it no longer copies |
| Compliance Storage | Where encryption keys are stored |
| Event Redaction | Removing an event’s content rather than its key |