Provisioning
DB9 databases are created synchronously and return a usable connection string in under a second. This makes DB9 suitable for workflows where databases are created on demand — per agent, per user, per task, or per CI run.
This page explains the provisioning model: how databases are created, what metadata they carry, how their lifecycle works, and how to manage fleets of databases programmatically.
Mental model
Section titled “Mental model”A DB9 database is an isolated tenant backed by a dedicated TiKV keyspace.
Creating a database provisions the keyspace, bootstraps an admin user, installs default extensions (http and pg_cron), and returns connection credentials — all in a single synchronous API call.
Each database is identified by a 12-character opaque ID (e.g., t1a2b3c4d5e6) and an optional human-readable name.
The ID is permanent. The name must be unique within your account.
Creating a database
Section titled “Creating a database”# Minimal — auto-generates a name like "brave-tiger-42"db9 create
# Nameddb9 create --name my-agent-db
# With region hintdb9 create --name my-agent-db --region us-east
# Show connection string in outputdb9 create --name my-agent-db --show-connection-stringAvailable flags:
| Flag | Description |
|---|---|
--name <NAME> | Human-readable name (auto-generated if omitted) |
--region <REGION> | Region hint (stored as metadata) |
--password <PASSWORD> | Set a specific admin password (random if omitted) |
--show-password | Print admin password in output |
--show-connection-string | Print the full PostgreSQL DSN |
--show-secrets | Shorthand for both --show-password and --show-connection-string |
--output json | Machine-readable JSON output |
TypeScript SDK
Section titled “TypeScript SDK”The SDK offers two creation patterns:
instantDatabase() — idempotent, agent-friendly:
import { instantDatabase } from 'get-db9';
const db = await instantDatabase({ name: 'agent-workspace', seed: ` CREATE TABLE context (id SERIAL, key TEXT, value JSONB); CREATE TABLE artifacts (id SERIAL, path TEXT, content TEXT); `,});
console.log(db.databaseId); // "t1a2b3c4d5e6"console.log(db.connectionString); // "postgresql://..."console.log(db.adminUser); // "admin"instantDatabase() checks for an existing database with the same name and returns it if found.
If no match exists, it creates a new one.
This makes agent restarts safe — the same code path won’t duplicate databases.
Note: The seed SQL only runs when a new database is created. If an existing database is returned, the seed is skipped.
Options:
| Option | Type | Description |
|---|---|---|
name | string | Database name (default: 'default') |
seed | string | SQL to execute after creation |
seedFile | string | SQL file content to execute after creation |
baseUrl | string | API base URL (default: https://api.db9.ai) |
timeout | number | Request timeout in milliseconds |
maxRetries | number | Retry count for transient failures |
databases.create() — direct API call:
import { createDb9Client } from 'get-db9';
const client = createDb9Client();const db = await client.databases.create({ name: 'my-agent-db' });The create request accepts name (required), region (optional), and admin_password (optional).
REST API
Section titled “REST API”curl -X POST https://api.db9.ai/customer/databases \ -H "Authorization: Bearer $DB9_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name": "my-agent-db"}'Response (HTTP 201):
{ "id": "t1a2b3c4d5e6", "name": "my-agent-db", "state": "ACTIVE", "admin_user": "admin", "admin_password": "generated-password", "created_at": "2025-01-15T10:30:00Z", "connection_string": "postgresql://t1a2b3c4d5e6.admin@sql.db9.ai:5432/postgres"}Database metadata
Section titled “Database metadata”Every database carries these fields:
| Field | Description |
|---|---|
id | 12-character opaque identifier, permanent |
name | Human-readable name, unique per account |
state | Current lifecycle state (see below) |
state_reason | Error message when state is not ACTIVE |
admin_user | Always "admin" |
admin_password | Plaintext password (only returned at create time or after reset) |
connection_string | PostgreSQL DSN |
region | Region hint as supplied at creation |
endpoints | Host, port, and type for each endpoint (only on single-database queries) |
created_at | RFC 3339 timestamp |
parent_database_id | Source database ID (branches only) |
snapshot_at | Snapshot timestamp (branches only) |
Connection string format: postgresql://{id}.admin@{host}:{port}/postgres
The database ID is embedded in the username ({id}.admin), which is how the server routes connections to the correct tenant.
Lifecycle states
Section titled “Lifecycle states”A database transitions through these states:
| State | Meaning | Terminal? |
|---|---|---|
CREATING | Keyspace being provisioned | No — transitions to ACTIVE or CREATE_FAILED |
CLONING | Branch copy in progress | No — transitions to ACTIVE or CREATE_FAILED |
ACTIVE | Normal operating state | No |
DISABLING | Delete in progress | No — transitions to DISABLED |
DISABLED | Deleted | Yes |
CREATE_FAILED | Provisioning failed | Yes |
SUSPENDED | Operator-imposed suspension | Yes (requires operator action to resume) |
Normal lifecycle: CREATING → ACTIVE → DISABLING → DISABLED
Branch lifecycle: CREATING → CLONING → ACTIVE → DISABLING → DISABLED
Recovery: A background reconciler recovers databases stuck in CREATING or DISABLING for more than 10 minutes.
If recovery fails, the database moves to CREATE_FAILED.
DISABLED databases are excluded from list responses.
Anonymous and claimed accounts
Section titled “Anonymous and claimed accounts”DB9 supports a trial-first model where databases can be created without signing up.
Anonymous accounts:
- Created automatically on first
db9 createwhen no credentials are present - Limited to 5 databases (including branches)
- Receive a 90-day bearer token
- Can be refreshed using the stored anonymous credentials
Claiming an account:
- Run
db9 claimto upgrade via Auth0 SSO - The database limit is removed after claiming
- All existing databases are retained under the claimed account
If an anonymous account hits the 5-database limit, the CLI returns an error directing you to run db9 claim.
→ Deeper guide: Anonymous and Claimed Databases (coming soon)
Listing databases
Section titled “Listing databases”# Table outputdb9 list
# JSON outputdb9 list --jsonTable columns: ID, NAME, STATE, REGION, CREATED.
const databases = await client.databases.list();// Returns DatabaseResponse[] — excludes DISABLED databasesREST API
Section titled “REST API”curl https://api.db9.ai/customer/databases \ -H "Authorization: Bearer $DB9_TOKEN"Getting database details
Section titled “Getting database details”A single-database query returns the full metadata including endpoints and connection string:
db9 db status my-agent-dbOr via the SDK:
const db = await client.databases.get('t1a2b3c4d5e6');console.log(db.endpoints); // [{ host, port, type, enabled }]console.log(db.connectionString);Deleting databases
Section titled “Deleting databases”# Interactive confirmationdb9 delete my-agent-db
# Skip confirmationdb9 delete my-agent-db --yesawait client.databases.delete('t1a2b3c4d5e6');Deletion transitions the database through DISABLING → DISABLED.
Once disabled, the database is excluded from list results and its TiKV keyspace is cleaned up.
Credential management
Section titled “Credential management”Password reset:
db9 db reset-password my-agent-dbReturns a new random password and updated connection string.
Connect tokens (short-lived, for sharing or CI):
db9 db connect-token my-agent-dbReturns a token usable as PGPASSWORD with psql.
Connect tokens expire in 10 minutes by default.
SDK equivalents:
const creds = await client.databases.credentials('t1a2b3c4d5e6');const token = await client.databases.connectToken('t1a2b3c4d5e6');Fleet patterns
Section titled “Fleet patterns”When managing many databases programmatically, consider these patterns:
Database-per-agent
Section titled “Database-per-agent”Each agent instance creates its own database on startup and uses it for the duration of its lifecycle.
Use instantDatabase() with a deterministic name derived from the agent’s identity.
const db = await instantDatabase({ name: `agent-${agentId}`, seed: 'CREATE TABLE state (key TEXT PRIMARY KEY, value JSONB)',});Database-per-user
Section titled “Database-per-user”Multi-tenant applications create a database for each end user. Name databases with a user identifier to make them idempotent.
const db = await instantDatabase({ name: `user-${userId}` });Ephemeral databases
Section titled “Ephemeral databases”CI pipelines or one-shot tasks create a database, run their work, and delete it:
ID=$(db9 create --name "ci-run-$BUILD_ID" --json | jq -r '.id')# ... run tests against the database ...db9 delete "$ID" --yesFleet listing and cleanup
Section titled “Fleet listing and cleanup”Use the SDK or CLI to list and clean up databases that match a naming pattern:
const all = await client.databases.list();const stale = all.filter(db => db.name.startsWith('ci-run-') && new Date(db.created_at) < oneDayAgo);for (const db of stale) { await client.databases.delete(db.id);}Constraints and boundaries
Section titled “Constraints and boundaries”- Name uniqueness: Database names must be unique within an account. Duplicate names return HTTP 409.
- Anonymous limit: 5 databases per anonymous account (including branches). Claim to remove the limit.
- No rename: Database names cannot be changed after creation.
- No region migration: The region hint is metadata only — databases cannot be moved between regions after creation.
- Synchronous creation: Database creation blocks until the keyspace is provisioned. This typically completes in under a second, but can take longer under load.
- State recovery: Databases stuck in
CREATINGorDISABLINGfor more than 10 minutes are automatically recovered by a background reconciler.
Next steps
Section titled “Next steps”- Connect — connection strings, TLS, and authentication options
- Agent Workflows — how provisioning fits into the full agent lifecycle
- Multi-Tenant Patterns — database-per-user, database-per-app, ephemeral-per-task, and branch-per-preview
- Anonymous and Claimed Databases — trial-first usage and account upgrade paths (coming soon)
- Recovery and Branch Lifecycle — database states, branch phases, deletion, and disaster recovery expectations
- Production Checklist — secrets, auth, and operational readiness
- TypeScript SDK — full SDK reference for
instantDatabase()andcreateDb9Client()