Platform

Branches

Connectable Postgres databases with independent credentials, metrics, and configurable lifespans.

Branches

A branch is a connectable Postgres database instance within a project. Each branch has its own database, its own credentials, its own metrics, and its own lifespan. Branches are the fundamental unit of work in AxiomDB — every connection your application makes targets a specific branch.

The main branch is created automatically when you create a project. It is protected, has an infinite lifespan, and cannot be deleted. All other branches are ephemeral: they are created from main (or from another branch), inherit the source schema, and are automatically cleaned up when they expire.


Branch Anatomy

Every branch consists of:

┌─────────────────────────────────────────────────────┐
│                    Branch                            │
│                                                     │
│  ┌───────────────┐  ┌───────────────┐               │
│  │   Database     │  │   Roles       │               │
│  │               │  │               │               │
│  │ sq_<key>_<e>  │  │ *_owner (CDb) │               │
│  │ _br_<slug>    │  │ *_rw          │               │
│  │               │  │ *_ro          │               │
│  └───────────────┘  └───────────────┘               │
│                                                     │
│  ┌───────────────┐  ┌───────────────┐               │
│  │  Credentials  │  │   Metrics     │               │
│  │               │  │               │               │
│  │ DATABASE_URL  │  │ connections   │               │
│  │ DIRECT_URL    │  │ storage_bytes │               │
│  │               │  │ cpu_seconds   │               │
│  └───────────────┘  └───────────────┘               │
│                                                     │
│  ┌───────────────┐  ┌───────────────┐               │
│  │   Lifespan    │  │   Status      │               │
│  │               │  │               │               │
│  │ 7d / 1m /     │  │ active /      │               │
│  │ 6m / 1y /     │  │ expired /     │               │
│  │ forever       │  │ deleting /    │               │
│  │               │  │ deleted /     │               │
│  │               │  │ failed        │               │
│  └───────────────┘  └───────────────┘               │
│                                                     │
│  ┌───────────────────────────────────────┐          │
│  │  Metadata                             │          │
│  │                                       │          │
│  │  id, slug, source_branch_id,          │          │
│  │  created_by, created_at,              │          │
│  │  expires_at, protected                │          │
│  └───────────────────────────────────────┘          │
└─────────────────────────────────────────────────────┘

Database Naming Convention

AxiomDB derives database and role names from the project's app_key and env, plus the branch's slug.

Main Branch

Database:  sq_<app_key>_<env>
Example:   sq_payments_prod

Child Branches

Database:  sq_<app_key>_<env>_br_<branch_slug>
Example:   sq_payments_prod_br_fix-checkout-bug

Roles

RolePatternExamplePrivileges
Owner<app_key>_<env>_ownerpayments_prod_ownerCREATEDB, full DDL + DML
Read-Write<app_key>_<env>_rwpayments_prod_rwSELECT, INSERT, UPDATE, DELETE
Read-Only<app_key>_<env>_ropayments_prod_roSELECT

Role sharing

Roles are shared across all branches in a project. The payments_prod_rw role exists once in the Postgres cluster and can access any branch database within the project.


Lifespans

Every branch has a lifespan — a duration after which it is automatically expired and deleted.

Available Lifespans

LifespanDurationTypical Use
7d7 daysFeature branches, quick experiments
1m30 daysSprint-length work, QA cycles
6m180 daysLong-running staging environments
1y365 daysAnnual environments, compliance holds
foreverNo expiryProduction (main branch only)

Lifespan Rules

  1. The main branch always has lifespan forever. This cannot be changed.
  2. Child branches can use any lifespan except forever (unless explicitly authorized).
  3. The default lifespan is determined by the project's env:
    • dev7d
    • staging1m
    • prod1y
  4. You can extend a branch's lifespan before it expires:
curl -X PATCH https://gateway.axiomdb.io/v1/branches/br_x1y2z3 \
  -H "Authorization: Bearer paseto_v4_..." \
  -H "Content-Type: application/json" \
  -d '{ "lifespan": "6m" }'
  1. You can shorten a branch's lifespan to force earlier cleanup:
