GDPR & CCPA Privacy Engineering - Lawful Bases, DSARs, Erasure in Practice, Data Transfers, DPIAs & Consent
GDPR vs CCPA/CPRA in practice: controller vs processor, the six lawful bases, DSAR workflows and deadlines, erasure in backups, event logs, search indexes, warehouses and processors (with crypto-shredding), international transfers (EU-US DPF, SCCs, TIAs), DPIAs, consent and Global Privacy Control, and the 2026 CPPA regulations. Includes a runnable crypto-shredding demo and a DSAR orchestrator with legal holds.
- 1Gist
- 2Maps
- 3Q&A
- 4Sandbox
Voice readout needs Web Speech Synthesis in this browser.
Question ladder
L1
Is a B2B SaaS a controller or a processor?
Answer
Usually a processor for customers' end-user data and a controller for its own account, billing and marketing data.
L2
Which lawful basis fits core service processing?
Answer
Contract. Consent can be withdrawn, which would stop the service.
L3
How long do you have to answer a GDPR access request?
Answer
One month, extendable by two further months for complex or numerous requests. CCPA allows 45 days plus 45.
L4
How do you erase someone from immutable backups?
Answer
Keep backups beyond use until they expire on a short schedule and replay a suppression list after any restore, or crypto-shred.
L5
What does crypto-shredding leave behind?
Answer
Ciphertext that no one can read, but plaintext copies elsewhere and pseudonymous IDs with a live mapping still need their own handling.
L6
Does GDPR require EU-only storage?
Answer
No. It regulates transfers; residency usually comes from customer contracts or sector rules.
L7
When does a legal hold beat an erasure request?
Answer
When litigation or a legal obligation applies (Art. 17(3)): retain, restrict processing and tell the requester what was kept and why.
Failure modes
Erasure that misses derived copies
The primary row is deleted, but search indexes, caches, warehouses and ML features keep serving the person's data.
Restore resurrects erased users
A backup restore brings deleted subjects back because the suppression list was not replayed.
Personal data in immutable events
Names and emails inside append-only events force a choice between rewriting history and breaking replay.
Misconceptions
Pseudonymized data is anonymous.
Under GDPR it is still personal data while any mapping to the person exists.
Hosting in the EU removes transfer issues.
Remote access from another country, such as US support engineers viewing EU data, is itself a transfer.
Consent is the safest lawful basis.
It must be freely given and as easy to withdraw as to give. For core service processing, contract is the right basis.
Interviewer traps
Loading analytics tags before the consent banner.
That is unlawful device storage access in the EU and a likely CCPA sharing violation.
Sending a DSAR export to an unverified requester.
Verification comes first; otherwise attackers use access requests to steal someone else's data.
Design scenario
Same prompt for every reader.
Requirements
Meet GDPR and CCPA deadlines with a receipt that proves what happened without keeping the deleted data.
Failure assumptions
- One customer is under a litigation hold.
- The warehouse keeps time-travel snapshots for 90 days.
- The email vendor confirms deletions late.
Constraints
- Kafka topics are append-only and replayed to rebuild projections.
- Backups are immutable.
Prompt
Build the erasure and access request pipeline for a SaaS with EU and California users whose data lives in Postgres, Kafka, a search index, a warehouse, 35-day backups and an email vendor.
API
How do requests arrive, get verified and become one tracked ticket with a deadline?
Data
How do per-subject keys, tombstones and delete jobs reach every store, including backups and processors?
Architecture
How does the orchestrator handle legal holds, retries and escalation, and what does the receipt record?
Overview
Engineering guidance, not legal advice. Whether a law applies to your company, and what a contract obliges you to do, is a question for counsel and your auditors. Facts were checked against official sources on 2026-10-08.
GDPR and CCPA/CPRA are privacy laws, and they change how systems are designed, not only how data is secured. GDPR (EU, and UK GDPR in the UK) requires a lawful basis for every processing purpose, minimization and purpose limitation, enforceable data subject rights (access, erasure, portability, objection), contracts with processors, rules for international transfers, DPIAs for high-risk processing and a 72-hour breach notice to regulators. CCPA/CPRA (California) is an opt-out model built around notices, the rights to know, delete, correct and opt out of selling or sharing, limits on sensitive data, and, from 2026, risk assessments and cybersecurity audits for larger businesses. The hard engineering problems are erasure in systems that never forget (backups, event logs, search indexes, warehouses), data residency, and making consent real. This page compares the two laws and shows working patterns: crypto-shredding and a DSAR orchestrator.
Not re-taught here (see Elsewhere in the library): event sourcing and CDC tombstones, multi-region home-region routing, search index lifecycle, backups and PITR, envelope encryption, object storage lifecycle.
Roles: controller vs processor
| Role (GDPR Art. 4) | Who | Duties |
|---|---|---|
| Controller | Decides why and how personal data is processed | Lawful basis, transparency, rights handling, DPIAs, breach notice to the regulator, choosing processors |
| Processor | Processes on the controller's documented instructions | Art. 28 contract (DPA), security, help the controller with rights and breaches, use subprocessors only with authorization, delete or return data at the end |
| Joint controllers | Decide purposes together | An arrangement setting out who does what (Art. 26) |
A B2B SaaS is usually a processor for its customers' end-user data and a controller for its own account, billing and marketing data. That split decides who answers a data subject: as a processor you route requests to the customer and help them, as a controller you answer yourself. CCPA has parallel concepts: businesses, service providers and contractors (contract-bound, like processors) and third parties.
Crypto-shredding, or rewriting every copy?
Prefer
Encrypt per subject and destroy the key
Personal fields are encrypted with one data key per subject; erasure deletes that key.
- The event log and its backup were never modified.
- The erased user's records read as unreadable in both copies.
- Non-personal aggregates such as counts by country still worked.
Alternative
Delete or rewrite the data in every store
Find and remove the person's records from each log, replica and backup.
- Rewriting append-only events breaks replay and integrity.
- Restoring backups to delete one person is impractical.
- Any missed replica keeps readable personal data.
An erasure request, condensed
Diagram 1 condensed.
- 1
Receive and verify the request
One intake ticket starts the deadline, and identity checks stop impersonation. - 2
Check legal holds
Litigation holds and legal retention duties override erasure for the held systems. - 3
Delete primary rows and shred the subject key
Delete at the system of record first, then destroy the per-subject key so log and backup ciphertext is unreadable. - 4
Purge indexes, caches and warehouse copies
Derived copies keep serving data unless they are purged too. - 5
Notify processors and wait for confirmation
Vendors must delete too; claim the erasure only when every system has confirmed.
Lawful bases and principles
GDPR Art. 6 gives six lawful bases: consent, contract, legal obligation, vital interests, public task, legitimate interests. Pick one per purpose, before processing, and record it. Special categories (health, biometrics, religion and others, Art. 9) need an additional Art. 9 condition, usually explicit consent.
| Basis | Typical engineering use | Watch out |
|---|---|---|
| Contract | Processing needed to deliver the service the user signed up for | Not a blank cheque for analytics or ads |
| Legitimate interests | Fraud prevention, security logging, some product analytics | Needs a documented balancing test; users can object |
| Consent | Marketing cookies, optional tracking, newsletters | Must be freely given, specific, informed, unambiguous; as easy to withdraw as to give; pre-ticked boxes are invalid (CJEU Planet49, 2019) |
| Legal obligation | Tax records, KYC retention | Retention is set by that law |
The Art. 5 principles become design rules: purpose limitation (tag data with the purposes it was collected for and enforce them in access policy), data minimization (do not collect what no purpose needs), storage limitation (a retention schedule with deletion), accuracy, integrity and confidentiality, and accountability (be able to show all of it). Art. 25 makes data protection by design and by default a legal requirement.
Data subject rights, and erasure in practice
Rights include access (Art. 15), rectification (16), erasure (17), restriction (18), portability (20) and objection (21), plus limits on solely automated decisions (22). Respond within one month, extendable by two further months for complex or numerous requests (Art. 12(3)). CCPA: respond within 45 days, extendable once by 45, and confirm receipt within 10 business days.
Erasure is easy in a primary database and hard everywhere else:
| Where data hides | Why it is hard | Practical pattern |
|---|---|---|
| Backups | Immutable by design; restoring to delete one person is absurd | Put backups "beyond use" until they expire on a short schedule, and keep a suppression list that is replayed after any restore |
| Event-sourced logs, Kafka | Append-only; deleting events breaks replay and integrity | Keep personal fields out of events (reference IDs), or crypto-shred: encrypt per subject and destroy the key; compacted topics with tombstones for keyed state |
| Search indexes | Derived copies, often rebuilt from snapshots | Delete by subject ID, verify count is zero, and make sure rebuilds read post-deletion sources |
| Warehouses and lakes | Many copies, snapshots, time travel | Partition or index by subject, run delete jobs, expire time-travel or snapshot history within the erasure window |
| Caches, CDNs, ML features | Forgotten copies | Short TTLs, purge APIs, feature stores keyed by pseudonymous IDs |
| Processors | Outside your systems | Art. 28 contract obliges them to delete; forward requests and collect confirmations |
Crypto-shredding is the cleanest answer for append-only and replicated stores: one data key per subject (wrapped by a KMS key, see envelope-encryption-dek-kek-cmk), personal fields encrypted with it, and erasure becomes "delete one small key". Every copy of the ciphertext, in every replica and backup, becomes unreadable at once. Two caveats: plaintext copies elsewhere still need their own deletion, and a pseudonymous subject ID left behind remains personal data while any mapping to the person exists.
How an erasure request flows
Diagram 1: a GDPR erasure request across a typical stack, with the failure path.
Decisions
- 1
Step 1: receive request
- nextStep 2: verify identity
- 2
Step 2: verify identity
- nextStep 3: check legal holds
- 3
Step 3: check legal holds
- nextStep 4: delete primary rows
- 4
Step 4: delete primary rows
- nextStep 5: shred subject key
- 5
Step 5: shred subject key
- nextStep 6: purge index and caches
- 6
Step 6: purge index and caches
- nextStep 7: purge warehouse copies
- 7
Step 7: purge warehouse copies
- nextStep 8: notify processors
- 8
Step 8: notify processors
- nextStep 9: all systems confirmed?
- ?
Step 9: all systems confirmed?
- nextStep 10: reply and keep receipt
- nextFailure: copy found or deadline near
- 10
Step 10: reply and keep receipt
- 11
Failure: copy found or deadline near
- nextEscalate, extend with reason
- 12
Escalate, extend with reason
- nextStep 6: purge index and caches
Lesson map
GDPR & CCPA Privacy Engineering - Lawful Bases, DSARs, Erasure in Practice, Data Transfers, DPIAs & Consent
GDPR vs CCPA/CPRA in practice: controller vs processor, the six lawful bases, DSAR workflows and deadlines, erasure in backups, event logs, search indexes, warehouses and processors (with crypto-shredding), international transfers (EU-US DPF, SCCs, TIAs), DPIAs, consent and Global Privacy Control, and the 2026 CPPA regulations. Includes a runnable crypto-shredding demo and a DSAR orchestrator with legal holds.
Architecture. Architecture
Select a node to see why it exists, or an edge to see the protocol, direction, effect, and consequence.
Mermaid export
flowchart TB s1["Step 1: receive request"] s2["Step 2: verify identity"] s3["Step 3: check legal holds"] s4["Step 4: delete primary rows"] s5["Step 5: shred subject key"] s6["Step 6: purge index and caches"] s7["Step 7: purge warehouse copies"] s8["Step 8: notify processors"] s9["Step 9: all systems confirmed?"] s10["Step 10: reply and keep receipt"] f1["Failure: copy found or deadline near"] f2["Escalate, extend with reason"] s1 -->|continues| s2 s2 -->|continues| s3 s3 -->|continues| s4 s4 -->|continues| s5 s5 -->|continues| s6 s6 -->|continues| s7 s7 -->|continues| s8 s8 -->|continues| s9 s9 -->|continues| s10 s9 -->|continues| f1 f1 -->|continues| f2 f2 -->|continues| s6
Runnable: crypto-shredding an append-only event log
"""Right to erasure in an append-only world: crypto-shredding.
Concept first:
* GDPR Art. 17 says erase personal data "without undue delay" when a ground
applies. Mutable tables are easy (DELETE). Append-only event logs, Kafka
topics with long retention, immutable backups and warehouse snapshots are not.
* Crypto-shredding: encrypt each subject's personal fields with a per-subject
data key (DEK). Erasure = destroy the DEK. Every copy of the ciphertext, in
every log, replica and backup, becomes unreadable at once.
* It only works if plaintext never leaked elsewhere (search index, warehouse,
logs, caches). Those still need their own delete/rebuild path.
Cipher: dependency-free HMAC-SHA256 keystream + tag for the demo; production
uses AES-GCM with DEKs wrapped by a KMS key (see envelope encryption page).
"""
import hmac, hashlib, json, secrets
class KeyStore: # in production: KMS-wrapped DEKs in a small, backed-up table
def __init__(self): self.keys: dict[str, bytes] = {}
def dek(self, subject: str) -> bytes:
return self.keys.setdefault(subject, secrets.token_bytes(32))
def shred(self, subject: str) -> None:
self.keys.pop(subject, None)
def _ks(key: bytes, nonce: bytes, n: int) -> bytes:
out = b""; i = 0
while len(out) < n:
out += hmac.new(key, nonce + i.to_bytes(4, "big"), hashlib.sha256).digest(); i += 1
return out[:n]
def seal(key: bytes, pt: str) -> str:
nonce = secrets.token_bytes(12); b = pt.encode()
ct = bytes(x ^ y for x, y in zip(b, _ks(key, nonce, len(b))))
tag = hmac.new(key, nonce + ct, hashlib.sha256).digest()[:16]
return (nonce + tag + ct).hex()
def open_(key: bytes | None, blob: str) -> str:
if key is None:
return "<unreadable: key shredded>"
raw = bytes.fromhex(blob); nonce, tag, ct = raw[:12], raw[12:28], raw[28:]
assert hmac.compare_digest(tag, hmac.new(key, nonce + ct, hashlib.sha256).digest()[:16])
return bytes(x ^ y for x, y in zip(ct, _ks(key, nonce, len(ct)))).decode()
ks = KeyStore()
event_log: list[dict] = [] # append-only; also replicated + backed up
def emit(subject: str, kind: str, pii: dict, non_personal: dict) -> None:
event_log.append({"subject_ref": subject, "kind": kind,
"pii": seal(ks.dek(subject), json.dumps(pii)), **non_personal})
emit("u_101", "signup", {"email": "lena@example.eu", "name": "Lena K"}, {"country": "DE", "plan": "pro"})
emit("u_102", "signup", {"email": "omar@example.fr", "name": "Omar B"}, {"country": "FR", "plan": "free"})
emit("u_101", "address_changed", {"street": "Hauptstr 5", "city": "Berlin"}, {"country": "DE"})
backup = json.loads(json.dumps(event_log)) # nightly immutable backup copy
def project(log: list[dict]) -> list[str]:
return [f"{e['subject_ref']}:{e['kind']}:{open_(ks.keys.get(e['subject_ref']), e['pii'])}" for e in log]
print("before erasure:")
for row in project(event_log):
print(" ", row)
ks.shred("u_101") # erasure request for u_101
print("after shredding u_101's key (log untouched, backup untouched):")
for row in project(event_log):
print(" ", row)
print("backup copy, same result:", project(backup)[0])
# non-personal analytics survive erasure because they were never personal
print("aggregate still works:", {c: sum(1 for e in event_log if e.get("country") == c and e["kind"] == "signup") for c in ("DE", "FR")})
print("still to purge separately: search index docs, warehouse rows, caches, vendor copies -> see DSAR orchestrator")Output:
before erasure:
u_101:signup:{"email": "lena@example.eu", "name": "Lena K"}
u_102:signup:{"email": "omar@example.fr", "name": "Omar B"}
u_101:address_changed:{"street": "Hauptstr 5", "city": "Berlin"}
after shredding u_101's key (log untouched, backup untouched):
u_101:signup:<unreadable: key shredded>
u_102:signup:{"email": "omar@example.fr", "name": "Omar B"}
u_101:address_changed:<unreadable: key shredded>
backup copy, same result: u_101:signup:<unreadable: key shredded>
aggregate still works: {'DE': 1, 'FR': 1}
still to purge separately: search index docs, warehouse rows, caches, vendor copies -> see DSAR orchestratorThe log and its backup were never modified, yet the erased user's personal fields are unreadable everywhere, while non-personal aggregates still work.
Runnable: DSAR orchestrator with deadlines and legal holds
// DSAR / erasure orchestrator: one request, many systems, two legal clocks.
// Concept: a deletion or access request is a distributed workflow, not a SQL
// statement. Every system holding personal data registers a handler; the
// orchestrator fans out, tracks deadlines, respects legal holds, and records
// evidence. Deadlines: GDPR Art. 12(3) = one month (extendable by two more
// months for complex/numerous requests); CCPA = 45 days (one 45-day extension).
type Law = "GDPR" | "CCPA";
type Outcome = "deleted" | "shredded" | "suppressed-until-expiry" | "held" | "forwarded" | "pending";
interface Handler { system: string; run: (subject: string) => Outcome; note: string }
interface DsarRequest { id: string; subject: string; law: Law; received: Date; heldSystems: string[] } // legal hold scope, set by counsel
function addDays(d: Date, n: number): Date { const x = new Date(d); x.setUTCDate(x.getUTCDate() + n); return x; }
function addMonths(d: Date, n: number): Date { const x = new Date(d); x.setUTCMonth(x.getUTCMonth() + n); return x; }
const iso = (d: Date) => d.toISOString().slice(0, 10);
function deadline(r: DsarRequest): { due: Date; maxExtended: Date } {
return r.law === "GDPR"
? { due: addMonths(r.received, 1), maxExtended: addMonths(r.received, 3) }
: { due: addDays(r.received, 45), maxExtended: addDays(r.received, 90) };
}
const handlers: Handler[] = [
{ system: "primary Postgres", run: () => "deleted", note: "hard delete rows, cascade" },
{ system: "event log (Kafka)", run: () => "shredded", note: "destroy per-subject DEK" },
{ system: "search index", run: () => "deleted", note: "delete by subject_ref, then verify count = 0" },
{ system: "warehouse", run: () => "deleted", note: "delete + reprocess snapshots older than the delete" },
{ system: "backups (35-day)", run: () => "suppressed-until-expiry", note: "beyond use; on restore, replay suppression list" },
{ system: "email vendor (processor)", run: () => "forwarded", note: "Art. 28 / service-provider contract obliges them to delete" },
];
function handleRequest(r: DsarRequest) {
const { due, maxExtended } = deadline(r);
console.log(`== ${r.id} ${r.law} received ${iso(r.received)} due ${iso(due)} (max with extension ${iso(maxExtended)})`);
const evidence = handlers.map((h) => {
const outcome: Outcome = r.heldSystems.includes(h.system) ? "held" : h.run(r.subject);
return { system: h.system, outcome, note: outcome === "held" ? "legal hold: retain, restrict processing, tell requester why" : h.note };
});
for (const e of evidence) console.log(` ${e.system.padEnd(24)} ${e.outcome.padEnd(24)} ${e.note}`);
const held = evidence.filter((e) => e.outcome === "held").length;
const waiting = evidence.filter((e) => e.outcome === "forwarded").length;
console.log(` status: ${held ? "PARTIAL (" + held + " held)" : "DONE"}; ${waiting} processor confirmation(s) awaited; receipt stores subject_ref + outcomes, not the data`);
}
handleRequest({ id: "DSAR-81", subject: "u_101", law: "GDPR", received: new Date("2026-10-08T00:00:00Z"), heldSystems: [] });
handleRequest({ id: "DSAR-82", subject: "u_555", law: "CCPA", received: new Date("2026-10-08T00:00:00Z"), heldSystems: ["primary Postgres", "event log (Kafka)", "backups (35-day)"] });Output:
== DSAR-81 GDPR received 2026-10-08 due 2026-11-08 (max with extension 2027-01-08)
primary Postgres deleted hard delete rows, cascade
event log (Kafka) shredded destroy per-subject DEK
search index deleted delete by subject_ref, then verify count = 0
warehouse deleted delete + reprocess snapshots older than the delete
backups (35-day) suppressed-until-expiry beyond use; on restore, replay suppression list
email vendor (processor) forwarded Art. 28 / service-provider contract obliges them to delete
status: DONE; 1 processor confirmation(s) awaited; receipt stores subject_ref + outcomes, not the data
== DSAR-82 CCPA received 2026-10-08 due 2026-11-22 (max with extension 2027-01-06)
primary Postgres held legal hold: retain, restrict processing, tell requester why
event log (Kafka) held legal hold: retain, restrict processing, tell requester why
search index deleted delete by subject_ref, then verify count = 0
warehouse deleted delete + reprocess snapshots older than the delete
backups (35-day) held legal hold: retain, restrict processing, tell requester why
email vendor (processor) forwarded Art. 28 / service-provider contract obliges them to delete
status: PARTIAL (3 held); 1 processor confirmation(s) awaited; receipt stores subject_ref + outcomes, not the dataExpected== DSAR-81 GDPR received 2026-10-08 due 2026-11-08 (max with extension 2027-01-08) primary Postgres deleted hard delete rows, cascade event log (Kafka) shredded destroy per-subject DEK search index deleted delete by subject_ref, then verify count = 0 warehouse deleted delete + reprocess snapshots older than the delete backups (35-day) suppressed-until-expiry beyond use; on restore, replay suppression list email vendor (processor) forwarded Art. 28 / service-provider contract obliges them to delete status: DONE; 1 processor confirmation(s) awaited; receipt stores subject_ref + outcomes, not the data == DSAR-82 CCPA received 2026-10-08 due 2026-11-22 (max with extension 2027-01-06) primary Postgres held legal hold: retain, restrict processing, tell requester why event log (Kafka) held legal hold: retain, restrict processing, tell requester why search index deleted delete by subject_ref, then verify count = 0 warehouse deleted delete + reprocess snapshots older than the delete backups (35-day) held legal hold: retain, restrict processing, tell requester why email vendor (processor) forwarded Art. 28 / service-provider contract obliges them to delete status: PARTIAL (3 held); 1 processor confirmation(s) awaited; receipt stores subject_ref + outcomes, not the data
Press Run. Snippets must be self-contained — no network, files, or native modules.
The second request shows the important exception: a legal hold (litigation, regulatory investigation) overrides deletion for the held systems. You retain, restrict processing and tell the requester which data was kept and why. GDPR Art. 17(3) lists such exemptions, including legal claims and legal obligations.
International transfers and data residency
GDPR Chapter V restricts transfers of personal data outside the EEA. A transfer needs one of:
| Mechanism | Use | Notes |
|---|---|---|
| Adequacy decision (Art. 45) | Country or framework judged essentially equivalent: UK, Japan, Switzerland and others, plus the EU-US Data Privacy Framework for certified US companies | DPF adequacy adopted 2023-07-10; the EU General Court upheld it on 2025-09-03 (Latombe); an appeal (C-703/25 P) is pending at the Court of Justice as of 2026-10-08 |
| Standard Contractual Clauses (Art. 46) | The 2021 SCCs, modules for controller and processor combinations | Need a transfer impact assessment since Schrems II (2020) and supplementary measures where local law allows disproportionate access |
| Binding Corporate Rules | Intra-group transfers | Slow to approve |
| Derogations (Art. 49) | Occasional, specific situations | Not for systematic transfers |
History explains the caution: the CJEU invalidated Safe Harbor (Schrems I, 2015) and Privacy Shield (Schrems II, 2020), and the Irish DPC fined Meta EUR 1.2 billion in 2023 over SCC-based transfers. Many teams keep the DPF as primary mechanism with SCCs as fallback.
Residency vs transfer. GDPR does not require EU-only storage; it regulates transfers. Residency usually comes from customer contracts, sector rules or risk appetite. Engineering patterns: a home region per tenant with region-pinned storage and processing (see active-active-multi-region-data-home-region-routing), region-scoped KMS keys, telemetry and support tools that do not quietly copy data to another region, and policy-as-code gates that block deployments of tagged data outside allowed regions. Remote access from another country can itself be a transfer.
DPIAs, consent and cookies
A DPIA (Art. 35) is required before processing likely to result in high risk: large-scale special-category data, systematic monitoring, profiling with significant effects, new technologies such as many AI uses. It describes the processing, assesses necessity and risks, and records mitigations. Engineers supply the data flows, retention and security design.
Cookies and similar storage are governed by the ePrivacy Directive (Art. 5(3)), implemented in each member state: storing or reading non-essential cookies or device identifiers needs prior consent with GDPR-quality consent; strictly necessary cookies do not. Engineering consequences: no analytics or ad tags fire before consent, reject is as easy as accept, consent records are stored with version and timestamp, and withdrawal actually stops collection.
GDPR vs CCPA/CPRA
| Topic | GDPR | CCPA/CPRA |
|---|---|---|
| Model | Lawful basis required up front | Notice at collection plus opt-out rights |
| Scope | Any controller or processor targeting or monitoring people in the EU | For-profit businesses doing business in California above thresholds: revenue over $26,625,000 (2025 CPI-adjusted), or 100,000+ consumers or households, or 50%+ of revenue from selling or sharing |
| Sensitive data | Special categories need an Art. 9 condition | Right to limit use and disclosure of sensitive personal information |
| Sale and sharing | No separate concept; covered by lawful basis | Right to opt out of sale and of sharing for cross-context behavioral advertising; "Do Not Sell or Share" link; honor Global Privacy Control signals |
| Response time | One month (+2) | 45 days (+45) |
| Breach notice | Regulator within 72 hours where feasible; individuals without undue delay if high risk | California Civil Code 1798.82: notify residents in the most expedient time possible; private right of action for certain breaches caused by unreasonable security, statutory damages $107 to $799 per consumer per incident (2025 adjustment) |
| Enforcement | Supervisory authorities; fines up to EUR 20 million or 4% of worldwide annual turnover | California Privacy Protection Agency and the Attorney General; fines up to $2,663 per violation or $7,988 if intentional or involving minors (2025 adjustment) |
| 2026 additions | Regulations effective 2026-01-01: risk assessments for high-risk processing, ADMT rules, and annual cybersecurity audits for larger businesses, with first audit certifications due from 2028-04-01 depending on revenue |
The Sephora settlement (California AG, 2022, $1.2 million) is the canonical engineering lesson: the company was found to sell data through third-party tracking and to ignore Global Privacy Control signals.
72-hour breach notice under GDPR
Art. 33: the controller notifies the supervisory authority without undue delay and, where feasible, within 72 hours of becoming aware of a personal data breach, unless it is unlikely to result in a risk. Late notices need reasons. Information may be provided in phases. Processors notify the controller without undue delay. Art. 34: tell affected people without undue delay if the risk is high, unless the data was protected (for example encrypted with an uncompromised key). Every breach, notified or not, must be documented. The engineering requirement is the ability to answer within hours: which subjects, which data categories, which systems, which regions.
Decision chart: designing a new data flow
Decisions
- 1
Q1
- nextNo GDPR duties
- nextQ2: purpose and lawful basis recorded?
- 2
No GDPR duties
- ?
Q2: purpose and lawful basis recorded?
- nextStop: define purpose and basis
- nextQ3: high risk processing?
- 4
Stop: define purpose and basis
- ?
Q3: high risk processing?
- nextRun a DPIA before launch
- nextQ4: leaves the EEA?
- 6
Run a DPIA before launch
- nextQ4: leaves the EEA?
- ?
Q4: leaves the EEA?
- nextDPF, SCCs plus TIA, or keep in region
- nextQ5: erasable in every copy?
- 8
DPF, SCCs plus TIA, or keep in region
- nextQ5: erasable in every copy?
- ?
Q5: erasable in every copy?
- nextAdd crypto-shredding or delete jobs
- nextShip with retention and RoPA entry
- 10
Add crypto-shredding or delete jobs
- 11
Ship with retention and RoPA entry
What happens if you choose otherwise
- Put emails and names into immutable events: erasure means rewriting history or breaking replay; crypto-shredding or reference IDs avoid it.
- Rely on consent for core service processing: users can withdraw, and you cannot deliver the service; contract is the right basis there.
- Load analytics before the consent banner: unlawful storage access in the EU and a likely CCPA sharing violation.
- Assume EU hosting removes transfer issues: US support engineers viewing EU data from the US is still a transfer.
Pitfalls
- Treating pseudonymized data as anonymous; it is still personal data under GDPR.
- Deleting from the primary DB while the search index, warehouse and support tool keep copies.
- Restoring a backup and resurrecting erased users because the suppression list was not replayed.
- DSAR exports that include other people's data, or that are sent to an unverified requester.
How the code was checked
- The crypto-shredding demo ran under Python 3.13 and the DSAR orchestrator under
tsc --strictand Node 20. Their output blocks are the real captured output. - Subjects, systems and the legal hold are example data; deadlines are computed from the 2026-10-08 receipt date.
Interview Q&A
Controller vs processor, and why does it matter for a SaaS?
Answer
The controller decides why and how data is processed; the processor acts on its documented instructions under an Art. 28 contract. A B2B SaaS is usually a processor for customer end-user data and a controller for its own account and marketing data, which decides who answers data subject requests.
Name the six GDPR lawful bases and when you use legitimate interests.
Answer
Consent, contract, legal obligation, vital interests, public task and legitimate interests. Legitimate interests fits fraud prevention, security logging and some analytics, with a documented balancing test and a right to object.
How do you honor the right to erasure in an event-sourced system?
Answer
Keep personal data out of events where possible, and encrypt what remains with a per-subject key so erasure means destroying the key. Compacted topics with tombstones handle keyed state. Derived stores such as search indexes and warehouses need their own deletion.
What about backups?
Answer
Do not restore and edit backups. Keep them on a short retention, treat erased data in them as beyond use, and replay a suppression list after any restore so erased people are re-deleted.
What is crypto-shredding and what are its limits?
Answer
Encrypting each subject's data with its own key and deleting the key on erasure, which makes every replica and backup unreadable. It does not cover plaintext copies elsewhere, and leftover pseudonymous IDs are still personal data while a mapping exists.
How long do you have to answer a DSAR?
Answer
GDPR: one month, extendable by two months for complex or numerous requests. CCPA: 45 days, extendable once by 45, with receipt confirmed within 10 business days.
How can you transfer EU personal data to the US today?
Answer
Through the EU-US Data Privacy Framework if the recipient is certified (upheld by the General Court in 2025, appeal pending), or through Standard Contractual Clauses with a transfer impact assessment and supplementary measures, or BCRs within a group.
Does GDPR require data to stay in the EU?
Answer
No. It regulates transfers, not location. Residency requirements usually come from contracts or sector rules, and remote access from outside the EEA can itself be a transfer.
When is a DPIA required?
Answer
Before processing likely to result in a high risk, such as large-scale special-category data, systematic monitoring of public areas, or profiling with significant effects. It documents necessity, risks and mitigations.
How does CCPA differ from GDPR in model?
Answer
GDPR needs a lawful basis before processing. CCPA lets businesses process with notice at collection and gives consumers rights to know, delete, correct, opt out of sale and sharing, and limit sensitive data use, including honoring Global Privacy Control.
What is the GDPR breach notification deadline?
Answer
Notify the supervisory authority without undue delay and, where feasible, within 72 hours of becoming aware, unless the breach is unlikely to result in risk. Tell individuals if the risk is high, unless the data was protected, for example by encryption with an uncompromised key.
Check yourself
Pick one user ID in a system you know and list every store, cache, index, warehouse table, backup and vendor that would hold their data. Write the delete or shred step for each and mark which ones you could not prove today.
Elsewhere in the library
These pages stay as they are. This lesson only points at them: Event Types — Notification, Event-Carried State Transfer & Event Sourcing, CDC Failure Modes — Lag, Schema Breaks, Tombstones, Backfills & Replays, Active-Active vs Active-Passive Multi-Region - Data, Write Routing & Home Regions, Indexing Pipelines, Bulk, ILM & Snapshots, Backups That Actually Restore - Snapshots, PITR, Immutable Copies & Restore Drills, Envelope Encryption — DEK, KEK & CMK Hierarchy, Lifecycle, Storage Classes, CDN & Presigned URLs, Security — IAM, Encryption, Public Buckets & Threat Model.
Go Deeper
- GDPR Art. 5: principles
- GDPR Art. 6: lawful bases
- GDPR Art. 17: right to erasure
- GDPR Art. 28: processors
- GDPR Art. 33: breach notification
- GDPR Art. 35: DPIA
- GDPR Chapter V: international transfers
- EUR-Lex: Regulation (EU) 2016/679 (GDPR)
- EUR-Lex: Standard Contractual Clauses, Decision 2021/914
- EUR-Lex: EU-US Data Privacy Framework adequacy decision 2023/1795
- California Attorney General: CCPA
- California Privacy Protection Agency: 2026 regulations (risk assessments, audits, ADMT)
- California Privacy Protection Agency: CPI-adjusted thresholds