Metadata-Version: 2.4
Name: dpdpstack-python-sdk
Version: 0.1.1
Summary: DPDP-compliant data erasure for Indian apps: legal-hold-aware deletion, PII anonymization, and tamper-evident Certificates of Erasure. Zero-egress - runs inside your app.
Author: getdpdp.net
License: MIT
Project-URL: Homepage, https://getdpdp.net
Project-URL: Documentation, https://getdpdp.net/docs
Project-URL: Repository, https://github.com/getdpdp/dpdpstack-python-sdk
Project-URL: Issues, https://github.com/getdpdp/dpdpstack-python-sdk/issues
Keywords: dpdp,dpdpa,privacy,data-deletion,erasure,rbi,pmla,compliance,india
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: django
Requires-Dist: Django>=4.2; extra == "django"
Provides-Extra: crypto
Requires-Dist: pyjwt[crypto]>=2.8; extra == "crypto"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Dynamic: license-file

# dpdpstack

**DPDP-compliant data erasure for Indian apps - handled in your code.**

Indian developers keep hitting the same wall: DPDP says *erase the user's data on
withdrawal*, but RBI (KYC, 5 yrs), PMLA, CERT-In (logs, 180 days) and the Companies
Act say *keep it*. So teams hand-delete data across tables, can't prove it, and
enterprise tools "cost more than a month's revenue."

`dpdpstack` is a small, **zero-egress** library that handles the hard part:

- **Legal-hold-aware erasure** - delete now, or *defer* under RBI/PMLA/CERT-In holds (with the basis recorded), then erase when the hold lapses.
- **PII anonymization** - irreversibly null/hash PII while keeping the ledger row (referential integrity), the way teams actually solve this.
- **Certificate of Erasure** - a verifiable, tamper-evident proof you erased (or are lawfully holding) a user's data.
- **Zero-egress** - *you* perform the mutation in your own DB; the library only decides and records. Personal data never leaves your systems.

> Not a cookie banner. Not a consultant. A deletion/retention engine for developers.

**[Documentation](https://getdpdp.net/docs)** · **[Source](https://github.com/getdpdp/dpdpstack-python-sdk)** · **[Hosted platform](https://getdpdp.net)**

## Install
```bash
pip install dpdpstack-python-sdk            # core, no dependencies
pip install "dpdpstack-python-sdk[django]"  # + Django adapter
pip install "dpdpstack-python-sdk[crypto]"  # + RS256-signed certificates (PyJWT + cryptography)
```

## Quickstart (framework-agnostic)
```python
from dpdpstack import ErasureEngine, AuditLog, RetentionPolicy, Action, rbi_kyc, issue_certificate

engine = ErasureEngine(AuditLog())

# Normal purpose: hard-delete on withdrawal. Your delete runs in `executor`.
engine.request_erasure(
    subject="user_42",
    policy=RetentionPolicy(purpose="marketing", action=Action.DELETE),
    reason="consent_withdrawn",
    executor=lambda action: my_delete_user(42),
)

# KYC: RBI mandates 5y retention -> erasure is DEFERRED, not refused.
res = engine.request_erasure(subject="user_42", policy=rbi_kyc("kyc"), reason="consent_withdrawn")
print(res.status, res.legal_basis, res.erase_after)   # deferred  RBI KYC...  2031-...

cert = issue_certificate(engine.audit, "user_42", "marketing")  # verifiable proof
```

## Django (zero-egress, runs against your models)
```python
# settings.py
INSTALLED_APPS += ["dpdpstack.contrib.django"]
# python manage.py migrate

from dpdpstack import RetentionPolicy, Action, null, redact, rbi_kyc
from dpdpstack.contrib.django.service import erase_instance, pii

# Declare a model's PII fields once with @pii - no pii_fields= on every call.
@pii(name=null, email=null, phone=redact(keep_last=4))
class User(models.Model):
    ...

# Hard delete + audit
erase_instance(user, policy=RetentionPolicy(purpose="marketing", action=Action.DELETE),
               subject=user.external_ref)

# Anonymize PII, keep the (regulated) row - uses the @pii declaration above
erase_instance(user, policy=RetentionPolicy(purpose="profile", action=Action.ANONYMIZE),
               subject=user.external_ref)

# KYC withdrawal -> deferred under RBI hold, nothing deleted, basis recorded
erase_instance(user, policy=rbi_kyc("kyc"), subject=user.external_ref)
```

## Signed certificates (optional, `[crypto]`)
The hash-chained Certificate of Erasure is tamper-evident on its own; add an RS256
signature so anyone can verify it with your public key (and you can't forge it):
```python
from dpdpstack import issue_certificate
from dpdpstack.signing import generate_keypair, issue_signed_certificate, verify_certificate

private_pem, public_pem = generate_keypair()      # keep private secret; publish public
cert = issue_certificate(engine.audit, "user_42", "marketing")
token = issue_signed_certificate(cert, private_pem)   # compact JWT
verify_certificate(token, public_pem)                 # -> {"valid": True, ...}
```
This is the basis for the **hosted, counter-signed** certificate at getdpdp.net - a
regulator/auditor verifies it independently, and the issuer cannot fake it.

### CLI (verify a certificate offline)
With the `[crypto]` extra installed, an auditor can verify a Certificate of Erasure
from the shell - no code, just the cert and your public key:
```bash
dpdpstack keygen --out-dir ./keys                       # one-time: make a signing keypair
dpdpstack verify cert.jwt --public-key ./keys/cert_public.pem
# VALID - signature verified.
#   subject: user_42 · status: erased (delete) · chain ok: True
```
(`python -m dpdpstack verify ...` works too.)

## Presets for the common conflicts
`rbi_kyc()` (5-yr hold, anonymize) · `pmla()` · `cert_in_logs()` (180-day log hold). Or build your own `RetentionPolicy(retention_days=…, legal_hold_days=…, legal_basis="…", action=…)`.

## What's in the box
| Module | What |
|---|---|
| `policies` | `RetentionPolicy` + RBI/PMLA/CERT-In presets |
| `anonymize` | `null` / `hashed` / `redact` / `constant` field strategies |
| `audit` | hash-chained, tamper-evident log; pluggable store (in-memory, JSONL, Django) |
| `erasure` | `ErasureEngine` - legal-hold-aware resolve + your `executor` |
| `certificate` | `issue_certificate()` → verifiable Certificate of Erasure |
| `signing` *(extra)* | RS256-sign/verify a certificate - `pip install dpdpstack-python-sdk[crypto]` |
| `contrib.django` | model-backed audit store + `erase_instance()` + `@pii(...)` |

## Status & scope
Alpha (0.1). The core is dependency-free and framework-agnostic; the Django adapter is the first integration (FastAPI/Flask next, on demand). Hosted/managed version (dashboard, cross-system fan-out, certificate vault): [**getdpdp.net**](https://getdpdp.net/).

dpdpstack is tooling, not legal advice; you remain the Data Fiduciary. MIT licensed.
