| name | map-privacy-data-gdpr |
| description | Implements privacy/data-protection engineering — personal-data inventory/mapping (RoPA), lawful-basis and versioned consent capture, DSAR machine-readable export and right-to-erasure cascades across derived data/logs/backups, TTL/scheduled retention purge, and PII minimization/pseudonymization — for GDPR/CCPA-style compliance. |
| when_to_use | A product stores personal data and needs consent capture, data export, deletion/erasure, or retention controls. Distinct from auth-jwt-session and design-authorization-model (who may access), build-audit-logging (tamper-evident action trail), and security-review (vulnerability audit). |
When to Use
Reach for this skill when the requirement is what happens to a person's data, not who may touch it:
- "A user requested their data / file a DSAR export endpoint"
- "Implement account deletion / right to be forgotten / erasure that actually removes them everywhere"
- "Record consent for marketing/analytics — granular, withdrawable, with proof"
- "We keep PII forever — add retention limits / a purge job"
- "Map where personal data lives for our DPIA / Article 30 record of processing"
- "Stop collecting/storing PII we don't need; pseudonymize the rest"
NOT this skill:
- Logging in users, sessions, OAuth, refresh rotation → auth-jwt-session
- Deciding which role/tenant may read a record (RBAC/ABAC/row scoping) → design-authorization-model
- The tamper-evident trail of who did what (including who ran an erasure) → build-audit-logging
- Hunting injection/SSRF/access-control bugs in changed code → security-review
- Design-level threat enumeration over a system handling PII → threat-model-stride
- Backup/PITR/retention of the datastore itself (RPO/RTO, WAL archiving) → design-backup-dr-recovery
Steps
-
Build the data inventory first — you cannot delete or export what you haven't mapped. Produce a machine-readable record of processing (RoPA, GDPR Art. 30). One row per (data element × store). Drive everything downstream — export, erasure, retention — off this file, not off tribal knowledge.
- element: email
category: contact
store: postgres.users.email
purpose: account login, transactional mail
lawful_basis: contract
retention: account_lifetime_plus_30d
subject_key: users.id
export: true
erase: anonymize
- element: ip_address
category: identifier
store: postgres.request_logs.ip
purpose: fraud/abuse detection
lawful_basis: legitimate_interest
retention: 90d
subject_key: request_logs.user_id
export: true
erase: delete
Common Errors
- Erasure that hits the primary DB only. The subject survives in the search index, cache, warehouse, logs, and backups. Drive deletion from the inventory across every store; assert absence afterward.
- Consent as one boolean column. Can't prove which policy version, when, or how; an
UPDATE erases the prior state. Use an append-only versioned ledger; withdrawal is a new row.
- No suppression list. A backup restore or a delayed event re-creates an erased subject ("data resurrection"). Keep a tombstone list and re-apply erasure on restore/ingest.
- Reversible "anonymization". A plain SHA-256 of an email is re-identifiable by dictionary attack — still personal data, still in scope. Keyed-hash/tokenize and delete the mapping, or aggregate so individuals can't be singled out.
- Treating backups as out of scope entirely. Ignoring them fails erasure; trying to surgically edit them corrupts them. Use bounded retention + restore-time re-erasure, and document it.
- Weak DSAR identity check. Emailing an export to whoever asks lets an attacker harvest a victim's data. Re-authenticate and verify before export or erasure.
- Retention policy with no enforcement job. "We keep logs 90 days" while the table grows unbounded. Wire a TTL or a scheduled purge and verify it actually runs.
- Logging full PII. Request/error logs capturing emails, tokens, full bodies become an uncontrolled PII store with infinite retention. Redact at the logger; set log retention.
- Forgetting processors. Deleting locally but leaving the subject in Stripe/Segment/Intercom/Sentry. Call each processor's deletion API as part of the cascade.
- Hardcoding the store list in deletion code instead of the inventory. A new table added without touching the deletion code is silently skipped forever. Single source of truth, generated cascade.
Verify
- Erasure completeness: run
erase_subject(id), then query every store in the inventory for that subject's subject_key → zero rows (or only anonymized/legal-hold-retained rows with a recorded reason). This is the test that catches the forgotten store; automate it per store.
- Resurrection resistance: restore a backup taken before an erasure (or replay a late event) → the subject is re-suppressed, not present. Suppression list is consulted on restore/ingest.
- Export completeness: for a seeded subject with data in N stores, the DSAR package contains all N, is valid JSON/machine-readable, and contains no other subject's PII.
- Consent gate: withdrawing consent (new
granted=false row) stops the gated processing on the next check; granted/withdrawn history is fully reconstructable; no non-essential tag fires before consent.
- Retention enforcement: advance a record past its retention window (or wait/seed) → the TTL/purge job removes it on the next scheduled run; purge job logs counts and alerts on anomalies.
- Lawful basis coverage: every element in the inventory has a basis; legal-obligation elements correctly survive an erasure request with a recorded reason.
- Minimization: no element is collected/stored without a row in the inventory; derived/analytics stores key on a pseudonym, not the raw identifier.
- Transfers: every cross-border element maps to a transfer mechanism + DPA; processor list matches actual integrations.
Done = an erasure request provably removes or anonymizes the subject across every inventoried store (with backups handled by bounded retention + restore-time re-erasure and a suppression list), the DSAR export is complete and machine-readable with no third-party PII leakage, consent is versioned/withdrawable with reconstructable history, and every retention window is enforced by a TTL or scheduled purge that demonstrably runs.