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

# open()

Last updated 29 October 2026

The two `open` functions: every option, what they return and what they throw.

`@tonbo/sqlite-ursula/tonbo` opens a Tonbo database by name with an API key. The root entry opens a stream by URL with a token you supply; the `/tonbo` function calls it once it has the URL and the token.

## open() by name

From `@tonbo/sqlite-ursula/tonbo`, since 0.11.1.

```text
function open(
  nameOrUrl: string | URL,
  options?: TonboOpenOptions,
): Promise<UrsulaDatabase>

interface TonboOpenOptions extends OpenOptions {
  apiKey?: string;
  apiOrigin?: string;
  authOrigin?: string;
}
```

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

const db = await open("notes", { dir: "./notes-cache" });
```

`nameOrUrl` is a database name, or a URL: a `URL` object, or a string that starts with a scheme such as `https://`. A name is looked up with `GET {apiOrigin}/v1/databases/{name}`, the key as its bearer token, in the key’s account; a URL skips the lookup, and a URL of another account’s database answers not found with this key. A database that is still being created is waited for, and one that is being deleted fails as not found. Then the stream is opened as the root [open()](#root-open) opens it. Unless a `token` or `URSULA_VFS_TOKEN_FILE` serves the stream, it waits for the key’s first Streams token, which the extension exchanges and keeps fresh in the background (see [Tokens](https://tonbo.dev/docs/reference/nodejs/tokens.md#sources)); this is so since 0.11.2, and 0.11.1 passed the open a token provider. The lookup and the exchanges go out from the extension, with its certificate authorities and the environment’s proxy (see [URSULA_VFS_CA_FILE](https://tonbo.dev/docs/reference/nodejs/environment.md#ursula-vfs-ca-file)). The name’s rule is on [Manage databases](https://tonbo.dev/docs/databases/manage.md#names).

### Options

