Skip to content

feat: add database export, import, and copy for backup and migration#37

Merged
AshDevFr merged 4 commits into
mainfrom
feat/migrate-data-transfer-engine
Jul 5, 2026
Merged

feat: add database export, import, and copy for backup and migration#37
AshDevFr merged 4 commits into
mainfrom
feat/migrate-data-transfer-engine

Conversation

@AshDevFr

@AshDevFr AshDevFr commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the ability to move a Codex instance's entire dataset between databases and to back it up to a portable archive. This enables migrating an existing SQLite deployment to a PostgreSQL-backed, worker-separated setup with a faithful 1:1 copy of all data, restoring from a snapshot, and pulling one instance's data into another.

Motivation

Codex previously had no way to move data between database engines or to take a portable backup. The existing backup tooling was PostgreSQL-only, and a raw SQL dump cannot cross engines safely because SQLite and PostgreSQL store UUIDs, JSON, and booleans differently, risking silent corruption. Operators who started on SQLite had no supported path to a scalable PostgreSQL deployment without losing metadata, ratings, reading progress, uploaded covers, and plugin state.

Changes

  • Adds a codex export command that writes the database and its on-disk artifacts (thumbnails, uploaded covers, plugin data) to a single portable .tar.gz, with flags to include the PDF render cache or to exclude individual artifact groups.
  • Adds a codex import command that loads an archive into an instance, translating data correctly for the target engine and rewriting stored file paths to the target's configured directories. It refuses to run against a database that already holds user data unless --replace is given, or when the archive doesn't match the instance's schema.
  • Adds a codex copy command that transfers rows directly between two databases; each side is selected via --from/--to URLs, CODEX_SOURCE_DATABASE_URL/CODEX_TARGET_DATABASE_URL environment variables, --from-config/--to-config files, or the local config for an omitted side.
  • Export/import bundle and restore the on-disk artifact directories automatically; copy transfers database rows only and leaves files to be synced separately.
  • Adds a Backup & Migration guide documenting the commands, a SQLite-to-Kubernetes/PostgreSQL migration runbook, and backup/restore usage.

Notes

  • Import and copy build the target schema by running migrations on the destination; the source is treated as read-only. For a consistent result the source should be quiesced (writes stopped) during export or copy.
  • Encrypted values (such as plugin credentials) are transferred as ciphertext, so the destination must be configured with the same encryption key as the source.
  • The bulk load temporarily disables foreign-key enforcement, which on PostgreSQL requires the target connection to be a superuser or the database owner.
  • Live bidirectional synchronization between two running instances is intentionally out of scope.

AshDevFr added 4 commits July 4, 2026 14:21
Introduce the codex-migrate crate: a portable, engine-agnostic core for
1:1 data transfer between any two SeaORM backends, the foundation for the
export/import/copy subcommands and SQLite-to-PostgreSQL migration.

Rows move as typed Models so SeaORM handles engine-specific representation
differences (UUID blob vs native uuid, text JSON vs JSONB, 0/1 vs bool)
by construction, avoiding the corruption risks of a raw SQL dump.

- generic per-table dump/load/copy/count/truncate over any entity
- an x-macro registry listing every table once, with a drift-guard test
- transaction-scoped foreign-key disabling during bulk load
- per-table row-count verification
- transfer(): disable FK, truncate, load, and commit in one destination
  transaction (SQLite re-validates foreign keys at commit)

Loads truncate first because migrations seed data, so a faithful mirror
must overwrite the seeded rows. Adds SQLite round-trip, JSON-fidelity,
overwrite/idempotency, and registry-drift tests.
Build the tar.gz export bundle on top of the transfer engine: the
database as one NDJSON file per table plus a manifest, alongside the
on-disk artifacts the database only references by path (thumbnails,
covers, plugin data).

- manifest records format and schema version, per-table row counts, and
  each bundled artifact group's source base directory
- export dumps the database to a staging dir and streams artifacts
  straight from their source dirs into the archive (no intermediate copy)
- import extracts, loads the database (disable FK, truncate, load, commit),
  unpacks artifacts to the target dirs, and re-roots stored file paths
- path re-rooting rewrites thumbnail and cover path prefixes from the
  source base dir to the target's, with path-boundary-safe matching, so
  images resolve on an instance whose files.*_dir differ

The schema-version and fresh-target safety checks consume the extracted
manifest and are left to the CLI layer. Adds an archive round-trip test
(import into differing artifact dirs) and path re-rooting unit tests.
Expose the migration engine as three subcommands on the codex binary,
completing the SQLite-to-PostgreSQL migration and backup workflow.

- export: write the database and on-disk artifacts (thumbnails, uploads,
  plugin data; PDF cache opt-in) to a portable .tar.gz, sourced from the
  instance config
- import: run migrations on the target, refuse a schema-version mismatch
  or a target that already holds user data (unless --replace), then load,
  unpack artifacts, re-root file paths, and print a summary plus an
  encryption-key reminder
- copy: transfer rows directly between two databases; each side resolves
  from a URL flag, a *_DATABASE_URL env var, a --*-config file, or the
  local config, requiring at least one explicit side

Adds a database-URL parser (sqlite/postgres), a fresh-target guard over
user-owned tables, and a manifest peek that reads only the manifest for
pre-flight checks. Covered by command round-trip, guard, and resolver
tests.
…guide

Verify the migration path end to end and document how to use it.

- add a gated SQLite-to-PostgreSQL round-trip test that seeds a connected
  fixture and asserts row-count parity plus JSON, float, bool, and UUID
  foreign-key fidelity across engines; skips when no test database is
  reachable
- re-export the migrate crate as codex::migrate so integration tests can
  reach it
- document export, import, and copy with a SQLite-to-Kubernetes-Postgres
  runbook, covering the encryption-key carry-over and the database-owner
  requirement for the bulk load
@AshDevFr AshDevFr merged commit b07d856 into main Jul 5, 2026
22 checks passed
@AshDevFr AshDevFr deleted the feat/migrate-data-transfer-engine branch July 5, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant