Rust · no server · one file per database
Ledgerstone is an embedded relational database in Rust. Tables, rows, and a genuine hand-written SQL parser and executor, all in a single readable crate with no server process and no C dependency.
runs fully client-side, a JS port of the same lexer, parser, and executor as the Rust crate, preloaded with a sample schema
Type a SQL statement in the box (or click a sample chip to load one), then press Run to execute it against the in-browser database, which is preloaded with a sample schema. Results and typed errors appear below; run CREATE TABLE, INSERT, SELECT, UPDATE, and DELETE just as you would against the Rust engine.
No page cache, no B-tree, no bytecode VM. Three layers doing exactly what their names say.
Every CREATE TABLE, INSERT, UPDATE, and DELETE is one length-prefixed JSON record appended to the database file. Opening a database replays the log into memory, the way a write-ahead log replays after a restart.
Each table keeps its live rows in a hash map keyed by row id. The log on disk is write-only once a database is open, every read goes through memory.
A lexer turns text into tokens, a recursive-descent parser turns tokens into a small statement tree, no grammar generator and no external SQL crate.
Columns declare INTEGER, TEXT, or REAL. Every INSERT and UPDATE is checked against that type before a row is written, and every error is a typed value, nothing in the engine panics on bad input.
The subset, in full
Same engine, on the command line
The exec subcommand runs one statement against a database file and prints the result: an aligned table for a SELECT, a row count for a write, and a typed error for anything malformed. This is a real session against a fresh database:
$ ledgerstone --db data.lst exec "CREATE TABLE users (id INTEGER, name TEXT, dept TEXT, score REAL)" OK $ ledgerstone --db data.lst exec "INSERT INTO users VALUES (1, 'alice', 'eng', 9.5)" 1 row(s) affected $ ledgerstone --db data.lst exec "SELECT name, score FROM users WHERE dept = 'eng' AND score >= 8.0 ORDER BY score DESC" name | score ------+------ alice | 9.5 carol | 8.75 (2 rows) $ ledgerstone --db data.lst exec "SELECT nope FROM users" error: not found: column 'nope' does not exist
ledgerstone --db data.lst opens a REPL, ledgerstone --db data.lst exec "SELECT ..." runs one statement and exits. State persists across runs.
ledgerstone --db data.lst serve exposes a local POST /query endpoint that takes raw SQL and returns JSON.
ledgerstone --db data.lst mcp runs a stdio MCP server with a single ledgerstone_query tool, so an agent can query the database directly.
Bad SQL, wrong types, and missing tables all come back as typed errors. A malformed query never brings the process down.
Ledgerstone is not trying to replace SQLite. It is trying to be the relational engine you can read in full. The comparison is about legibility, not coverage.
A superb, production embedded database, and a large C engine with a page cache, a B-tree store, and a bytecode VM underneath. You depend on it, you do not read it end to end.
A single readable Rust crate: an append-only typed log, an in-memory index, and a hand-written lexer, parser, and executor. No C dependency, no server, and small enough to open in an editor and follow start to finish.