Skip to content

libredb/libredb-database

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LibreDB

LibreDB - Multi-model without the magic. One core, three lenses, every line tested.

Multi-model without the magic. One core, three lenses, every line tested.

npm version JSR Docker Hub CI Quality Gate Coverage license types: included dependencies: 0 bundle size status: pre-alpha Ask DeepWiki

LibreDB is a small, readable, embeddable, multi-model database written in TypeScript. It is built on one idea: a database can be powerful and still be understood by opening its source. A single ordered key-value core handles durability and transactions; key-value, document, and relational APIs are thin lenses over that one core — not three separate engines. It runs in-memory for tests or file-backed for durability, ships zero runtime dependencies, and proves its crash recovery with deterministic simulation testing. Today it is pre-alpha, aimed at test and development environments — small enough to learn how a database actually works, and serious enough to grow into more.

Highlights

  • One small core, three lenses — key-value, document, and relational over a single ordered key-value engine (FoundationDB-style), not three engines bolted together.
  • Multi-model — raw strings, JSON documents, and schema-validated typed tables in the same database, even the same file.
  • Readable by design — the kernel is under 600 lines; open the source and learn how a database actually works.
  • Embeddable, zero dependenciesbun add @libredb/libredb and go; nothing else to install or run.
  • In-memory or durableopen() for tests, open({ path }) for a crash-safe, WAL-backed, fsync-on-commit file.
  • TypeScript-native — full types shipped, ESM-only, tree-shakeable, under 4 kB min+brotli.
  • Crash recovery you can trust — 100% line coverage on the core, plus deterministic simulation testing that tortures the write-ahead log under a simulated crashing filesystem.
  • Nothing hidden — queries are plain in-engine scans, errors surface, and costs are obvious (O(n) scans, no secret indexes).

Quick start

bun add @libredb/libredb
# or: npm install @libredb/libredb

LibreDB is ESM-only, ships zero runtime dependencies, and targets Bun (the development runtime) and Node 22+. The same database speaks all three lenses — here they are in one file:

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

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

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

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

// 3. Relational: a schema-validated, typed table with where / select / join.
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(); // [{ id: "1", age: 36 }]

db.close();

Each lens has its own guide: key-value · document · relational · catalog.

Install elsewhere: JSR, CDN, and the browser

LibreDB is the same ESM-only package everywhere; only how you reach it changes.

JSR — published to jsr.io alongside npm:

bunx jsr add @libredb/libredb
# or: npx jsr add @libredb/libredb / deno add jsr:@libredb/libredb

CDN — every release is served from the npm registry by the usual CDNs. Pin a version:

import { open, kv } from "https://esm.sh/@libredb/libredb@0.1.3";

Browser — a dedicated entry that imports nothing from node:, so it bundles for the browser cleanly. Its open carries no default filesystem: an in-memory database works anywhere, and a path-backed open takes a filesystem you inject (e.g. the OPFS adapter shown below).

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

const db = open(); // in-memory
kv(db).set("greeting", "hello");

For durable storage in the browser, run inside a Web Worker and back the database with an OPFS sync access handle (the kernel stays synchronous — no async core):

import { open, opfsFileSystem } from "@libredb/libredb/browser";

const root = await navigator.storage.getDirectory();
const file = await root.getFileHandle("app.libredb", { create: true });
const handle = await file.createSyncAccessHandle();
const db = open({ path: "app.libredb", fs: opfsFileSystem(handle) });

A browser-targeting bundler also resolves the browser build from the main @libredb/libredb entry via the package's browser export condition. Note that TypeScript usually still resolves the Node types for that entry (where fs is optional) unless it is configured for the browser condition (customConditions). To get the browser-specific typing — fs required when path is given — and keep types in step with the runtime, import the explicit @libredb/libredb/browser subpath.

Using LibreDB in a web app with no backend? The full guide — in-memory vs durable (OPFS) storage, the Web Worker pattern, and React / Vite / Next.js / Astro setup — is in docs/BROWSER.md.

Command-line tool

The package ships a libredb bin for inspecting and editing .libredb files — no code required:

npx libredb inspect data.libredb          # namespaces, kinds, and table schemas
npx libredb stats data.libredb            # file size and namespace counts
npx libredb get data.libredb user:1       # print one value
npx libredb scan data.libredb user:       # print key=value under a prefix
npx libredb set data.libredb user:1 Ada   # set a key
npx libredb delete data.libredb user:1    # remove a key
npx libredb import data.libredb seed.json # bulk-set from a JSON object, atomically

Read commands open the file read-only, so inspection never mutates it. Write commands take an advisory <path>.lock to refuse a second concurrent writer. Use --force only to clear a stale lock left by a crashed writer: the lock is advisory and LibreDB is single-process, so two writers that force at the same time can still race and corrupt the file.

Prefer a standalone binary with no Node or Bun installed? Each release attaches self-contained executables (Linux, macOS, Windows; x64 and arm64) with .sha256 checksums on its GitHub Release. Or build one locally with bun run compile.

Or run the CLI from a container (multi-arch, published to GHCR and Docker Hub) — mount your data and pass a command:

docker run --rm -v "$PWD:/data" ghcr.io/libredb/libredb inspect /data/app.libredb
# or from Docker Hub: docker run --rm -v "$PWD:/data" libredb/libredb inspect /data/app.libredb

The same multi-arch image is published to both registries: Docker Hub and GHCR. It is a CLI shell, not a server: LibreDB stays an embedded, in-process database.

Full references: the CLI (every command, safety model, exit codes), the standalone binaries (download + verify), and the Docker image.

How it works: one core, three lenses

One core, three lenses: kv, document, and relational APIs over a single ordered key-value core (core.ts), reaching disk through one FileSystem seam.

