zanith

migrate / recover · 01 — Phase 2

A drop is not a delete until you say it is.

Every destructive op produces an artifact— a row in a real Postgres table, plus the renamed column, archived data, or shadow copy that lets the op be reversed. Restore in one command. Cleanup when you're sure. None of Prisma, Drizzle, Atlas, or Sequelize ship this.

artifact lifetime
t0
live

column or table is in use

t1
destructive op

softDropColumn / archiveColumn / dropTable

t2
artifact created

row in _zanith_migration_artifacts

t3a
restore

rename back · or refill from archive

t3b
cleanup

drop archive · delete row · permanent

↑ restore (default expectation)
↓ cleanup --older-than (acceptance)
5 artifact kinds · 6 CLI commandsartifacts table: _zanith_migration_artifactschecksum-verified restorerecover.ts · v0.2

02 — Five strategies

Two strategies, three shapes, one forensic.

soft-drop renames in place — cheap, instant, no data movement. archive copies data out before dropping. Each comes in two flavours (column / table). The fifth captures schema for forensic use only.

soft_drop_columnrename only

Soft-drop column

The column gets renamed to a parked name. Restore renames it back. Cheapest reversible drop.

no row movement
soft_drop_tablerename only

Soft-drop table

The whole table gets renamed. Indexes and constraints follow. Restore renames it back.

no row movement
archive_columncopy + drop

Archive column

Values are copied into a parallel archive table before the column is dropped. Restore back-fills using the source PK.

row count + checksum
archive_tableschema move

Archive table

The table moves to a parallel schema. Frees the original name. Restore moves it back.

no row movement (Postgres)
rebuild_tableforensic only

Rebuild table

Captures the pre-rebuild schema for inspection. Auto-restore would be unsafe — clients have already adapted to the new shape.

full row copy

03 — The bookkeeping

One row remembers everything.

Every reversible op writes a row. The row knows what was dropped, where the data parked, how to verify it's untouched, and when the auto-cleanup window closes.

anatomy of an artifactsoft_drop_column · users.legacy_uuid
what's gone
users.legacy_uuid
where it parked
_zanith_dropped_legacy_uuid_a4c
integrity proof
md5 · 8a3f…
rows captured
when
2026-05-01 11:30
auto-cleanup
2026-05-31
live artifacts · sample5 reversible
  • users.legacy_uuidsoft_drop_column2h ago
  • orders.discount_codearchive_column14h ago
  • audit_log_2024soft_drop_table3d ago
  • sessionsarchive_table12d ago
  • usersrebuild_table1d ago

04 — Seven commands

Two read. Five write.

The same zanith binary that applied the migration knows how to walk it back.

read-only

recover list

Show every artifact, sorted oldest last.

recover inspect

Detailed view of one artifact: source, physical name, recovery plan, checksum.

write

recover restore-column

Re-add the column. Picks the latest matching artifact (soft-drop or archive).

recover restore-table

Bring a soft-dropped or archived table back. Indexes and constraints follow.

recover export

Stream archive rows out as CSV / JSONL / JSON before purge.

recover purge

Drop the archive data, delete the bookkeeping row. Permanent.

recover cleanup

Bulk purge artifacts older than N days. The maintenance command — run on a cron.