Skip to content
Discord Get Started

Security and Auth

DB9 has multiple authentication layers: API-level bearer tokens for managing databases, database-level roles for SQL access, and short-lived connect tokens for secure credential handoff. This page explains each layer, how they interact, and where the current boundaries are.

LayerMechanismLifetimePurpose
Customer API tokenBearer token (128-char hex)365 days (configurable)Manage databases, users, branches, tokens
Anonymous tokenBearer token (auto-refreshed)90 days (renewable)Trial access before SSO claim
API key loginDB9_API_KEY env or db9 login --api-keySame as tokenCI/CD and headless automation
Connect tokenJWT (RS256-signed)5–15 minutesShort-lived database connection credential
Connect keydb9ck_-prefixed keyConfigurable (up to permanent)Long-lived programmatic database access
Database passwordSCRAM-SHA-256Permanent until resetDirect pgwire authentication

All database management goes through the customer API, protected by bearer tokens.

Every API request requires an Authorization: Bearer <token> header. Tokens are:

  • 128-character hex strings (64 random bytes)
  • Stored as SHA-256 hashes in the metadata database (one-way; cannot be recovered)
  • Created with an expiry (default: 365 days)
Terminal
# Login via SSO (browser-based Auth0 device flow)
db9 login
# Login with a pre-created API key (headless)
db9 login --api-key <TOKEN>
# Check current auth state
db9 status

Create additional tokens for automation, CI/CD, or team members:

Terminal
# Via CLI (uses the SDK under the hood)
db9 db connect-token <database>

Tokens can be listed and revoked through the API. The token value is only returned on creation — store it securely.

First-time users get an anonymous bearer token automatically when running db9 create without credentials. These auto-refresh using an anonymous secret stored locally. See Anonymous and Claimed Databases for the full lifecycle.

Every database is bootstrapped with an admin user that has superuser privileges:

  • SUPERUSER, LOGIN, CREATEDB, CREATEROLE
  • Password is randomly generated and stored encrypted (AES-256-GCM) in the backend

The admin password is used when you run db9 db connect <database> — the CLI retrieves it from the backend automatically.

Create regular (non-superuser) database users through the CLI or API:

Terminal
db9 db users create <database> <username> --password <password>

Regular users:

  • Can LOGIN to the database
  • Cannot create other users, alter the admin password, or perform superuser-only operations
  • Access is controlled by standard PostgreSQL GRANT statements

Usernames must be 1–63 characters, alphanumeric plus underscore, and cannot start with _db9_sys_ (reserved for internal system users).

CapabilitySuperuser (admin)Regular user
Run any SQLYesSubject to GRANTs
Use extensions (http, fs9, embedding, pg_cron)YesRestricted (most require superuser)
Create/drop usersYesNo
Create/drop tablesYesOnly if granted
View all cron jobsYesOwn jobs only
Cancel running cron jobsYesNo
Use cron.running_jobsYesNo

Connect tokens are short-lived JWTs for establishing database connections from external clients, ORMs, or tools that need temporary credentials.

Terminal
db9 db connect-token <database>
db9 db connect-token <database> --role <role>

Returns:

JSON
{
"host": "pg.db9.io",
"port": 5433,
"database": "postgres",
"user": "<tenant_id>.<role>",
"token": "<JWT>",
"expires_at": "2026-03-12T12:10:00Z"
}
  • Signing: RS256 (RSA private key), verifiable via public JWKS endpoint
  • TTL: 5–15 minutes (default 10 minutes), not configurable per-request
  • Scope: db:connect — limited to establishing a database connection
  • Role binding: Each token is bound to a specific database and role
  • JWKS verification: Public keys available at /.well-known/db9-connect-jwks.json for external token validation
  • Single use intent: Designed for handoff to a driver; create a new token for each connection
  • Passing credentials to an ORM or migration tool without exposing the admin password
  • Short-lived CI/CD database access
  • Programmatic connection from serverless functions

Connect keys are long-lived, scoped API keys for programmatic database access (especially the fs9 filesystem):

  • Prefixed with db9ck_ for easy identification
  • SHA-256 hashed in storage (like bearer tokens)
  • Support scopes: fs9:ro (read-only filesystem), fs9:rw (read-write filesystem)
  • Configurable expiry or permanent
  • Revocable at any time

Use connect keys when you need long-lived, revocable credentials for automation that touches the filesystem path. For short-lived SQL/ORM handoff, prefer Connect Tokens.

All connect key APIs require a customer bearer token:

Terminal
AUTH_TOKEN="<your_customer_token>"
DB_ID="<database_id_or_name>"

POST /customer/databases/{database_id}/connect-keys

Terminal
curl -s -X POST "https://api.db9.ai/customer/databases/$DB_ID/connect-keys" \
-H "Authorization: Bearer $AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-fs",
"role": "admin",
"scopes": ["fs9:rw"],
"expires_in_days": 30
}'

Example response (201 Created):

JSON
{
"id": "f8f5f11a-0f9f-4f6d-bf04-a69de8d5f3b5",
"name": "ci-fs",
"connect_key": "db9ck_...",
"role": "admin",
"scopes": ["fs9:rw"],
"created_at": "2026-03-16T18:00:00Z",
"expires_at": "2026-04-15T18:00:00Z"
}

Notes:

  • scopes currently allow fs9:ro and fs9:rw.
  • Omit expires_in_days for a non-expiring key.

GET /customer/databases/{database_id}/connect-keys

Terminal
curl -s "https://api.db9.ai/customer/databases/$DB_ID/connect-keys" \
-H "Authorization: Bearer $AUTH_TOKEN"

Example response (200 OK):

JSON
[
{
"id": "f8f5f11a-0f9f-4f6d-bf04-a69de8d5f3b5",
"name": "ci-fs",
"role": "admin",
"scopes": ["fs9:rw"],
"created_at": "2026-03-16T18:00:00Z",
"expires_at": "2026-04-15T18:00:00Z",
"revoked_at": null
}
]

The list endpoint returns metadata only and never returns connect_key secrets.

DELETE /customer/databases/{database_id}/connect-keys/{key_id}

Terminal
KEY_ID="<connect_key_id>"
curl -s -X DELETE "https://api.db9.ai/customer/databases/$DB_ID/connect-keys/$KEY_ID" \
-H "Authorization: Bearer $AUTH_TOKEN"

Example response (200 OK):

JSON
{
"message": "Connect key revoked"
}

Publishable keys (db9pk_...) are browser-safe API keys for the Browser SDK. Unlike bearer tokens, they are designed to be embedded in client-side code.

Terminal
curl -X POST https://api.db9.ai/customer/databases/<database_id>/publishable-keys \
-H "Authorization: Bearer $DB9_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "web-app",
"allowed_origins": ["https://myapp.com"],
"exposed_schemas": ["public"],
"exposed_tables": [],
"rate_limit": { "rps": 100, "burst": 200 }
}'
FieldTypeDescription
namestringOptional key name for identification.
allowed_originsstring[]CORS whitelist. Empty array allows all origins.
exposed_schemasstring[]Required. Schemas accessible through this key.
exposed_tablesstring[]Optional. Restrict to specific tables (empty = all tables in exposed schemas).
rate_limitobjectOptional. { rps, burst } for request throttling.
expires_in_daysnumberOptional. Key expiry in days.
Terminal
# List all publishable keys
curl https://api.db9.ai/customer/databases/<database_id>/publishable-keys \
-H "Authorization: Bearer $DB9_TOKEN"
# Revoke a key
curl -X DELETE https://api.db9.ai/customer/databases/<database_id>/publishable-keys/<key_id> \
-H "Authorization: Bearer $DB9_TOKEN"

The key value (db9pk_...) is only returned at creation time. Store it in your environment variables or deployment config.

  • Keys are SHA-256 hashed in storage (like bearer tokens)
  • Scoped to specific schemas and tables
  • Origin-restricted via allowed_origins
  • Rate-limited per key
  • Revocable at any time

For per-user Row-Level Security with the Browser SDK, configure BYO JWT authentication on your database:

Terminal
curl -X PUT https://api.db9.ai/customer/databases/<database_id>/auth-config \
-H "Authorization: Bearer $DB9_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"auth_mode": "byo_jwt",
"byo_jwt": {
"jwks_url": "https://your-auth-provider.com/.well-known/jwks.json",
"issuer": "https://your-auth-provider.com",
"audience": "your-app-id",
"subject_claim": "sub"
}
}'
FieldTypeDescription
jwks_urlstringRequired. URL to your JWKS endpoint (RS256 keys).
issuerstringOptional. Expected iss claim value.
audiencestringOptional. Expected aud claim value.
subject_claimstringWhich JWT claim holds the user identity. Default: sub.

When a browser SDK request includes a Bearer <JWT> header:

  1. DB9 fetches your JWKS endpoint (cached for 1 hour)
  2. Validates the JWT signature (RS256), expiry, issuer, and audience
  3. Resolves the user’s database role from the sub claim
  4. Issues a connect token with that role
  5. Queries execute under that role — RLS policies filter results

Compatible with Auth0, Clerk, Supabase Auth, Firebase Auth, and any provider that exposes a JWKS endpoint.

CredentialStorage method
Customer bearer tokensSHA-256 hash (irreversible)
Connect keysSHA-256 hash (irreversible)
Database admin passwordsAES-256-GCM encrypted (requires DB9_CREDENTIAL_KEY)
Auth0 ID tokensValidated and discarded (not stored)

The CLI stores credentials in ~/.db9/credentials with owner-only permissions (0600). This file contains:

  • Bearer token
  • Customer ID
  • Anonymous ID and secret (if anonymous)

Running db9 logout clears this file.

DB9 uses the PostgreSQL wire protocol (pgwire v3) for SQL connections:

  • Authentication: Password-based (SCRAM-SHA-256 or plain, depending on configuration)
  • Multi-tenant isolation: Connection usernames are prefixed with the tenant ID (tenant_id.username), enforcing keyspace isolation at the wire level
  • Encryption: Wire-level TLS is not currently enforced between client and db9-server. Network-level encryption (VPN, private networking) is recommended for production.

All privilege-changing operations are logged to an audit trail:

  • Token creation and revocation
  • User creation and deletion
  • Connect key operations
  • Database lifecycle events

Audit logs are retained for 90 days by default.

These features are common in enterprise database platforms but are not available in DB9 today:

  • IP allowlisting — no built-in IP restrictions; use network-level controls
  • Wire-level TLS — pgwire connections are not encrypted; rely on network security
  • Multi-factor authentication — SSO (Auth0) is the only identity verification layer
  • Password complexity rules — no enforcement of length, character, or rotation policies
  • Automated credential rotation — revoke and re-create tokens manually
  • Column-level encryption — encrypt sensitive data at the application level