Skip to content

Latest commit

 

History

17 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

codex-devpod

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.

Philosophy

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.

Dependencies

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 devpod CLI, with the intended provider and workspace already configured.
  • An authenticated codex CLI with --remote, app-server, and exec-server support. 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-latest CI 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.

Install

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 | sh

The 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.sh

Sign in once on the host:

codex login

For 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.

Use

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_ID

With 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.

Security notes

  • 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.toml is 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.

Development

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.sh

The 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/*.sh

Formatting 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.

License

MIT

About

Run host-authenticated Codex securely inside DevPod workspaces without forwarding credentials.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages