> Complete content index: https://sondelali.com/llms.txt

# Sirannon

> Turn any SQLite database into a distributed data layer with live queries, offline-first device sync, multi-node replication, and coordinator-backed failover.

URL: https://sondelali.com/projects/sirannon-db
Category: Technology

<Frame>
  <img src="/images/projects/sirannon-db/banner.png" alt="Sirannon: distributed data layer that embeds in your process or runs as a replicated database server" loading="eager" />
</Frame>

Sirannon turns a local SQLite database into a distributed data layer: local <Tooltip tip="ACID names the four guarantees a reliable database gives each transaction: atomicity, consistency, isolation, and durability. Together they mean every change either completes fully or leaves no trace.">ACID</Tooltip> queries, trigger-based change data capture, live queries that stay current, offline-first device sync, primary-replica replication, coordinator-backed failover, and client access over HTTP or WebSocket. The [documentation site](https://sirannon.sondelali.com) covers every feature, and the [source](https://github.com/assetcorp/sirannon-db) is on GitHub.

The production path is explicit. One authorised primary accepts every write, while replicas serve reads and can forward a write to that primary. Coordinator mode uses primary terms, in-sync sets, and fail-closed promotion rules, so write authority stays clear during a failure.

## What you get

<Steps>
  <Step title="Connection pooling and WAL mode">
    Sirannon opens one write connection and N read connections, four of them by default, and it enables <Tooltip tip="WAL stands for write-ahead logging. SQLite appends each change to a separate log before applying it to the main database, which lets readers keep reading while a write is in progress.">WAL</Tooltip> mode so that reads carry on while a write is in progress.
  </Step>
  <Step title="Real-time change data capture">
    It tracks INSERT, UPDATE, and DELETE events through triggers and polling, and a client can subscribe to a table's changes over WebSocket.
  </Step>
  <Step title="Live queries">
    It keeps a query result current from those change events, applying each change to the rows it already holds instead of running the read again. The same result works locally, over a WebSocket, and through React hooks.
  </Step>
  <Step title="Registered operations instead of open SQL">
    By default the server accepts only named operations from the network. Server-side code registers each read and write under a name and fills the owner and tenant arguments from the authenticated identity, while a code generator emits the typed references a client calls that operation through.
  </Step>
  <Step title="Offline-first device sync">
    Sirannon keeps an end-user device's local database in step with a server in both directions. A device stages every pulled change before it applies it, resolves conflicts on both sides, replaces its database from a snapshot when it falls too far behind, and reconciles schema through a migration handshake.
  </Step>
  <Step title="Primary-replica replication">
    It replicates <Tooltip tip="An HLC, or hybrid logical clock, is a timestamp that combines wall-clock time with a counter so that events on different machines can be put in a consistent order.">HLC</Tooltip>-stamped change batches from one primary to the read replicas over pluggable transports, including gRPC with TLS.
  </Step>
  <Step title="Coordinator-backed failover">
    Coordinator mode uses etcd authority, primary terms, in-sync sets, and majority write concern. A partition fails closed for writes whenever Sirannon cannot prove which node is the safe primary.
  </Step>
  <Step title="Repair-time conflict resolution">
    An explicit repair resolves divergent rows with LWW, PrimaryWins, or FieldMerge, since the primary serialises every normal write.
  </Step>
  <Step title="Migrations and scheduled backups">
    You can write a migration as a file or in code, and roll it back either way. Snapshot backups run on a schedule and rotate, so a copy of the data always survives.
  </Step>
  <Step title="Multi-tenant lifecycle management">
    You can run many database instances at once, with idle timeouts and LRU eviction between them. Each tenant holds its own isolated resources, which Sirannon provisions and tears down cleanly.
  </Step>
</Steps>

<Callout type="note" title="Runs across runtimes">
  Pluggable drivers cover better-sqlite3, Node.js 22+ built-in SQLite, wa-sqlite (browser via IndexedDB), Bun, and Expo. The server layer uses uWebSockets.js with authentication hooks for network access.
</Callout>

## What this means

- Sirannon replicates HLC-stamped change batches between nodes, which is a different mechanism from sharing one SQLite file over a network file system.
- Between server nodes, conflict resolution belongs to explicit repair and disaster recovery, because one primary serialises every normal write. Between a device and a server, it runs on every sync, in both directions.
- A network client calls a named operation by default, so a browser or a mobile application sends only that name and its arguments, and the server sets the tenant and owner itself.
- Coordinator mode fails closed under an unsafe partition, and it promotes only a replica it has proved to be in sync.
- DDL replication runs against an allowlist, and SQLite's own constraints still apply on every node.

## Where this is going

By its own estimate, SQLite is used more than all other database engines combined, and yet the ecosystem around it usually treats it as a local-only tool. Sirannon's production shape today covers both halves of that. On the server side it is a distributed primary-replica database layer, which writes through the current primary, replicates to read replicas, routes clients across nodes, and fails closed when the cluster cannot prove where write authority lies. On the device side, the sync path is built and specified. A phone or a browser holds its own SQLite database, works offline, pushes what it wrote once it reconnects, pulls everyone else's writes live, and settles conflicts by the same rule at both ends.

What remains open is the middle ground between the two. Server-side replication still serialises writes through one primary, so a node that accepts writes while partitioned has no supported path back into the group. Extending device sync's offline write semantics to full nodes needs clear guarantees about causality, constraints, and operator control, and that is the work ahead. Edge nodes serving reads close to users are the other direction, and topology-aware routing with read concerns is the first piece of it.