curl -X PATCH https://gateway.axiomdb.io/v1/branches/br_x1y2z3 \
  -H "Authorization: Bearer paseto_v4_..." \
  -H "Content-Type: application/json" \
  -d '{ "lifespan": "7d" }'

Expiration Timeline

Created          Extended to 6m         Original expiry (7d)     New expiry (6m)
   │                 │                       │                       │
   ▼                 ▼                       ▼                       ▼
───┬─────────────────┬───────────────────────┬───────────────────────┬───
   │                 │                       │                       │
   │   Branch is     │   Extension applied;  │   Would have expired  │
   │   active        │   new expiry set      │   here without ext.   │
   │                 │                       │                       │
                                                           Branch expires
                                                           and is deleted

Protected Branch Rules

The main branch is protected. The following restrictions apply:

OperationmainChild Branches
CreateAuto-created with projectManual creation
Delete❌ Forbidden✅ Allowed
Change lifespan❌ Locked to forever✅ Allowed
Rename slug❌ Locked to main✅ Allowed
Schema changes✅ Allowed✅ Allowed
Data mutations✅ Allowed✅ Allowed

Attempting to delete the main branch returns 403 Forbidden with error code PROTECTED_BRANCH.


Branch Status Values

              ┌──────────┐
              │ creating │
              └────┬─────┘


              ┌──────────┐
        ┌────►│  active  │◄────┐
        │     └────┬─────┘     │
        │          │           │
        │          │ (expires) │ (extend)
        │          ▼           │
        │     ┌──────────┐     │
        │     │  expired  │─────┘
        │     └────┬─────┘
        │          │
        │          ▼
        │     ┌──────────┐
        │     │ deleting │
        │     └────┬─────┘
        │          │
        │          ├──────────────┐
        │          ▼              ▼
        │     ┌──────────┐  ┌──────────┐
        └─────│  deleted  │  │  failed  │
              └──────────┘  └──────────┘
StatusDescription
creatingThe branch database is being provisioned. Schema is being copied from the source.
activeThe branch is fully operational and accepting connections.
expiredThe lifespan has elapsed. The branch is read-only; no new connections are accepted. Existing connections are drained with a 5-minute grace period.
deletingThe branch is being torn down. Database and roles are being dropped.
deletedThe branch has been fully removed.
failedAn error occurred during creation or deletion. Manual intervention may be required.

Expired branches

Expired branches enter a read-only state for 5 minutes before being hard-deleted. During this window, you can extend the lifespan to reactivate the branch. After hard deletion, data is unrecoverable.


Branch Creation Flow

When you call POST /v1/projects/:project_id/branches:

curl -X POST https://gateway.axiomdb.io/v1/projects/proj_a1b2c3d4/branches \
  -H "Authorization: Bearer paseto_v4_..." \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "fix-checkout-bug",
    "lifespan": "7d",
    "source_branch_id": "br_main"
  }'

Internal Sequence

Client              Gateway (4060)         Control Plane         square-dbctl         Postgres
  │                      │                       │                    │                    │
  │  POST /branches      │                       │                    │                    │
  │ ────────────────────►│                       │                    │                    │
  │                      │  Auth (PASETO v4)     │                    │                    │
  │                      │  Validate payload     │                    │                    │
  │                      │  Check branch limit   │                    │                    │
  │                      │ ─────────────────────►│                    │                    │
  │                      │                       │  Enqueue command   │                    │
  │                      │                       │  CREATE_BRANCH ───►│                    │
  │                      │                       │                    │                    │
  │                      │                       │                    │  1. CREATE DATABASE │
  │                      │                       │                    │  sq_payments_prod  │
  │                      │                       │                    │  _br_fix-checkout  │
  │                      │                       │                    │ ──────────────────►│
  │                      │                       │                    │                    │
  │                      │                       │                    │  2. Copy schema    │
  │                      │                       │                    │     from source    │
  │                      │                       │                    │ ──────────────────►│
  │                      │                       │                    │                    │
  │                      │                       │                    │  3. Grant roles    │
  │                      │                       │                    │ ──────────────────►│
  │                      │                       │                    │                    │
  │                      │                       │  4. Generate creds │                    │
  │                      │                       │  5. Write audit    │                    │
  │                      │                       │  6. Set expiry     │                    │
  │                      │                       │                    │                    │
  │  ◄───────────────────│◄──────────────────────│                    │                    │
  │  201 Created         │                       │                    │                    │

