Skip to content

gordon-migrate

Purpose

One-shot binary that owns the Gordon v7 schema. Connects to Postgres via DATABASE_URL, applies every pending migration bundled in migrations/, and exits 0 on success or 1 on failure. No HTTP server, no workers, no long-running state. Runtime services connect under least-privilege roles and trust that migrations are current. Every service boot waits on service_completed_successfully in the compose dependency graph.

Version + status

3.25.4 — production. Single source of truth for the Gordon v7 schema since v1.0.0 (2026-04-20). Domain-split migration layout complete (2026-04-25). Live on srv-apps since 2026-05-01. No open epics.

Commands

gordon-migrate [apply]    # Apply all pending migrations (default, no subcommand needed)
gordon-migrate drift-check # Verify live schema matches build-time fixtures; exits 0/1
  • apply — connects as the migration-privileged role (CREATE TABLE + GRANT + ALTER). Idempotent: re-running is a no-op when migrations are current.
  • drift-check — read-only, connects under gordon_drift_reader (pg_read_all_metadata + introspection). Checks: schema checksum, grant matrix, column grant matrix, NOTIFY allowlist. Triggers the Ansible cron + Prometheus alert (DP-08).

Migration layout

Nine domain-split files (post-split, 2026-04-25), each owning one concern:

FileConcern
0001_schemas.sqlSchemas + role search_path
0002_market_data_tables.sqlAll market_data.* tables
0003_trading_core.sqlCore trading.* (runs, roundtrips, equity, pipeline_state)
0004_trading_orders.sqlOrder lifecycle (intents, events, fills, trades)
0005_trading_bots.sqlBot lifecycle (configs, commands, events, leases, positions)
0006_trading_risk.sqlRisk (events, config, audit, state singleton + NOTIFY)
0007_ipc_channels.sqlipc_notify_trigger() + AFTER INSERT triggers
0008_roles_and_grants.sqlSecurity file (roles, GRANTs, REVOKEs, RLS)
0009_market_data_reader_views.sqlCross-cutting reader views

38 migrations total as of 2026-05-17. Incremental patches accumulate after the base nine. Latest: 0036_daily_notional_caps.sql, 0037_audit_event_flatten_crashed_during.sql, 0038_runs_strategy_code_revision.sql.

Environment

  • Required: DATABASE_URL (or --database-url CLI flag)
  • Optional: RUST_LOG (default info)
  • No HTTP port. Headless binary.

Docker

Image: ghcr.io/dlepaux/gordon-migrate. Distroless-nonroot, no exposed port. Runs as a one-shot container on srv-apps. Not published to kellnr — consumed exclusively as a container image.

Invariants (safety-critical)

  • Superuser / bootstrap role only. The only gordon-* container that connects as the migration-privileged role. Every runtime service uses a least-privilege DSN.
  • One invocation per deploy. The compose / Ansible flow gates on service_completed_successfully — every service boot waits for this.
  • Forward-only migrations. No rollback logic. Pre-prod: edit in place (volume wipes). Post-prod: forward-only patches only.
  • Schema-checksum + grant-matrix fixtures pinned in git. Out-of-band DDL or grant drift fails the drift-check command loud.
  • Zero #[allow(...)].

SPOF status

A failed apply keeps the entire v7 stack down by design. Recovery procedure and failure-mode table: gordon-migrate/docs/spof-runbook.md.

Where it lives

  • Repo: gordon-migrate/
  • Container: ghcr.io/dlepaux/gordon-migrate
  • Not on kellnr.
  • gordon-test-db — applies the bundled MIGRATIONS for per-test DB clones (feature test-migrations).
  • gordon-bus — owns bus.outbox schema (migrations 0017 + 0025).

Gordon — keep compounding without blowing up