FFS is a Rust database engine for graph, vector, and write-back workloads.
Typed records, relationship tables, embedding indexes, and provenance share one pager, one WAL, and one transaction boundary.
Every benchmark on this site reproduces from one cargo command, losses included.
Thirty seconds
$ cargo run -p ffs-demo --release ✓ pager one file, 8 KiB pages, MVCC/WAL, catalog root ✓ schema Person, KNOWS edge, Embedding(dim=4) ✓ rel table 6 KNOWS edges, A+ double CSR persisted ✓ primary index on-disk B+-tree, 5 nodes ✓ Cypher MATCH (n:Person) WHERE n.age > 30 RETURN count(n) → 3 ✓ GNN sampler 2-hop batch over 5 anchors in 47 µs ✓ provenance derived embedding written back with source link ✓ recovery crash, reopen, replay, equal state
Every line runs in the same engine, against the same file. The traversal on line 5, the GNN sample on line 6, and the provenance write on line 7 sit inside one transaction — if line 7 fails, lines 5 and 6 don't commit.
Measured
| Path | ffs | comparator | Result |
|---|---|---|---|
| B+-tree insert, random u64 | 429 ns | sled · 1241 ns | ffs 2.89x |
| HNSW query, 0.966 recall@10 | 113 µs | instant-distance · 298 µs | ffs 2.64x |
| 1-hop neighbour read | 11 ns | petgraph · 102 ns | ffs 9.28x |
| CSR edge append | 30 ns | petgraph · 3 ns | petgraph 9.64x |
| 15-seed BFS, depth 4, 35K nodes | 14 ms | per-seed driver loop · 68.8 s | ffs 4858x |
One harness, one command, Apple M-series. The append row is a loss by design — gap-preserving CSR pays at write time and collects on every read. Every number, with caveats.
The loop
// vector candidates, then graph context MATCH (d:Dispute) WHERE d.embedding NEAR $claim TOP 5 MATCH (c:Customer)-[:FILED]->(d) RETURN c.tier, d.amount, d.status // the decision goes back through the same qlog MATCH (d:Dispute {id: $id}) SET d.triage = 'manual_review', d.matched_pattern = $pattern
This is the workload FFS is shaped around: an agent retrieves by embedding, expands through relationships, decides, and writes the decision back durably. In the usual stack those steps span Postgres, a vector store, and application glue. Here they span one transaction boundary.
Why this code exists
Modern heavy workloads do not just store records. They scan, retrieve, traverse, embed, derive, and write back continuously. Today that usually means a relational database for facts, a graph database for relationships, a similarity engine for embeddings, a queue for events, and application code keeping the IDs and indexes aligned.
At scale, each boundary adds latency, duplicate indexes amplify writes, and a cross-system query means serializing rows and joining them on IDs in application code. Most failures happen in that glue.
FFS brings the data path into one engine: one buffer pool, one WAL, one fsync discipline per commit. A graph update, a vector upsert, and the typed fact they support land in the same commit; if it aborts, none of them happened.
And yes, the name means what you think it means. It is what you say the third time the relational store, the vector index, and the graph database disagree about one record.
What it runs
ERP.ai, Neo Agent, Proto. The shared data path for thousands of apps and hundreds of thousands of agents: app state, agent memory, tenant context, graph relationships, vector retrieval, provenance, query history, and durable write-back in one engine.
Agent fleet query layer. Runtime reads for large agent fleets asking what an app knows, which entities are connected, what changed, which source produced it, and what should be retrieved next.
Operational write-back. ERP.ai-style workflows where approvals, disputes, procurement, finance, customer records, tool calls, and model outputs continuously read, derive, index, and write back state.
All six production use cases →
Install
$ curl -fsSL https://ffsdb.com/install.sh | sh $ ffsd --db ./my.ffs --create $ printf 'PING\n' | nc 127.0.0.1 7767 OK pong
The script builds ffsd from a checkout when run inside
one, otherwise clones the repo — ssh first, then https, prompts
disabled — and ends in cargo install --path ffsd --locked.
FFS is in private preview, so the clone needs access to the
repository. The daemon answers line-oriented commands:
PING, QUERY, LOAD_MANIFEST,
SNAPSHOT, VERIFY, and the rest of
HELP.
Status
v1 — done. The single-node disk engine shipped as
v1.0.0 on 2026-06-13 and is the default: data and reads serve from the
pager-backed engine, the working set is bounded by the unflushed tail,
opens are checkpoint-anchored and replay only the log suffix, and one
kill-switch (FFS_SPINE_LEGACY=1) restores the pre-cut engine
for a release. The typed query pipeline covers its read surface and a
four-stage mutation ladder, each shape proven against the live engine.
v2 storage — done. A database checkpoints one
consistent generation to an S3-compatible object store and restores any
retained generation, or attaches to one read-only with data pages served
by ranged GETs rather than a whole-image download. ffs-s3
speaks SigV4 to AWS S3, MinIO, and R2, with multipart upload for large
objects. Many readers share one checkpoint prefix while one local writer
keeps writing; checkpoint pins hold an attached generation against
retention while a reader is on it, attach is lazy, and ffsd
drains and exits cleanly on a signal.
v2 compute — now (2.0.0-alpha.1). The typed
columnar pipeline is reaching read parity with the in-memory evaluator so
reads can serve from it. Property and aggregate projections, scalar and
numeric functions, arithmetic, and collect/list results now
lower over anchor and joined columns. Measured against the product read
surface, the typed path matches the live engine on every shape it lowers
and falls back to it on the rest — zero divergence — the bar
the read flip waits on.
Later. Typed mutations and a planner-driven physical path; the catalog and the product surfaces (control plane, studio, flow); read sharing and distribution across attached compute. Then the larger-scale proof ladder — the first beyond-RAM rungs, then 10GB, 100GB, and up.