Find & classify PII
You declare PII once with @pii(...), but which fields are PII? And when a breach
happens, which categories of data were involved? The dpdpstack.detect tools answer
both — locally, reading names and values you pass in, never a row from your DB.
Zero-egress and zero-dependency.
Discover the fields to declare (scan)
scan reads field names and types only and matches them against an India-first
catalog (Aadhaar, PAN, GST, UPI, phone, email, special-category…), suggesting an
anonymize strategy for each. Output is advisory — review, then paste.
Django — scan your models and get pasteable @pii(...) blocks:
python manage.py dpdp_scan --format python # or: text (default) | json
# or, without a manage.py:
dpdpstack scan --django --settings myproject.settings --app accounts --format pythonRe-running tags each field new (PII, not declared), covered (already declared), or
drift (declared, but no longer looks like PII) — so it doubles as an ongoing audit.
Anything else — a sample dict, an API payload, a column list:
from dpdpstack import anonymize_fields
from dpdpstack.detect import suggest_strategies
record = {"email": "[email protected]", "phone": "9876543210", "ledger_balance": 500}
clean = anonymize_fields(record, suggest_strategies(record.keys()))
# {'email': None, 'phone': '••••••3210', 'ledger_balance': 500} — non-PII untoucheddpdpstack scan --keys email,phone,pan --format python # comma-separated names
dpdpstack scan --dict sample.json --format python # keys of a JSON object ('-' = stdin)Bring your own catalog by passing a JSON file of the same shape to load_catalog(path=...).
Detect PII in values
Where scan reads field names, detect_values reads values and free text. Aadhaar
is validated with the Verhoeff checksum and cards with Luhn, so random 12-/16-digit
numbers don't false-positive.
from dpdpstack import detect_values
detect_values("PAN ABCDE1234F, card 4111 1111 1111 1111")
# [ValueMatch(type='PAN', …), ValueMatch(type='Payment Card', …)]Classify a breach
For a Rule 7 breach report's nature field, turn a free-text description into the DPDP
data categories involved (validated value matches + keyword cues for format-less
special-category data):
from dpdpstack import classify_breach_nature
classify_breach_nature("leaked rows: [email protected], Aadhaar 2341 2341 2346, plus medical records")
# ['Email Address', 'Aadhaar Number', 'Health Data']Everything here runs in-process — the catalog and checksums are local, so no data leaves your systems.