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 python

Re-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 untouched
dpdpstack 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.