Coding agents in the container
The image installs both Claude Code and Codex, so the container has a coding agent the moment it boots. You sign each one in once, with the subscription you already pay for. There is no API key to configure anywhere.
Run make claude-login and make codex-login. Each prints a URL and a code:
approve in your browser, paste the code back. The token lands in the
git-ignored ./agents directory on the host, so it survives image rebuilds.
Sign in
make claude-login
make codex-loginBoth use a device-code flow, which is what makes them work in a container at all: the browser step happens on your machine, and only the resulting token comes back. Check either at any time:
make agents-statusThere is deliberately no API-key setting for either agent. Metered API billing is a different and far more expensive product than the subscription these CLIs authenticate against, so the container only supports the interactive sign-in.
Where the login is stored
Both CLIs are redirected to write everything they own into one host directory:
agents/
claude/ credentials, session history, project state
codex/ credentials, configurationThat directory is a bind mount, not a volume, and the split is deliberate. The
executables belong to the image, so a rebuild replaces them with current
versions. The credentials belong to the host, so a login outlives an image
rebuild, a container recreation, and even a down --volumes reset of the
relay’s own identity.
Treat agents/ as private key material. It holds live logins and the session
history alongside them. It is git-ignored, and the repository’s commit hook
refuses any staged path under it — but that protects the repository, not a
backup, an archive, or a support bundle. Never copy it to share a setup.
Updates
Automatic self-update is switched off in the image, on purpose. The binaries live in a root-owned path that the runtime user cannot write, so an enabled updater would report a permission failure on every run and never actually update. The supported update path is an image rebuild, which resolves the current version of both CLIs:
docker compose build --no-cache
docker compose up -dYour logins are untouched by this — they are on the host, not in the image.
Codex and its sandbox
Codex runs each command it executes inside its own sandbox. On Linux that sandbox is built on an unprivileged user namespace, and a container frequently cannot create one. When that happens the failure is total rather than graceful: every command Codex tries to run fails.
CMDOP_CODEX_SANDBOX decides how to handle that, and auto tests the sandbox
at startup rather than guessing from the kernel version:
| Value | Behavior |
|---|---|
auto (default) | Probe the sandbox on boot. Use the restricted mode when it works, fall back when it does not, and say which it chose in the log. |
workspace-write | Force the sandbox on. Codex fails if the container cannot start it. |
read-only | Force the sandbox on, with no writes. |
danger-full-access | No inner sandbox. |
The fallback is a considered trade, not a shortcut: the container is already the security boundary, and the alternative — granting the container the privileges its inner sandbox needs — weakens the real boundary to restore a nested one.
Do not “fix” a sandbox failure with --privileged, seccomp=unconfined, or by
adding SYS_ADMIN. Each of those weakens the boundary that is actually
protecting your host, in order to reinstate one that is protecting a directory
inside a container you can delete.
Leaving one out
Both are build arguments, so a build can omit either:
CMDOP_CLAUDE_CODE=0
CMDOP_CODEX=0The image builds and runs normally without them; the Cmdop agent itself is unaffected.