Skip to content

The database

Multi-model without the magic

LibreDB is a small, readable, embeddable, multi-model database written in TypeScript. One ordered key-value kernel handles durability and transactions; key-value, document and relational are thin lenses over that one core rather than three engines bolted together. It runs in memory for tests or file-backed for durability, ships zero runtime dependencies, and proves its crash recovery instead of asserting it.

Early beta · MIT · zero dependencies · v0.2.2

Three lenses, one file

The same database, three shapes

Not three databases behind one wrapper — three typed views of a single store, in the same file, in the same transaction.

import { open, kv, doc, table } from "@libredb/libredb";

// In-memory for tests, or open({ path: "data.libredb" }) for a durable file.
const db = open();

// 1. Key-value: a durable, ordered, string-keyed map.
const cache = kv(db);
cache.set("user:1", "Ada");

// 2. Document: JSON documents under string ids.
const logs = doc(db, "logs");
logs.put("l1", { level: "info", message: "started", at: 1 });
logs.find({ level: "info" }).toArray();

// 3. Relational: a schema-validated, typed table.
const users = table(db, "users", {
  primaryKey: "id",
  columns: { id: "string", name: "string", age: "number" },
});
users.insert({ id: "1", name: "Ada", age: 36 });
users.where({ name: "Ada" }).select("id", "age").toArray();

db.close();

bun add @libredb/libredb — ESM only, zero runtime dependencies, targeting Bun and Node 22+. Also on JSR ↗ and any npm CDN, with a browser entry that imports nothing from node:.

Architecture

One core, three lenses

  • One kernel, not three engines

    A single ordered byte key-value kernel in one file. A relational table is physically a document collection, which is physically ordered key-value entries under composite keys like users:42 — so the three APIs cannot disagree about what is stored.

  • One filesystem seam

    The kernel reaches disk through a single injectable filesystem interface. That seam is what makes the browser build work with no node: imports, and it is the same seam the crash tests use to tear the log on command.

  • Readable on purpose

    The kernel is under a thousand lines, roughly half of it explanatory prose. The claim is not that it is short — it is that you can open it and learn how a database actually works.

  • Nothing hidden

    Queries are plain in-engine scans, errors surface rather than being swallowed, and the costs are visible: O(n) scans and no secret indexes. Under 6 kB min+brotli, ESM only, full types shipped.

Reliability

Crash recovery you can check for yourself

  • Committed means fsynced

    A transaction that returns has been written to a length-framed, CRC-32-checksummed write-ahead log and fsynced before the commit becomes visible. On a healthy disk a committed write survives a crash, and a crash can only damage the last un-fsynced record — which recovery detects, truncates and reports.

  • The dirty failures are handled, not assumed away

    A failed append or fsync latches the database rather than writing past a torn record. A second writer is refused by an exclusive lock instead of silently corrupting the file. A file that is not a LibreDB database is refused untouched via its LRDB header, mid-log corruption refuses to open rather than quietly truncating, and a short read is an IO error, never data loss.

  • Proven, not asserted

    The crash and recovery path is exercised by deterministic simulation testing — the real engine against a seeded in-memory filesystem that tears, corrupts, errors and crashes the log on command — plus a binary round-trip fuzz, on top of 100% line coverage of the core.

The precise durability contract and a walkthrough of the simulation harness are in the repository. RELIABILITY.md ↗

Fit

What it is for, and what it refuses

These limits are deliberate v1 scope, not gaps left quiet. The project's own position is that its strength comes from what it declines to do — so the second list is published at the same size as the first.

Reach for it when you want to

  • Backing tests and local development with a real durable store instead of mocks.
  • Embedding a small database directly in a TypeScript, Bun or Node app with zero infrastructure.
  • Prototyping across key-value, document and relational shapes without standing up three systems.
  • Learning how a database works by reading — and changing — a small, honest codebase.

Do not use it yet when you need

  • A hardened production datastore at scale. It is an early beta; the beachhead is test and dev.
  • Secondary indexes or a query planner — a find or where is an O(n) scan by design in v1.
  • Concurrent multi-process access, replication, or a networked client/server. It is embedded, in-process and strictly single-writer.
  • SQL wire compatibility or an existing driver ecosystem.

The manifesto ↗ · Source on GitHub ↗ · npm ↗ · Run it in your browser →