RelataDB SDK Cookbook
One page. Every method. One story — Operation Shadow Ledger, a $2.8 M embezzlement investigation — runs through the whole thing as the connective tissue, but each numbered section is self-contained and every code block is copy-pasteable.
Every captured output on this page is real — taken from a live
relata serveprocess. The scenario is fictional; the data shapes, query patterns, and governance are production-grade.
What you'll build
4 suspects · 4 wire transfers ($3.77 M) · 3 phone records · 3 case documents
↓
┌──────────────────────────────────────────────────┐
│ RelataDB (one binary) │
│ ingest → index → query → search → recall │
│ → resolve → trace → justify → audit │
└──────────────────────────────────────────────────┘
↓
RelataClient · IngestClient · Memory · McpClient · AuditClient · Namespace
In a DIY stack this would be Postgres + Elasticsearch + Pinecone + Neo4j + mem0 + Splunk + an identity-resolution ETL + Kafka glue — 8+ services. Here it's one binary.
Setup
pip install relata-sdk httpx
relata serve # starts on localhost:9090Imports every snippet below assumes:
from relata import (
RelataClient, IngestClient, Memory, McpClient,
AuditClient, Namespace, QueryResult,
)A single client carries auth, tenant, and the default purpose so you don't
repeat yourself on every call:
client = RelataClient(
"http://localhost:9090",
bearer_token="perftoken",
purpose="analytics",
)PURPOSE is optional but never invisible. Omit it and the server records
purpose: nullin the audit log; set it once on the client and every query, ingest, and MCP call is tagged. Governance travels in the substrate.
1. Connect & Verify
Before anything else, confirm the server is up, what profile it's running, and what build you're talking to.
client.health()
# HealthResponse(status="ok", profile="free", node_id="node-7f3a...")
client.status()
# StatusResponse(profile="free", role="coordinator",
# query_quota=QueryQuota(cost_remaining=1000000, ...))
client.ready()
# ReadyReport(is_ready=True, checks={
# "storage": True, "wal": True, "ingest_queue": True, ...})
client.version()
# VersionInfo(version="2.0.0", commit="a1b2c3d", build_profile="release")
client.stats()
# Stats(records=11, states=11, snapshot_rows=11, log_leaves=4, tokens=0)
client.list_modules()
# {"modules": ["aml", "maritime", "sanctions", "sigma"]}| Method | What it returns | Use it for |
|---|---|---|
health() | liveness + profile + node id | load-balancer probe |
status() | profile, role, query quota | pre-flight a batch job |
ready() | 9-condition readiness report | Kubernetes readinessProbe |
version() | build-info | migration / capability checks |
stats() | engine-wide counts | health dashboards |
list_modules() | installed extension packs | feature negotiation |
Async mirrors exist for every method in this page (
ahealth(),astatus(),aready(),aversion(),astats()). Theaprefix is the only difference.
2. Define Your Schema
RelataDB is ontology-driven: types are data, not DDL. Register them at runtime, evolve them online, branch them.
client.register_type(
"Person",
description="A natural person under investigation",
owner="fraud-team",
properties={
"id": {"type": "text"},
"name": {"type": "text"},
"email": {"type": "text"},
"phone": {"type": "text"},
"role": {"type": "text"},
"company": {"type": "text"},
"risk": {"type": "text"},
},
)
# {"created": True, "name": "Person"}List and inspect what you registered:
client.list_types()
# {"types": [{"name": "Person", "rows": 0}, {"name": "Transaction", "rows": 0}, ...]}
client.type_detail("Person")
# {"name": "Person", "owner": "fraud-team", "rows": 4,
# "properties": {"id": {...}, "name": {...}, ...}}Evolve the schema without downtime — add / drop / rename / retype:
# Add a `sanctions_status` column to every existing Person row.
client.schema_alter("Person", "add", "sanctions_status", col_type="text")
# Rename `risk` → `risk_band`.
client.schema_alter("Person", "rename", "risk", new_column="risk_band")
# Retype a column.
client.schema_alter("Person", "retype", "phone", col_type="text")Register a typed edge for graph traversal (ADR-007):
client.register_edge_type("Person", "Transaction", "AUTHORIZED")
# {"from_type": "Person", "to_type": "Transaction", "label": "AUTHORIZED"}
client.list_edge_types()
# {"edges": [{"from_type": "Person", "to_type": "Transaction",
# "label": "AUTHORIZED"}, ...]}Remove a type when it's no longer needed (admin token required):
client.deregister_type("ExperimentalType")
# {"deleted": True, "name": "ExperimentalType"}Migrate a whole ontology in one governed call
ontology_migrate registers type specs, link types, and property constraints
together so a SHACL-consistent ontology lands atomically:
client.ontology_migrate({
"types": [
{"name": "Person", "properties": {"name": {"type": "text"}}},
{"name": "Transaction", "properties": {"amount": {"type": "float"}}},
],
"links": [
{"from": "Person", "to": "Transaction", "label": "AUTHORIZED"},
],
})And register SmartIngest enrichment rules so custom identifiers get auto-detected alongside the 76 built-in canonical types:
client.enrichment_rules({
"rules": [
{"name": "internal_acct", "pattern": r"ACME-\d{6}",
"canonical_kind": "account_number"},
],
})3. Ingest Data
Four shapes: NDJSON bulk, JSON upsert/skip, CSV, and document. All route
through IngestClient and all trigger SmartIngest (76 canonical identifier
types auto-detected on the way in).
ingest = IngestClient.from_client(client)NDJSON bulk — the fast path
ingest.bulk("Person", [
{"id": "alice", "name": "Alice Chen", "email": "alice.chen@acmecorp.com",
"phone": "+14155550100", "role": "CFO", "company": "Acme Corp", "risk": "HIGH"},
{"id": "bob", "name": "Bob Smith", "email": "bob@shellco.io",
"phone": "+14155550101", "role": "Director", "company": "ShellCo Ltd", "risk": "HIGH"},
{"id": "carla", "name": "Carla Nunez", "email": "carla.nunez@acmecorp.com",
"phone": "+34666123456", "role": "Accountant","company": "Acme Corp", "risk": "LOW"},
{"id": "david", "name": "David Kim", "email": "d.kim@offshore.bn",
"phone": "+822012345678","role": "Nominee", "company": "Pacific Trust","risk": "MEDIUM"},
]){"rows_queued": 4, "rows_rejected": 0, "task_id": "itsk_019fe254-3647-...", "connector": "direct", "errors": []}Under the hood: SmartIngest scanned every field and auto-detected 4 phone numbers (E.164) and 4 emails (RFC 5322). They're now in the IdentityIndex — linkable across sources with no detection code on your side.
The money trail
ingest.bulk("Transaction", [
{"id": "tx1", "from_account": "Acme Corp", "to_account": "Pacific Trust 7742",
"amount": 2300000, "currency": "USD", "date": "2026-01-15", "authorized_by": "Alice Chen"},
{"id": "tx2", "from_account": "Pacific Trust 7742","to_account": "ShellCo Ltd",
"amount": 850000, "currency": "USD", "date": "2026-01-22", "authorized_by": "David Kim"},
{"id": "tx3", "from_account": "ShellCo Ltd", "to_account": "CASH",
"amount": 120000, "currency": "USD", "date": "2026-02-01", "authorized_by": "Bob Smith"},
{"id": "tx4", "from_account": "Acme Corp", "to_account": "Pacific Trust 7742",
"amount": 500000, "currency": "USD", "date": "2026-02-10", "authorized_by": "Alice Chen"},
])Case documents (rich text for BM25)
ingest.bulk("CaseDoc", [
{"id": "whistleblower", "title": "Whistleblower Complaint by Carla Nunez",
"body": "I am writing to report suspected embezzlement by CFO Alice Chen. Over three "
"months, Alice authorized four wire transfers totaling $2.8 million from Acme "
"Corp to an offshore account at Pacific Trust Bank held by David Kim. The money "
"was then moved to ShellCo Ltd, directed by Bob Smith."},
{"id": "sar", "title": "Suspicious Activity Report - Pacific Trust Bank",
"body": "Account 7742 held by David Kim received $2.8 million from Acme Corp. Funds "
"were rapidly moved to ShellCo Ltd and partially withdrawn as cash. Pattern "
"consistent with money laundering layering."},
{"id": "news", "title": "Acme Corp CFO Under Scrutiny",
"body": "Federal investigators examine whether CFO Alice Chen orchestrated a $2.8 "
"million embezzlement through offshore accounts. A whistleblower complaint "
"triggered the probe. Bob Smith of ShellCo denied involvement."},
])JSON upsert / skip — conflict resolution
# Re-ingest with on_conflict='upsert' → update existing rows by id.
ingest.bulk("Person", [
{"id": "alice", "risk": "CRITICAL", "sanctions_status": "under_review"},
], on_conflict="upsert")
# 'skip' keeps the existing row untouched if the id already exists.
ingest.bulk("Person", [
{"id": "alice", "risk": "LOW"},
], on_conflict="skip") # alice's risk stays CRITICALCSV ingest — bulk from a file
csv_text = """id,from_account,to_account,amount,currency,date
tx5,ShellCo Ltd,CASH,80000,USD,2026-02-05
tx6,Acme Corp,Pacific Trust 7742,300000,USD,2026-02-08
"""
ingest.bulk_csv("Transaction", csv_text)
# {"rows_queued": 2, "rows_rejected": 0, ...}Document ingest — datagrep-extractor envelope
chunks = '{"chunk_id":"c1","text":"SAR filed on Pacific Trust account 7742"}\n' \
'{"chunk_id":"c2","text":"Alice Chen authorized 4 transfers totaling $2.8M"}'
manifest = '{"source":"sar.pdf","extractor":"dgrep-v1","chunks":2}'
client.ingest_document(chunks, manifest)
# IngestDocumentResponse(report_id="rep_019fe2...", chunks_ingested=2,
# warnings=[], queue_depth=0)Streaming, CDR, OTLP — the long tail of ingest shapes
| Method | Shape | Use it for |
|---|---|---|
ingest.bulk("T", rows, detect_packs="network,financial") | NDJSON + detector override | per-call SmartIngest pack selection |
ingest.ingest_iter("T", generator, batch_size=500) | streaming iterator | O(batch_size) memory for huge CSVs |
ingest.ingest_cdr(rows) | CSV via /ingest/cdr | call-detail records (caller/callee/tower) |
ingest.otlp_traces(payload) / otlp_logs(...) / otlp_metrics(...) | OTLP/JSON | OpenTelemetry ingest |
ingest.media_status(task_id) | poll | multipart media upload progress |
# Stream a million rows without holding them all in memory:
def row_gen():
for i in range(1_000_000):
yield {"id": f"r{i}", "amount": i}
total = ingest.ingest_iter("Transaction", row_gen(), batch_size=1000)
# → 1_000_0004. Query
SQL is the primary query language. RelataDB extends it with bi-temporal,
graph, identity, and search operators — all reachable through query().
Plain SQL
result = client.query("SELECT name, role, company FROM Person WHERE risk = 'HIGH'")
for row in result:
print(row["name"], row["role"]){"data": [
{"name": "Alice Chen", "role": "CFO", "company": "Acme Corp"},
{"name": "Bob Smith", "role": "Director", "company": "ShellCo Ltd"}
]}Aggregates
client.query("SELECT SUM(amount) FROM Transaction")
# {"data": [{"SUM(amount)": 3770000}]}
client.query(
"SELECT from_account, COUNT(*), SUM(amount) "
"FROM Transaction GROUP BY from_account"
){"data": [
{"from_account": "Acme Corp", "COUNT(*)": 2, "SUM(amount)": 2800000},
{"from_account": "Pacific Trust 7742", "COUNT(*)": 1, "SUM(amount)": 850000},
{"from_account": "ShellCo Ltd", "COUNT(*)": 1, "SUM(amount)": 120000}
]}$3.77 million moved. The trail: Acme → offshore → shell company → cash.
Parameterized query (no SQL injection)
result = client.query_params(
"SELECT name, role FROM Person WHERE risk = $1 AND company = $2",
["HIGH", "Acme Corp"],
)
# ?-placeholders are rewritten to $1, $2, … automatically:
client.query_params("SELECT * FROM Person WHERE id = ?", ["alice"])Typed select helper (fluent builder)
result = (
client.select("name", "risk")
.from_("Person")
.where("risk = 'HIGH'")
.order_by("name")
.limit(10)
.execute()
)Arrow IPC (zero-copy, large result sets)
tbl = client.query_arrow("SELECT * FROM Transaction LIMIT 1000")
df = tbl.to_pandas() # requires pyarrowFederated multi-query
client.multi_search({
"queries": [
{"query": "alice", "type": "Person", "limit": 5},
{"query": "embezzlement","type": "CaseDoc", "limit": 5},
{"query": "pacific trust","type": "Transaction","limit": 5},
],
})
# {"results": [<SearchResponse>, <SearchResponse>, ...],
# "processing_time_ms": 7.2}GraphQL
client.graphql("""
query {
Person(where: { risk: { _eq: "HIGH" } }, limit: 10) {
id name role company
}
}
""")SPARQL
client.sparql("""
PREFIX rel: <https://relata.io/ns#>
SELECT ?s ?o WHERE { ?s rel:authorizedBy ?o } LIMIT 5
""")| Method | Wire | Returns |
|---|---|---|
query(sql) | POST /query | QueryResult (iterable) |
query_params(sql, params) | POST /query (positional binds) | QueryResult |
query_arrow(sql) | POST /query/arrow | pyarrow.Table |
multi_search(queries) | POST /multi-search | dict |
graphql(q) | POST /graphql | data field |
sparql(q) | POST /sparql | dict |
5. Search
Three ways to search: the dedicated search() (BM25, faceted, highlighted),
the hybrid SQL operator, and the typed namespace handle.
POST /search — BM25 with facets & highlights
res = client.search(
"alice chen", "Person",
limit=10,
facets=["company", "risk"],
highlight=True,
filters={"company": "Acme Corp"},
matching_strategy="all",
)
for hit in res.hits:
print(hit.score, hit.fields["name"])
# 8.42 Alice Chen{
"hits": [{"score": 8.42, "fields": {"name": "Alice Chen", ...}}],
"total": 1,
"estimated_total_hits": 1,
"facets": {"company": {"Acme Corp": 1}, "risk": {"HIGH": 1}},
"processing_time_ms": 2.1
}HYBRID_SEARCH — fused BM25 + vector (via SQL)
client.query(
"HYBRID_SEARCH FROM CaseDoc "
"QUERY 'embezzlement offshore transfers' LIMIT 3"
){"rows": 3, "data": [
{"title": "Whistleblower Complaint by Carla Nunez", "_score": 12.84},
{"title": "Acme Corp CFO Under Scrutiny", "_score": 9.21},
{"title": "Suspicious Activity Report", "_score": 7.55}
]}All three documents found, ranked by relevance — no Elasticsearch, no external service.
Weighted fusion — [graph, bm25, vector]
# Pure BM25 (keyword precision, no semantic fuzziness):
client.query(
"HYBRID_SEARCH FROM CaseDoc QUERY 'ShellCo shell company' "
"LIMIT 3 WEIGHTS 0.0 1.0 0.0"
)
# Balanced BM25 + vector via the search() door (set metric or weights to
# trigger the hybrid channel — #2672):
client.search(
"embezzlement offshore", "CaseDoc",
metric="cosine", weights=[0.0, 0.5, 0.5],
)The
WEIGHTStriple is[graph, bm25, vector]. Setting any one to1.0and the others to0.0gives you single-channel mode.
6. Resolve Identities
SmartIngest already linked every phone, email, IBAN, IMEI… on the way in. These six operators query that index.
detect_identities — pull identifiers out of free text
client.detect_identities(
"Contact Alice at alice.chen@acmecorp.com or +14155550100. "
"Wire to Pacific Trust account 7742, IBAN GB29NWBK60161331926819."
)
# {"data": [
# {"kind": "email", "value": "alice.chen@acmecorp.com"},
# {"kind": "phone", "value": "+14155550100"},
# {"kind": "iban", "value": "GB29NWBK60161331926819"}, ...]}resolve_ids / identity_cluster — "who does this belong to?"
# The SQL operator — one query, no detection code:
client.query("LOOKUP_IDENTITY '+14155550100'")
# {"rows": 1, "data": [{"id": "alice", "name": "Alice Chen",
# "phone": "+14155550100", "risk": "HIGH"}]}
# The typed SDK helper — resolve to the full identity cluster:
client.identity_cluster("+14155550100")
# {"data": [{"id": "alice", "match_kind": "phone",
# "cluster": [{"kind":"email","value":"alice.chen@acmecorp.com"},
# {"kind":"phone","value":"+14155550100"}, ...]}]}
client.resolve_ids("+14155550100", mode="canonical")
# Returns the single best canonical match.same_identity — predicate
client.same_identity("+14155550100", "alice.chen@acmecorp.com")
# True — both resolve to Alice.
client.same_identity("+14155550100", "bob@shellco.io")
# Falsefuse_identities / split_identities — ontological merge
# Merge two records that are actually the same person:
client.fuse_identities("alice", "a.chen.duplicate")
# Writes an IdentityLink with link_type='fused'; returns the merged cluster.
# Undo a mistaken fuse:
client.split_identities("alice", "a.chen.duplicate")76 canonical types are auto-detected: phones (E.164), emails (RFC 5322), IBANs (ISO 13616), IMEIs (GSM), MMSIs (maritime), VINs (vehicles), passport numbers, BTC addresses, URLs, MAC addresses, and 65 more.
| Method | SQL operator | Returns |
|---|---|---|
detect_identities(text) | DETECT_IDENTITIES($1) | list of {kind, value} |
resolve_ids(v, mode=) | RESOLVE_IDENTITY($1[, MODE => …]) | matched rows |
identity_cluster(v) | RESOLVE_IDENTITY($1, MODE => 'cluster') | full cluster |
same_identity(a, b) | SAME_IDENTITY($1, $2) | bool |
fuse_identities(a, b) | FUSE_IDENTITIES($1, $2) | merged cluster |
split_identities(a, b) | SPLIT_IDENTITIES($1, $2) | unmerged clusters |
7. Trace Graphs
Twelve graph operators — reachable as typed SDK methods or as SQL operators. The story: trace the money from Acme to cash.
Create the edges first
client.create_link("AUTHORIZED", "alice", "Person", "tx1", "Transaction")
client.create_link("AUTHORIZED", "david", "Person", "tx2", "Transaction")
client.create_link("AUTHORIZED", "bob", "Person", "tx3", "Transaction")
client.create_link("AUTHORIZED", "alice", "Person", "tx4", "Transaction")
# {"link_name": "AUTHORIZED", "source_id": "alice", ...}Shortest path
client.graph_shortest_path("alice", "tx3")
# {"path": ["alice", "tx1", "Pacific Trust 7742", "tx2", "ShellCo Ltd", "tx3"],
# "hops": 5}
client.graph_dijkstra("Transaction", "tx1", "tx3") # weighted variantTraversal
client.graph_traverse("alice", direction="out", max_depth=3, limit=50)
# {"nodes": [{"id": "tx1"}, {"id": "tx4"}, ...],
# "edges": [{"from": "alice", "to": "tx1", "label": "AUTHORIZED"}, ...]}Centrality & community detection
client.graph_pagerank("Person", damping=0.85, max_iter=100)
# {"data": [{"id": "alice", "score": 0.42},
# {"id": "bob", "score": 0.21}, ...]}
client.graph_community("Person")
# {"communities": [{"id": 0, "members": ["alice", "bob", "david"]}, ...]}
client.graph_scc("Person") # strongly connected components
client.graph_triangle_count("Transaction") # graph density / cohesionLink prediction & similarity
client.graph_link_predict("Person")
# Predicts missing relationships: [{"from": "alice", "to": "david", "score": 0.78}, ...]
client.graph_node_similarity("Person", "alice")
# {"data": [{"id": "bob", "score": 0.91}, {"id": "david", "score": 0.64}]}All 12 graph operators
| Method | SQL operator | Finds |
|---|---|---|
graph_shortest_path(src, dst) | GET /graph/shortest-path | shortest path (HTTP door, supports max_hops) |
graph_dijkstra(type, src, dst) | GRAPH_DIJKSTRA(...) | weighted shortest path |
graph_traverse(src, depth=) | GET /graph/traverse | BFS traversal |
graph_pagerank(type) | GRAPH_PAGERANK(...) | centrality |
graph_community(type) | GRAPH_COMMUNITY(...) | Louvain/label-prop communities |
graph_scc(type) | GRAPH_SCC(...) | strongly connected components (fraud rings) |
graph_cycles(type) | GRAPH_CYCLES(...) | cycle detection |
graph_triangle_count(type) | TRIANGLE_COUNT(...) | cohesion |
graph_node_similarity(type, node) | GRAPH_NODE_SIMILARITY(...) | similar entities |
graph_link_predict(type) | GRAPH_LINK_PREDICT(...) | missing edges |
list_edge_types() | GET /types/edges | registered edges |
register_edge_type(from, to, label) | POST /types/edges | register an edge |
create_link(name, src, srcT, dst, dstT) | POST /links | create an edge instance |
8. Investigate
One-click entity investigation plus twelve domain-specific operators (financial crime, maritime, telecom, geospatial). The SDK methods wrap governed SQL operators; the MCP tools wrap the same operators for agents.
Investigate entity (composite profile)
mcp = McpClient.from_client(client)
mcp.investigate_entity("Person", "alice")
# {"profile": {...}, "timeline": [...], "connections": [...],
# "risk": {"score": 0.92, "factors": ["sanctions_proximity", ...]}}Sanctions screening
client.sanctions_screen("Alice Chen")
# {"data": []} — no hits yet
mcp.screen_sanctions("Alice Chen", threshold=0.85)
# {"rows": 0}Beneficial ownership chain
client.beneficial_ownership_chain("Pacific Trust 7742", max_depth=6)
# {"chain": [{"account": "Pacific Trust 7742", "holder": "David Kim"},
# {"holder": "David Kim", "nominee_for": "Bob Smith"}, ...]}Crypto trace, wire reconstruction, hawala
client.crypto_trace("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb")
# {"hops": [{"from": "0x742d...", "to": "0x9ab1...", "amount": 12.5}, ...]}
client.wire_reconstruction("Pacific Trust 7742", tolerance_pct=5.0)
# {"chain": [{"account": "...", "in": 2800000, "out": 850000}, ...]}
client.hawala_trace("alice", max_hops=5)
# {"network": [{"node": "alice", "linked_to": ["hawaladar_1", ...]}, ...]}Geospatial
client.geofence("POINT(-97.74 30.27)", target_type="MovementEvent")
# {"data": [{"id": "m1", "lat": 30.27, "lon": -97.74}, ...]}
client.crime_pattern_cluster("downtown_austin")
# {"clusters": [{"centroid": [...], "events": 23}, ...]}Telecom: burner & convoy detection
client.burner_detect(max_age_days=30, max_calls=3)
# {"burners": [{"phone": "+14155550199", "age_days": 12, "calls": 2}, ...]}
client.convoy_detect(radius_m=200, time_tol_secs=300, min_points=3)
# {"convoys": [{"members": ["veh_1", "veh_2", "veh_3"], "window": [...]}]}Maritime
client.vessel_track(538005644, window_secs=86400) # MMSI → AIS track
client.dark_fleet_detect(max_gap_hours=24) # AIS gaps ("going dark")
client.vessel_to_vessel_transfer(proximity_nm=0.5,
time_window_minutes=120)
# {"transfers": [{"vessel_a": 538..., "vessel_b": 636..., ...}]}Full investigation operator table
| Method | Domain | What it finds |
|---|---|---|
sanctions_screen(name) | compliance | sanctions-list hits (fuzzy threshold) |
beneficial_ownership_chain(party) | compliance | ultimate beneficial owner |
crypto_trace(entity) | financial | cryptocurrency fund flow |
wire_reconstruction(account) | financial | wire-transfer chain |
hawala_trace(seed) | financial | informal value-transfer network |
geofence(area) | geospatial | entities within a geographic fence |
crime_pattern_cluster(area) | geospatial | spatial crime clusters |
burner_detect(...) | telecom | burner phone numbers |
convoy_detect(...) | telecom/transport | entities traveling together |
dark_fleet_detect(...) | maritime | vessels with AIS gaps |
vessel_track(mmsi) | maritime | AIS position reports |
vessel_to_vessel_transfer(...) | maritime | ship-to-ship transfers |
9. Agent Memory
Memory is the mem0-style high-level surface over the governed /memory/*
verbs (ADR-144). Every belief is bi-temporal, provenance-tracked, and
governable.
mem = Memory("http://localhost:9090",
purpose="agent-notes",
bearer_token="perftoken",
session_id="shadow-ledger")add — store a belief
mid1 = mem.add("Alice Chen authorized $2.3M wire to Pacific Trust account 7742 on Jan 15.",
confidence=0.95, memory_class="episodic")
mid2 = mem.add("Bob Smith directs ShellCo Ltd, received $850K from the offshore account.")
mid3 = mem.add("Carla Nunez blew the whistle — four fraudulent transfers, $2.8M total.")
mid4 = mem.add("Classic laundering: placement → layering → integration.",
memory_class="procedural")
# mid1 = "019fe254-3647-77fc-..."One call stored a bi-temporal row (valid_from/valid_to + system_from/system_to), linked it to the session, scored it with confidence, and hash-chained it to the provenance graph. No extra tables, no vector store setup.
add_batch — high-throughput write
ids = mem.add_batch([
"The wire transfer matches a known fraud pattern: rapid offshore movement.",
{"content": "Customer #1234 has no prior history with this beneficiary.",
"confidence": 0.8, "memory_class": "semantic"},
"Beneficiary account opened 2 days before the transfer request.",
])
# ids = ["019fe255-...", "019fe256-...", "019fe257-..."]search — recall ranked by relevance × recency × confidence
hits = mem.search("How much money was transferred?", top_k=2)| Score | Memory |
|---|---|
| 1.0000 | Classic laundering: placement → layering → integration. |
| 0.6444 | Carla Nunez blew the whistle — four fraudulent transfers, $2.8M total. |
mem.search("Who is the whistleblower?")
# [{"content": "Carla Nunez blew the whistle...", "score": 1.0, ...}]
mem.search("What is ShellCo?")
# [{"content": "Bob Smith directs ShellCo Ltd...", "score": 1.0, ...}]search_detailed — observe the ADR-145 retrieval-quality knobs
envelope = mem.search_detailed(
"money transfer",
top_k=5,
min_confidence=0.5, # CONFIDENCE
recency_half_life_secs=86400, # RECENCY
budget_tokens=2048, # BUDGET
stability_days=30.0, # FORGETTING_CURVE
cancel_threshold=0.2, # CANCEL_WHEN
)
# envelope = {"rows": [...],
# "recall_cost_tokens": 412, # BUDGET running total
# "cancelled": False} # CANCEL_WHEN short-circuitassociate — link two memories
mem.associate(mid1, mid2, relation="same_investigation")
# {"from_id": mid1, "to_id": mid2, "relation": "same_investigation"}episodes — list sessions
mem.episodes(session_id="shadow-ledger")
# [{"id": "ep_1", "session_id": "shadow-ledger",
# "summary": "Operation Shadow Ledger investigation", ...}]justify — provenance chain
mem.justify(mid1)
# {"found": True,
# "provenance": {"prov_hex": "a3f8b2c1...",
# "source": "memory:remember",
# "timestamp": "2026-08-08T16:20:14Z"}}When the regulator asks "why did the agent flag this transaction?", you have the answer — every belief is traceable.
update / resolve / summarise / forget
new_id = mem.update(mid1, "UPDATED: Alice authorized $2.3M — confirmed by 2 sources.")
# Old belief is superseded, not deleted. Bi-temporal history preserves it.
mem.resolve(new_id) # follow the supersession chain to canonical head
# {"id": new_id, "content": "UPDATED: ...", "supersedes": [mid1]}
mem.summarise([mid1, mid2, mid3], summary_content="Three findings on Shadow Ledger.")
# {"id": "summ_...", "content": "Three findings on Shadow Ledger."}
mem.forget(mid4) # governed retention-policy retract (not a hard delete)
# {"memory_item_id": mid4, "policy": "soft_delete",
# "forget_at_ns": 1789234560000000000}The full Memory surface (15 methods)
| Method | Verb | Purpose |
|---|---|---|
add(content, ...) | remember | store a belief, return its id |
add_batch(items) | remember_batch | bulk write, return ids in order |
search(query, top_k=) | recall | ranked retrieval |
search_detailed(query, ...) | recall | full envelope with cost/cancelled |
batch_search(queries) | recall×N | multiple queries merged |
get(memory_id) | recognize | single fetch, or None |
update(id, content) | consolidate | supersede an old belief |
forget(memory_id) | forget | governed retention retract |
associate(src, dst, rel) | associate | typed link between memories |
episodes(session_id=) | episodes_in | list sessions |
justify(memory_id) | justify | PROV-O provenance chain |
resolve(memory_id) | resolve | follow supersession to canonical head |
summarise(ids) | summarise | summary belief from sources |
get(memory_id) | recognize | single fetch |
close() | — | close the HTTP pool |
10. MCP Tools (69 governed agent tools)
McpClient is the typed Python surface over the server's 69 MCP tools — the
same tools Claude / Cursor / Cline get when you point them at RelataDB.
mcp = McpClient.from_client(client)
mcp.initialize() # handshake
tools = mcp.list_tools()
# tools = [{"name": "query_knowledge", ...}, {"name": "recall", ...}, ...]
# len(tools) == 69Connect Claude directly:
claude mcp add relata http://localhost:9090/mcp \
--header "Authorization: Bearer perftoken"Knowledge & query
mcp.query_knowledge("SELECT name, risk FROM Person WHERE risk = 'HIGH'",
purpose="analytics")
mcp.search_knowledge("embezzlement offshore", purpose="analytics", top_k=5)
mcp.explain_policy("SELECT * FROM Person", purpose="analytics")
# Shows the ACL / org-isolation policy that would apply, without executing.
mcp.suggest_extensions()
# Lists extension packs and their data availability.Entity discovery
mcp.list_entity_types()
mcp.get_entities("Person", filters={"risk": "HIGH"}, limit=10)
mcp.search_entities("alice", entity_types=["Person"])
mcp.get_domain_summary("financial")
# {"counts": {"Person": 4, "Transaction": 4, ...}, "freshness": {...}}Case & investigation
mcp.get_entity_profile("alice", purpose="analytics")
mcp.get_timeline("alice", purpose="analytics",
since_ns=1736899200000000000) # since 2025-01-15
mcp.find_connections("alice", purpose="analytics", limit=50)
mcp.get_relationships(subject="alice", predicate="AUTHORIZED",
purpose="analytics")
mcp.investigate_entity("Person", "alice")
mcp.find_threats("ProcessEvent")
mcp.add_case_note("case-2026-001",
"Alice authorized 4 transfers to Pacific Trust 7742.",
author="investigator-1")
mcp.get_case_summary("case-2026-001", purpose="analytics")Identity
mcp.lookup_identity("+14155550100", purpose="analytics")
mcp.resolve_entity_identity("alice", purpose="analytics")Memory (the 10 cognitive verbs, reachable via MCP too)
mid = mcp.remember("Alice authorized $2.3M wire on Jan 15.", purpose="agent-notes")
mcp.remember_batch([{"content": "ShellCo received $850K."},
{"content": "Carla is the whistleblower."}],
purpose="agent-notes")
mcp.recall("whistleblower", purpose="agent-notes", top_k=3)
mcp.recognize(mid, purpose="agent-notes")
mcp.justify(mid, purpose="agent-notes")
mcp.consolidate(mid, "UPDATED: confirmed by 2 sources.", purpose="agent-notes")
mcp.forget(mid, retain_days=90, purpose="agent-notes")
mcp.remember_procedure("fraud-agent", "SAR_FILING",
"1. Screen. 2. Document. 3. File SAR within 30 days.",
purpose="agent-notes")
mcp.recall_procedure("fraud-agent", name="SAR_FILING", purpose="agent-notes")
mcp.associate(mid, mid2, relation="same_case", purpose="agent-notes")
mcp.resolve(mid, purpose="agent-notes")
mcp.summarise(ids=[mid, mid2], purpose="agent-notes")
mcp.episodes_in("shadow-ledger", purpose="agent-notes")Natural language → SQL
mcp.nl_query("show all high risk persons", purpose="analytics")
# {"dialect": "sql", "sql": "SELECT * FROM Person WHERE risk = 'HIGH'",
# "rows": [...]}
mcp.nl_query("who is alice connected to",
purpose="analytics", max_sub_questions=2)
# {"dialect": "cypher", "decomposed": True,
# "sub_results": [{"dialect": "sql", ...}, {"dialect": "cypher", ...}]}Detection rules & jobs
mcp.import_sigma("""
title: Suspicious Large Wire Transfer
status: experimental
logsource:
product: relata
service: Transaction
detection:
selection:
amount: 1000000
condition: selection
level: high
""", purpose="security")
# {"rule_id": "019fe24e-e333-...", "name": "Suspicious Large Wire Transfer",
# "status": "active"}
mcp.list_rules()
mcp.create_rule("high_value_wire",
"SELECT * FROM Transaction WHERE amount > 1000000",
severity="high", purpose="security")
mcp.list_jobs()
mcp.schedule_job("high_value_wire")
mcp.job_status()Workflows
mcp.list_workflows()
mcp.run_workflow("sar_filing_workflow")
mcp.workflow_status("<run_id>")Graph via MCP
mcp.detect_communities("Person", purpose="analytics")
mcp.rank_key_nodes("Person", metric="pagerank", purpose="analytics")
mcp.hub_authority("Person", purpose="analytics")
mcp.find_scc("Person", purpose="analytics")
mcp.predict_links("Person", from_id="alice", purpose="analytics")
mcp.paths_between("alice", "tx3", max_hops=4, purpose="analytics")
mcp.list_link_types()Financial-crime via MCP
mcp.trace_crypto("0x742d...", max_hops=5, purpose="analytics")
mcp.beneficial_ownership("alice", max_depth=6, purpose="analytics")
mcp.reconstruct_wire("Pacific Trust 7742", tolerance_pct=5.0)
mcp.trace_hawala("alice", max_hops=5)
mcp.screen_sanctions("Alice Chen", purpose="compliance_review")
mcp.geofence(30.27, -97.74, radius_m=1000, purpose="analytics")RAG & multimodal
mcp.rag_store_answer("Who authorized the transfers?", "Alice Chen.",
source_ids=["tx1", "tx4"], purpose="rag")
mcp.rag_store_elements([{"type": "fact", "text": "ShellCo is a front."}],
purpose="rag")
mcp.ingest_document(chunks_jsonl=chunks, manifest_json=manifest,
purpose="rag")
mcp.hybrid_search("CaseDoc", "embezzlement", top_k=5, purpose="analytics")
mcp.similar_multimodal("MediaEmbedding", "img_42",
modality="image", purpose="investigation")
mcp.search_video_frames("frame_1", top_k=20, purpose="security_incident")
mcp.ingest_media("MediaImage", bytes_b64="...", modality="image")Ops
mcp.server_health()
mcp.job_status()
mcp.metrics()
mcp.aggregate_stats("Transaction", agg="SUM", column="amount")
mcp.get_audit_trail(principal_filter="investigator-1", limit=100)The remaining MCP tools (gist)
| Tool | What it does |
|---|---|
find_in_social_corpus(object_type, text_query=, user=) | search ingested social-media corpus |
face_match(probe_id, threshold=) | GATED (ADR-155) — match a probe face |
erase_subject(subject, reason=) | GDPR Art. 17 crypto-shred erasure |
metrics() / server_health() / job_status() | ops observability |
11. Media & Biometrics
Four governed SQL operators for face search, image similarity, perceptual-hash
matching, and DNS-tunnel detection. (The MCP face_match tool is gated behind
ADR-155 — these SQL-reachable operators are the unblocked path.)
client.face_search(
"gallery-1", [0.12, -0.34, 0.56, ...], # 128-dim probe embedding
k=5, threshold=0.6, purpose="investigation",
)
# QueryResult: columns entity_id, gallery_id, score, modality (ranked desc)
client.match_pdq(
"corpus-1", "a1b2c3d4e5f67890...", # PDQ hex hash
threshold=0.9, purpose="investigation",
)
# QueryResult: entity_id, media_type, corpus_id, score, matcher
client.similar_image(
"media-42", threshold=0.6, index="ncmec",
purpose="investigation",
)
# QueryResult: near-duplicate images, ranked desc
client.dns_tunnel_detect("ws-finance-01", purpose="security")
# {"data": [{"domain": "exfil.bad.tld", "entropy": 7.8, "count": 1242}]}12. Governance
Erasure, sessions, and export — the compliance surface.
GDPR Art. 17 erasure — irreversible crypto-shred
client.erase_subject("alice", reason="gdpr-art17-request")
# {"subject": "alice", "shredded": True,
# "rows_affected": 4, "vectors_removed": 1, "blobs_removed": 0,
# "receipt": {"signed": "..."}}Session management (draft → review → commit)
# Stage some writes, review them, then commit or discard.
client.session_diff("session-shadow-ledger")
# {"changes": [{"type": "Person", "id": "alice", "op": "upsert", ...}]}
client.session_commit("session-shadow-ledger")
# {"committed": True, "rows": 4, "commit_hash": "f3a2c8b1..."}
# Or throw them away:
client.session_discard("session-shadow-ledger")
# {"discarded": True}Data export
client.export_data("Transaction", format="json")
# {"type": "Transaction", "format": "json",
# "rows": [...], "exported_at": "2026-08-08T..."}
client.export_data("Person", format="csv")Enrichment rules & ontology migration (the governance long tail)
# Register custom SmartIngest enrichment rules.
client.enrichment_rules({
"rules": [{"name": "internal_acct", "pattern": r"ACME-\d{6}",
"canonical_kind": "account_number"}],
})
# Governed SHACL ontology migration.
client.ontology_migrate({
"types": [{"name": "SanctionsHit",
"properties": {"name": {"type": "text"},
"list": {"type": "text"}}}],
})13. Branches & Namespaces
Schema branches for "what-if" analysis; namespace handles for the search-developer shape.
Schema branches (copy-on-write forks)
client.create_schema_branch("hypothesis-bribery", from_branch="main")
# {"created": True, "branch": "hypothesis-bribery", "source": "main"}
# Ingest alternative theories into the branch, run queries, compare.
# Then either merge or delete:
client.delete_schema_branch("hypothesis-bribery")
# {"deleted": True}Namespace handle — schemaless write + typed search
docs = client.namespace("CaseDoc")
# Schemaless upsert (POST /ingest/auto — auto-creates the type if absent).
docs.write([
{"id": "memo1", "title": "Bribery hypothesis",
"body": "Pacific Trust may be a kickback conduit.", "status": "draft"},
], schema={"title": "text", "body": "text"})
# Typed ranked search — text compiles to BM25; filters narrow server-side.
res = docs.query(
text="bribery kickback",
match_column="title",
filters=[{"field": "status", "op": "eq", "value": "draft"}],
limit=5,
)
for row in res:
print(row["title"])
docs.get("memo1") # point lookup
docs.delete_all() # governed bi-temporal tombstone every row
docs.branch_from("CaseDoc") # T10 copy-on-write namespace branchOne client, one connection pool, reused across every namespace. Governance (auth, tenant, purpose) travels in the substrate.
14. Cluster Ops
Four methods for multi-node deployments (RELATA_PROFILE=cluster).
client.cluster_nodes()
# [ClusterNode(id="node-7f3a", role="coordinator", addr="10.0.0.1:9090",
# partitions=[0,1,2], state="healthy"),
# ClusterNode(id="node-9b2c", role="reader", addr="10.0.0.2:9090", ...)]
client.cluster_topology()
# {"nodes": [...], "partitions": [{"id": 0, "primary": "node-7f3a",
# "replicas": ["node-9b2c"]}], "roles": {...}}
client.cluster_rebalance()
# {"rebalanced": True, "partitions_moved": 3, "duration_ms": 1240}
client.cluster_drain("node-9b2c") # evacuate for maintenance
# {"drained": True, "node": "node-9b2c", "partitions_relocated": 3}15. System Ops
SSE event stream, webhooks, and the high-throughput wire protocols.
Observe stream (live SSE)
for event in client.observe_stream():
print(event["kind"], event["level"], event["message"])
# query INFO SELECT name FROM Person (rows=4, latency_ms=2.1)
# ingest INFO bulk Person rows=4
# ...Requires
RELATA_OBSERVE_STREAM=onserver-side. A connection drop ends the generator cleanly rather than raising.
Webhooks
client.register_webhook(
"https://hooks.slack.com/services/...",
event_types=["ingest.completed", "alert.triggered"],
)
# {"id": "wh_019fe2...", "url": "https://...", "event_types": [...]}
client.list_webhooks()
# {"webhooks": [{"id": "wh_019fe2...", ...}]}
client.delete_webhook("wh_019fe2...")
# {"deleted": True}Arrow Flight (zero-copy columnar over gRPC)
tbl = client.query_flight(
"SELECT * FROM Transaction LIMIT 1000", purpose="analytics",
)
df = tbl.to_pandas()
# Requires RELATA_FLIGHT_ENABLE=true (port 8815); pyarrow only, no grpcio.Plain gRPC (RelataQuery.Execute)
result = client.query_grpc("SELECT * FROM Person LIMIT 1000")
# Same QueryResult shape as query(); requires grpcio
# (pip install relata-sdk[grpc]).
result = client.query_grpc_stream("SELECT * FROM Transaction")
# Server-streaming variant — same shape, frame-by-frame.16. Audit & Provenance
The audit chain is hash-chained and tamper-evident. A chain_valid: False
response means tampering — escalate immediately.
from relata import AuditClient
audit = AuditClient.from_client(client)
audit.count()
# AuditCountResponse(count=47, entries=47,
# chain_valid=True, chain_head="f3a2c8b1...")chain_valid: True — every query, ingest, and MCP call is recorded and
provably unmodified.
# Paginated entries with filters:
audit.entries(principal="investigator-1",
purpose="analytics",
limit=5)
# {"entries": [{"ts_ns": ..., "principal": "investigator-1",
# "action": "query", "sql": "SELECT ...",
# "decision": "allow", "request_id": "req_..."}, ...],
# "next_cursor": "...", "chain_valid": True}
audit.find_by_request_id("req_abc123")
# Single entry, or NoneCourt-grade PDF + signed receipts
pdf_bytes = audit.export_pdf("case-2026-001", template="default")
Path("shadow-ledger.pdf").write_bytes(pdf_bytes)
receipt = audit.sign_receipt({
"case_id": "case-2026-001",
"subject": "alice",
"action": "gdpr_erasure",
})
# {"signed": True, "signature": "...", "signed_at": "..."}17. Temporal & Provenance
Every row carries four timestamps: valid_from, valid_to (when the fact was
true in the real world) and system_from, system_to (when RelataDB knew it).
AS OF — point-in-time queries
# Valid time: what was true on Jan 20?
client.query("SELECT COUNT(*) FROM Person AS OF '2026-01-20T00:00:00Z'")
# System time: what did we KNOW on Jan 20?
client.query("SELECT COUNT(*) FROM Person AS OF SYSTEM TIME '2026-01-20T00:00:00Z'")WITH PROVENANCE — every row carries its chain
result = client.query("SELECT name, company FROM Person WITH PROVENANCE")
# Each row includes prov_hex, source, commit_hash, timestamp.Bi-temporal via the fluent builder
result = (
client.select("Person")
.where("risk = 'HIGH'")
.as_of("2026-01-20")
.with_provenance()
.limit(20)
.execute()
)The DIY alternative (for contrast)
| Component | DIY stack | RelataDB |
|---|---|---|
| Relational store | Postgres | ✅ built-in |
| Full-text search | Elasticsearch / Typesense | ✅ custom BM25 (WAND, 12-language stemmers) |
| Vector store | Pinecone / pgvector | ✅ custom HNSW + DiskANN |
| Graph database | Neo4j | ✅ CSR + PLL |
| Memory layer | mem0 / custom | ✅ 15 methods, governed |
| Audit log | Splunk / custom | ✅ hash-chained, tamper-evident |
| Identity resolution | Custom ETL | ✅ SmartIngest (76 types) |
| Agent tools | Custom MCP server | ✅ 69 tools, governed |
| ETL glue | Kafka / Fivetran | ✅ not needed (one store) |
| Total services | 8+ | 1 |
Summary: 100% SDK Coverage Table
Every public method of RelataClient (+ the companion clients), which section
shows it, and the recipe it belongs to.
| # | Method | Client | § | Recipe |
|---|---|---|---|---|
| 1 | health() | RelataClient | 1 | liveness probe |
| 2 | status() | RelataClient | 1 | profile + quota |
| 3 | stats() | RelataClient | 1 | dashboard counts |
| 4 | version() | RelataClient | 1 | build info |
| 5 | ready() | RelataClient | 1 | readiness probe |
| 6 | list_modules() | RelataClient | 1 | installed packs |
| 7 | register_type(name, **) | RelataClient | 2 | register a type |
| 8 | deregister_type(name) | RelataClient | 2 | remove a type |
| 9 | list_types() | RelataClient | 2 | list all types |
| 10 | type_detail(name) | RelataClient | 2 | type details |
| 11 | schema_alter(name, action, col, **) | RelataClient | 2 | online ALTER |
| 12 | register_edge_type(from, to, label) | RelataClient | 2 | register edge |
| 13 | list_edge_types() | RelataClient | 2 | list edges |
| 14 | ontology_migrate(schema) | RelataClient | 2 | SHACL migration |
| 15 | enrichment_rules(rules) | RelataClient | 2 | custom detectors |
| 16 | bulk(type, rows) | IngestClient | 3 | NDJSON bulk |
| 17 | bulk(type, rows, on_conflict='upsert') | IngestClient | 3 | JSON upsert |
| 18 | bulk(type, rows, on_conflict='skip') | IngestClient | 3 | skip existing |
| 19 | bulk_csv(type, csv_text) | IngestClient | 3 | CSV ingest |
| 20 | ingest_iter(type, iter, batch_size) | IngestClient | 3 | streaming |
| 21 | ingest_cdr(rows) | IngestClient | 3 | call-detail records |
| 22 | otlp_traces(payload) / otlp_logs / otlp_metrics | IngestClient | 3 | OpenTelemetry |
| 23 | ingest_document(chunks, manifest) | RelataClient | 3 | datagrep doc |
| 24 | query(sql) | RelataClient | 4 | SQL SELECT |
| 25 | query_params(sql, params) | RelataClient | 4 | parameterized |
| 26 | query_arrow(sql) | RelataClient | 4 | Arrow IPC |
| 27 | select(*cols) → .execute() | RelataClient | 4 | fluent builder |
| 28 | multi_search(queries) | RelataClient | 4 | federated |
| 29 | graphql(query) | RelataClient | 4 | GraphQL |
| 30 | sparql(query) | RelataClient | 4 | SPARQL |
| 31 | search(query, type, **) | RelataClient | 5 | POST /search |
| 32 | query("HYBRID_SEARCH ...") | RelataClient | 5 | fused search |
| 33 | query("HYBRID_SEARCH ... WEIGHTS") | RelataClient | 5 | weighted |
| 34 | detect_identities(text) | RelataClient | 6 | detect from text |
| 35 | resolve_ids(value, mode=) | RelataClient | 6 | resolve identity |
| 36 | identity_cluster(value) | RelataClient | 6 | full cluster |
| 37 | same_identity(a, b) | RelataClient | 6 | predicate |
| 38 | fuse_identities(a, b) | RelataClient | 6 | merge |
| 39 | split_identities(a, b) | RelataClient | 6 | unmerge |
| 40 | graph_shortest_path(src, dst) | RelataClient | 7 | shortest path |
| 41 | graph_traverse(src, depth=) | RelataClient | 7 | BFS traversal |
| 42 | graph_community(type) | RelataClient | 7 | Louvain |
| 43 | graph_pagerank(type) | RelataClient | 7 | centrality |
| 44 | graph_scc(type) | RelataClient | 7 | SCC (fraud rings) |
| 45 | graph_cycles(type) | RelataClient | 7 | cycle detection |
| 46 | graph_link_predict(type) | RelataClient | 7 | missing edges |
| 47 | graph_node_similarity(type, node) | RelataClient | 7 | similar entities |
| 48 | graph_triangle_count(type) | RelataClient | 7 | cohesion |
| 49 | graph_dijkstra(type, src, dst) | RelataClient | 7 | weighted path |
| 50 | create_link(name, src, sT, dst, dT) | RelataClient | 7 | create edge |
| 51 | sanctions_screen(name) | RelataClient | 8 | sanctions hit |
| 52 | beneficial_ownership_chain(party) | RelataClient | 8 | UBO trace |
| 53 | crypto_trace(entity) | RelataClient | 8 | crypto flow |
| 54 | wire_reconstruction(account) | RelataClient | 8 | wire chain |
| 55 | hawala_trace(seed) | RelataClient | 8 | hawala network |
| 56 | geofence(fence) | RelataClient | 8 | geo-fence |
| 57 | burner_detect(**) | RelataClient | 8 | burner phones |
| 58 | convoy_detect(**) | RelataClient | 8 | convoys |
| 59 | crime_pattern_cluster(area) | RelataClient | 8 | crime clusters |
| 60 | dark_fleet_detect(**) | RelataClient | 8 | AIS gaps |
| 61 | vessel_track(mmsi) | RelataClient | 8 | AIS track |
| 62 | vessel_to_vessel_transfer(**) | RelataClient | 8 | STS transfers |
| 63 | face_search(gallery, embedding) | RelataClient | 11 | face k-NN |
| 64 | similar_image(media_ref) | RelataClient | 11 | near-dup images |
| 65 | match_pdq(corpus, hash) | RelataClient | 11 | PDQ hash match |
| 66 | dns_tunnel_detect(entity) | RelataClient | 11 | DNS tunneling |
| 67 | add(content, **) | Memory | 9 | remember |
| 68 | add_batch(items) | Memory | 9 | bulk remember |
| 69 | search(query, top_k=) | Memory | 9 | recall |
| 70 | search_detailed(query, **) | Memory | 9 | recall + knobs |
| 71 | batch_search(queries) | Memory | 9 | multi-recall |
| 72 | get(memory_id) | Memory | 9 | recognize |
| 73 | update(id, content) | Memory | 9 | consolidate |
| 74 | forget(memory_id) | Memory | 9 | retention retract |
| 75 | associate(src, dst, rel) | Memory | 9 | link memories |
| 76 | episodes(session_id=) | Memory | 9 | list episodes |
| 77 | justify(memory_id) | Memory | 9 | provenance chain |
| 78 | resolve(memory_id) | Memory | 9 | canonical head |
| 79 | summarise(ids) | Memory | 9 | summary belief |
| 80 | list_tools() / call_tool / initialize | McpClient | 10 | MCP core |
| 81 | query_knowledge / search_knowledge / explain_policy | McpClient | 10 | knowledge |
| 82 | list_entity_types / get_entities / search_entities | McpClient | 10 | discovery |
| 83 | get_domain_summary / find_in_social_corpus | McpClient | 10 | domain/social |
| 84 | lookup_identity / resolve_entity_identity | McpClient | 10 | identity |
| 85 | get_entity_profile / get_timeline / find_connections | McpClient | 10 | entity dossier |
| 86 | get_relationships / add_case_note / get_audit_trail | McpClient | 10 | case/audit |
| 87 | get_case_summary / investigate_entity / find_threats | McpClient | 10 | investigation |
| 88 | remember / recall / recognize / justify / consolidate / forget | McpClient | 10 | memory verbs |
| 89 | remember_procedure / recall_procedure / associate / resolve / summarise / episodes_in | McpClient | 10 | memory long tail |
| 90 | remember_batch / nl_query | McpClient | 10 | batch + NL |
| 91 | rag_store_answer / rag_store_elements / ingest_document | McpClient | 10 | RAG |
| 92 | hybrid_search / similar_multimodal / search_video_frames | McpClient | 10 | retrieval |
| 93 | ingest_media / face_match (gated) | McpClient | 10 | media |
| 94 | paths_between / detect_communities / rank_key_nodes | McpClient | 10 | graph |
| 95 | hub_authority / find_scc / predict_links / list_link_types | McpClient | 10 | graph long tail |
| 96 | trace_crypto / beneficial_ownership / reconstruct_wire / trace_hawala / screen_sanctions / geofence | McpClient | 10 | fincrime |
| 97 | import_sigma / list_rules / create_rule / list_jobs / schedule_job / job_status | McpClient | 10 | detection |
| 98 | list_workflows / run_workflow / workflow_status | McpClient | 10 | workflows |
| 99 | aggregate_stats / server_health / metrics / erase_subject | McpClient | 10 | ops/governance |
| 100 | erase_subject(subject, reason) | RelataClient | 12 | GDPR erasure |
| 101 | session_commit(id) / session_diff(id) / session_discard(id) | RelataClient | 12 | session mgmt |
| 102 | export_data(type, format=) | RelataClient | 12 | data export |
| 103 | cluster_nodes() | RelataClient | 14 | list nodes |
| 104 | cluster_topology() | RelataClient | 14 | topology |
| 105 | cluster_rebalance() | RelataClient | 14 | rebalance |
| 106 | cluster_drain(node_id) | RelataClient | 14 | drain |
| 107 | create_schema_branch(name, from) | RelataClient | 13 | create branch |
| 108 | delete_schema_branch(name) | RelataClient | 13 | delete branch |
| 109 | namespace(name).query(**) / .write(rows) / .get(id) / .delete_all() / .branch_from() | Namespace | 13 | retrieval surface |
| 110 | observe_stream() | RelataClient | 15 | SSE events |
| 111 | register_webhook(url, ...) / list_webhooks() / delete_webhook(id) | RelataClient | 15 | webhooks |
| 112 | query_flight(sql) | RelataClient | 15 | Arrow Flight |
| 113 | query_grpc(sql) / query_grpc_stream(sql) | RelataClient | 15 | gRPC query |
| 114 | audit_count() / AuditClient.count() | RelataClient / AuditClient | 16 | audit count |
| 115 | AuditClient.entries(**) / .find_by_request_id(...) | AuditClient | 16 | audit entries |
| 116 | AuditClient.export_pdf(case) / .sign_receipt(payload) | AuditClient | 16 | PDF + receipt |
76 RelataClient methods · 15 Memory methods · 69 MCP tools · 100% covered.
~30 lines of code per recipe. Zero external services. One binary.
Related Docs
- Quickstart — boot, ingest, query in 5 minutes
- Concepts: Governance — PURPOSE, ACL, cell masking
- Concepts: Agent Memory — the full memory model
- Concepts: Provenance — hash-chain, tamper-evidence
- Guides: Detection Rules — Sigma syntax support
- Reference: Graph Analytics — all 10+ operators
- SDK Reference — every class, every method
- Operation Nightwatch (Cyber SOC) — EDR threat hunting
Every captured response on this page was taken from a live RelataDB server. No mockups.