# Postgres transfer rehearsal

Standalone Node + Postgres 16 example that rehearses a **logical** source →
destination transfer with `pg_dump` / `pg_restore`, verifies data and
application grants, then practices a write-freeze cutover. Use disposable local
databases only — never a customer or live Bex database.

## Prerequisites

- Node.js ≥ 22.12
- Docker (pulls `postgres:16-alpine`)
- `pg_dump` / `pg_restore` matching major 16 — host `PATH` tools are used only
  when their major matches the pinned server, otherwise the rehearsal
  container's tools are used (a newer host `pg_dump` emits settings the
  Postgres 16 server cannot replay)

## Quick start

```bash
cd examples/postgres-transfer
npm install
export SOURCE_DATABASE_URL=postgres://transfer_admin:transfer_admin@127.0.0.1:55434/transfer_source_fixture
export TARGET_DATABASE_URL=postgres://transfer_admin:transfer_admin@127.0.0.1:55434/transfer_dest_fixture
export APP_DATABASE_URL=postgres://transfer_app:transfer_app@127.0.0.1:55434/transfer_dest_fixture
npm run setup
npm run rehearse          # dump → restore → verify
npm run cutover           # freeze sample writers → final transfer → activate dest
npm test
npm run teardown -- --container
```

Downloads of this tree omit `.rehearsal/` archives, `.env`, and `node_modules/`.

## What it proves

| Stage | Behavior |
| --- | --- |
| Setup | Owned source + empty destination DBs, seeded relationships, `transfer_app` role |
| Dump | Custom-format archive; failed/interrupted output is never promoted |
| Restore | Refuses nonempty/unowned targets; `--no-owner --no-acl` + explicit app grants |
| Verify | Row/relationship checks, sequence advancement, least-privilege + forbidden DROP |
| Cutover | Shows writes-after-dump loss, stops **sample** writers, enables destination once |

Stopping the sample writer is **not** proof that every production writer is frozen.
After destination writes begin, switching back to the source leaves unreconciled
data — the cutover report surfaces that boundary.

## Ownership and safety

Destructive setup/reset/cleanup require the `transfer_rehearsal_meta` marker this
sample wrote. Hostnames must be local and database names must include
`transfer` / `fixture` / `rehearsal` / `tmp` / `test`. Credentials are never
printed in dump/restore diagnostics.

## Related docs

- Platform guide: `/docs/platform/postgres-transfer`
- Migration sequence: `/docs/platform/migrate-from-render`