LibreDB has a single ordered byte key-value kernel (src/core.ts). Key-value, document, and relational are thin typed lenses over it — three faces of the same store. A relational table is physically a JSON document collection, which is physically ordered key-value entries built from composite keys like users:42. The kernel reaches the disk through one injectable filesystem seam, which is also what makes deterministic crash testing possible.

flowchart TB
    subgraph consumers [Consumers]
        App[Your app]
        Studio[LibreDB Studio]
        Platform[LibreDB Platform]
    end

    API["Public API · index.ts<br/>open · kv · doc · table · catalog"]

    subgraph lenses [Lenses and shared edges - open, fast to contribute]
        KV[kv<br/>strings]
        DOC[document<br/>JSON]
        REL[relational<br/>typed tables]
        CAT[catalog<br/>self-describing registry]
    end

    CORE["core.ts - THE KERNEL<br/>ordered byte KV · serializable txns · WAL · crash recovery"]
    FS["FileSystem seam<br/>node:fs adapter - or SimFS for crash tests"]

    App --> API
    Studio --> API
    Platform --> API
    API --> KV
    API --> DOC
    API --> REL
    API --> CAT
    REL --> DOC
    KV --> CORE
    DOC --> CORE
    REL --> CORE
    CAT --> CORE
    CORE --> FS
Loading
Open at the edges, guarded at the core: lenses, query surface, catalog, adapters, Studio, and docs are open to contribute; core.ts is guarded with 100% line coverage, deterministic crash tests, and heavy review - everything reaches the store through one narrow transact() port.

The file boundary is the trust boundary. Below the line (core.ts) is guarded: heavy review and deterministic crash tests, because a bug there corrupts data. Above the line (lenses, query, catalog) is open and fast to contribute to, because the worst a bug can do is present a bad view — it reaches the store only through one narrow transact port. For the full tour, read ARCHITECTURE.md.

When to use LibreDB

Reach for it when you want to:

  • Back tests and local development with a real, durable, multi-model store instead of mocks.
  • Embed a small database directly in a TypeScript / Bun / Node app with zero infrastructure.
  • Learn how a database works by reading — and hacking — a small, honest codebase.
  • Prototype across key-value, document, and relational shapes without standing up three systems.

Do not use it (yet) when you need:

  • A hardened production datastore at scale — it is pre-alpha; today's beachhead is test/dev.
  • Secondary indexes or a query planner — queries are O(n) scans by design in v1 (on the roadmap).
  • Concurrent multi-process access, replication, or a networked client/server — it is embedded and in-process.
  • SQL wire compatibility or an existing-driver ecosystem.

These limits are deliberate v1 scope, not hidden gaps — LibreDB's strength comes from what it refuses. See the Manifesto.

Reliability

Crash recovery you can trust: a length-framed, CRC-32-checksummed write-ahead log fsync'd before commit; recovery truncates the last un-fsync'd record so what remains is always a valid committed prefix - proven by deterministic simulation testing.

A transaction that returns has been written to a length-framed, CRC-32-checksummed write-ahead log and fsync'd before the commit becomes visible — so a committed write survives a crash, and a crash can only ever damage the last, un-fsync'd record (which recovery detects and truncates). This is not just asserted: the kernel's crash/recovery path is proven by deterministic simulation testing, running the real engine against a seeded in-memory filesystem that tears, corrupts, and crashes the log on command, then checking that recovery is always a valid committed prefix.

bun run test    # includes a bounded 50-seed DST run

The full durability and DST walkthrough is in docs/RELIABILITY.md.

Documentation

Topic Where
Lens guides (kv, document, relational, catalog) docs/guides/
Browser — embed in a web app with no backend (in-memory + OPFS) docs/BROWSER.md
CLI — inspect and edit .libredb files (npx libredb) docs/CLI.md
Standalone binaries — download and run, no Node/Bun docs/BINARY.md
Docker — run the CLI from a container docs/DOCKER.md
Architecture — the guided tour under the hood ARCHITECTURE.md
Design — the locked engineering decisions docs/DESIGN.md
Reliability — durability and crash recovery docs/RELIABILITY.md
Manifesto — what LibreDB is and refuses to be MANIFESTO.md
LibreDB Studio integration docs/STUDIO.md

Project status & roadmap

LibreDB is pre-alpha (0.0.x). The architecture is in place and every line of the core is tested, but the API may still change and it is not yet meant for production data.

  • Done: the ordered key-value kernel (transactions, WAL, crash recovery); the key-value, document, and relational lenses; the self-describing catalog; the deterministic simulation testing harness; 100% line/function/statement coverage on the core.
  • Next: secondary indexes and a richer query surface; more query operators; additional lenses; production-hardening milestones (directory fsync on first create, WAL compaction/checkpointing).

The LibreDB family

The LibreDB family: LibreDB (the database, this repo), LibreDB Studio (the open-source IDE for every database), and LibreDB Platform (the managed, team-oriented form of data) - three products, one access-model spine.

LibreDB is the database in a three-product family that shares one access-model spine:

  • LibreDB — the database (this repository).
  • LibreDB Studio — the open-source IDE for every database (Postgres, MySQL, MongoDB, Redis, and more). LibreDB is one database it supports natively, not a requirement.
  • LibreDB Platform — the managed, team-oriented form of data.

Contributing

Contributions are welcome. LibreDB is open at the edges and guarded at the durability core — see CONTRIBUTING.md for how to get set up, the bun run gate bar every change must pass, and where contributions land fastest. Please also read the CODE_OF_CONDUCT.md.

Security

To report a security vulnerability, see SECURITY.md — please do not open a public issue.

License

Open source and free under the MIT License.

About

LibreDB is a small, readable, embeddable, multi-model database. One ordered key-value core, thin model lenses on top.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages