Compatibility & Doors — bring your existing client
RelataDB is a server that speaks your existing database's wire protocol. Your existing clients connect to Relata; Relata does not connect to your existing databases. You keep your driver, your ORM, your GUI tool — you just repoint the host/port and use your bearer token as the password.
One binary speaks 13 wire surfaces from one governed store: 8 compatibility doors (MongoDB, Postgres + pgvector, Redis, Neo4j HTTP, Bolt, ClickHouse HTTP, ClickHouse native, S3) plus 5 native protocols (HTTP REST, gRPC, Arrow Flight, MCP, SPARQL). Write through any door, read back through any other — ACL, audit, and provenance apply uniformly.
New here? If you already run MongoDB / Postgres / Redis / Neo4j / ClickHouse / an S3 client, you can adopt Relata without rewriting your app. This page is the whole "how" — port table, connection strings, enable flags, and a 3-step quickstart per protocol. The deep wire-protocol reference lives at Protocol Compatibility.
The one-line architecture
your existing client ──► RelataDB (speaks the wire protocol) ──► one governed bi-temporal store
(Mongo / psql / (ACL + audit + provenance
redis-cli / Neo4j / on every write/read,
ClickHouse / boto3 / uniformly across doors)
pyarrow Flight)
Three things Relata is not, and people often guess one of them:
- Not a sync from your existing DB. Relata doesn't poll your Mongo/Postgres. Your clients speak to Relata instead. (For a one-time migration out of an existing DB into Relata, use
relata import --from— Postgres is live, Mongo/Neo4j/ClickHouse are honest stubs today.) - Not an ETL engine. Relata's signed binary has zero network dependencies. Polling/OAuth/vendor-SDK fetching lives in the external
datagrepETL tool that pushes governed rows in. See Connectors & Extensions. - Not a query federation layer. Relata doesn't forward your Mongo query to a hidden Mongo — it is the Mongo. Same for the other doors.
Three adoption patterns — pick one
| Pattern | What you do | When |
|---|---|---|
| Drop-in (door) | Repoint your existing client at Relata's port. Keep your code. | You have a working app and want governance/provenance/history without a rewrite. |
| Direct (SDK) | Use the Relata SDK (Python / TypeScript / Go) for first-class SQL, graph, memory, and identity verbs. | Greenfield, or you want the full surface (MCP, Memory, hybrid search, AML ops). |
| Mixed | Door for legacy writes (your Mongo app), SDK/SQL for new analytics reads. | Migration-in-place: existing app keeps writing via the door, new features read via SQL/SDK. |
The drop-in door and the SDK read and write the same governed rows. Pick per-workflow, not per-project.
The full port + credential table
Every door shares one credential: your RELATA_BEARER_TOKEN. It's the password for every protocol below (for S3 SigV4, the secret key defaults to the same token unless you set RELATA_S3_SECRET_KEY).
| Protocol | Enable flag | Port var (default) | Bind var (default) | Default state |
|---|---|---|---|---|
| MongoDB wire | RELATA_MONGO_ENABLE | RELATA_MONGO_PORT (27017) | RELATA_MONGO_BIND (127.0.0.1) | auto-enable on token |
| Postgres + pgvector | (token required) | RELATA_PG_PORT (5433) | RELATA_PG_BIND (127.0.0.1) | fail-closed without token |
| Redis RESP | RELATA_REDIS_ENABLE | RELATA_REDIS_PORT (6379) | RELATA_REDIS_BIND (127.0.0.1) | auto-enable on token |
| Neo4j HTTP (Cypher) | RELATA_NEO4J_ENABLE | RELATA_NEO4J_PORT (7474) | RELATA_NEO4J_BIND (127.0.0.1) | auto-enable on token |
| Neo4j Bolt | RELATA_BOLT_ENABLE | RELATA_BOLT_PORT (7687) | RELATA_BOLT_BIND (127.0.0.1) | auto-enable on token |
| ClickHouse HTTP | RELATA_CLICKHOUSE_ENABLE | RELATA_CLICKHOUSE_PORT (8123) | RELATA_CLICKHOUSE_BIND (127.0.0.1) | auto-enable on token, read-only |
| ClickHouse native TCP | RELATA_CLICKHOUSE_NATIVE_ENABLE | RELATA_CH_NATIVE_PORT (9000) | RELATA_CH_NATIVE_BIND (127.0.0.1) | auto-enable on token, read-only |
| S3 (boto3 / aws CLI / rclone / MinIO) | RELATA_S3_ENABLE | RELATA_S3_PORT (9191) | RELATA_S3_BIND (127.0.0.1) | auto-enable on token |
| Arrow Flight | RELATA_FLIGHT_ENABLE | RELATA_FLIGHT_PORT (8815) | RELATA_FLIGHT_BIND (127.0.0.1) | opt-in |
| HTTP REST | (always on) | RELATA_PORT (9090) | RELATA_HTTP_BIND (profile-scoped) | always on |
| gRPC | (always on) | RELATA_GRPC_PORT (50051) | RELATA_GRPC_BIND (profile-scoped) | always on |
| MCP | (always on) | /mcp on HTTP | — | always on |
| SPARQL | (always on) | /sparql on HTTP | — | always on |
Door enable rules (all profiles, no license gating):
- Off by default. Doors auto-enable when
RELATA_BEARER_TOKENis set (an unauthenticated port is never auto-exposed). RELATA_<DOOR>_ENABLE=trueforces a door on;=falseforces it off (overrides auto-enable).- pgwire is fail-closed: refuses to start without a token, period.
- On
server/cluster,RELATA_<DOOR>_ENABLE=truewithout a token fails closed. - Every door honors
RELATA_<DOOR>_BINDas a plain override on every profile — set0.0.0.0to reach Relata from another container/host/pod (see Deploying doors). No license needed.
3-step quickstarts (one per protocol)
The pattern is identical every time: (1) set your bearer token, (2) let the door auto-enable (or set RELATA_<DOOR>_ENABLE=true), (3) point your client at the port and authenticate with the token.
MongoDB
RELATA_BEARER_TOKEN=change-me relata serve
# Mongo door auto-enables. Default port 27017.const { MongoClient } = require("mongodb");
const c = new MongoClient("mongodb://localhost:27017", {
auth: { username: "relata", password: "change-me" }, // password = RELATA_BEARER_TOKEN
});
const db = c.db("cases");
await db.collection("exhibits").insertOne({ _id: "ex1", body: "hello" });
console.log(await db.collection("exhibits").findOne({ _id: "ex1" }));Read the same doc back over SQL:
SELECT * FROM MongoDocument WHERE collection = 'exhibits';Limits: SCRAM-SHA-256 auth, maxWireVersion 17, no transactions / change streams / $push / $pull / $unset, nested equality via flattened columns. See Protocol reference.
Postgres + pgvector (psql / psycopg2 / LangChain PGVector / DBeaver / TablePlus)
RELATA_BEARER_TOKEN=change-me relata serve
# pgwire auto-starts on 5433 (token required — fail-closed without one).psql -h 127.0.0.1 -p 5433 -U relata relata
# password = RELATA_BEARER_TOKENCREATE EXTENSION vector;
CREATE TABLE docs (id text PRIMARY KEY, embedding vector(3));
INSERT INTO docs VALUES ('a', '[1,0,0]'), ('b', '[0.9,0.1,0]');
-- Cosine KNN — auto-routed to Relata's HNSW index
SELECT id FROM docs ORDER BY embedding <=> '[0.9,0.1,0]' LIMIT 2;INSERT/UPDATE/DELETE and ordinary SELECT work; GUI clients browse schema via the catalog intercept. KNN ops: <=> cosine (preferred, ANN-native), <-> L2, <#> negative inner product (both metric-correct via over-fetch + re-rank).
Redis (any RESP client)
RELATA_BEARER_TOKEN=change-me relata serveredis-cli -h 127.0.0.1 -p 6379 -a change-me SET foo bar
redis-cli -h 127.0.0.1 -p 6379 -a change-me GET fooKeys persist as governed KvEntry rows; read back over SQL (SELECT key, value FROM KvEntry WHERE key = 'foo'). Unsupported: MULTI/EXEC, BLPOP, scripting, cluster commands. Pub/Sub is in-memory only.
Neo4j (HTTP Cypher or Bolt)
RELATA_BEARER_TOKEN=change-me relata serve# HTTP Cypher
curl -X POST http://neo4j:change-me@127.0.0.1:7474/db/neo4j/tx/commit \
-H "Content-Type: application/json" \
-d '{"statements":[{"statement":"MATCH (n) RETURN n LIMIT 5"}]}'from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://127.0.0.1:7687", auth=("neo4j", "change-me"))
with driver.session() as s:
print(s.run("MATCH (n) RETURN n LIMIT 5").data())Cypher subset: relationship path patterns incl. bounded -[r*1..5]->, single-identifier RETURN [AS alias], whitelisted property predicates. Typed labels (n:Person) and typed edges [:KNOWS] are parsed but ignored.
ClickHouse (HTTP or native TCP — read-only)
RELATA_BEARER_TOKEN=change-me relata serve# HTTP
curl -X POST http://127.0.0.1:8123/ \
-H "X-ClickHouse-Key: change-me" \
--data-binary "SELECT name FROM Person FORMAT JSONEachRow"from clickhouse_driver import Client
ch = Client(host="127.0.0.1", port=9000, password="change-me")
print(ch.execute("SELECT name FROM Person LIMIT 5"))Read-only — governed SELECTs routed through the planner. Writes are not supported over this door.
S3 (boto3 / aws CLI / rclone / MinIO client)
RELATA_BEARER_TOKEN=change-me relata serveimport boto3
from botocore.config import Config
s3 = boto3.client(
"s3",
endpoint_url="http://127.0.0.1:9191",
aws_access_key_id="change-me",
aws_secret_access_key="unused", # SigV4 secret defaults to RELATA_BEARER_TOKEN
config=Config(signature_version="s3v4", s3={"addressing_style": "path"}),
)
s3.create_bucket(Bucket="cases")
s3.put_object(Bucket="cases", Key="exhibit-1.txt", Body=b"hello world")
print(s3.get_object(Bucket="cases", Key="exhibit-1.txt")["Body"].read())Supported: ListBuckets, Create/Head/DeleteBucket, ListObjectsV2, GetBucketLocation, Put/Get/Delete/HeadObject, multipart upload. ETag is SHA-256. Bodies ≥ RELATA_S3_BLOB_THRESHOLD_MB (default 4 MiB) spill to the content-addressed blob store.
Arrow Flight (zero-copy columnar streaming)
RELATA_FLIGHT_ENABLE=true RELATA_BEARER_TOKEN=change-me relata serveimport pyarrow.flight as fl
client = fl.connect("grpc://localhost:8815")
reader = client.do_get(fl.FlightDescriptor.for_command(
b"PURPOSE 'analytics' SELECT * FROM Person"))
for batch in reader:
print(batch.data.num_rows, "rows")Arrow IPC — no JSON intermediate, no string serialisation. Use for high-throughput columnar reads.
Cross-protocol consistency (the actual win)
Write via S3, read over SQL. Write via Mongo, read over pgvector. Write via Redis, query the graph. It's the same store — ACL, org isolation, and the tamper-evident audit chain apply on every door, on every read and every write. You're not keeping two databases in sync; you have one database with thirteen front doors.
-- Wrote via Mongo? Read here:
SELECT * FROM MongoDocument WHERE collection = 'exhibits';
-- Wrote via Redis? Read here:
SELECT key, value FROM KvEntry WHERE key = 'foo';
-- Wrote via S3? Read here:
SELECT key, size, content_hash FROM S3Object WHERE bucket = 'cases';Which path should I pick?
- "I have a working Mongo/Postgres/Redis/Neo4j app and want governance + history + provenance." → Drop-in door. Repoint the client; you're done.
- "I'm starting fresh." → SDK (Python / TypeScript / Go) for the full surface — SQL + graph + memory + MCP + hybrid search + AML/intel operators.
- "I want to migrate data out of my existing DB into Relata once." →
relata import --from postgres(live; Mongo/Neo4j/ClickHouse are documented stubs — use the door for ongoing traffic meanwhile). - "I want Relata to ingest from Kafka / MISP / TAXII / sanctions feeds." → Native ingest doors — see Ingestion & SmartIngest.
- "I'm running a multi-node cluster — do I need
mongodb+srv:/// a seed list?" → No. Relata is smart-server, dumb-client: point your client at one load balancer / KubernetesServicein front of the cluster and the coordinator fans out internally. See Connecting clients to a cluster.
See also
- Protocol Compatibility (deep reference) — full per-protocol semantics, limits, and design notes
- Deploying Protocol Doors — bind vars, Docker
-ppublishing, KubernetescontainerPort, cross-host reachability - Environment Variables — the canonical door env-var table
- Connectors & Extensions — the ETL/extension framework (different from wire doors) and
relata import - Limits & Caveats — per-protocol status and known gaps