Compatibility & Doors — bring your existing client

One engine, many protocolsNative surfaces (psql, gRPC, HTTP, MCP, Arrow Flight, SPARQL) and compatibility doors (S3, ClickHouse, Neo4j, Redis, MongoDB, Bolt, pgvector) all funnel through the gateway into a single governed engine.NATIVE SURFACESCOMPAT DOORSpsql (pgwire)gRPCHTTPMCPArrow FlightSPARQLS3ClickHouseNeo4jRedisMongoDBBoltpgvectorGatewayauthenticatetenant contextPURPOSE stampACL + egress filterGoverned Enginerelational · graphvector · full-texthybrid searchidentity · provenancebi-temporal storage

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:

  1. 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.)
  2. Not an ETL engine. Relata's signed binary has zero network dependencies. Polling/OAuth/vendor-SDK fetching lives in the external datagrep ETL tool that pushes governed rows in. See Connectors & Extensions.
  3. 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

PatternWhat you doWhen
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).
MixedDoor 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).

ProtocolEnable flagPort var (default)Bind var (default)Default state
MongoDB wireRELATA_MONGO_ENABLERELATA_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 RESPRELATA_REDIS_ENABLERELATA_REDIS_PORT (6379)RELATA_REDIS_BIND (127.0.0.1)auto-enable on token
Neo4j HTTP (Cypher)RELATA_NEO4J_ENABLERELATA_NEO4J_PORT (7474)RELATA_NEO4J_BIND (127.0.0.1)auto-enable on token
Neo4j BoltRELATA_BOLT_ENABLERELATA_BOLT_PORT (7687)RELATA_BOLT_BIND (127.0.0.1)auto-enable on token
ClickHouse HTTPRELATA_CLICKHOUSE_ENABLERELATA_CLICKHOUSE_PORT (8123)RELATA_CLICKHOUSE_BIND (127.0.0.1)auto-enable on token, read-only
ClickHouse native TCPRELATA_CLICKHOUSE_NATIVE_ENABLERELATA_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_ENABLERELATA_S3_PORT (9191)RELATA_S3_BIND (127.0.0.1)auto-enable on token
Arrow FlightRELATA_FLIGHT_ENABLERELATA_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 HTTPalways on
SPARQL(always on)/sparql on HTTPalways on

Door enable rules (all profiles, no license gating):

  • Off by default. Doors auto-enable when RELATA_BEARER_TOKEN is set (an unauthenticated port is never auto-exposed).
  • RELATA_<DOOR>_ENABLE=true forces a door on; =false forces it off (overrides auto-enable).
  • pgwire is fail-closed: refuses to start without a token, period.
  • On server/cluster, RELATA_<DOOR>_ENABLE=true without a token fails closed.
  • Every door honors RELATA_<DOOR>_BIND as a plain override on every profile — set 0.0.0.0 to 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_TOKEN
CREATE 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 serve
redis-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 foo

Keys 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 serve
import 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 serve
import 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 / Kubernetes Service in front of the cluster and the coordinator fans out internally. See Connecting clients to a cluster.

See also