Secrets & Credentials
AxiomDB manages database credentials through the zone.env secrets file on the VPS and exposes them through the gateway API. The credentials API supports retrieval, rotation, and audit logging of every access.
How secrets work
AxiomDB credentials live in /home/opsdc/.creds/zone.env on the VPS. This file is written by square-dbctl during provisioning and never leaves the VPS unencrypted.
The gateway reads from this file (path configurable via AXIOMDB_SECRET_FILE) and exposes credentials through the API only to authenticated, authorized callers with a valid PASETO token.
Credential key naming convention:
DATABASE_URL_{APP_KEY}_{ENV} → runtime (PgBouncer) URL
DIRECT_URL_{APP_KEY}_{ENV} → direct (Postgres) URL
DATABASE_URL_{APP_KEY}_{ENV}_BR_{BRANCH} → branch runtime URL
DIRECT_URL_{APP_KEY}_{ENV}_BR_{BRANCH} → branch direct URLAll keys are uppercased and hyphens/slashes are converted to underscores.
Getting project credentials
GET /api/v1/projects/:project_id/credentials
Authorization: Bearer <paseto-v4-token>Response:
{
"project_id": "...",
"database": "sq_servers_prod",
"database_url": "postgresql://servers_prod_rw:...@db.squareexp.com:6432/sq_servers_prod?sslmode=require",
"direct_url": "postgresql://servers_prod_owner:...@db.squareexp.com:5432/sq_servers_prod?sslmode=require",
"runtime_key": "DATABASE_URL_SERVERS_PROD",
"direct_key": "DIRECT_URL_SERVERS_PROD"
}Every credential access is logged to audit_events with action project.credentials.viewed. The log includes the requester's user_id, source_ip, user_agent, and timestamp.
Getting branch credentials
GET /api/v1/projects/:project_id/branches/:branch_ref/credentials
Authorization: Bearer <paseto-v4-token>branch_ref can be a branch UUID or name (e.g. feature-auth).
Response:
{
"project_id": "...",
"branch_id": "...",
"branch_name": "feature-auth",
"database": "sq_servers_prod_br_feature-auth",
"runtime_key": "DATABASE_URL_SERVERS_PROD_BR_FEATURE_AUTH",
"direct_key": "DIRECT_URL_SERVERS_PROD_BR_FEATURE_AUTH",
"database_url": "postgresql://servers_prod_rw:...@db.squareexp.com:6432/sq_servers_prod_br_feature-auth?sslmode=require",
"direct_url": "postgresql://servers_prod_owner:...@db.squareexp.com:5432/sq_servers_prod_br_feature-auth?sslmode=require"
}Framework snippets
The ops console Secrets page generates ready-to-use snippets for all supported frameworks:
// schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
directUrl = env("DIRECT_URL")
}DATABASE_URL="postgresql://servers_prod_rw:...@db.squareexp.com:6432/sq_servers_prod?sslmode=require"
DIRECT_URL="postgresql://servers_prod_owner:...@db.squareexp.com:5432/sq_servers_prod?sslmode=require"import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: true });
export const db = drizzle(pool);import { Kysely, PostgresDialect } from 'kysely';
import { Pool } from 'pg';
export const db = new Kysely<Database>({
dialect: new PostgresDialect({
pool: new Pool({ connectionString: process.env.DATABASE_URL }),
}),
});import { Pool } from 'pg';
export const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
});from sqlalchemy import create_engine
engine = create_engine(
os.environ["DATABASE_URL"],
connect_args={"sslmode": "require"},
pool_pre_ping=True,
)import dj_database_url
DATABASES = {
"default": dj_database_url.config(
default=os.environ["DATABASE_URL"],
conn_max_age=600,
ssl_require=True,
)
}DB_CONNECTION=pgsql
DB_HOST=db.squareexp.com
DB_PORT=6432
DB_DATABASE=sq_servers_prod
DB_USERNAME=servers_prod_rw
DB_PASSWORD=...
DB_SSLMODE=requireimport "github.com/jackc/pgx/v5/pgxpool"
pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
defer pool.Close()use sqlx::postgres::PgPoolOptions;
let pool = PgPoolOptions::new()
.max_connections(5)
.connect(std::env::var("DATABASE_URL")?.as_str())
.await?;Credential rotation
Credential rotation replaces the PostgreSQL role password and updates zone.env atomically.
POST /api/v1/projects/:project_id/credentials/rotate
Authorization: Bearer <paseto-v4-token>
Content-Type: application/json
{
"role": "runtime",
"confirm": "rotate servers-prod runtime"
}| Field | Values | Description |
|---|---|---|
role | "runtime", "direct", "readonly" | Which role to rotate |
confirm | "rotate {slug} {role}" | Safety confirmation |
Credential rotation requires immediate .env updates in all running services. Old credentials stop working within seconds of rotation. Plan for downtime or use blue-green deployment.
Response:
{
"rotated": true,
"runtime_key": "DATABASE_URL_SERVERS_PROD",
"new_database_url": "postgresql://servers_prod_rw:NEW_PASS@db.squareexp.com:6432/sq_servers_prod?sslmode=require"
}Rotation writes credential.rotated to audit_events.
Environment variable conventions
| Convention | Description |
|---|---|
DATABASE_URL | Standard name used by Prisma, Drizzle, and most ORMs for pooled (PgBouncer) traffic |
DIRECT_URL | Used by Prisma for migration-only direct connections |
| Deterministic keys | DATABASE_URL_{APP}_{ENV} stored in zone.env on the VPS |
Best practice: In your app .env, always use the generic DATABASE_URL / DIRECT_URL names. The zone.env deterministic keys are for internal reference and rotation verification only.
Audit events for credentials
| Action | Description |
|---|---|
project.credentials.viewed | Someone accessed a project's DATABASE_URL / DIRECT_URL |
branch.credentials.viewed | Someone accessed a branch's credentials |
credential.rotated | A database role password was rotated |
All credential audit events include: actor_user_id, project_id, branch_id (if applicable), source_ip, source_user_agent, and created_at.
How is this guide?
Backups
AxiomDB uses pgBackRest to provide continuous WAL archiving and point-in-time recovery (PITR). Backup schedules, restore plans, and restore operations are managed through the gateway API and the ops console.
Security
AxiomDB's security model spans authentication, tenant isolation (Row-Level Security), network access control, audit logging, credential audit, CORS policy, and the VPS hardening stack.
