Storage
The framework ships its own embedded database — a crash-safe, copy-on-write B+tree in a single file — so an application persists data with no separate database server. It's pure Rust, zero dependencies.
The engine
crates/storage is the bedrock: a single-file pager of fixed 4 KiB pages, a B+tree over byte keys, and atomic commits. Durability comes from the design, not luck:
- Every mutation copies the path from leaf to root into fresh pages, leaving the
old tree intact.
- A commit
fsyncs the new pages, then atomically swaps the root pointer in a
double-buffered, checksummed meta page, and fsyncs again.
- A crash mid-commit leaves the previous committed tree intact — the model
proven by LMDB and redb. No write-ahead log.
The record store
On top of the tree, the CLI exposes a tiny append-and-list record store used by the live /guestbook:
- Records are JSON values, keyed by a monotonic 64-bit id (big-endian), so the
tree's key order is insertion order.
append(record)writes and commits — the record is durable the moment it
returns.
list()range-scans every record, oldest-first.
The store opens once at startup and is shared behind a mutex (the B+tree is a single writer).
Where data lives
Records live in the project's data/ directory (data/store.db), a sibling of frontend/. It is created on first run and is never part of the shipped binary or the deployed assets — so changing data never requires a rebuild, and data survives both a server restart and a redeploy. (Principle: data updates without a rebuild.)
What's next
This append/list store is the seed of the per-collection storage that the declarative collections.toml schema and the auto-generated API will grow into: typed records, indexes, filters, and ?search= semantic search.