Run Codex against any DevPod workspace while keeping OpenAI authentication on
the host. No auth.json, API key, access token, keyring, SSH agent, or GPG agent
is forwarded by this tool.
host Codex TUI -> authenticated host app-server -> SSH -> DevPod exec-server
auth loopback only files and commands
This mirrors a remote-editor setup: the UI and credentials stay on the trusted
host, while repository reads, writes, and commands happen inside the
devcontainer. It does not modify .devcontainer files or DevPod contexts.
Codex keeps its normal built-in tool surface and permission settings; the
DevPod exec-server performs filesystem and terminal operations as the
configured container user.
Codex app-server, exec-server, and their WebSocket transport are currently experimental. See the official Codex App Server documentation.
Unix way: one tool, one job, minimal dependencies. codex-devpod only connects
host-authenticated Codex to an existing DevPod devcontainer. It ships no skills,
AGENTS.md, prompts, MCP servers, plugins, or extensions, and changes no project
or host configuration. Keep project-specific tooling in the project and personal
tooling in host Codex.
Keeping Codex authentication and host agents outside the container reduces exposure to supply-chain attacks and prompt injections from project content. It is a narrow isolation boundary, not a guarantee that untrusted content is safe.
Required on the host:
- Bash 3.2 or newer. macOS includes a suitable
/bin/bash; Alpine and other minimal distributions normally require Bash to be installed separately. - The
devpodCLI, with the intended provider and workspace already configured. - An authenticated
codexCLI with--remote,app-server, andexec-serversupport. The wrapper checks these capabilities before connecting. - Standard Unix utilities:
awk,cat,sed,tail,tr,ln,mkdir,mktemp,uname, and basic file/process commands normally supplied by macOS, GNU coreutils, or BusyBox.
The devcontainer must provide a POSIX-compatible
shell and common base utilities such as grep, sed, tail, tr, uname,
id, mkdir, mktemp, chmod, mv, ln, and cat. Codex need not be
preinstalled: the wrapper can reuse a host binary after a compatibility
precheck, or download an official portable release for the container.
Real DevPod execution on macOS has not been tested yet. The wrapper targets the bundled Bash 3.2 and is covered by the
macos-latestCI job.
Conditional and optional dependencies:
| Dependency | When it is used | Fallback or way to avoid it |
|---|---|---|
curl |
One-line installation, release checks, and portable Codex downloads | Use an existing compatible binary or verified download cache |
readelf (Linux host) |
Inspecting host ELF architecture and dynamic dependencies before upload | Download the official container-compatible build |
python3 |
Validating release metadata, SHA-256 and safely extracting portable downloads on the host | Use an existing compatible binary with --sync=never |
OpenSSH client with ssh -G |
Automatic workdir detection for an explicitly named DevPod workspace |
Run without WORKSPACE from a project containing .devcontainer, or pass --workdir |
python3 or python |
Selecting free local ports | Bash chooses and probes random high ports |
nc |
Checking whether a local TCP port is open | Bash /dev/tcp support |
openssl |
Generating the per-run authentication token | /dev/urandom with od |
sha256sum or shasum, plus awk |
Verifying a synchronized Codex binary on both host and container | Install compatible Codex in the container and use --sync=never |
gzip on both sides |
Compressing a synchronized Codex binary | Uncompressed transfer |
install |
Copying the wrapper during installation | cp plus chmod |
At runtime, the wrapper prints a warning whenever a missing optional dependency causes it to use a fallback.
Docker and Podman are not direct dependencies of this wrapper; DevPod uses the
runtime required by its configured provider. jq and Node.js are not required.
Git is only needed for clone-based installation. Portable acquisition needs
curl and Python 3 on the host; it adds no container libraries or privileges.
Running tests/test.sh requires Bash, Python 3, a C compiler (cc) for native
fixtures, cmp, and either sha256sum or shasum. DevPod and downloads are
mocked; binary setup commands run in a local POSIX shell. Docker/Podman are
not needed for these tests.
The installer is POSIX sh compatible and installs only the wrapper script.
For a one-line install or update, run this in an interactive terminal:
curl -fsSL https://raw.githubusercontent.com/alltiptop/codex-devpod/main/install.sh | shThe installer reports whether codex-devpod will be installed or updated and
always asks Continue? [y/N]; only y or yes continues. In streamed mode it
downloads the wrapper only after confirmation, pins it to the matching release
tag, validates it, then atomically replaces the installed file. Run the same
command to update.
To install from a local checkout instead:
git clone https://github.com/alltiptop/codex-devpod.git
cd codex-devpod
./install.shSign in once on the host:
codex loginFor host-side OS credential storage, add this to ~/.codex/config.toml:
cli_auth_credentials_store = "keyring"Codex caches and refreshes host login sessions; see the official authentication documentation.
cd path/to/project
codex-devpod
codex-devpod -- --model gpt-5.6-sol
codex-devpod my-workspace
codex-devpod --workdir /workspace my-workspace
codex-devpod --version=latest my-workspace
codex-devpod --version=rust-v0.153.4 my-workspace
codex-devpod my-workspace -- --model gpt-5.6-sol
codex-devpod my-workspace resume SESSION_ID
codex-devpod -- resume SESSION_IDWith no WORKSPACE, the wrapper searches the current directory and its parents
for .devcontainer/devcontainer.json, .devcontainer.json, or exactly one
.devcontainer/*/devcontainer.json. It passes that project folder to DevPod and
reads workspaceFolder, then the target in workspaceMount, falling back to
/workspaces/PROJECT_NAME. The resulting directory is verified inside the
running workspace before Codex starts. Ambiguous or invalid configurations fail
with a warning and ask for an explicit workspace or --workdir.
An explicit workspace can be a DevPod name or folder accepted by devpod ssh.
Wrapper options go before it; remaining arguments are passed to the Codex TUI.
Use -- before Codex arguments when omitting WORKSPACE.
Before connecting, the wrapper prints a short list of its remote checks. They run together in one DevPod SSH command on normal startup. The first setup, or the first run after a host Codex update, can take some time while the executable is checksummed and cached; the transfer steps are shown in the console.
For a Linux container, preflight also checks whether bwrap is on its PATH.
If it is missing, the wrapper shows a Dockerfile installation command based on
the available package manager (apk, apt-get/apt, dnf, microdnf, or yum)
and offers:
1. Exit and install Bubblewrap in the container (recommended)
2. Continue without Codex sandbox (container isolation only)
3. Continue with read-only sandbox (approvals on request)
Enter selects option 1. Option 2 uses --sandbox danger-full-access and keeps
the configured approval policy. Option 3 uses --sandbox read-only --ask-for-approval on-request; commands may still fail without bwrap, and
Codex may request approval to execute them outside the sandbox. This does not
guarantee a prompt before every command. The selected mode overrides conflicting
forwarded sandbox flags for this invocation. Container permissions still apply;
host MCP integrations continue to run on the host.
The menu reads from the controlling terminal, preserving piped input for Codex.
Without an interactive terminal, startup stops when bwrap is missing. The
wrapper does not install packages or change container configuration. This is an
executable presence check, not a test of namespace or sandbox functionality.
In an interactive terminal, the codex-devpod: prefix is blue, warnings are
orange, and errors are red. Redirected output and CI logs contain no ANSI color
codes. Set NO_COLOR=1 to disable colors explicitly.
Codex subcommands are forwarded too. Use resume SESSION_ID to continue a
session in the selected devcontainer; codex-devpod -- resume SESSION_ID uses
the locally detected workspace. Prefer an explicit ID over --last when several
workspaces share a path such as /workspace. See the official
Codex CLI reference.
For an explicitly named workspace, the remote working directory is read from
the effective configuration of the DevPod-generated WORKSPACE.devpod SSH
alias. The selected directory is verified as part of the combined preflight
command under the configured container user. The probe does not use root or
start configured DevPod services. If the alias is unavailable (for example,
when passing a folder instead of a workspace name), specify --workdir
explicitly.
The wrapper first looks for a working Codex in the container or its caches.
With the default --version=latest, it checks GitHub's release JSON on each
startup. The request has a five-second total timeout. Checking for updates
only fetches metadata; it does not download a Codex archive.
If the container version is older, --sync=auto asks:
codex-devpod: DevPod has codex-cli 0.153.3; latest is codex-cli 0.153.4. Update? [y/N]
Enter y to update, or press Enter to keep the existing version. Without a
controlling terminal, the existing version is kept and stdin is left for
Codex. A timeout, failed lookup, or failed optional update also keeps a working
container binary. Newer container versions are not automatically downgraded.
--sync=always updates without prompting; --sync=never skips release checks
and acquisition. --remote-codex PATH selects that executable explicitly and
also disables automatic checks and acquisition.
When installation is needed, a host binary with the selected release version can be reused only after a compatibility precheck. For Linux, the wrapper inspects its ELF architecture and loader. Dynamic binaries require matching loader and dependency SHA-256 hashes at the same paths in DevPod; only this small manifest is sent before the executable. Static ELF binaries need no shared-library comparison. Custom loader paths, search-path overrides, missing inspection tools, and unsupported formats use the portable release instead. This conservative check can reject a binary that would work; it cannot prove runtime compatibility. macOS host builds currently use the portable path.
Otherwise, the host downloads the official release for the container target (Linux musl or macOS, x86_64 or ARM64). GitHub release asset SHA-256 digests verify archives before extraction. Every transferred binary must pass checksum, version, and transport capability probes before atomic publication. Failed runtime probes are remembered to avoid repeating the same rejected upload. A version difference between host and selected container Codex is reported.
Downloads are cached by version and target under
${XDG_CACHE_HOME:-$HOME/.cache}/codex-devpod/downloads; container binaries use
the corresponding codex-devpod/bin cache. If no working container binary
exists and the lookup is unavailable, setup can still try a compatible host
binary or a previously verified portable download. Credentials remain on the
host throughout.
Use --version=rust-v0.153.4 (or --version=0.153.4) to require a specific
release. A matching installed/cached version works without a release lookup;
otherwise setup acquires that exact version. Pins never fall back to a different
version. An explicit remote path or --sync=never must already provide the pin.
Latest and pinned selections have separate portable cache pointers.
Bare --version or -V prints the wrapper version.
Configuration precedence is CLI option, then environment variable, then automatic detection/default:
| Variable | Purpose | Default value |
|---|---|---|
CODEX_DEVPOD_CONTEXT |
DevPod context | Current DevPod context |
CODEX_DEVPOD_WORKDIR |
Absolute container working directory | Auto-detected |
CODEX_DEVPOD_REMOTE_CODEX |
Container Codex command or path; disables sync | Auto-detected |
CODEX_DEVPOD_SYNC |
Update policy: auto (ask), always, or never |
auto |
CODEX_DEVPOD_VERSION |
Portable release: latest, release tag, or version |
latest |
CODEX_DEVPOD_CODEX |
Host Codex command or path | codex |
CODEX_DEVPOD_HOST_BINARY |
Standalone host binary used for sync | Resolved host Codex executable |
CODEX_DEVPOD_DEVPOD |
DevPod command or path | devpod |
CODEX_DEVPOD_TIMEOUT |
Startup timeout in seconds | 20 |
CODEX_DEVPOD_LOCAL_EXEC_PORT |
Fixed host-side exec tunnel port | Auto-selected free port |
CODEX_DEVPOD_LOCAL_APP_PORT |
Fixed host-side app-server port | Auto-selected free port |
CODEX_DEVPOD_REMOTE_EXEC_PORT |
Fixed container exec-server port | Auto-selected high port |
CODEX_DEVPOD_DEBUG |
Set to 1 for diagnostics and retained logs |
Disabled |
CODEX_DEVPOD_KEEP_LOGS |
Set to 1 to retain runtime logs |
Disabled |
CODEX_DEVPOD_INSTALL_DIR |
Installer destination | ~/.local/bin |
Run codex-devpod --help for the CLI options. Uninstall with
./install.sh --uninstall.
- The host app-server listens only on
127.0.0.1, requires a per-run random token, and is not reverse-forwarded into the container. - A temporary Codex-home view keeps host configuration and tools available while
selecting DevPod for built-in filesystem and terminal execution; the host
environments.tomlis not modified. - Codex credentials and SSH/GPG agents are not forwarded; remote processes use
a dedicated credential-free
CODEX_HOME. - The wrapper does not request root; commands run as DevPod's configured devcontainer user.
- Project content can still influence Codex, and Codex can modify the mounted project. Host integrations and anything exposed by DevPod remain in the user's trust boundary.
This is an independent community project and is not affiliated with OpenAI or DevPod.
The installed wrapper remains one Bash file. Sourcing bin/codex-devpod
defines functions; startup, shell options, and traps are confined to main.
Keep the wrapper and tests compatible with Bash 3.2, and the installer and
generated remote scripts compatible with POSIX sh.
Run the local fixture suite with Python 3 and a C compiler available:
/bin/bash tests/test.shThe suite uses mocked DevPod/downloads and local loopback listeners. It does not require a real DevPod workspace or download Codex releases. CI runs it on Linux and macOS.
Use ShellCheck 0.11.0 and shfmt 3.12.0, matching CI:
/bin/bash tests/lint.sh
shfmt -w bin/codex-devpod install.sh tests/*.shFormatting follows .editorconfig. The lint script also checks generated
remote programs as POSIX shell. Tool downloads in CI are pinned by version and
SHA-256; these are development tools, not wrapper runtime dependencies.
bash-language-server can use these executables for editor diagnostics and
formatting. Associate the extensionless bin/codex-devpod with Bash and include
it in the language server background glob when configuring workspace analysis.
Shellharden is optional: use shellharden --suggest FILE to review quoting
suggestions, and check them against Bash 3.2/POSIX behavior before applying.