Migrations

Command line operations

Configure, inspect, baseline, and apply a complete migration chain with stable non-interactive behavior.

Install the CLI, migration library, and one verified migration adapter. For a libSQL application:

pnpm add @qubu/cli @qubu/migrate @qubu/adapter-libsql @libsql/client

The qubu binary is implemented with @alloc/cmd-ts. It loads qubu.config.js by default; --config <path> selects another application-owned module. Every command accepts --format human|json (default human) and --non-interactive. Commands do not prompt today; --non-interactive records the deployment contract and missing explicit input still fails.

Configuration

Export a typed config and keep credentials inside the adapter factory:

import { createClient } from "@libsql/client"
import { libsqlMigrationAdapter, readLibsqlMigrationSnapshot } from "@qubu/adapter-libsql/migration"
import { defineConfig } from "@qubu/cli/config"
import snapshot from "./schema.snapshot.js"

const url = process.env.DATABASE_URL
if (!url) throw new Error("DATABASE_URL is required")

export default defineConfig({
  artifacts: "./migrations",
  snapshot,
  environment: "production",
  adapter: () =>
    libsqlMigrationAdapter(createClient({ url }), {
      readSnapshot: readLibsqlMigrationSnapshot,
    }),
  provenance: { source: "my-service" },
})

snapshot may be a value or async factory. Alternatively provide both schema and snapshotFromSchema. Optional configuration owns operation approvals, custom programs, renderer/server constraints, baseline operator metadata, and reconciliation proof. artifacts is resolved from the CLI working directory.

Field Required Meaning
artifacts yes Artifact directory, relative to the command working directory unless absolute
snapshot one snapshot source Snapshot value or sync/async factory
schema + snapshotFromSchema one snapshot source Application-owned conversion when the source is a Qubu Schema
adapter database commands Sync/async factory returning a migration adapter
approvals no Sync/async operation policy; receives the operation, finding codes, and requested CLI reason
customPrograms no Exact operation substitutions with execution requirements and provenance
renderer, serverVersion, constraints no Renderer identity and target compatibility constraints
provenance no Artifact source/revision/actor/metadata; defaults to { source: "@qubu/cli" }
environment no development, test, staging, or production; context only
baselineOperator no JSON-safe operator metadata stored in a baseline
verifyReconciliation reconcile only Application-owned proof of the selected live outcome

Commands

Syntax Reads or writes Important failure behavior
qubu migrate create <id> [--approve <operation-id=reason>...] [--approved-by <actor>] [--dry-run] Verifies the full repository, plans from its embedded final snapshot, seals, then writes one canonical artifact unless dry-run Unknown operation IDs or missing exact approvals fail policy
qubu migrate verify Strictly decodes and verifies every artifact and the complete chain Any malformed, tampered, forked, gapped, or mismatched artifact fails validation
qubu migrate status Opens a session and lease; reports managed drift, unmanaged objects, pending artifacts, interrupted attempts, and incompatible requirements Recovery, validation, drift, and capability policy are distinct failures
qubu migrate apply [--dry-run] Applies the complete verified pending chain; dry-run performs status/preflight only It never limits discovery to Git-added or branch-diff files
qubu migrate baseline <id> --confirm <fact>... [--dry-run] Without dry-run, strictly compares the live managed schema, initializes an empty journal, records baseline, then writes the artifact Requires an empty artifact repository and all seven exact confirmations; dry-run does not inspect the database
qubu migrate reconcile <attempt-id> --outcome applied|rolled_back --reason <text> Runs application-owned verification, then records the explicit outcome Requires verifyReconciliation in config; no automatic inference
qubu schema bootstrap [--approve <operation-id=reason>...] [--dry-run] Plans an empty SQLite or PostgreSQL snapshot through diff/plan/program; executes through the normal executor unless dry-run Rejects other dialects; unsafe or incomplete facts still require exact approvals or custom programs

JSON output is stable, newline-terminated, recursively key-sorted, and redacts credential-like keys and credentials or secrets embedded in URLs. Human output is deliberately terse. Signals propagate through adapters; an abort exits 130.

Exit Meaning
0 Success
2 CLI usage or argument error
3 Artifact, repository, journal, or other validation failure
4 Policy or adapter-capability refusal
5 Managed schema drift
6 Recovery or reconciliation required
7 Adapter, concurrency, rollback, uncertain-outcome, or other execution failure
130 Aborted

Status, drift, and bootstrap

Status compares managed physical schema facts against the embedded expected snapshot. Logical IDs help reporting but do not prove equality. Objects not owned by the managed snapshot are returned separately as unmanagedObjects; Qubu journal objects are excluded by migration snapshot readers.

schema bootstrap is for a fresh SQLite database or a fresh PostgreSQL schema. It produces the same reviewed plan, versioned program, sealed artifact, and executor path as a migration. A complete PostgreSQL snapshot retains standalone enums as authoritative objects; bootstrap creates each enum before a table that uses it as a native column type. SQLite inline constraints are compiled into table creation, while table rebuilds are explicit phases with copy/postcondition checks. Session settings such as SQLite PRAGMAs remain in the application or adapter setup.

Use the reviewed complete snapshot directly as the PostgreSQL target:

qubu schema bootstrap --dry-run --format json --non-interactive

The dry run prints the ordered phases without opening the adapter. Remove --dry-run only after reviewing any operation IDs that require --approve or an application-owned custom program. Bootstrap does not import or replay Drizzle migration history.

Baseline and cutover checklist

A baseline is a statement about the live database now, not a replay of its history. Before supplying all seven confirmations, the operator must verify:

  • database-target: the connection names the intended environment;
  • snapshot-source: the reviewed snapshot is the intended source of truth;
  • zero-managed-drift: strict inspection reports no managed mismatch;
  • backup-restore-ready: backup and restore procedures are ready;
  • other-migrators-stopped: no other migration runner can race the cutover;
  • application-compatible: deployed code is compatible with the live schema;
  • legacy-history-cutover: the team accepts the new baseline as the lineage start.

For example, repeat --confirm once per exact value. The CLI rejects missing or unknown confirmation names even outside production; environment is reported as context rather than used to weaken the policy.

qubu migrate baseline lotta-cutover \
  --confirm database-target \
  --confirm snapshot-source \
  --confirm zero-managed-drift \
  --confirm backup-restore-ready \
  --confirm other-migrators-stopped \
  --confirm application-compatible \
  --confirm legacy-history-cutover \
  --format json --non-interactive

After success, preserve the written baseline artifact with the repository. Its sequence is zero, its parent is null, and later migrations extend its digest.