optio-codex
Run OpenAI Codex as an optio task — either as the interactive TUI embedded
in the optio dashboard via an iframe widget served by ttyd, or in
conversation mode (codex app-server over stdio) rendered by the
optio-conversation-ui widget. Local or remote (SSH) workers.
Install
pip install optio-codex
Requires Python 3.11+. Pulls optio-core, optio-host, optio-agents,
asyncssh, and aiohttp.
What it does
optio-codex launches codex inside a detached tmux session, serves the
TUI over ttyd, and coordinates with the host harness through the
optio.log keyword channel (STATUS / DELIVERABLE / DONE / ERROR). The
agent reads its task from an AGENTS.md file planted in the workdir.
The tmux+ttyd machinery follows the optio-claudecode pattern, including
browser handling: redirect — codex login opens the loopback OAuth URL
via xdg-open, which the redirect shim captures as a BROWSER: marker so
the harness surfaces it to the operator (who completes the sign-in),
instead of silently swallowing it.
Isolation
Each task runs under an isolated HOME (<workdir>/home, created at
prepare time) with CODEX_HOME pointing at <workdir>/home/.codex, so
the operator's real ~/.codex identity and config do not leak into the
session. The codex binary is launched via a per-task path
(<workdir>/home/.local/bin/codex), so teardown only ever kills this
task's process.
Filesystem sandbox
Beyond the per-task HOME, every codex tool subprocess is confined by
codex's own native sandbox — kernel-enforced (bundled bubblewrap
primary, Landlock+seccomp fallback on Linux), covering all shell/tool
commands the agent runs. optio-codex does not vendor claustrum for
this; it renders one resolved sandbox posture (fs_allowlist.py SSOT)
onto every launch surface: the interactive TUI argv, the codex exec
probe flags, and the codex app-server command line (-c sandbox_workspace_write.* overrides; the sandbox mode is selected
out-of-band via thread/start's sandbox enum — the 0.142.5 app-server
schema has no thread/start.sandboxPolicy object).
fs_isolation=True (the default) selects codex workspace-write: writes
are confined to the task workdir, /tmp, and any rw grants; reads are
not restricted. That read-open behaviour is a deliberate divergence from
optio-grok/optio-claudecode (whose sandboxes also deny reads) — so
AllowedDir("…", "ro") is a documented no-op on codex (an additive grant
that is already trivially satisfied), kept only for cross-wrapper config
portability. Only AllowedDir("…", "rw") changes behaviour, becoming a
sandbox_workspace_write.writable_roots entry (~/ expands against the
real host home at launch). Network access is OFF by default (stricter
than the other wrappers, whose fs sandboxes never touch the network);
network_access=True relaxes it. fs_isolation=False runs codex
unconfined (danger-full-access).
Extra grants and the mode are cross-validated at config time — e.g.
fs_isolation=True with sandbox="danger-full-access", or an rw grant
under an explicit read-only mode, raise ValueError rather than silently
mis-configuring the sandbox.
No optio-side enforcement guard is needed. codex fails closed: on a
host with no working sandbox mechanism (bubblewrap or Landlock), codex
errors or panics and the model's command never runs — it never falls back
to running unconfined (the only unconfined path is the explicit
--dangerously-bypass-approvals-and-sandbox opt-out, which optio-codex
never emits). This was verified empirically against codex-cli 0.142.5 (see
the Stage-8 probe verdict in docs/2026-07-02-optio-codex-design.md), so
optio-codex relies on that fail-closed guarantee instead of a launch-time
probe. As a free hardening bonus, .codex/ and .git/ under a writable
root stay read-only to the agent's shell, so the sandboxed agent cannot
rewrite its own per-task auth.json even though CODEX_HOME lives inside
the workdir.
Authentication
The primary mechanism is seeds: log in once, reuse the identity for
every later task. Run the setup task and log into codex interactively in
the embedded terminal (codex login --device-auth, or codex login --with-api-key); on teardown the session's home/.codex (auth.json +
config.toml) is captured as a reusable seed and surfaced through the
on_seed_saved callback. A later task started with
CodexTaskConfig(seed_id=…) merges that stored identity into its fresh
workdir before launch, so codex starts already logged-in — and the new
workdir is pre-trusted automatically ([projects."<workdir>"] trust_level = "trusted" appended to config.toml), so codex never prompts about an
untrusted directory. seed_id also accepts a SeedProvider callable that
leases a seed from a pool (the task's process_id is the lease holder).
Store-binding CRUD helpers (list_seeds / delete_seed / purge_seed)
operate over the {prefix}_codex_seeds collection.
Credential rotation (why a seeded session does more than merge-once):
codex's ChatGPT-mode auth.json carries a single-use rotating refresh
token (openai/codex#15410) — codex proactively refreshes it after 8 days
and on any 401, rewriting auth.json in place, and a used refresh token
invalidates every other copy. So a seeded session runs an in-session
credential watcher that saves the rotated auth.json back into the
seed (plus a final teardown backstop); pooled seeds take a lease (one
live lineage per seed — the watcher renews it and aborts the session on
lease loss); and verify_and_refresh_seed refreshes idle pooled seeds
host-free — a direct OpenAI OIDC refresh_token grant (no codex
process, no model turn, non-billable) that persists a fresh token before
the 8-day cliff, falling back to a headless codex exec probe only when
OIDC discovery is unreachable.
Fallbacks without a seed: pass an API key into the session env
(CodexTaskConfig(env={"OPENAI_API_KEY": …})) or log in interactively
(codex login) inside the embedded terminal.
Binary provisioning
The codex binary is resolved through an optio-owned, evictable cache on the
worker — OPTIO_CODEX_CACHE_DIR, else
${XDG_CACHE_HOME:-~/.cache}/optio-codex/bin — resolved host-side, so it is
correct on a remote SSH worker and never lives under a task workdir or the
operator's ~/.codex. On a cache miss the cache is seeded from a host
codex on PATH (cp -L deref → a stable copy), or, when none exists, the
pinned release is auto-downloaded (rust-v0.142.5, static musl,
{x86_64,aarch64}-unknown-linux-musl). The per-task launch symlink
(<workdir>/home/.local/bin/codex) is preserved and points into the cache,
so task-scoped teardown stays unaffected.
Status — Stages 0–8 (feature-complete against the Appendix-A parity bar)
Verified against docs/writing-agent-wrappers.md Appendix A — 28 of 29 items
green (see docs/2026-07-02-optio-codex-parity-audit.md for per-item
file:line evidence). Suite: 188 passed, 4 skipped (the skips are the opt-in
real-binary tests, env-gated, never in the default suite).
Shipped:
- Two run modes — iframe/ttyd interactive TUI and conversation mode
(codex app-server over stdio) with a conversation-ui widget
(
optio-conversation-ui,widgetData.protocol = "codex"→CodexView) optio.logkeyword-protocol coordination + exit-status DONE/ERROR channel- per-task
HOME/CODEX_HOMEisolation (tree provisioned at prepare) - filesystem isolation via codex's native sandbox — default-ON
fs_isolation(workspace-write),extra_allowed_dirs(rwgrants →writable_roots),network_access(OFF by default); fail-closed, no optio-side guard needed (see the Filesystem sandbox section above) - task-scoped teardown (per-task codex path; orphan-ttyd reap = crash-orphan rescue)
create_codex_task,run_codex_session,CodexTaskConfig- remote SSH workers (
ssh=SSHConfig(...)routes toRemoteHost; verified end-to-end against a docker-sshd harness) - resume / workdir snapshots: session-id-keyed relaunch (
codex resume <id>, neverresume --last), Mongo snapshot store (retention 5, single workdir GridFS blob carryinghome/.codex/sessions),resume.log+ AGENTS.md resume section synced to the snapshot exclude list (workdir_exclude; defaults drophome/.codex/packages,*.sqlite*, caches — neverhome/.codex/sessions); auto-resume-on-restart via optio-core (supports_resume=True) - seeds: log-in-once capture (
on_seed_saved) +seed_idconsume with automatic workdir pre-trust; store-binding CRUD (list_seeds/delete_seed/purge_seed) over{prefix}_codex_seeds - pool leases + in-session credential save-back (single-use rotating refresh token) with a teardown backstop; lease loss aborts the session
- host-free
verify_and_refresh_seed— primary path is a direct OpenAI OIDCrefresh_tokengrant (non-billable, no codex process) with rotated-token write-back and pool-status stamping; falls back to a headlesscodex execprobe (stdout-only verdict) only when OIDC discovery is unreachable - optio-owned evictable binary cache (
OPTIO_CODEX_CACHE_DIR), seeded from a host binary (cp -L) or real GitHub-release auto-download (pinnedrust-v0.142.5, musl); per-task launch symlink preserved - conversation-ui surface: permission gate, inline model switching, file
upload/download (
optio-file:), tool verbosity - demo trio: seed-setup + seed-pinned iframe + seed-pinned conversation tasks
(auto-appear via
fw.resync())
Remaining opt gaps (deliberate — see the parity audit):
- session restore / rebase (scripted transcript reconstruction): not
shipped. codex's resume story is snapshot +
codex resume <id>(shipped above); claudecode's scriptedtranscript.pyrebase is engine-specific and has no codex analogue. optio-grok and optio-opencode also omit it — parity, not a regression. - at-rest encryption of the session blob: the
encrypt/decryptseam is plumbed through but not activated (sessions passencrypt=None) — identical posture to every other optio wrapper.
Not yet published to PyPI (first release is a separate, user-approved step).
Metadata
Release files for optio-codex 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| optio_codex-0.2.1.tar.gz | 123.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| optio_codex-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 196.6 kB
Release files / optio_codex-0.2.1.tar.gz
| Download URL | optio_codex-0.2.1.tar.gz |
|---|---|
| Size | 123.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cd60fd42f53b8f0074ef7d43656df41f9b04880322163f11da5794f53eeaebfd
|
|
BLAKE2b-256 checksum How to use checksums |
6335bbfe080c57d5a848b44806a674cc20b3fd0c2d9143beef3fa08b13c22c0e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / optio_codex-0.2.1-py3-none-any.whl
| Download URL | optio_codex-0.2.1-py3-none-any.whl |
|---|---|
| Size | 73.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a95f6bfa5c4be7012b4eb6946920d45787a415ba4086463730818da7a213944e
|
|
BLAKE2b-256 checksum How to use checksums |
6420012d5fc9c793d75d07a88d093efad5eba075762842444f927b2f65f72011
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|