Provenance
Chain of custody is not something you add to a database after a finding. By the time a compliance team asks "who wrote this row, from what source, and has it been tampered with?", the answer either exists in the storage layer or it does not exist at all. Relata records provenance on every write: who, what source, what purpose, and a hash that chains to the previous commit. The result is a tamper-evident ledger where any row can be traced to its origin, and any logged exhibit can be re-derived byte-identically.
WITH PROVENANCE
A trailing SQL modifier that attaches a lineage object to every returned row:
-- Basic provenance on a type query
SELECT id, amount, currency
FROM Transaction
LIMIT 50
WITH PROVENANCE
-- Combine with temporal travel — what did we know on 1 March, with full lineage?
SELECT id, amount, currency
FROM Transaction
AS OF SYSTEM TIME '2024-03-01T00:00:00Z'
LIMIT 50
WITH PROVENANCEWITH PROVENANCE must come after LIMIT. It attaches a per-row provenance object (alongside the row data) carrying:
| Field | Meaning |
|---|---|
source | The source system that produced the row (e.g. kafka:topic-payments, http-ingest, csv-upload) |
method | Collection method (e.g. bulk-ingest, realtime-stream, derived-update) |
confidence | The assertion's confidence in [0.0, 1.0] |
recorded_at | System-time when the assertion was recorded (i64 ns UTC) |
derived_from | Hex ProvenanceRef of the source assertion this row derives from, or null for flat genesis attribution |
This metadata is not stored redundantly per row — it is derived from the assertion chain at query time. The principal and purpose for each write live in the parallel audit entry (see Governance).
Commit manifests
Every write produces a commit manifest entry containing:
- A monotonically-increasing sequence number
- The commit's system-time (
committed_at, i64 ns UTC) - The row count in the batch
- A
batch_fingerprint— SHA-256 over the row-level provenance refs concatenated in insertion order (order-dependent, so re-ordering a batch breaks the chain) - A
prev_refto the previous manifest entry (genesis for the first)
The principal and purpose for each write are recorded in the parallel audit entry (AuditEntry), which carries its own principal, purpose, cost, and result-fingerprint fields and is itself hash-chained. Together the manifest chain and the audit chain give you "who wrote what, when, under which purpose, and from what source."
The chaining means that any retroactive modification — to any row, at any point in history — changes the hash of that manifest, which cascades forward to invalidate every subsequent manifest hash. The chain can be verified at any time:
# Full chain verification + node health check
relata doctor
# Quick count + validity flag
curl http://localhost:9090/audit/count
# → { "entries": 4821, "chain_valid": true }EXPLAIN_REPLAY
EXPLAIN_REPLAY re-derives a specific logged exhibit's seal byte-identically. This is the audit replay path used in legal and regulatory contexts: given an exhibit ID and a sequence number, Relata reconstructs the exact bytes that were sealed, so an independent auditor can confirm the conclusion without trusting the database's current state.
-- Re-derive exhibit-7, sequence point 5
EXPLAIN_REPLAY('exhibit-7', SEQ => 5)The replay path:
- Reads the manifest chain from the anchor point to SEQ 5.
- Re-derives each referenced blob by hash.
- Confirms the chain is intact end-to-end.
- Returns the reconstructed exhibit seal and a
chain_valid: trueconfirmation.
If any blob is missing or any hash does not match, replay returns an explicit failure with the first broken link identified.
Content-addressed blobs
Large payloads (media, documents, binary fields) are stored out-of-row in a content-addressed blob store (SHA-256). The same bytes stored twice are deduplicated automatically — the prov_hash points to the same blob.
The blob store wires into three ingest paths:
- S3 door: objects at or above
RELATA_S3_BLOB_THRESHOLD_MB(default 4 MiB) are stored out-of-row - Media ingest:
ingest_mediaMCP tool accepts base64 payloads; the blob is stored and the hash recorded in the manifest - IVF cold tier: large vector indexes spill to the object-store-backed
PagedAnnIndex, with each segment referenced by hash
Provenance on agent memory
When an agent stores a memory item with remember, the resulting MemoryItem row carries the same provenance fields as any WITH PROVENANCE request:
source: the write path that produced it (e.g.mcp:remember,http:/memory/remember)method: the collection methodrecorded_at: the system-time stamp (i64 ns UTC)derived_from: the upstream assertion the memory was derived from (ornullfor a fresh genesis write)
The principal and purpose are recorded in the parallel audit entry, not on the row itself.
The justify cognitive verb retrieves the complete provenance + audit trail for any memory item — useful when an agent needs to explain how it reached a conclusion:
# Via MCP
{ "method": "tools/call", "params": { "name": "justify",
"arguments": { "id": "<MemoryItem-uuid>", "purpose": "audit" } } }
# Via HTTP
curl -X POST http://localhost:9090/memory/justify \
-H "Authorization: Bearer $RELATA_BEARER_TOKEN" \
-d '{"id":"<MemoryItem-uuid>","purpose":"audit"}'The response includes the source rows that the memory item was derived from, the chain of cognitive operations that produced it, and the manifest hashes covering each step.
Python SDK — AuditClient
from relata import RelataClient, AuditClient
with RelataClient(url, bearer_token=token, purpose="compliance_review") as client:
audit = AuditClient.from_client(client)
# Paginated audit log, filtered by principal and time window
for page in audit.entries(filter={
"principal": "api-user-finance",
"since": "2026-01-01T00:00:00Z",
"until": "2026-06-30T23:59:59Z",
}):
for entry in page:
print(entry.timestamp, entry.purpose, entry.prov_hash)
# Signed receipt for an exhibit (court-ready)
receipt = audit.signed_receipt(exhibit_id="exhibit-7")
print(receipt.chain_valid, receipt.seal_hash)
# PDF export for regulatory submission
pdf_bytes = audit.export_pdf(filter={"case_id": "case-42"})
open("audit-case-42.pdf", "wb").write(pdf_bytes)See also
- Governance — the ABAC policy layer that sits in front of every write
- Bi-Temporal Model — provenance rows are themselves bi-temporal;
system_fromrecords when each manifest entry was committed - Agent Memory —
justifyreturns the provenance chain for any memory item - SQL Reference —
WITH PROVENANCE,EXPLAIN_REPLAYsyntax