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.
function open(
nameOrUrl: string | URL,
options?: TonboOpenOptions,
): Promise<UrsulaDatabase>
interface TonboOpenOptions extends OpenOptions {
apiKey?: string;
apiOrigin?: string;
authOrigin?: string;
}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() 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); 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). The name’s rule is on Manage databases.
Options
TonboOpenOptions: every root option, 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 | 30 seconds | Bounds the lookup and the open together. |
The key’s order and the login file’s rules are on Opening by name, and the variables on Environment variables.
open() by URL
From @tonbo/sqlite-ursula.
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;
}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. |
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), 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. Not with recreate. |
timeout | number | 5000 | node:sqlite’s busy timeout in milliseconds. The connections of a binding share its files, hence the default. |
| The rest | DatabaseSyncOptions | node:sqlite | Passed to 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, 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). Without such a cache the open waits for the stream until its signal fires.
Errors
Both functions reject with:
UrsulaSetupErrorwhen the open cannot succeed as asked, such asinvalid_url,not_found,bound_elsewhereorwrong_cluster. Its action isfix, orretryforin_useandclaim_races.UrsulaUnavailableErrorwhen the signal fired first: the last condition the open met, orcancelled.- With a token provider: the provider’s own
UrsulaErrorwhen it failed for good,UrsulaSetupErrorinvalid_argumentwhen it handed out a token that cannot be sent, andUrsulaUnavailableErrorunavailablewhen it handed out none before the signal fired.
The /tonbo open also rejects with TonboError 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), 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.