Skip to main content

Codex session migration

orka session migrate preserves Codex's original thread UUID and rollout bytes. It supports the codex/paginated@0.160.0 profile on Linux and macOS. Legacy rollouts and encrypted reasoning context are outside this delivery.

Use a model that supports non-reasoning turns and set the destination Codex Agent's spec.runtime.defaultReasoningEffort: none. Set model_reasoning_effort = "none" in the source and return homes too. This selects Codex's non-reasoning mode; it does not bypass capture validation. A rollout with encrypted reasoning is still rejected, even if the configured effort is none.

Orka defaults to 8 MiB of encoded bundle data, configurable up to 64 MiB. This is a consumer transport limit, separate from SessionKit's native storage limits. A bundle contains the verified manifest and rollout. Authentication files, native SQLite databases, provider configuration, and process memory are excluded.

The controller and runtime must both support native Sessions. Use the pinned Codex 0.160.0 / codex-acp 2.1.1 runtime image. Private checkpoints use the controller's existing execution-snapshot encryption key with separate associated data. Retain that key with the SQLite backup.

Deploy this schema on a new installation. Existing Orka databases are not upgraded in place; see Upgrading.

Current limitations​

Returning to an existing Codex home​

Export cannot merge a returned thread into a populated ~/.codex or replace its older local copy. The original UUID is preserved, so a return can collide with that copy. Existing paginated SQLite state can also shadow a newly copied rollout. SessionKit does not modify those databases or reconcile divergent local and cluster histories.

Use a separate fresh CODEX_HOME on return and resume the UUID explicitly. Keep CODEX_SQLITE_HOME unset or set it to an absolute path without surrounding whitespace resolving to that exact private CODEX_HOME. The export CLI rejects relative and whitespace-padded overrides, resolves symlinks, and rejects a different database directory, including a nested directory whose existing database state is not checked by fresh-home validation, before fetching or staging the checkpoint. Your original home remains unchanged and can still be an import source. Safe per-thread replacement in an existing home is not implemented.

Encrypted native reasoning​

A non-null encrypted_content field anywhere in the selected rollout, including reasoning items or compaction history, rejects the migration. This is provider-issued opaque native context, not Orka's checkpoint encryption at rest. Preserving its bytes has not established a supported reload contract with destination provider settings and credentials. Migration neither decrypts nor strips it to make the thread acceptable.

Setting reasoning effort to none before creating and continuing a thread can avoid reasoning items on a model that supports that mode. It does not remove existing encrypted content, and some models do not support none. Supported non-encrypted compaction records remain byte-identical. This is not general migration support for reasoning-enabled Codex sessions.

Bundle size​

The default limit is 8 MiB, or 8,388,608 bytes, for the complete encoded bundle. Operators can select a limit from 1 byte through the 64 MiB hard ceiling with --native-session-max-bytes or ORKA_NATIVE_SESSION_MAX_BYTES on the controller. The controller propagates that policy to built-in runtime pools, including workspace-backed pools. Independent runtimes must set the same environment variable; their advertised capability limit is also enforced. Changing the built-in runtime policy uses the existing pool replacement and drain path.

The local CLI has a separate matching limit. Use orka session migrate --max-bundle-bytes 16777216 import ... or the same flag with export for a 16 MiB bundle. ORKA_NATIVE_SESSION_MAX_BYTES supplies the CLI default when the flag is omitted. Raising only the local CLI limit cannot raise the cluster limit. New clients request the additive native-size capability with X-Orka-Native-Session-Limits: 1; older clients receive the original capability shape without the new field. An older runtime that does not advertise its limit retains the previous 512 KiB bound and rejects larger native restores before request delivery. Capture requests use the same opt-in header; older callers retain the 512 KiB capture limit and its structured rejection behavior.

This is a byte limit, not a model token limit. The JSON bundle base64-encodes both the rollout and manifest, leaving approximately 6 MiB of combined raw bytes under the 8 MiB default, slightly less after JSON overhead. The HTTP envelope adds another encoding layer with its own bounded allowance. Ordinary API, prompt, and ACP message limits are unchanged.

