Not legal advice
dpdpkit is software that helps you implement obligations under India's Digital Personal Data Protection Act, 2023 and the DPDP Rules, 2025. It does not provide legal advice and does not guarantee compliance.
Anchoring the ledger root hash¶
The hash chain proves that no row was edited or removed from the middle. On its own it cannot prove that the newest rows were not cut off the end, or that someone with database access did not rebuild the whole chain from scratch. A root hash closes that gap: it records the head of each chain at a point in time. Keep a copy somewhere the database operator cannot rewrite, and any later truncation or rebuild no longer matches it.
{
"tenant_id": "default",
"consent_head": "9f2c…", "consent_seq": 1841,
"audit_head": "41ab…", "audit_seq": 7302,
"root": "c07e…",
"exported_at": "2027-06-01T03:00:00Z"
}
A root hash contains only hashes and counters, never personal data, so it is safe to publish.
Exporting¶
# append today's root to a JSON-lines file
dpdpkit ledger root --kit myapp:dpdp --out /mnt/anchors/roots.jsonl
from dpdpkit import FileRootSink
dpdp.start_scheduler(
root_export_hour=3, # daily, 03:00 UTC (None disables)
root_sinks=[FileRootSink("/mnt/anchors/roots.jsonl")],
)
root = kit.ledger.export_root(FileRootSink("roots.jsonl"))
Every export also publishes a ledger.root_exported event. Subscribe a webhook to it to hand each root
to a third party as it is produced (see Where to keep it).
A sink is any object with a write(root) method, so you can write directly to your own store:
import datetime as dt
import boto3
s3 = boto3.client("s3")
class ObjectLockSink:
def write(self, root):
s3.put_object(
Bucket="acme-dpdp-anchors",
Key=f"{root.tenant_id}/{root.exported_at:%Y/%m/%d}.json",
Body=root.model_dump_json(),
ObjectLockMode="COMPLIANCE",
ObjectLockRetainUntilDate=root.exported_at + dt.timedelta(days=3 * 365),
)
If a sink raises, export_root() raises too, so a failed anchor is never skipped silently. The FastAPI
job logs the failure and tries again the next day.
Checking¶
dpdpkit ledger verify --kit myapp:dpdp # chain is internally consistent
dpdpkit ledger check-root --kit myapp:dpdp --anchors /mnt/anchors/roots.jsonl
check-root takes the newest root for the tenant and confirms the chains still contain a row at each
recorded sequence number with the recorded hash. Because each row includes the hash of the row before
it, matching the newest anchor also proves nothing before it changed. Exit codes: 0 ok, 1 no usable
anchor, 2 mismatch.
Run both checks on the same schedule as the export, for example right before it.
Where to keep it¶
Pick at least one place you cannot quietly edit. More than one is better.
| Option | What protects it | Effort |
|---|---|---|
| S3 (or compatible) bucket with Object Lock in compliance mode | Not even the account root user can delete or overwrite an object before its retention date | Low; use a separate AWS account if you can |
| Webhook to a partner: your auditor, legal counsel or parent company | The copy is held by someone else | Low; subscribe to ledger.root_exported |
RFC 3161 timestamp (openssl ts -query, then send it to a public timestamping authority) |
A signed token showing the root existed at that time | Medium |
OpenTimestamps (ots stamp roots.jsonl) |
Anchored in the Bitcoin blockchain, free | Medium; confirmation takes a few hours |
| Public Git repository | Forks and mirrors keep old history; a force-push shows up | Low, but weaker: the owner can still rewrite it |
| Local file on the app server | Protects against accidents only | None |
Same credentials, no protection
If the credentials that can write your database can also delete the anchor, the anchor proves nothing. Use a different account, a different owner, or write-once storage.
How often¶
Daily is a sensible default. Rows written since the last anchor are protected by the chain alone, so the export interval is the window in which a truncation could go unnoticed. High-volume or regulated deployments can export hourly. The job is cheap: it reads two rows.
In an incident or an audit¶
Give the auditor three things: the evidence pack (dpdpkit export --evidence), the anchor file, and
the output of dpdpkit ledger check-root. They can recompute every row hash independently. See
Consent ledger for the hashing rules.