Run OpenAI Codex as an optio task; local subprocess; ttyd-served TUI iframe.
Project description
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).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file optio_codex-0.1.0.tar.gz.
File metadata
- Download URL: optio_codex-0.1.0.tar.gz
- Upload date:
- Size: 109.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a79c843fe4b947b2e80f9597a038e0c745d7289fa3ae664b6ff8a7f7de1a21b8
|
|
| MD5 |
09299aa52d9f163d63fbfa85ec60dc95
|
|
| BLAKE2b-256 |
a2de04a2b796795f47b3781a736504fdbbcc46b22268035b89a499a02eb00b9d
|
File details
Details for the file optio_codex-0.1.0-py3-none-any.whl.
File metadata
- Download URL: optio_codex-0.1.0-py3-none-any.whl
- Upload date:
- Size: 66.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
56f77f3c5415a082fc3f0fc1dac899edaae72b6d2fc250c48535555180b9b561
|
|
| MD5 |
a0ca5468d7e6b63375c6727def188335
|
|
| BLAKE2b-256 |
7cdb0fd7578207ef11624a4385e1e180304557eac48f5edef269800d9014fe41
|