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/1apply— 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 undergordon_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:
| File | Concern |
|---|---|
0001_schemas.sql | Schemas + role search_path |
0002_market_data_tables.sql | All market_data.* tables |
0003_trading_core.sql | Core trading.* (runs, roundtrips, equity, pipeline_state) |
0004_trading_orders.sql | Order lifecycle (intents, events, fills, trades) |
0005_trading_bots.sql | Bot lifecycle (configs, commands, events, leases, positions) |
0006_trading_risk.sql | Risk (events, config, audit, state singleton + NOTIFY) |
0007_ipc_channels.sql | ipc_notify_trigger() + AFTER INSERT triggers |
0008_roles_and_grants.sql | Security file (roles, GRANTs, REVOKEs, RLS) |
0009_market_data_reader_views.sql | Cross-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-urlCLI flag) - Optional:
RUST_LOG(defaultinfo) - 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-checkcommand 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.
Related
- gordon-test-db — applies the bundled
MIGRATIONSfor per-test DB clones (featuretest-migrations). - gordon-bus — owns
bus.outboxschema (migrations 0017 + 0025).