The cap applies to import and every runtime checkpoint, including growth after continuation. It counts the full saved rollout, including tool outputs and historical records retained after compaction. Orka does not compress or truncate the bundle. Oversized native data is rejected; an established native-continuity Session does not silently fall back to transcript-only continuation. Lowering the policy preserves stored checkpoints but can block export, restore, or new capture until the limit is raised again. Before lowering the cap, pause new native Tasks, let active turns finish and durably finalize their checkpoints, and verify that retained checkpoints fit the proposed cap. If capture is already blocked, preserve the exact runtime Pod and its capture evidence; force-deleting it can lose uncommitted native history. This is an Orka transport restriction, not SessionKit's native storage limit. Larger limits increase memory, temporary storage, and encrypted database usage; choose them for the expected workload.

Explicit API target and authentication​

Native migration requires --server or a saved API endpoint. Automatic Kubernetes Service discovery is not used: a familiar name, labels, and Service UID do not establish the application's identity. Select the trusted Orka API endpoint explicitly before sending portable history.

Provide an API bearer credential (--token, a saved Orka token, or a static kubeconfig bearer credential) or a supported transaction token. Kubernetes exec, auth-provider, and certificate credentials are not automatically resolved into an Orka API credential. Missing API authentication fails before capture or persistence of the migration bundle. Configure the endpoint and authentication before running the commands below.

Import a local thread​

Stop every Codex process using the source home before capture. --source-stopped records that assertion; it does not terminate a local client. Use a new Session name and a journal directory outside the source home:

orka session migrate import migrated-review \
--codex-home /absolute/source/codex-home \
--thread 01a10020-1222-76e3-977d-d5165792ae72 \
--journal-dir /absolute/private/import-journal \
--source-stopped

Import reserves a fresh non-Gateway Session. It allocates no runtime or Task lease. An existing Session, including an empty one, cannot be overwritten.

Submit a normal Codex Task with spec.sessionRef.name: migrated-review and spec.sessionRef.create: true for the first continuation. Use the Task's current workspace and tool policy. During admission, Orka installs the bundle in the child's fresh private CODEX_HOME before starting codex-acp. It uses capability-gated session/resume with the original UUID and current cwd, provider, MCP tools, approvals, and sandbox configuration. The runtime must advertise resume support and prove restoring the exact bundle before Orka sends the new user prompt. Canonical history is not injected again. Missing resume support or a failed resume rejects creation without falling back to session/new. The imported history stays native; the Orka transcript records new Tasks.

Export a checkpoint​

After a successful Task, Orka proves the provider and its descendants have exited before capture. The checkpoint and canonical terminal result commit together before the Session lease is released. The runtime home is deleted only after that commit. The next Task cold-starts with the saved checkpoint and fresh credentials.

Export uses the last committed checkpoint. It does not force an active Task to stop. The checkpoint must still match the exact Session owner and canonical transcript boundary.

orka session migrate export migrated-review \
--codex-home /absolute/fresh/codex-home \
--cwd /absolute/local/workspace \
--journal-dir /absolute/private/export-journal \
--codex-bin /absolute/codex-0.160.0

The destination must be a fresh isolated Codex home with directory mode 0700. The journal must be outside it and private, also with directory mode 0700. Authenticate the destination with current local credentials and configure its provider before resuming the UUID. Migration does not copy source authentication or source policy into authority.

Retry and failure handling​

Retain the journal and repeat the same command after an uncertain response. The CLI reuses its saved bundle and operation ID. Export also reuses the same frozen SessionKit install plan and receipt. Changing the target, bundle, or cwd with that journal is rejected. The explicitly selected API endpoint must stay unchanged across retries. Automatic Kubernetes Service discovery and streaming are not available for native migration; use --server or a saved trusted API endpoint and supply API authentication as described above.

An uncertain runtime install retains its target and frozen journal and closes pool admission. Exact create retries report that retained outcome without allocating another home or starting a child. Automatic reconciliation of that supervisor creation state is not part of this delivery.

A capture failure after proven writer exit can retry from the retained private home under a new fenced operation. Receipt reconciliation never starts a capture. If native capture is unsupported and there is no prior native checkpoint, Orka records one warning execution event with reason native_capture_unsupported. The next turn uses the canonical transcript instead of native continuity. The warning survives controller retries and restarts without duplicates. If a prior native checkpoint exists, unsupported capture still blocks settlement; Orka never silently replaces required native continuity with transcript replay.

