It stores nodes, relationships and attributes in compact, purpose-built binary files, optimised for SSD. The defining design constraint:
An element id decodes arithmetically into the exact file and byte offset of its record. Reading an element by id costs zero index probes and zero extra seeks.
No lookup. No probe. No seek beyond the one the read itself performs:
long byteOffset = dataStart(kind) + (long) slot(id) * stride(kind);
What it is
Durability |
A write-ahead log. Every commit is fsynced before it touches a data file, so uncommitted data cannot reach disk and recovery is a single redo pass. |
Query language |
An openCypher subset that reads and writes, in its own module, plus a single-jar server and a React and D3 browser. |
Transactions |
|
Isolation |
One writer at a time; many concurrent readers. A reader gets MVCC snapshot isolation — a view fixed at one instant across many reads, while the writer keeps committing. Reads take no lock and scale to 10x at 32 threads. No concurrent writers. |
Indexes |
Label and property B+trees, declared composite indexes, unique constraints, and an HNSW index for nearest-neighbour search over vector properties. |
Space reclamation |
|
Native layer |
Rust via FFM. io_uring is 12.4x over a |
Operations |
Prometheus metrics, a slow query log, container images and a Helm chart, and an MCP server so an agent can query the graph. |
What it is not
Stated plainly, because a database page that implies capabilities the system lacks is a liability.
-
No concurrent writers. Writers serialise on one lock. That is a property of the storage engine, not a configuration, and it is why the Helm chart refuses more than one replica.
-
No backup mechanism. A cold copy of a stopped store is the only correct procedure today.
-
No authentication on any port. Not the HTTP port, not the binary port, not the monitoring port.
-
No
OPTIONAL MATCH, and several other openCypher constructs — each refused by name at parse time rather than silently doing something else. -
No repair tool for a store that fails
verify.
At scale
Ten million nodes and twenty million random relationships load in 11 to 25 minutes and occupy 6.1 GiB.
Reopening the finished store takes 1 ms; nodeLabel runs at 4.2M/s over it and neighbours at 1.1M/s.
The load figure is a range because it was measured four times with the same command and the edge phase varied 2.5x across the runs while the resulting store stayed identical to the byte. The benchmarks page has every run and what is known about the spread.
Where to go next
Ids, segments, records, and how the address function works |
|
The openCypher subset, and what is deliberately not in it |
|
The built-in Cypher editor and graph view |
|
The embedded Java API and the binary streaming port |
|
Sizing, tuning, configuration, the slow query log, security posture |
|
The Prometheus catalogue and the alert rules it implies |
|
Letting an AI client query the graph |
|
Container images and the Helm chart |
|
Measured numbers, with their caveats |