Schema Copying

When creating a branch from a source, AxiomDB copies the schema only, not the data. The process:

  1. pg_dump --schema-only from the source database.
  2. Apply the SQL to the new database.
  3. Verify table count matches.
-- Verification query run after schema copy
SELECT COUNT(*) AS table_count
FROM information_schema.tables
WHERE table_schema = 'public';

If the table count does not match, the branch is marked as failed and an alert is raised.


Branch Credentials

Each branch exposes two connection URLs:

URLPortLayerPurpose
DATABASE_URL6432PgBouncer (session mode)Application runtime. Prisma-compatible.
DIRECT_URL5432Direct PostgresMigrations, admin tasks.

Credential Structure

{
  "branch_id": "br_x1y2z3",
  "slug": "fix-checkout-bug",
  "database_url": "postgresql://payments_prod_rw:<password>@gateway.axiomdb.io:6432/sq_payments_prod_br_fix-checkout-bug?sslmode=require",
  "direct_url": "postgresql://payments_prod_owner:<password>@gateway.axiomdb.io:5432/sq_payments_prod_br_fix-checkout-bug?sslmode=require",
  "role": "payments_prod_rw",
  "database_name": "sq_payments_prod_br_fix-checkout-bug"
}

See Connections for URL anatomy and framework integration.


Branch Metrics

Each branch reports metrics collected from the Postgres cluster:

{
  "branch_id": "br_x1y2z3",
  "metrics": {
    "storage_bytes": 188743680,
    "storage_gib": 0.176,
    "active_connections": 4,
    "total_connections": 127,
    "cpu_seconds": 42.7,
    "rows_inserted": 150000,
    "rows_updated": 32000,
    "rows_deleted": 500,
    "cache_hit_ratio": 0.97,
    "transactions_committed": 890000,
    "transactions_rolled_back": 120
  },
  "sampled_at": "2025-03-20T14:00:00Z"
}

Storage Measurement Query

The storage metric is derived from:

SELECT pg_database_size(current_database()) AS storage_bytes;

Cache Hit Ratio

SELECT
  sum(heap_blks_hit) / (sum(heap_blks_hit) + sum(heap_blks_read)) AS cache_hit_ratio
FROM pg_statio_user_tables;

Metrics refresh

Metrics are sampled every 60 seconds and cached. The API returns the most recent sample. Historical metrics are retained for 30 days.


Branch Limits

LimitValueNotes
Active branches per project10Includes main. Cannot be exceeded.
Max slug length48 charactersLowercase alphanumeric + hyphens.
Min slug length3 characters
Max database name length63 charactersPostgres limit. Derived from app_key + env + slug.
Schema copy timeout5 minutesBranch is marked failed if exceeded.

Checking Branch Count

Before creating a branch, the gateway checks:

SELECT COUNT(*) AS active_branches
FROM branches
WHERE project_id = $1 AND status IN ('creating', 'active', 'expired');

If active_branches >= max_branches, the API returns 429 Too Many Requests with error code BRANCH_LIMIT_REACHED.


Source Branch Inheritance

A child branch can be created from any existing branch, not just main. This enables branch stacking:

main (protected, forever)
├── feat-payments (1m)
│   ├── fix-payment-bug (7d)      ← branched from feat-payments
│   └── payment-tests (7d)        ← branched from feat-payments
└── feat-users (1m)
    └── user-schema-v2 (7d)       ← branched from feat-users

When branching from a non-main source, the schema copy pulls from the source branch's current schema state, including any migrations that have been applied to it.


API Reference

MethodEndpointDescription
POST/v1/projects/:pid/branchesCreate a new branch.
GET/v1/projects/:pid/branchesList all branches.
GET/v1/branches/:bidGet a single branch.
PATCH/v1/branches/:bidUpdate branch (lifespan, slug).
DELETE/v1/branches/:bidDelete a branch.
GET/v1/branches/:bid/credentialsGet connection URLs.
GET/v1/branches/:bid/metricsGet branch metrics.
POST/v1/branches/:bid/extendExtend branch lifespan.

On this page