You're reading the v2.0.0 docs. View the latest (v2.2.0) →

Protocol Compatibility

RelataDB speaks 8 compatibility doors plus 5 native protocols (13 wire surfaces total) from one binary and one governed store. Any existing client library that speaks one of the compat doors works without the Relata SDK. Backed by ADR-163–ADR-170.

All compat doors bind to 127.0.0.1 and share one credential: RELATA_BEARER_TOKEN. Doors auto-enable when RELATA_BEARER_TOKEN is set (any tier); an explicit RELATA_<DOOR>_ENABLE=true|false overrides. With no token and no explicit enable, doors stay off by default (an unauthenticated port is never auto-exposed). pgwire refuses to start without a token. On non-free profiles, an explicit =true without a token fails closed, and RELATA_TENANCY_MODE=multi refuses the tenant-less shared-token doors (use pgwire, which carries per-connection org).

Cross-protocol consistency: write via S3, Redis, or MongoDB and read the same data back over SQL, pgwire, or any other surface. ACL, org isolation, and the audit log apply on every door uniformly (ADR-170).

Protocol matrix

DoorADRPort env var (default)Writes to governed store?
S3 (AWS / boto3 / rclone)164, 170RELATA_S3_PORT (9191)Yes — S3Object, S3Bucket
Postgres + pgvector165RELATA_PG_PORT (5433)Yes — typed rows + _emb_text vectors
ClickHouse HTTP166RELATA_CLICKHOUSE_PORT (8123)Read-only (governed SELECT)
ClickHouse native TCPRELATA_CH_NATIVE_PORT (9000)Read-only
Neo4j HTTP Cypher167RELATA_NEO4J_PORT (7474)Yes — CREATE/MERGE via governed write door (#1090)
Neo4j BoltRELATA_BOLT_PORT (7687)Yes — CREATE/MERGE via governed write door (#1090)
Redis RESP168, 170RELATA_REDIS_PORT (6379)Yes — KvEntry
MongoDB wire169, 170RELATA_MONGO_PORT (27017)Yes — MongoDocument

Native Relata protocols (always on):

DoorDefault portNotes
HTTP REST9090/query, /ingest, /search, /memory/*, /mcp, /health, /status, /metrics, /types, /specs, /sparql, /watch/stream
gRPC50051gRPC door (ADR-073)
Arrow Flight8815Zero-copy columnar streaming; enable with RELATA_FLIGHT_ENABLE=true
MCP/mcp on HTTPModel Context Protocol tools
SPARQL/sparql on HTTPSingle Basic Graph Pattern over KnowledgeTriple + optional LIMIT

Start the doors

export RELATA_BEARER_TOKEN=<your-strong-token>
 
RELATA_S3_PORT=9191 \
RELATA_PG_PORT=5433 \
RELATA_CLICKHOUSE_PORT=8123 \
RELATA_NEO4J_PORT=7474 \
RELATA_REDIS_PORT=6379 \
RELATA_MONGO_PORT=27017 \
relata serve

A single end-to-end smoke test for all six compat doors: scripts/protocol_smoke_test.py.

S3 — boto3 / aws CLI / rclone / MinIO client

Supported operations: ListBuckets, CreateBucket / HeadBucket / DeleteBucket, ListObjectsV2, GetBucketLocation, PutObject / GetObject / DeleteObject / HeadObject, and multipart upload.

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",
    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")
body = s3.get_object(Bucket="cases", Key="exhibit-1.txt")["Body"].read()
print(body)

When RELATA_S3_SECRET_KEY is set the door requires verified SigV4 and rejects plaintext bearer auth.

Read S3 objects over SQL

SELECT key, size, content_hash FROM S3Object WHERE bucket = 'cases';

Limits

  • Buckets must be empty to delete.
  • Multipart parts are in-memory only (lost on restart).
  • ETag is SHA-256.
  • Object bodies ≥ RELATA_S3_BLOB_THRESHOLD_MB (default 4 MiB) spill to the content-addressed blob store.

Postgres + pgvector — psql / psycopg2 / LangChain PGVector

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 (TablePlus, DBeaver, pgAdmin, DataGrip) connect and browse schema via the catalog intercept.

KNN operatorMetricNote
&lt;=>cosine distancePreferred — ANN index is cosine-only
&lt;->L2 distanceMetric-correct via over-fetch + re-rank
&lt;#>negative inner productMetric-correct via over-fetch + re-rank

pgwire is fail-closed: refuses to start when RELATA_BEARER_TOKEN is unset.

ClickHouse — HTTP or native TCP

# HTTP
curl -X POST "http://127.0.0.1:8123/?query=SELECT+1" \
  -H "X-ClickHouse-Key: change-me"
 
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")
rows = ch.execute("SELECT name FROM Person LIMIT 5")
print(rows)

Read-only. The door routes governed SELECTs through the planner; writes are not supported.

Neo4j — HTTP Cypher or Bolt

# 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 supported:

  • Relationship path patterns including bounded -[r*1..5]->
  • Single-identifier RETURN [AS alias]
  • Whitelisted property predicates

Typed labels (n:Person) and typed edges [:KNOWS] are parsed but ignored. Read Cypher only.

Redis — any RESP client

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

Governed keys persist as KvEntry rows. Read back over SQL:

SELECT key, value FROM KvEntry WHERE key = 'foo';

Not supported: MULTI/EXEC, BLPOP, scripting, cluster commands. Pub/Sub is in-memory only (zero persistence).

MongoDB — any Mongo wire client

const { MongoClient } = require("mongodb");
const c = new MongoClient("mongodb://localhost:27017", {
  auth: { username: "relata", password: "change-me" },
});
const db = c.db("cases");
await db.collection("exhibits").insertOne({ _id: "ex1", body: "hello" });
console.log(await db.collection("exhibits").findOne({ _id: "ex1" }));

Governed docs persist as MongoDocument rows. Read back over SQL:

SELECT * FROM MongoDocument WHERE collection = 'exhibits';
LimitDetail
AuthSCRAM-SHA-256 only
maxWireVersion17
Transactions / change streamsNot supported
$push / $pull / $unsetNot supported
Nested equalityVia flattened columns only

Arrow Flight — zero-copy streaming

# Enable on the server
RELATA_FLIGHT_ENABLE=true relata serve

Connect any Arrow Flight client (Python pyarrow.flight, etc.) to grpc://localhost:8815:

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 format — no JSON intermediate, no string serialisation. Use for high-throughput columnar reads.

SPARQL

# GET
curl "http://localhost:9090/sparql?query=SELECT+%3Fs+%3Fp+%3Fo+WHERE+%7B+%3Fs+%3Fp+%3Fo+%7D+LIMIT+10" \
  -H "Authorization: Bearer $RELATA_BEARER_TOKEN"
 
# POST
curl -X POST http://localhost:9090/sparql \
  -H "Content-Type: application/sparql-query" \
  -H "Authorization: Bearer $RELATA_BEARER_TOKEN" \
  -d "SELECT ?s ?p ?o WHERE { ?s ?p ?o } LIMIT 10"

Single Basic Graph Pattern over KnowledgeTriple. Optional LIMIT. Both GET and POST verify bearer auth.

Security notes

  • All doors bind to 127.0.0.1.
  • All doors share RELATA_BEARER_TOKEN.
  • pgwire is fail-closed (refuses to start without a token).
  • All other doors default to open dev mode when the token is unset.
  • Egress filtering (ADR-057) applies uniformly on every door.
  • Cross-protocol reads and writes go through the same planner with the same ACL, org isolation, and audit chain.

See also