> Fetch the index of every Tonbo docs page as Markdown at https://tonbo.dev/llms.txt

# Guarantees

Last updated 29 October 2026

Read this when you need to know what Tonbo guarantees for a database: when a commit is durable, and what happens when your program moves, runs twice or loses the network.

## What is guaranteed

| When | What you can rely on |
| --- | --- |
| A statement returns | The commit is on this machine. It survives the process crashing, not the loss of the machine or its disk. |
| `synced()` resolves | Every commit made before the call is durable: two of three replicas, each in its own zone of the region, hold it, and older data moves to object storage in the same region. It survives the loss of the machine, of a replica or of a whole zone. |
| The program moves | Any machine opens the database by name and finds every commit `synced()` confirmed. |
| Two instances run | Only the last one to open the database writes it. The commits of the other never reach Tonbo, and its next `synced()` says so. |
| The network drops | Commits continue on the machine and `synced()` waits, until 64 MiB of compressed commits wait; then writes fail and commit nothing. |
| Always | The data stays in the region it was created in. |

```text
  YOUR MACHINE: a laptop, a server, a sandbox
  +--------------------------------------------------+
  |  your process                                    |
  |    node:sqlite --commit--> the local copy        |   t0  the statement returns,
  |                            on this disk          |       at local speed
  +-------------------------+------------------------+
                            |  in the background, over HTTPS
                            v
  TONBO, in the database's region: three replicas, one per zone
  +--------------+       +--------------+       +--------------+
  |    zone a    |<----->|    zone b    |<----->|    zone c    |
  +------+-------+       +------+-------+       +------+-------+
         |                      |                      |      t1  two of three hold it:
         +----------------------+----------------------+          synced() resolves
                                |  in the background
                                v
                      +-------------------+
                      |  object storage,  |                     t2  older snapshots and
                      |  the same region  |                         commits move here
                      +---------+---------+
                                |  opening by name where there is no copy:
                                v  the latest snapshot, then the commits after it
  ANOTHER MACHINE, any time later
  +--------------------------------------------------+
  |  your process --> the local copy, rebuilt. It is |
  |                   now the writer, and the one    |
  |                   before it is refused           |
  +--------------------------------------------------+
```

## When a commit is durable

A database is a local copy on your machine and its data at Tonbo. A commit moves from one to the other in the background, and it is durable once it reaches Tonbo:

| When | Where the commit is | What it survives |
| --- | --- | --- |
| The statement returns | The local copy and a log on the same disk | The process crashing: the next open on this machine sends it. Not yet the loss of the machine or its disk |
| `synced()` resolves | Two of three replicas, one per availability zone, and later object storage in the region | Durable: the loss of the machine, of a replica or of a whole zone |

So await `synced()` before anything outside the process acts on a commit, such as a reply, an acknowledgement or a payment. [Durability](https://tonbo.dev/docs/databases/durability.md) says where each region keeps the replicas and how maintenance pauses them. On your machine, the package’s [SQLite extension](https://tonbo.dev/docs/databases/internals/sqlite-extension.md) logs each commit before the statement returns, and [replication](https://tonbo.dev/docs/databases/internals/replication.md) sends it to Tonbo.

A process killed with its disk, such as a sandbox that is thrown away, can lose every commit since its last resolved `synced()`. So where a step must survive the machine, await `synced()` after it, not only before the process exits:

```js
db.prepare("INSERT INTO steps(body) VALUES (?)").run(result);
await db.synced(); // from here, losing the machine does not lose this step
```

## When the program moves

Open the database by name on another machine. Where there is no local copy, the open downloads the latest snapshot of the database and the commits after it; where a copy is left, it only catches up on what it missed. The process that writes the database takes a snapshot whenever enough commits follow the last one, so the commits to replay stay few (see [Cold starts](https://tonbo.dev/docs/databases/cold-starts.md) for how long a rebuild takes, and [Snapshots and S3](https://tonbo.dev/docs/databases/internals/snapshots.md) for how).

## When two instances run

One process writes a database at a time: the last one to open it. A restarted agent that opens its database becomes its writer, even while the old instance is still running:

```text
  instance A                 Tonbo                  instance B
  ----------                 -----                  ----------
  commit, synced() ------->  A writes the database
                             B writes the database <------ open("agent-42")
  commit (local only)
  synced()  <-- refused ---  A's commits are refused
  UrsulaResetError                                  carries on from every
  (kind fenced): stop                               commit A synced
```

The old instance’s later commits may still succeed on its own machine, but they never reach Tonbo: its next `synced()` throws `UrsulaResetError` with the kind `fenced`, and its statements fail once it learns that it no longer writes the database. This stops writes to the database, not the old instance’s calls to other services, so an agent that acts outside the process awaits `synced()` right before it acts: that call also checks that the instance still writes the database (see [Run side effects once](https://tonbo.dev/docs/databases/side-effects.md)). Inside the writing process, use as many connections as you like (see [Connections](https://tonbo.dev/docs/databases/connect.md#connections)). The refusal is called fencing, and [Ownership and fencing](https://tonbo.dev/docs/databases/internals/ownership.md) shows how it works.

## When the network drops

Statements keep committing on the machine, and `synced()` waits until Tonbo takes the commits; neither throws for a network failure. `db.status().replication` says why commits wait. Once 64 MiB of compressed commits wait, writes fail with `backlog_full` and commit nothing, until Tonbo catches up (see [The backlog](https://tonbo.dev/docs/databases/internals/replication.md#backlog)).

## Where the data lives

A database lives in one AWS region, fixed when it is created: its replicas run in three availability zones of that region, and its older snapshots and commits move to object storage in the same region. Every `synced()` waits for a round trip to the region, so create a database close to where its program runs (see [Regions and URLs](https://tonbo.dev/docs/databases/manage.md#regions)). To take a database elsewhere, or keep a copy to go back to after a mistake of your own, [export it](https://tonbo.dev/docs/databases/backups.md#export).

Each database is a [durable stream](https://durablestreams.com) of its commits, the open protocol from ElectricSQL, served by [Ursula](https://github.com/tonbo-io/ursula), Tonbo’s open-source stream server; [Ursula in Tonbo](https://tonbo.dev/docs/databases/internals/ursula.md) describes the replicas and the quorum.
