The database you never outgrow.

Keep real SQLite as your app grows: replication, failover, offline device sync, and a database for every user and every AI agent. Sirannon embeds in Node.js, Bun, the browser, and React Native, or answers HTTP and WebSocket behind its own server, all from a single npm package.

Pick a driver, open a database, and query it

Parameterised queries and typed results behave the same on an embedded file as on a replicated cluster, so the code you start with is the code you deploy.

One driver API covers Node.js, Bun, the browser, and React Native, which is how the same database opens in all four. The bundled server answers HTTP and WebSocket from your clients, and nodes replicate to each other over gRPC.

Read the getting started guide
pnpm add -E @delali/sirannon-db
database.ts
import { Sirannon } from '@delali/sirannon-db'
import { betterSqlite3 } from '@delali/sirannon-db/driver/better-sqlite3'
 
const driver = betterSqlite3()
const sirannon = new Sirannon({ driver })
const db = await sirannon.open('app', './data/app.db')
 
await db.execute(
  'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)'
)
await db.execute('INSERT INTO users (name, email) VALUES (?, ?)', [
  'Ada',
  'ada@example.com',
])
 
const users = await db.query<{ id: number; name: string }>('SELECT * FROM users')

The same database from your laptop to your cluster

One library opens a file on your laptop, serves that same file over HTTP and WebSocket, and replicates it across nodes that fail over through a coordinator. Your query code stays the same through all three. What changes as you grow is the configuration, never the application.

The line that opens a database is identical in every one of the three deployments.

One codebasethree deployments
const db = await sirannon.open('app', path)

Laptop

one file on disk

app.db

Server

HTTP and WebSocket

4 readers

Cluster

three nodes with failover

coordinator
The line above stays the same in all threeno rewrite

Restore a terabyte to any moment, however little memory the machine has

Sirannon copies the whole database once, then on every run after that it sends only the write-ahead log frames written since the previous one. Every backup after the first therefore moves only the writes since the last one, never the terabyte again. You name the moment you want back, and Sirannon rebuilds the database from that chain.

The restore fetches one piece and applies it before it asks for the next, which keeps the memory it holds at one piece however large the database it rebuilds.

Backup chainrestore to 14:02
02:00Full copy1.0 TB
06:00WAL frames412 MB
10:00WAL frames96 MB
14:00WAL frames233 MB
14:02Rebuilt to this momentone piece in memory
Every piece up to 14:02 replays in orderrestoreBackup

A database for every customer and every AI agent

A resolver maps any identifier to a file path, so creating a database means opening a file and your code makes no provisioning call. The lifecycle manager then holds the open count under your cap by closing idle handles and evicting the least recently used one.

Migrations apply to each database as it opens, so a tenant added long after the last schema change still opens on the current schema.

Instancescustomers and AI agents

brightwood-legal

data/tenants/brightwood-legal.db

4 connsActive

drafting-agent-04

data/ai-agents/drafting-agent-04.db

2 connsActive

patel-associates

data/tenants/patel-associates.db

12 minIdle

volta-tours

data/tenants/volta-tours.db

freedEvicted
Opening a file is what creates oneLRU eviction
See what you can build

One dependency covers the whole database

Drivers, migrations, backups, change capture, the network server, device sync, and replication install together as one package.

Pluggable drivers
One API covers better-sqlite3, the Node.js built-in driver, wa-sqlite in the browser, Bun, and Expo on React Native.
Registered operations
A server refuses SQL from the network by default, which limits a browser or an AI agent to the reads and writes you registered by name.
Change capture and live queries
Clients subscribe to row-level changes as they commit, and a live query sends its results again whenever the rows behind it change.
Offline device sync
A device keeps its own database file, writes to it with no network, and reconciles with the server through the conflict rules replication uses.
Replication and failover
Primary-owned changes replicate over gRPC with hybrid logical clocks, and coordinator mode fails over automatically when a primary goes down.
Migrations, backups, and hardening
Versioned migrations load from SQL files, continuous backups rebuild a database to any moment, and the core enforces parameterised queries and path checks.

Measured against the server most applications would otherwise use

A recorded run drove Sirannon and PostgreSQL through their own clients on one machine, at ten million rows, each engine on dedicated cores under the same memory ceiling.

4.00x

Point-select rate

Sirannon held 64.0K point-selects per second against PostgreSQL on 16.0K, with every commit fsynced.

16.00x

YCSB-A rate

On the mix of half reads and half updates, Sirannon held 16.0K per second against PostgreSQL on 1.0K.

6.2 ms

Point-select tail latency

Sirannon held p99 at 6.2 ms while sustaining 64.0K point-selects per second, and every operating point had to hold p99 under 25 ms.

Read the full benchmark results

Start building with Sirannon

The documentation walks you from installation to production configuration.