Files
wownerogue/docs/CLONE_MIGRATIONS.md
T

80 lines
4.0 KiB
Markdown

# Disposable clone migration gate
`npm run db:migrate:clone` applies the release's normal ordered migrations to an existing,
disposable PostgreSQL clone. It is the migration step for a restored production dump before an
immutable release is eligible for activation. It never creates, restores, drops, audits, or
activates a database.
## Hard target contract
The runner refuses to open a connection unless all of these conditions hold:
- `DB_HOST` is explicit loopback (`127.0.0.1`, `::1`, or `localhost`) or the standard local Unix
socket directory `/run/postgresql` or `/var/run/postgresql`;
- `DB_NAME` starts with `monerogue_`, `wownerogue_`, or `wowngeon_` and contains a complete
underscore-delimited `clone`, `restore`, or `canary` token;
- the name contains no live marker beginning with `prd`, `prod`, `production`, `live`, `mainnet`,
`master`, `primary`, `current`, or `active`;
- `CLONE_MIGRATION_EXPECT_DATABASE` exactly equals `DB_NAME`;
- `CLONE_MIGRATION_CONFIRM` exactly equals `MIGRATE_DISPOSABLE_CLONE:<DB_NAME>`;
- `DB_PORT` and `DB_USER` are explicit, `DATABASE_URL` is absent, and the process receives no
command-line arguments.
After connecting, the runner checks PostgreSQL's `current_database()` before any DDL. It then calls
the same `DatabaseManager.runMigrations()` used by application startup: lexical filename order,
the `schema_migrations` ledger, and one transaction per newly applied file. Finally, it requires the
ledger to equal the complete repository migration manifest, in order, with no missing or extra
entry. The manifest is discovered from the candidate release and must be contiguous from 001; the
current floor is migration 043. A successful current release reports:
```text
clone_migration_status=ok
database=monerogue_restore_20260721t220048z_28d513b29b36
migration_count=43
latest_migration=043_durable_solo_restart_snapshots.sql
```
## Invocation
Create and restore the clone with the reviewed backup procedure first. Keep the target variables in
a protected operator-owned environment file and database credentials in a mode-0600 `PGPASSFILE`;
do not type a password, connection URL, or target as a command argument. For example, the inherited
non-secret target environment is:
```dotenv
DB_HOST=127.0.0.1
DB_PORT=5432
DB_NAME=monerogue_restore_20260721t220048z_28d513b29b36
DB_USER=monerogue
PGPASSFILE=/run/wowngeon/clone-migration.pgpass
CLONE_MIGRATION_EXPECT_DATABASE=monerogue_restore_20260721t220048z_28d513b29b36
CLONE_MIGRATION_CONFIRM=MIGRATE_DISPOSABLE_CLONE:monerogue_restore_20260721t220048z_28d513b29b36
```
Database names use lowercase letters, digits, and underscores only. Normalize a release ID into a
separate safe tag rather than copying a hyphenated artifact ID into `DB_NAME`.
Load that protected environment through the reviewed service/wrapper, change into the extracted
candidate's `src` directory, and run only:
```bash
npm run db:migrate:clone
```
Do not append `--` arguments. The runner never logs host, role, password, connection URL, or
`PGPASSFILE` contents. `DB_PASSWORD` is supported only for an already-protected inherited service
environment and is kept in memory; `PGPASSFILE` is preferred.
## Required follow-on gates and limits
Run `scripts/deploy/financial-audit.sql` and the restored-data constraint validation gate against
the same clone after migration. Compare the result with the pre-migration audit, then test the
candidate and predecessor against the migrated clone as required by the fleet runbook.
This runner does not prove that a database with a disposable-looking name is operationally
disposable, does not prevent another application process from connecting, and does not acquire an
activation-wide drain lock. The operator must create the clone from the intended fresh dump, keep
it outside public service configuration, and destroy it separately after the release decision. A
successful result authorizes only the next audit/test gate; it does not authorize a symlink switch,
wallet action, payout, or traffic restoration.