Connect to DB9
DB9 speaks the PostgreSQL wire protocol on port 5433 and on the PostgreSQL default 5432.
Any client that connects to Postgres — psql, a Node.js driver, Prisma, SQLAlchemy, or a raw libpq binding — connects to DB9 with no adapter or proxy needed.
Connection string format
Section titled “Connection string format”Every DB9 connection string follows this pattern:
postgresql://<tenant_id>.<role>@<host>:<port>/postgres| Component | Value | Notes |
|---|---|---|
| Scheme | postgresql:// or postgres:// | Both work. |
| Username | <tenant_id>.<role> | The first component is the tenant ID DB9 uses to route the connection. Use the tenant-qualified username from the generated connection string, not the database name. The default role is admin. |
| Host | pg.db9.io | The public endpoint for hosted DB9. The CLI and API print pg.d0000.db9.io, which is the same server. |
| Port | 5433 | 5432 works too; the CLI and API print 5432. |
| Database | postgres | All DB9 databases use the postgres database name. |
Example:
postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgresGet your connection string
Section titled “Get your connection string”Using the CLI
Section titled “Using the CLI”db9 create prints a long-lived connection string with the admin password once, on its Connection String: line — save it then. db9 db status shows the connection string without the password. To get connection details at any time, use db9 db connect:
# Create a databasedb9 create --name myapp
# Show connection infodb9 db connect myappdb9 db connect mints a short-lived token by default (recommended for production and automation). It prints a ready-to-use connection string with the token already embedded as the password, plus the matching psql command:
Temporary connection string (10min):
postgresql://a1b2c3d4e5f6.admin:ey...@pg.d0000.db9.io:5432/postgres
User: a1b2c3d4e5f6.admin
Expires: 10min (at 2026-03-12T10:30:00Z)
psql Command: psql "postgresql://a1b2c3d4e5f6.admin:ey...@pg.d0000.db9.io:5432/postgres"
Long-lived DSN: db9 db users myapp create --username <user> --password <password> db9 db reset-password myappThe token is embedded in the connection string rather than printed as a separate field. Use --json for machine-readable output (connection_string, host, port, user, database, expires_at, expires_in, expires_in_seconds).
To get a new long-lived connection string instead, reset the admin password — the command prints the new password and connection string:
db9 db reset-password myappUsing the TypeScript SDK
Section titled “Using the TypeScript SDK”The SDK’s connection strings carry no password. instantDatabase() returns adminPassword only on the call that creates the database — when it reuses an existing one, connectionString, adminUser and adminPassword are empty strings. client.databases.credentials(id) returns the connection string and the stored admin password on every call:
import { instantDatabase, createDb9Client } from 'get-db9';
const db = await instantDatabase({ name: 'myapp' });
const creds = await createDb9Client().databases.credentials(db.databaseId);const url = new URL(creds.connection_string); // postgresql://a1b2c3d4e5f6.admin@pg.d0000.db9.io:5432/postgresurl.password = creds.admin_password;url.searchParams.set('sslmode', 'verify-full');console.log(url.toString()); // pass this to your driverOr call the connect-token REST endpoint directly to get a short-lived token — the SDK does not yet publish a native connectToken() method:
const res = await fetch(`https://api.db9.ai/customer/databases/${databaseId}/connect-token`, { method: 'POST', headers: { Authorization: `Bearer ${process.env.DB9_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ role: 'admin' }),});const { host, port, database, user, token, expires_at } = await res.json();Connect with psql
Section titled “Connect with psql”Use the connection string directly or let db9 db connect generate the command for you:
# Using a connect token (recommended)PGPASSWORD='<token>' psql "postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgres"
# Using a static passwordPGPASSWORD='<password>' psql "postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgres"You can also use a .pgpass file:
echo "pg.db9.io:5433:postgres:a1b2c3d4e5f6.admin:<password>" >> ~/.pgpasschmod 600 ~/.pgpasspsql "postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgres"Or use the built-in DB9 SQL REPL, which handles authentication automatically:
db9 db sql myapp -q "SELECT version()"Connect with ORMs and drivers
Section titled “Connect with ORMs and drivers”DB9 works with any PostgreSQL-compatible driver or ORM. Set the connection string as your database URL.
import pg from 'pg';
const client = new pg.Client({ // node-postgres uses TLS only when asked — keep the sslmode parameter connectionString: 'postgresql://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres?sslmode=verify-full',});await client.connect();const res = await client.query('SELECT NOW()');console.log(res.rows[0]);await client.end();# .envDATABASE_URL="postgresql://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres"// schema.prismadatasource db { provider = "postgresql" url = env("DATABASE_URL")}import { drizzle } from 'drizzle-orm/node-postgres';
const db = drizzle('postgresql://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres');from sqlalchemy import create_engine
# With psycopg2 — name the driver in the URL; on SQLAlchemy 2.1 a bare postgresql:// selects psycopg (v3)engine = create_engine( "postgresql+psycopg2://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres")conn, err := pgx.Connect(context.Background(), "postgresql://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres")For detailed setup guides with migrations, schema patterns, and troubleshooting, see the integration guides for Prisma, Drizzle, SQLAlchemy, TypeORM, Sequelize, Knex, and GORM.
Authentication
Section titled “Authentication”DB9 supports three ways to authenticate connections:
Static password
Section titled “Static password”Set at database creation or reset with db9 db reset-password. The password is embedded in the connection string or passed via PGPASSWORD. Simple, but the credential does not expire.
Short-lived connect token
Section titled “Short-lived connect token”Generated with db9 db connect. The token is used as the password and expires automatically. Recommended for production and CI/CD. The TypeScript SDK does not yet publish a native connectToken() method — see API Reference — Connect Tokens & Keys for the REST equivalent.
# Generate a token for a specific roledb9 db connect myapp --user adminAPI token (for SDK and REST API)
Section titled “API token (for SDK and REST API)”Named tokens created with db9 token create are used for the DB9 REST API and SDK, not for direct pgwire connections. Use them when your application manages databases programmatically rather than connecting with SQL.
db9 token create --name ci-agent --expires-in-days 90export DB9_API_KEY=<token>See the TypeScript SDK docs for the full auth lifecycle, including anonymous trial accounts and Auth0 SSO.
Publishable key (for browser access)
Section titled “Publishable key (for browser access)”For client-side applications, use a publishable key (db9pk_...) together with a user JWT in the Browser SDK. Publishable keys are scoped to specific schemas and tables and enforce Row-Level Security policies. The key identifies the application, not the user: queries also need BYO JWT, and a key on its own fails with 28P01.
import { createDb9BrowserClient } from '@db9/browser';
const db9 = createDb9BrowserClient({ apiUrl: 'https://api.db9.ai', databaseId: 'your-database-id', anonKey: 'db9pk_your_publishable_key',});
// Required: a publishable key alone cannot query — without a user JWT the// request fails with 28P01. Configure BYO JWT and attach the user's token.db9.auth.setSession({ accessToken: userJwt });
const { data } = await db9.from('todos').select('*');Publishable keys are created via the REST API. See Security & Auth — Publishable Keys for setup.
Every DB9 endpoint negotiates TLS (TLSv1.3), but the server does not require it: a client that does not ask for TLS, or sets sslmode=disable, connects in plaintext. The server uses SCRAM-SHA-256 authentication by default.
libpq-based clients such as psql and psycopg2 default to sslmode=prefer and use TLS when it is offered. node-postgres (pg) does not use TLS unless the connection string or its ssl option asks for it. Set the mode explicitly:
psql "postgresql://a1b2c3d4e5f6.admin@pg.db9.io:5433/postgres?sslmode=require"Or in a connection string for an ORM:
DATABASE_URL="postgresql://a1b2c3d4e5f6.admin:password@pg.db9.io:5433/postgres?sslmode=require"sslmode=verify-full also works and checks the server certificate against the system CA roots: add sslrootcert=system for psql with libpq 16+ (psycopg binary wheels: sslrootcert=<CA bundle file>); node-postgres 8 treats sslmode=require as verify-full. To see what a session negotiated, run SELECT ssl, version FROM pg_stat_ssl WHERE pid = pg_backend_pid();.
Multi-tenant username format
Section titled “Multi-tenant username format”DB9 reads the tenant ID from the username to route the connection to the correct tenant. The format is:
<tenant_id>.<role>- The tenant ID is the routing prefix in the PostgreSQL username. Use the complete username printed in the connection string or by
db9 db connect; do not substitute the database name. - The role defaults to
admin. You can create additional roles withdb9 db users create.
This encoding means your username in connection strings will look different from a typical Postgres setup, but it works transparently with all standard PostgreSQL drivers.
Connection checklist
Section titled “Connection checklist”| Check | Details |
|---|---|
| Host and port | pg.db9.io:5433, or pg.d0000.db9.io:5432 as the CLI prints it — the same server; port 5432 works on either host |
| Database name | Always postgres |
| Username | Must include the tenant ID prefix: <tenant_id>.<role> — use the username from the generated connection string, not the database name |
| Password | Static password or connect token — both go in PGPASSWORD or the connection string. db9 db status, the API’s connection_string and the SDK’s connectionString carry no password |
| TLS | Use sslmode=require or sslmode=verify-full — the server does not enforce TLS |
| Protocol | Standard PostgreSQL wire protocol — no special drivers needed |
Troubleshooting
Section titled “Troubleshooting”“password authentication failed” or “fe_sendauth: no password supplied”
- Verify the tenant ID and role in the username field. A database name such as
myappis not the tenant ID prefix, even when the password is correct. - Check that the connection string has a password at all — the one from
db9 db status, the API, or the SDK’sconnectionStringdoes not. - If using a connect token, check that it has not expired.
- Run
db9 db connect myappto generate a fresh token, ordb9 db reset-password myappfor a new long-lived connection string.
“connection refused” on port 5432
- DB9 accepts port
5432as well as5433, so the client is not reaching DB9 at all. A refusal fromlocalhostor127.0.0.1means the connection string never reached the driver — node-postgres, for example, falls back tolocalhost:5432whenconnectionStringis undefined.
“SSL SYSCALL error” or TLS handshake failures
- Ensure your client supports TLS. Try adding
?sslmode=requireto the connection string. - Some older clients may need updated CA certificates.
ORM connection timeouts
- Check that your ORM’s connection string uses the DB9 host, port
5433or5432, and database namepostgres. - Verify your network allows outbound connections on the port you use.
Next steps
Section titled “Next steps”- Quick Start — create your first database and run SQL
- CLI Reference — full command reference including
db connectanddb reset-password - TypeScript SDK —
instantDatabase(), credential management, and programmatic access - Browser SDK — client-side data access with publishable keys and RLS
- Architecture — how DB9 routes connections through the pgwire protocol to TiKV
- Extensions — enable vector search, fs9, HTTP from SQL, and pg_cron after connecting