Failed, cancelled, or lost provider prompts do not publish a new native checkpoint. The runtime that ran them is poisoned and retired, so a Task that appends to a Session with a native checkpoint settles its terminal marker with the existing checkpoint carried forward: the checkpoint's transcript boundary advances past the marker, the lease is released, and the next Task resumes the same native state. That state omits only the prompt that produced no result. A successful prompt can retain its native state after delivery failure once workspace finalization and canonical settlement complete. Poisoned workspace validation failures cannot produce a native checkpoint; for appending Tasks on a Session with a native checkpoint they retain the exact runtime evidence and hold the Session lease until reconciliation. Accepted turns with sessionRef.append: false leave the checkpoint unchanged and retire their runtime before a later Task loads the saved state. Native restore reloads Codex's whole saved conversation, so sessionRef.maxMessages limits only the canonical transcript Orka sends when no checkpoint is used. A Task whose sessionRef.throughMessageId names any message other than the Session's latest fails before it binds the Session, with reason NativeSessionHistoryBoundaryUnsupported, because restore cannot stop at an earlier message. A capture intent that reaches the runtime only after it expired is reconciled against the runtime's own record. When the same runtime incarnation never recorded that capture, it reports native_capture_not_started and the controller begins one fresh capture under a superseding intent instead of reconciling indefinitely. A changed canonical boundary makes an older checkpoint unavailable for restore or export. If a native-continuity Session cannot produce a supported checkpoint, Orka retains the runtime evidence and blocks finalization. It does not silently start a new provider thread. An ordinary Session with no native checkpoint may use canonical continuity after a terminal format or size rejection, but only after the runtime proves process exit. Before a capture intent or native checkpoint exists, canonical recovery also remains available after the exact runtime has been retired or replaced. A surviving native-capable runtime still produces its first checkpoint.

Normal pool drain preserves resident native evidence until the controller commits the checkpoint and deletes the runtime. Uncommitted native state uses the runtime Pod's temporary storage; a forced Pod loss can destroy it. Recovery with native continuity or a capture intent then blocks on the missing exact runtime or capture receipt.

Native bundles are private Session data, accessible only through the migration endpoint. Export requires get on core.orka.ai/sessions. Import requires create on the Session collection and get on the requested name, plus the corresponding context-token Session scopes when enabled. Session deletion removes the native bundle and its receipts with the existing cleanup flow.

Abrupt-crash verification​

The subprocess regressions use explicit barrier acknowledgements and SIGKILL, not graceful shutdown or returned persistence errors. They cover completed capture before checkpoint commit, committed checkpoint before runtime deletion, and supervisor death after capture admission but before snapshot publication. They use scripted ACP responses, without live inference.

The controller test runs the production dispatcher in subprocesses. It keeps the Kubernetes API and runtime fixture outside the killed process and reopens the same SQLite database in a replacement process. It checks the prior checkpoint, canonical boundary, immutable capture receipt, epoch fences, prompt counts, and exact-generation cleanup. By default it uses an isolated envtest API server. To exercise a disposable kind API, run from the VM checkout with a kindctl-scoped kubeconfig:

ORKA_NATIVE_CRASH_KUBECONFIG="$(.agents/skills/kindctl/bin/kindctl path --tag native-crash)" \
go test ./internal/controller \
-run '^TestACPDispatcherNativeSessionProcessCrash$' -count=3 -v

The supervisor test is Linux-only and requires root plus the production ACP exec helper from the pinned runtime image. Its parent kills the subprocess at an acknowledged in-progress capture and checks the missing success receipt, unchanged committed bundle, private evidence, and replacement-boot fencing. It pins the ACP child with a pidfd and proves child exit after supervisor death before any failure-path test cleanup signals the child:

./supervisor.test -test.run '^TestSupervisorNativeCaptureProcessCrash$' -test.count=3 -test.v

Run these checks only in disposable test environments. They do not make uncommitted runtime storage durable or enable automatic recovery of a missing capture receipt after forced runtime loss. That case still fails closed.