FileDB v2 stores each collection as append-only NDJSON — human-readable, one JSON object per line. Get a durable document store with gRPC + REST from a single Go binary, or embed the engine directly in your program. No daemon, no external runtime, no hidden state.
$ filedb serve --data ./data --api-key dev-key & $ filedb-cli insert users '{"name":"alice","age":30}' --api-key dev-key ✓ id=01J8... key=01J8... rev=1 $ filedb-cli find users '{"field":"name","op":"eq","value":"alice"}' ▶ index hit users.name (1 row scanned, 1 returned) {"name": "alice", "age": 30} $ tail -1 data/users/000001.ndjson # it's just text on disk {"id":"01J8...","key":"01J8...","rev":1,"data":{...},"crc":"a1b2"}
Why FileDB
When you need a real document store but standing up PostgreSQL or MongoDB is
overkill — and you'd like to still be able to cat your data.
Every collection is plain NDJSON — one JSON object per line. Inspect, grep, back up, or migrate your data with any text editor. No binary format, no hidden state.
Inserts, updates, and deletes are always new lines. Choose your fsync policy, and a CRC32C checksum on every entry catches silent on-disk bit-rot on read.
A dual API served from one binary — plus a `go get`-able storage engine you can run in-process, with zero gRPC/Prometheus/OTel dependencies.
No JVM, no Python, no daemon zoo, no config file to get started. One small Go binary that serves, and one CLI that talks to it over a Unix socket.
How it works
Writes append to a segment log; a background compactor merges and dedups sealed segments; an in-memory id index and per-field secondary indexes keep reads fast.
Records carry a key, a
monotonic rev, and a crc. A limited Find is
bounded by the page size, not the collection size, and streams rows as it
reads — with keyset pagination that covers every row exactly once.
Plus a generated OpenAPI spec
for any other language.
Trade throughput against your crash-loss
window with --sync. The embedded façade defaults to interval
(~1s).
A follower bootstraps from a snapshot then
tails the leader's committed writes by global LSN — resumable, no gaps or
dupes. Bound staleness by diffing applied vs leader LSN.
Two binaries, three ways to run
Run filedb serve and get gRPC on :5433 and REST on :8080 from one binary. Talk to it from any language, or from the CLI over a Unix socket.
Skip the server entirely — go get the engine and run the database in-process. Keyed CRUD, CAS, secondary indexes, and in-process Watch.
Pull ghcr.io/srjn45/filedbv2 or docker compose up. Data persists in a named volume; REST and gRPC ports exposed and ready.
The API surface
Keyed CRUD with caller-supplied string keys, insert-or-replace upsert, and
revision-checked compare-and-swap — plus streaming queries, aggregations, TTL, optimistic
transactions, and live Watch subscriptions.
Quickstart
# start the server (or: docker compose up -d) filedb serve --data ./data --api-key dev-key # insert, then query — via the CLI over the Unix socket filedb-cli insert users '{"name":"alice","age":30}' --api-key dev-key filedb-cli find users '{"field":"age","op":"gt","value":18}' --api-key dev-key # or embed it directly in Go — no server at all go get github.com/srjn45/filedbv2/filedb