Embeddable & disaggregated · Rust · Postgres-wire compatible

The database that is embeddable and disaggregated at once.

Twill DB is a serverless OLTP engine — a SQLite-style Rust library at function-call latency, whose storage backend is pluggable. Point it at a local file to run purely embedded, or at object storage to run storage-disaggregated and scale-to-zero. Same engine, same C ABI; the only difference is a connection string.

1
engine, two delivery modes
in-proc
embedded hot path (no socket)
→0
idle compute cost
O(1)
copy-on-write branch creation
Why it exists

Two properties that usually exclude each other

Embeddability wants the engine to be your application's address space. Disaggregated storage wants durable state to live over the network so compute is stateless. Twill DB resolves the tension by moving the seam inward.

📦

Truly embeddable

Links in-process as a native library over a frozen C ABI. The hot path is a function call, never a network round-trip — SQLite-style ergonomics, link it anywhere Bun, Node, or C can call.

☁️

Storage-disaggregated

Durability bottoms out on object storage (S3 / Cloudflare R2 / MinIO) via an LSM page store and an S3-CAS commit log. Compute holds no durable state, so it is disposable.

🔌

One pluggable seam

The engine never touches disk — it talks to a single narrow Storage trait. The backend is chosen by URL scheme: file:// embeds, s3:// disaggregates. No rebuild.

🌿

Instant branching

A branch is a cheap LSN pointer over shared immutable layers (copy-on-write). Fork a database in O(1); writes diverge into a private overlay and never touch the base.

💤

Scale-to-zero

The lifecycle controller cold-starts on first connection and tears down when idle. At rest you pay only for object-storage bytes — no idle compute bill.

🐘

Postgres-wire compatible

The same engine runs behind a Postgres-wire listener. psql, Bun.sql, pgbench, and PostgREST connect unchanged — no bespoke driver, no bespoke REST layer.

🧭

Vector search in-core

A vector(N) type and an HNSW index live behind the same storage seam as the rows — so vector search branches and scales-to-zero with the database. An agent can fork its memory.

🧊

Snapshot isolation

MVCC throughout: every reader sees a stable snapshot and never blocks a writer, while a first-committer-wins check keeps concurrent transactions correct. One writer per database, lock-free reads.

🛡️

Crash-safe durability

Commits are WAL-centric: every acked write is fsync-durable before the call returns — never acked from a buffer. Recovery replays the log deterministically and discards a torn trailing frame.

Embedded in seconds

Open a database with a connection string

Build the native library once, then use the typed Bun wrapper. The backend is selected purely by the URL scheme — flip file:// to s3:// and the same code path becomes disaggregated and scale-to-zero, with no recompile.

Branching is a first-class operation: a branch sees the base's data and writes in isolation.

notes.ts
import { open } from "@twilldb/bun";

using db = open("file://./local.db");   // or "s3://bucket/db"
db.exec(`CREATE TABLE notes (id INTEGER PRIMARY KEY, body TEXT)`);
db.query("INSERT INTO notes VALUES (?, ?)", [1, "hello"]);

const rows = db.query("SELECT id, body FROM notes");
// [{ id: "1", body: "hello" }]

// Instant copy-on-write branch: sees the base, writes in isolation.
using preview = db.branch("preview");
preview.exec("INSERT INTO notes VALUES (2, 'branch-only')");
// the base never sees the branch's write
server.sh
# The same engine, behind a Postgres-wire listener
cargo run -p twill-server -- \
  --listen 127.0.0.1:5433 \
  --db file://./srv.db          # or s3://bucket/db

# Any Postgres client connects (cleartext):
psql "host=127.0.0.1 port=5433 user=postgres sslmode=disable"
Server mode falls out for free

Wrap the library in a wire listener

Server mode is the same library wrapped in a Postgres-wire listener — there is no second engine to maintain. Multi-client access, existing Postgres tooling, and connection pooling all come along for the ride.

Whether a caller reaches the engine through an FFI call or a network socket is the only thing that changes.

Where to go next

Four doors into the project

Use the database, follow the design intent, track what shipped and what's next, or meet the person behind it.

"SQLite that forgot where its file was — and found it on S3."

Keep the in-process, link-it-anywhere ergonomics; point the storage backend at the network instead of a local file. Embeddable and disaggregated stop being contradictory the moment the seam is a pluggable backend rather than a server you connect to.