For legal & compliance teams

In most systems, "audit" means a created_at column you hope people fill in, and "e-discovery" means restoring a backup and guessing. RelataDB treats provenance, history, and tamper-evidence as first-class query-time properties of the data — every fact is traceable, every past state is reconstructable, and every deletion is governed. This is the platform for regulated, audited, defensible workloads.

What you replace

TodayWith Relata
Audit log (append-only, unverifiable)Tamper-evident hash chain (/audit/proof) on every fact
Records management + legal-hold toolingNative legal holds + WORM retention policies per type
e-discovery + snapshot restorationBi-temporal AS OF — any past state is a live query
"Chain of custody" spreadsheetsVERIFY_CUSTODY + EXPLAIN_REPLAY — byte-identical exhibit replay
DLP / cell-level access toolingCedar ABAC in the query path — cell masking, by purpose / team / country
GDPR/DSAR request toolingNative DSAR + Art. 17 erasure with retention windows

Court-grade query replay — EXPLAIN_REPLAY

Every executed query persists an immutable replay record (plan SHA, params, principal, ACL bitmap digest, branch HEAD, materialized-view-set digest, detector versions, snapshot pointer). EXPLAIN_REPLAY re-executes that plan against the recorded snapshot and asserts byte-identical Arrow IPC output.

This is the "court-grade" answer to the defence's question: "what exactly did the system tell the analyst on the date of the decision?" It's stronger than AS OF — it captures plan + ACL state + MV freshness at the decision moment, which row-level time-travel alone doesn't.

PURPOSE 'legal'
EXPLAIN_REPLAY('exhibit-7', SEQ => 5);
-- → reconstructed exhibit seal + chain_valid: true confirmation
 
VERIFY_CUSTODY('exhibit-001');   -- chain-of-custody assertion

Signed, timestamped PDF case reports: POST /report/pdf. Per-row receipts via AuditClient.sign_receipt(exhibit_id=...) in every SDK.

Bi-temporal history — every past state is a query

Every row carries valid_from/to (when the fact was true) and system_from/to (when Relata learned it). No restore, no snapshot digging — the state of the world at any past moment is one query:

PURPOSE 'regulator-response'
-- What was the sanctions status of this entity on the filing date?
SELECT * FROM SanctionsHit AS OF '2026-03-15T00:00:00'
WHERE entity_id = '0xabc...';
 
-- Reconstruct the entire case file as the analyst saw it
SELECT * FROM Person AS OF '2026-03-15T09:30:00' WHERE case_id = 'case-7';

See Bi-temporal queries.

Provenance on every fact (PROV-O)

Every row carries a provenance chain: source connector, batch id, record offset, observed_at, recorded_at — stamped into the tamper-evident audit hash chain. Any mutation leaves a forensic trail; any deletion is detectable.

# Is the audit chain intact?
curl 'http://127.0.0.1:9090/audit/proof' -H 'Authorization: Bearer <token>'
 
# Where did this fact come from?
curl 'http://127.0.0.1:9090/memory/justify/<fact-id>' \
  -H 'Authorization: Bearer <token>'
-- Every row that touched a case, with provenance
SELECT _pk, _provenance_source, _provenance_observed_at, _audit_seq
FROM Person WHERE case_id = 'case-7'
ORDER BY _audit_seq;

Prevent destruction of records under legal hold or WORM (write-once-read-many) policy — enforced in the write path, not by application discipline:

# Place a legal hold on a case (across all object types)
curl -X POST http://127.0.0.1:9090/retention/holds \
  -H 'Authorization: Bearer <token>' -H 'Content-Type: application/json' \
  -d '{"case_id":"case-7","object_type":"Person"}'
 
# Set WORM retention on a records type
curl -X POST 'http://127.0.0.1:9090/retention/worm/TradeRecord' \
  -H 'Authorization: Bearer <token>' \
  -H 'Content-Type: application/json' \
  -d '{"retention_secs":2592000}'   # 30 days immutable
client.governance_client.place_legal_hold("case-7", "Person")
client.governance_client.set_worm_policy("TradeRecord", retention_secs=2_592_000)

Cell-level governance + PURPOSE

Cedar-inspired ABAC fires in the query path, not as a view layer. Who-can-see-what differs by purpose, team, country, and clearance — enforced on every read, on every door. PURPOSE is recorded in the audit log on every request.

-- A query with a declared purpose — recorded for audit
PURPOSE 'cross-border-finance'
SELECT name, country FROM Person WHERE risk_score > 0.7;
 
-- Cell-level mask: redact SSN unless the caller's purpose is 'compliance'
-- (configured via Cedar policy / forbid clause — see Per-Door ACL)
// Compliance team may read ssn; nobody else — deny-wins
permit(
  principal in Role::"compliance",
  action == Action::"read",
  resource == Resource::"Person.ssn"
);

See Governance and Per-Door ACL.

GDPR / DSAR / Art. 17 erasure

Subject-access requests and right-to-erasure are native operations, not bespoke scripts:

# Generate a DSAR export for a subject
curl -X POST http://127.0.0.1:9090/gdpr/dsar \
  -H 'Authorization: Bearer <token>' \
  -d '{"subject_identity":"Person:alice-001"}'
 
# Erase a subject (governed — audit-logged, retention-aware)
curl -X POST http://127.0.0.1:9090/gdpr/erase \
  -H 'Authorization: Bearer <token>' \
  -d '{"entity_id":"Person:alice-001","purpose":"gdpr-art17"}'
client.governance_client.submit_dsar("Person:alice-001", reason="DSAR-2026-042",
                                     scope="all")
client.erase_subject("Person:alice-001", purpose="gdpr-art17-request")

forget (on memories) is retention-policy governed, not a hard delete — the item stays queryable for the window, then retracts. See Privacy & GDPR.

How to start

  1. Ingest records through any door (pg/Mongo/S3/HTTP) — provenance attaches automatically.
  2. Set WORM / retention policies on regulated types.
  3. Write Cedar policies for cell-level access (or use RELATA_ACL_GRANT shorthand).
  4. Place legal holds when a matter opens.
  5. Respond to requests with AS OF + EXPLAIN_REPLAY + signed PDF reports.

See also