GDPR Article 17 — Right to Erasure: Integration Pattern¶
Status: Normative
Applies to: All Aevum deployments that process personal data of EU/EEA data subjects
The Tension: Append-Only Sigchains vs the Right to Erasure¶
Aevum's episodic ledger is append-only by design. Every commit operation
appends a signed, chained AuditEvent to the sigchain. Barrier 4 (Audit Seal)
makes deletion or mutation of ledger entries a hardcoded unconditional barrier —
no policy override is possible.
GDPR Article 17 grants data subjects the right to erasure ("right to be forgotten") of their personal data. A naïve implementation that stores raw personal data inside ledger payloads creates an irresolvable conflict: the data cannot be deleted without breaking the cryptographic chain.
Aevum resolves this tension at the architectural level. The episodic ledger
is not permitted to store raw personal data. The Cedar policy gdpr_pii.cedar
enforces this unconditionally.
The Solution: Off-Chain Data + On-Chain Hash Pointer + Crypto-Shredding¶
The pattern has three components:
1. Off-Chain Personal Data Store¶
All personal data (PII, special-category data, biometrics, etc.) is stored in a separate, erasable datastore (database, object store, encrypted vault) — never in the sigchain payload.
[Personal Data Store] [Aevum Episodic Ledger]
subject_id: "s-123" AuditEvent {
name: "Alice" → payload: {
email: "alice@..." subject_id: "s-123",
... data_hash: "sha256:abc123...",
key_id: "kek-s-123-2026-01",
}
}
The sigchain stores only:
- A subject identifier (pseudonymous, not directly identifying)
- The SHA-256 hash of the personal data at ingestion time
- The key ID used to encrypt the personal data
2. Per-Subject Encryption Key Derivation¶
Personal data is encrypted with a per-subject, per-period key derived from a Key Encryption Key (KEK). The KEK is stored in a secrets manager (e.g., HashiCorp Vault, AWS KMS).
import os, hashlib, secrets
def derive_subject_key(kek: bytes, subject_id: str, period: str) -> bytes:
"""Derive a deterministic 256-bit encryption key for a subject+period."""
material = f"aevum:subject:{subject_id}:period:{period}".encode()
return hashlib.blake2b(material, key=kek, digest_size=32).digest()
def generate_subject_kek(subject_id: str) -> bytes:
"""Generate a fresh random KEK for a new subject. Store in secrets manager."""
return secrets.token_bytes(32)
The key ID stored in the sigchain payload (key_id) encodes the subject and
period (e.g., kek-s-123-2026-01) but does not contain the key material.
3. Erasure via Crypto-Shredding¶
When a subject invokes Article 17, the erasure procedure is:
- Delete the subject's personal data from the off-chain datastore.
- Delete the subject's KEK from the secrets manager (crypto-shredding).
- The sigchain entries remain intact — they now contain an opaque hash pointer to data that no longer exists and cannot be re-derived (the KEK is gone).
The on-chain hash (data_hash) cannot be reversed to recover personal data
without both the original data AND the encryption key. With the KEK deleted,
the hash is cryptographically inert.
def erase_subject(subject_id: str, secrets_manager: SecretsManager,
personal_data_store: DataStore) -> None:
"""
Execute GDPR Article 17 erasure for subject_id.
Crypto-shredding: delete KEK, then delete raw data.
"""
# 1. Delete KEK — makes all derived keys irrecoverable
secrets_manager.delete(f"kek/{subject_id}")
# 2. Delete raw personal data
personal_data_store.delete_subject(subject_id)
# Sigchain entries are NOT touched — they remain valid chain links
# but reference data that is now cryptographically inaccessible.
Aevum's Supported Integration Pattern¶
Only the pattern described above is supported. Specifically:
| Requirement | Status |
|---|---|
| Raw PII in sigchain payloads | Forbidden (enforced by gdpr_pii.cedar) |
| Subject identifier in payload | Permitted (pseudonymous, non-identifying alone) |
| Hash pointer in payload | Permitted |
| Key ID reference in payload | Permitted |
| Off-chain personal data store | Required (operator responsibility) |
| Per-subject KEK in secrets manager | Required (operator responsibility) |
| Crypto-shredding on Article 17 request | Required (operator responsibility) |
Aevum enforces the first row via Cedar policy. Rows 2–7 are architectural requirements that operators must implement in their integration layer.
Cedar Policy Enforcement¶
The Cedar policy packages/aevum-core/src/aevum/core/policies/gdpr_pii.cedar
unconditionally blocks any relate_graph_write action where
context.contains_raw_pii == true:
forbid (
principal,
action == Action::"relate_graph_write",
resource
) when {
context.contains_raw_pii == true
};
Callers of ingest() must set context.contains_raw_pii in their Cedar
context. If the flag is absent (defaults to false), the policy does not fire.
Operators must set this flag correctly at their integration boundary.
Verifying the Pattern¶
# Validate the Cedar policy loads correctly
uv run python -c "
from aevum.core.policy.cedar_engine import CedarPolicyEngine
e = CedarPolicyEngine.default()
print('OK')
"
Formal Tombstoning Procedure¶
When a data subject exercises their Article 17 right, Aevum's erasure pattern produces a tombstone: a sigchain entry whose on-chain hash pointer is permanently rendered cryptographically inert.
What a tombstone is¶
A tombstone is an existing sigchain AuditEvent in urn:aevum:provenance
that:
-
Retains its chain position. The event's
prior_hash,signature, andsequenceremain intact. Removing or modifying it would break the hash chain — Barrier 4 (Audit Seal) prevents this unconditionally. -
Contains only non-identifying fields. The
payloadstores a pseudonymoussubject_id, adata_hash(SHA-256 of the now-deleted personal data), and akey_id(identifier of the now-deleted KEK). No personal data is stored in the sigchain payload —gdpr_pii.cedarenforces this unconditionally at ingest time. -
References data that no longer exists. After erasure, the off-chain personal data store is empty for this subject and the KEK is deleted (crypto-shredded). The
data_hashcannot be reversed — the pre-image data is gone. Thekey_idreferences a key that no longer exists.
The sigchain entry is preserved to maintain chain integrity. It is opaque: it proves that a governed operation occurred at a specific time with a specific actor, but it reveals no personal data.
Tombstoning procedure (normative)¶
The following steps constitute the complete Article 17 erasure procedure for an Aevum deployment:
1. Identify all sigchain entries referencing subject_id.
→ These are tombstones-in-waiting; they stay.
2. Delete the subject's personal data from the off-chain datastore.
→ The data_hash in chain entries now references a deleted object.
3. Crypto-shred the subject's KEK:
a. Derive all per-period keys from the KEK (they are now unrecoverable).
b. Delete the KEK from the secrets manager (Vault, AWS KMS, etc.).
→ Any ciphertext encrypted under derived keys is permanently inaccessible.
4. Optionally: append a GDPR.erasure.complete AuditEvent to the sigchain
recording the erasure timestamp and actor.
→ This creates an auditable record that the erasure was performed.
What operators must NOT do
Do not delete or mutate sigchain entries. Barrier 4 (Audit Seal) prevents
this at the kernel level — any attempt raises ImmutableLedgerError.
An auditor examining the sigchain must be able to confirm the chain was
intact at every point in time. The tombstone's presence is itself evidence
of compliance: it shows the operation was governed when it occurred.
Crypto-shredding path (technical)¶
def execute_article17_erasure(
subject_id: str,
secrets_manager: SecretsManager,
personal_data_store: DataStore,
engine: Engine,
actor: str,
) -> None:
"""
Execute GDPR Article 17 erasure. Sigchain entries are NOT touched.
"""
# Step 2: Delete personal data from off-chain store
personal_data_store.delete_subject(subject_id)
# Step 3: Crypto-shred KEK — all derived per-period keys become irrecoverable
secrets_manager.delete(f"kek/{subject_id}")
# Step 4 (optional): Record erasure in the sigchain
engine.commit(
event_type="gdpr.erasure.complete",
payload={"subject_id": subject_id, "basis": "article_17"},
actor=actor,
)
# The commit appends a signed AuditEvent to the sigchain.
# It does NOT contain personal data — it records that erasure occurred.
After this procedure, any attempt to decrypt ciphertext that was encrypted under a derived key from the now-deleted KEK will fail with a key-not-found error. The personal data is permanently inaccessible even to an operator with direct database access.
ConsentLedger integration¶
ConsentLedger.shred(subject_id) performs the in-memory crypto-shredding
path for Aevum's built-in DEK store. After shred(), any call to
ConsentLedger.decrypt_for_subject(subject_id, ...) raises ConsentRequired.
This is verified by conformance invariant 9
(consent_revoke_destroys_dek) on every installation.
References¶
- GDPR Article 17: Right to erasure ('right to be forgotten')
- Aevum Frozen Invariant 5: Append-only property of the episodic ledger
- Aevum Frozen Invariant 7: Provenance as precondition
- Aevum Barrier 4 (
barriers.cedar): Audit Seal — no deletions or mutations packages/aevum-core/src/aevum/core/policies/gdpr_pii.cedar