DEV Community

李成龙
李成龙

Posted on

Why Copying .codex Isn't Enough: Building a Cross-Platform Codex Session Migrator

When I moved Codex work between computers, I expected the migration process to be simple: copy the old .codex directory to the new machine and reopen Codex.

The files were there, but the result was not what I expected. Historical projects did not reliably appear, sessions could be missing from the interface, and paths stored on macOS made no sense on Windows.

That problem led me to build Codex Migrate, an open-source Rust desktop application and CLI for migrating, repairing, backing up, and exporting local Codex sessions.

Codex Migrate application overview

This article is less about announcing the project and more about the engineering problems behind it.

The first incorrect assumption: files are the whole state

A Codex session is primarily represented by rollout JSONL data, but simply placing those files somewhere under the new .codex directory does not guarantee that the application will present them correctly.

There are several pieces of state involved:

  • Rollout JSONL files containing the conversation history
  • Thread metadata such as title, timestamps, archive state, and current working directory
  • SQLite indexes used by the local application
  • Paths that were valid on the source computer

This creates an important distinction:

Preserving the conversation data is not the same as restoring a usable project and session index.

For the migration tool, I therefore treat rollout JSONL as the source of truth, while SQLite is an adapter used to restore the metadata needed by the target environment. The source database is never copied wholesale.

Paths are part of the problem

Consider a session created under:

/Users/alex/Projects/my-app

After moving to Windows, the corresponding project might be:

D:\Projects\my-app

The old path is not merely cosmetic. It can affect how a session is grouped, displayed, and reopened. A migration tool must understand several path families:

  • POSIX paths
  • Windows drive-letter paths
  • UNC paths
  • WSL paths such as /mnt/d/Projects

Codex Migrate groups sessions by their original working directory and asks the user to bind each selected project to a real directory on the target computer. It also supports parent-directory mapping. If all projects moved from /Users/alex/Projects to D:\Projects, one rule can map the entire tree.

For sessions whose projects no longer exist, there is a history-only mode. This preserves access to the conversation without pretending that the original working directory still exists.

Three-step migration workflow

A migration tool should not silently invent history

The next issue was conflict handling. A thread UUID may already exist on the target computer, so blindly overwriting files would be unsafe.

The merge planner uses conservative rules:

Source and target state Result
UUID does not exist locally Import it
UUID and content hash are identical Skip the duplicate
Target rollout is a full prefix of source Keep the longer source
Source rollout is a full prefix of target Keep the longer target
Same UUID has divergent content Stop and report a conflict

The last case matters. Two JSONL files with the same UUID may represent histories that diverged after synchronization or manual copying. Automatically concatenating them could create a conversation that never actually happened. Generating a new UUID would hide the identity conflict rather than resolve it.

The first version therefore refuses to splice divergent histories.

Transactions matter even for a local desktop utility

Session migration modifies user data, so a partially completed import is unacceptable.

The import flow is transactional at the application level:

  1. Check whether the target database appears to be in use.
  2. Create a SQLite Online Backup snapshot.
  3. Validate the selected rollout files.
  4. Write files into a staging area.
  5. Move them into their final locations.
  6. Update only schema fields detected at runtime.
  7. Verify rollout and database consistency.
  8. Restore the snapshot and remove new files if a step fails.

Rollback snapshots are stored under:

$CODEX_HOME/migration_transactions/<TRANSACTION_ID>/

The GUI also lets users inspect, restore, and delete old snapshots.

This was more work than a direct file copy, but migration software needs a clear failure model. "Most files were copied" is not a valid success state.

Supporting existing synchronized .codex directories

Not every user performs an explicit migration. Some people replace the new machine's .codex directory with the old one. Others synchronize it between two computers.

For those cases, copying sessions is unnecessary—the data already exists. What is broken is the current path metadata.

That led to a separate path repair workflow. It scans the local Codex environment, displays projects with invalid paths by default, and lets the user:

  • Map projects individually
  • Apply a parent-directory mapping
  • Include all projects when reviewing mappings
  • Preview changes before updating local metadata

Keeping path repair separate from import made the model easier to reason about: one operation moves session data, while the other repairs references to data already present.

Exporting a conversation is more complicated than printing JSON

I also wanted sessions to remain readable outside Codex. The first HTML exporter exposed another class of problems:

  • A rollout can contain multiple representations of the same assistant response
  • Tool events should not appear as duplicated chat messages
  • User and assistant messages need different layouts
  • Images and tool screenshots may be referenced through local paths or structured payloads
  • A portable export should not depend on a separate asset folder

The exporter now produces one self-contained HTML file per selected session. User messages appear on the right, while Codex responses use the wider left-side layout. Images and tool screenshots are embedded directly as data URLs.

This makes the export larger, but the portability tradeoff is worth it: one conversation equals one file.

Cross-platform GUI details still matter

The core is written in Rust, with egui/eframe for the native GUI. Cross-platform behavior was not limited to filesystem logic.

I encountered platform-specific differences in:

  • Font baseline alignment
  • Application icon embedding on Windows
  • macOS bundle signing
  • Native folder selection
  • Path separators during validation
  • GUI versus console subsystem behavior on Windows

One example: the initial macOS release contained a linker-generated ad-hoc signature on the executable but no complete bundle signature after resources were added. Gatekeeper reported the downloaded application as damaged. The fix was to sign the completed app bundle and verify it with codesign --verify --deep --strict before packaging.

It is a useful reminder that a successful compilation does not mean a distributable desktop application is correctly packaged.

Deliberate data boundaries

The tool intentionally migrates only session-related data. It excludes:

  • Authentication
  • Configuration
  • Skills
  • Plugins
  • Logs and caches
  • Device-specific state

This keeps the operation understandable and avoids moving credentials between computers. It also makes backups easier to inspect: the minimal backup uses the same basic structure as a Codex directory rather than a proprietary archive format.

Current state and limitations

Codex Migrate currently provides:

  • A native GUI in English and Chinese
  • A CLI using the same migration engine
  • macOS, Windows, Linux, and WSL path handling
  • Active and archived session support
  • Selective project and session import
  • Path repair and parent-directory mapping
  • Conflict-aware merging
  • Transaction backups and rollback
  • Self-contained HTML export

The main limitation is compatibility risk. Local Codex storage structures can change between versions, so the project performs runtime schema inspection instead of assuming one permanent SQLite layout. Even so, real-world reports remain important.

Source code

The project is available on GitHub under the MIT license:

https://github.com/ChenglongLi777/codex-migrate

Prebuilt releases are available for macOS and Windows. The binaries are not currently signed with commercial certificates or Apple-notarized, so the repository documents the relevant first-launch warnings and publishes SHA-256 checksums.

Codex Migrate is an independent community project and is not affiliated with or endorsed by OpenAI.

If you have moved Codex history between operating systems—or regularly synchronize a .codex directory—I would be interested in the edge cases you encountered.

Top comments (0)