How it works

DPDPStack has four moving parts. Understanding them is enough to use the whole library.

The erasure engine

ErasureEngine.request_erasure(...) resolves what should happen to a subject's data for a purpose, runs your mutation, and records the decision.

res = engine.request_erasure(
    subject="user_42",         # opaque reference - never PII
    policy=rbi_kyc("kyc"),     # the retention rule for this purpose
    reason="consent_withdrawn",
    executor=lambda action: do_the_actual_mutation(action),  # your code
)

It returns an ErasureResult with status ("erased" or "deferred"), action, legal_basis, and erase_after. The key idea: the engine never touches your data itself - it calls your executor (zero-egress).

DPDP requires erasure on withdrawal, but other Indian laws require retention. DPDPStack reconciles this: when a policy carries a legal_hold_days + legal_basis, erasure is deferred until the hold lapses rather than refused.

res = engine.request_erasure(subject="user_42", policy=rbi_kyc("kyc"), reason="consent_withdrawn")
res.status        # "deferred"
res.erase_after   # the date it becomes erasable

This is the uncontested wedge - see Retention policies for the presets and how to build your own.

Anonymize vs delete

A policy's action is either Action.DELETE (hard-delete the row) or Action.ANONYMIZE (irreversibly null/hash the PII fields, keep the row). Anonymize is how teams satisfy "erase the person" while keeping regulated/relational records.

The anonymize strategies decide how each field is scrubbed:

StrategyEffect
nullset the field to null/empty
hashedreplace with a one-way hash
redact(keep_last=4)keep the last N chars, mask the rest
constant("[redacted]")replace with a fixed value
from dpdpstack import null, hashed, redact, constant

The hash-chained audit log

Every decision is appended to an AuditLog as a tamper-evident entry: each entry's hash includes the previous entry's hash, so any edit or deletion breaks the chain.

engine.audit.verify()   # True if the chain is intact

Stores are pluggable:

  • InMemoryAuditStore - default, ephemeral.
  • JsonlAuditStore - append to a JSONL file.
  • The Django adapter - a model-backed store (see Django integration).

The chain is also the source of truth for the Certificate of Erasure: a certificate is just a verifiable snapshot of the latest erasure event for a subject.