Ledgerstone logo

Rust · no server · one file per database

A database engine
you can read start to finish.

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.

Try it in your browser View on GitHub

runs fully client-side, a JS port of the same lexer, parser, and executor as the Rust crate, preloaded with a sample schema

How to use this playground

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.

Storage, parsing, and execution, each small enough to read.

No page cache, no B-tree, no bytecode VM. Three layers doing exactly what their names say.

Append-log storage

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.

In-memory index

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.

Hand-written SQL

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.

Typed everywhere

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

Everything below runs today.

Same engine, on the command line

Real rows, from the real binary.

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

Built to be run, not just read.

CLI

ledgerstone --db data.lst opens a REPL, ledgerstone --db data.lst exec "SELECT ..." runs one statement and exits. State persists across runs.

HTTP API

ledgerstone --db data.lst serve exposes a local POST /query endpoint that takes raw SQL and returns JSON.

MCP server

ledgerstone --db data.lst mcp runs a stdio MCP server with a single ledgerstone_query tool, so an agent can query the database directly.

No panics

Bad SQL, wrong types, and missing tables all come back as typed errors. A malformed query never brings the process down.

How it compares

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.

SQLite

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.

Ledgerstone

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.