Skip to content

HTTP server

The namidb-server binary opens a NamiDB namespace and exposes it over a small REST API. It’s the same engine as the embedded library; all this binary adds is the HTTP boundary, bearer-token auth, a periodic flush loop, and an opt-in Bolt listener.

See Install for the install command (cargo install --path crates/namidb-server) or the Docker image.

Run

Terminal window
namidb-server \
--store "s3://my-bucket/data?ns=prod&region=us-east-1" \
--listen 0.0.0.0:8080 \
--auth-token "$NAMIDB_AUTH_TOKEN" \
--flush-interval 30s

Every flag can also be set via env vars (NAMIDB_STORE, NAMIDB_LISTEN, NAMIDB_AUTH_TOKEN, NAMIDB_FLUSH_INTERVAL, NAMIDB_BOLT_LISTEN). The --store URI follows the same grammar as the Python client and the CLI — see Storage backends.

If you don’t pass --auth-token, the server boots in unauthenticated mode and prints a loud warning. Don’t expose that port to the public internet.

Endpoints (v0)

MethodPathAuthDescription
GET/v0/healthpublicLiveness + manifest version + epoch
GET/v0/versionpublicServer build version
POST/v0/cypherbearerRun a Cypher query (read or write)
POST/v0/admin/flushbearerForce a memtable to L0 SST flush

POST /v0/cypher

Request:

{
"query": "MATCH (p:Person) WHERE p.age >= $min RETURN p.name AS name",
"params": {"min": 18}
}

Response (read):

{
"columns": ["name"],
"rows": [{"name": "Alice"}, {"name": "Bob"}]
}

Response (write):

{
"columns": ["a"],
"rows": [{"a": {"_kind": "node", "id": "...", "label": "Person", "properties": {}}}],
"write_outcome": {
"nodes_created": 1,
"edges_created": 0,
"nodes_deleted": 0,
"edges_deleted": 0,
"properties_set": 0
}
}

End-to-end curl round-trip

Terminal window
TOKEN=$(openssl rand -hex 32)
namidb-server --store memory://demo --listen 127.0.0.1:8080 --auth-token "$TOKEN" &
curl -s http://127.0.0.1:8080/v0/health | jq .
curl -s -X POST http://127.0.0.1:8080/v0/cypher \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"query": "CREATE (a:Person {name: \"Alice\", age: 30}) RETURN a.name AS name"}' \
| jq .
curl -s -X POST http://127.0.0.1:8080/v0/cypher \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"query": "MATCH (p:Person) RETURN p.name AS name, p.age AS age"}' \
| jq .

Concurrency

namidb-server keeps one WriterSession per process. Writes are serialised behind a single-writer-per-namespace invariant (RFC-001): at most one write statement is in flight against the namespace at a time. Reads no longer take the writer mutex — a snapshot cell (RFC-021) publishes the latest durable view, multiple readers share it through an Arc, and a write refreshes the cell after each commit_batch / flush. The integration test (crates/namidb-server/tests/concurrent_reads.rs) measures a ~7x fan-out at 8 readers on a 4-core box.

If you need horizontal scale today, point several namidb-server processes at the same --store URI. Each one serves reads off the same manifest version, and only one is allowed to commit writes (the rest get fenced via epoch CAS).

Periodic flush

--flush-interval (default 30s) controls how often the background task turns the memtable into L0 SSTs. Set it to 0s to disable the loop and call POST /v0/admin/flush from cron or a sidecar instead.

Bolt protocol

Pass --bolt-listen 0.0.0.0:7687 (or NAMIDB_BOLT_LISTEN) to expose a Bolt 4.4 / 5.0 / 5.4 listener alongside the HTTP API. Both protocols share the same WriterSession, the same auth token, and the same single-writer-per-namespace invariant.

Terminal window
namidb-server \
--store memory://demo \
--listen 0.0.0.0:8080 \
--bolt-listen 0.0.0.0:7687 \
--auth-token "$NAMIDB_AUTH_TOKEN"
from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://localhost:7687",
auth=("namidb", "$NAMIDB_AUTH_TOKEN"))
with driver.session() as s:
s.run("CREATE (:Person {name: 'Alice'})")
for r in s.run("MATCH (p:Person) RETURN p.name AS name"):
print(r["name"])

The Bolt username is the literal string namidb; the password is the same bearer token. See Bolt (Neo4j drivers) for the driver matrix and RFC-022 for the wire-level design.

On the roadmap

The server README lists these endpoints as planned but not landed yet:

  • /v0/cypher/stream — NDJSON streaming for large read result sets.
  • /v0/cypher/arrow — an Arrow IPC body for zero-copy DataFrame ingestion.
  • /v0/metrics — Prometheus exposition (counters, latency histogram, cache hit rates).

What’s next