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

# One database per agent run

Last updated 29 October 2026

Use this when each run of an agent, in a sandbox that goes away afterwards, needs SQLite state of its own: an orchestrator creates the run’s database, the sandbox opens it with a key that reaches only runs, syncs after each step, and the orchestrator deletes the database once the run’s results are safe.

## Who holds which key

- The orchestrator, the program that starts runs, holds a key that is not narrowed, acting as you or as an Agent of the account: an identity of the account with its own keys and the role you give it, created on **Members**. Only such a key creates and deletes databases (see [What a key can reach](https://tonbo.dev/docs/platform/api-keys.md#key-scope)). The role `member` is enough: it creates and deletes databases and creates its own keys. An owner or admin creates the Agent and its first key, which only they may do.
- Each sandbox holds a key narrowed to the runs’ databases: it reads and writes them, and never creates or deletes one.
- Each run gets a database named `run-` and the run’s id, such as `run-42`. A name is unique in the account while its database exists (see [Names](https://tonbo.dev/docs/databases/manage.md#names)), and the account holds up to 10,000 databases at once (see [Limits](https://tonbo.dev/docs/databases/limits.md#databases-per-account)).

## Create the run’s database

Before it starts the sandbox, the orchestrator creates the database, with its key in `TONBO_API_KEY`:

```shell
curl -sS https://api.tonbo.dev/v1/databases \
  -H "Authorization: Bearer $TONBO_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"name":"run-42"}'
```

It answers `201` once the database is active, or `202` while it is still being created, and opening never creates a database. Keep one `Idempotency-Key` per run and send it again on a retry: the retry then answers the same database instead of creating a second one (see [Create a database](https://tonbo.dev/docs/databases/manage.md#create)). After a `202`, read the database until its `state` is `active`:

```shell
curl -sS https://api.tonbo.dev/v1/databases/run-42 \
  -H "Authorization: Bearer $TONBO_API_KEY"
```

Its answer holds the database under `data`, so wait in a loop such as this one, with `jq`:

```shell
until [ "$(curl -sS https://api.tonbo.dev/v1/databases/run-42 \
  -H "Authorization: Bearer $TONBO_API_KEY" | jq -r .data.state)" = active ]; do
  sleep 1
done
```

## Give the sandboxes a key

One key narrowed to the prefix reaches every run’s database, those created later included, and nothing else:

```shell
tonbo api-key create run-sandboxes --database-prefix run-
```

Every sandbox that holds it can reach every run’s database. Where runs must not reach each other, give each run a key of its own, narrowed to its database, and revoke it after the run; a key may name a database before it exists:

```shell
tonbo api-key create run-42-sandbox --database run-42
```

The orchestrator, with the role `member`, creates these keys for itself, so they act as the orchestrator, narrowed. Hand the key to the sandbox through its environment, as `TONBO_API_KEY`, from your secret store.

## Run the sandbox

The sandbox opens the run’s database by name, with its local copy in `/tmp/tonbo-cache`, a directory any sandbox can write, and syncs after each step:

**run.mjs**

```js
import { open } from "@tonbo/sqlite-ursula/tonbo";

// The run's database, by name, with the sandbox's key in TONBO_API_KEY,
// and its local copy in a directory the sandbox can write.
const db = await open("run-42", { dir: "/tmp/tonbo-cache" });
db.exec("CREATE TABLE IF NOT EXISTS steps(id INTEGER PRIMARY KEY, body TEXT)");

// steps: the run's work, as async functions that return what to keep.
for (const step of steps) {
  const result = await step();
  db.prepare("INSERT INTO steps(body) VALUES (?)").run(result);
  await db.synced(); // from here, losing the machine does not lose this step
}
db.close();
```

A database works in any Node.js process with a writable local directory for its cache, `/tmp` included, as long as only one process opens it at a time. A process that starts without the last one’s directory, such as a fresh sandbox, a new container or a serverless function, rebuilds the local copy from Tonbo when it opens the database, a [cold start](https://tonbo.dev/docs/databases/cold-starts.md), and its open counts one write. Killed with its disk, a process can lose every commit since its last resolved `synced()`, so await `synced()` after each step that must survive, not only before it exits. Processes that open the same database at once, as concurrent invocations of a function do, take it over from each other, and the commits of all but the last are refused.

So run one sandbox per run: a second one that opens the same database becomes its writer (see [When two instances run](https://tonbo.dev/docs/databases/how-it-works.md#takeover)).

## Delete it when the run is done

A database lives until it is deleted or its expiry passes. Once the run’s results are safe, the orchestrator deletes it:

```shell
curl -sS -X DELETE https://api.tonbo.dev/v1/databases/run-42 \
  -H "Authorization: Bearer $TONBO_API_KEY"
```

Or with the CLI, where `--yes` confirms without a terminal to ask:

```shell
tonbo database delete run-42 --yes
```

To have Tonbo delete a run’s database even when the run’s cleanup never happens, create it with an expiry, such as a week out: `expires_at` in the creation’s body (see [Expiry](https://tonbo.dev/docs/databases/manage.md#expiry)). Deletion cannot be undone: export anything you keep first (see [Export a copy](https://tonbo.dev/docs/databases/backups.md#export)). It costs nothing, and until it completes the database still counts toward the account’s databases.

## What it costs

Each run counts one write for its creation, one for the sandbox’s open, and one for each of its commits up to 10 KiB, and its storage is charged for the days it is kept. [The worked example](https://tonbo.dev/docs/databases/pricing.md#database-per-run) prices a thousand runs a day.