`TonboOpenOptions`: every [root option](#open-options), and:

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `apiKey` | `string` | `TONBO_API_KEY`, else the login `tonbo login` saved | The Tonbo API key, a `tbo_` key. It goes only to the two origins below. Not needed to open a URL when `token` or `URSULA_VFS_TOKEN_FILE` serves the stream. |
| `apiOrigin` | `string` | `TONBO_API_ORIGIN`, else `https://api.tonbo.dev` | The Management API’s origin, for the name lookup. `https://`, or `http://` to a loopback host, with no path. |
| `authOrigin` | `string` | `TONBO_AUTH_ORIGIN`, else `https://tonbo.dev` | The origin of the token endpoint, `/api/iam/token`, where the key is exchanged. The same rules as `apiOrigin`. |
| `token` | `string` or `TokenProvider` | A provider for the key | Serves the stream’s requests in place of the key’s tokens. A name still needs the key for its lookup. |
| `signal` | [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) | 30 seconds | Bounds the lookup and the open together. |

The key’s order and the login file’s rules are on [Opening by name](https://tonbo.dev/docs/platform/api-keys.md#open-by-name), and the variables on [Environment variables](https://tonbo.dev/docs/reference/nodejs/environment.md).

## open() by URL

From `@tonbo/sqlite-ursula`.

```text
function open(url: string | URL, options?: OpenOptions): Promise<UrsulaDatabase>

interface OpenOptions extends Omit<DatabaseSyncOptions, "open" | "readOnly"> {
  dir?: string;
  token?: string | TokenProvider;
  signal?: AbortSignal;
  maxPendingBytes?: number;
  recreate?: boolean;
  readOnly?: boolean;
}
```

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

const db = await open(process.env.NOTES_URL, {
  token: process.env.STREAMS_TOKEN,
  signal: AbortSignal.timeout(10_000),
});
```

The first open of a stream in the process binds its local cache, catches it up from the stream and claims the stream, which fences every earlier owner. Later opens of it in the process return another connection on the same binding, with no network, and concurrent opens share one bind and its outcome. Worker threads call `open` too. After a reset, an open binds anew at once, on a copy of the reset database’s cache.

### Options

`OpenOptions`:

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `dir` | `string` | `<user cache>/sqlite-ursula/0.8` | The cache root, created when missing. A stream is open under one root at a time in the process: an open under another root waits for the database there to retire, and fails with `bound_elsewhere` while it has connections open. |
| `token` | `string` or `TokenProvider` | None: `URSULA_VFS_TOKEN_FILE` when set | The bearer token requests carry, or a provider that hands out tokens and keeps them fresh. The process carries one token, set by the last open that names one. See [Tokens](https://tonbo.dev/docs/reference/nodejs/tokens.md). |
| `signal` | `AbortSignal` | 30 seconds | When the open gives up. It then rejects with `UrsulaUnavailableError`: the last condition the open met, or `cancelled`. |
| `maxPendingBytes` | `number` | 64 MiB | How many bytes of compressed commits may wait for the stream before writes fail with `backlog_full`. A whole number of bytes. |
| `recreate` | `boolean` | `false` | When the stream is missing and the cache holds data: create the stream again and move the cache into `salvage/` (see [Salvage](https://tonbo.dev/docs/databases/backups.md#salvage)), instead of failing with `not_found`. Tonbo creates database streams itself, so on Tonbo create the database first. |
| `readOnly` | `boolean` | `false` | Open without claiming the stream, so the owner is never fenced; writes fail with `SQLITE_READONLY`. See [Read-only opens](#read-only). Not with `recreate`. |
| `timeout` | `number` | 5000 | [node:sqlite](https://nodejs.org/docs/latest-v22.x/api/sqlite.html)’s busy timeout in milliseconds. The connections of a binding share its files, hence the default. |
| The rest | `DatabaseSyncOptions` | `node:sqlite` | Passed to [DatabaseSync](https://nodejs.org/docs/latest-v22.x/api/sqlite.html#class-databasesync), such as `enableForeignKeyConstraints`. Not `open`, which is always true, and not `readOnly`, which the option above sets. |

A `token`, `maxPendingBytes`, `recreate` or `readOnly` of the wrong type rejects with `UrsulaSetupError` `invalid_argument` before any request. The user cache is `$XDG_CACHE_HOME`, else `~/.cache` on Linux and `~/Library/Caches` on macOS.

### Read-only opens

In a process that owns the stream, `readOnly` returns a read-only connection on the owner’s binding: it sees the owner’s latest commits, acknowledged or not, and its `synced()` waits as the owner’s does. In any other process it builds or catches up a reader cache of its own (`role: "reader"`, `reader-<n>/` in the stream’s cache directory) to the stream’s tail at the open, and reads the stream as of then: it does not follow later commits, and its `synced()` resolves at once. Close every read-only connection of the process and open again to read newer commits. A closed stream opens too. When the stream is unreachable it opens an earlier reader cache as it is, and otherwise waits until the signal fires.

## Returns

A promise of an [UrsulaDatabase](https://tonbo.dev/docs/reference/nodejs/database.md), a `node:sqlite` `DatabaseSync` subclass, so drizzle, kysely and Pi’s driver take it. Commit, then `await db.synced()` before anything outside the process acts on what you wrote or read.

When the stream is unreachable and the cache is a trusted one that claimed the stream before, the open resolves anyway, with `status().state` `provisional`: reads come from the cache, commits are local and pending, and `synced()` waits. Once the stream answers, the claim is checked and applied, and the state turns `live`; if another owner took the stream meanwhile, the database resets with nothing sent (see [state](https://tonbo.dev/docs/reference/nodejs/status.md#field-state)). Without such a cache the open waits for the stream until its signal fires.

## Errors

Both functions reject with:

- [`UrsulaSetupError`](https://tonbo.dev/docs/reference/nodejs/errors.md#ursula-setup-error) when the open cannot succeed as asked, such as `invalid_url`, `not_found`, `bound_elsewhere` or `wrong_cluster`. Its action is `fix`, or `retry` for `in_use` and `claim_races`.
- [`UrsulaUnavailableError`](https://tonbo.dev/docs/reference/nodejs/errors.md#ursula-unavailable-error) when the signal fired first: the last condition the open met, or `cancelled`.
- With a token provider: the provider’s own `UrsulaError` when it failed for good, `UrsulaSetupError` `invalid_argument` when it handed out a token that cannot be sent, and `UrsulaUnavailableError` `unavailable` when it handed out none before the signal fired.

The `/tonbo` `open` also rejects with [`TonboError`](https://tonbo.dev/docs/reference/nodejs/errors.md#tonbo-error) for a key or a database to fix, `UrsulaUnavailableError` (`unreachable`, `unavailable` or `rate_limited`) when the Tonbo API did not answer before the signal fired, and `UrsulaSetupError`: `invalid_name` for an empty name, `invalid_argument` for an option or an answer it cannot use, or for a second API key while the first key’s tokens serve (since 0.11.2; see [One token per process](https://tonbo.dev/docs/reference/nodejs/tokens.md#one-token)), `invalid_url` for a stream URL it cannot take or that would get the key’s tokens over plain `http://` to another host, and `local_setup` for a login file it cannot read.
