Codex
OpenAI's CLI coding agent with an external launcher boundary and kernel sandbox enforcement.
Last updated
Cloud API TypeScript1. Installation
Prerequisites
- macOS 13+ or Linux (kernel 5.13+)
- Node.js 22+
- An OpenAI API key (or GitHub Copilot subscription for OAuth)
Install Codex
brew install --cask codex
Install the preferred stack
brew tap nvk/tap
brew install nvk/tap/agent-bondage
brew install nono
# Optional, for API-key launches instead of OAuth tokens on disk:
brew install nvk/tap/envchain-xtra
Verify
codex --version
bondage --help
nono --version
envchain --version # if installed
2. nono Profile
Codex has its own internal sandbox, but nono adds kernel-level enforcement that the tool itself cannot bypass. In the preferred setup, bondage chooses the exact target and the external custom-codex profile before Codex starts.
What the sandbox allows
| Resource | Access | Why |
|---|---|---|
| Current working directory | Read + Write | Project files |
api.openai.com | Network | API calls |
~/.codex/ | Read | Config and auth |
/dev/tty, /dev/null | Read + Write | Terminal I/O |
/dev/urandom | Read | Crypto randomness |
3. Optional envchain-xtra
Codex supports two auth methods. The preferred launcher path is still bondage + nono either way. Use envchain-xtra only if you want direct API keys instead of OAuth tokens on disk.
Store your API key
envchain --set codex OPENAI_API_KEY
API key vs OAuth
| Method | Credential storage | Recommendation |
|---|---|---|
| envchain + API key | macOS Keychain (encrypted) | Preferred — no plaintext on disk |
OAuth via /connect |
~/.codex/auth.json (plaintext) |
Use if Copilot subscription, but be aware tokens are on disk |
auth.json are long-lived and grant the same access. They sit in plaintext JSON just like a raw API key would.
4. bondage Wrapper
Add this thin wrapper to your shell config:
codex() {
bondage exec codex ~/.config/bondage/bondage.conf -- "$@"
}
Sample stack snippets
Assuming your shared [global] block already exists in ~/.config/bondage/bondage.conf, this is a minimal Codex shape to adapt:
# ~/.config/bondage/bondage.conf
[profile "codex"]
use_envchain = false
use_nono = true
nono_profile = codex
touch_policy = none
target_kind = native
target = /absolute/path/to/codex
target_fp = sha256:replace-me
nono_allow_cwd = true
nono_allow_file = /dev/tty
nono_allow_file = /dev/null
nono_read_file = /dev/urandom
env_set = GIT_TERMINAL_PROMPT=0
{
"extends": "codex",
"meta": {
"name": "codex",
"description": "Codex with project-only access"
},
"policy": {
"add_deny_access": ["/Volumes"]
},
"workdir": {
"access": "readwrite"
}
}
If you prefer API-key mode instead of OAuth tokens on disk, switch the profile to use_envchain = true and add namespace = codex.
How it works
- The shell wrapper passes only the profile name and your arguments
bondageverifies the exact Codex targetbondageapplies the externalnonoprofile- If configured for API-key mode,
bondagealso launches throughenvchain-xtra
Reload your shell:
source ~/.zshrc
5. Operational resilience
The most common failure is not “Codex broke.” It is that the launcher path quietly stopped being the thing your shell actually runs.
Keep home-shell bootstrap stable
Do not point ~/.zshrc straight at a repo symlink and hope it stays readable forever. Keep a tiny stable bootstrap file in $HOME and let it source the real repo-backed config if available.
# ~/.zshrc
if [ -r "$HOME/src-repo/.dotfiles/zsh/.zshrc" ]; then
. "$HOME/src-repo/.dotfiles/zsh/.zshrc"
elif [ -r "$HOME/src-repo/.dotfiles/ai/shell.zsh" ]; then
. "$HOME/src-repo/.dotfiles/ai/shell.zsh"
fi
Treat upgrades as launcher changes
If Homebrew or your package manager upgrades nono or Codex, re-verify the launcher stack. A stale pinned path is enough to strand the whole wrapper chain.
bondage verify codex ~/.config/bondage/bondage.conf
bondage chain codex ~/.config/bondage/bondage.conf -- --help
Keep repair access explicit
If you maintain several profiles, keep a named repair tier such as codex-fix. It should still be sandboxed, but broad enough to repair shell startup files, launcher config, hooks, and profile trees without dropping straight to rawdog.
Keep sandbox recovery out of Codex hooks
Codex hooks can be useful for deterministic checks, but hook output is model input. Do not inject nono recovery workflows through SessionStart or PostToolUse hooks. A hook that tells the agent to offer choices, wait, stop, or create a profile can derail the real task. Put normal path grants in the managed nono profile or bondage config, then restart Codex.
6. Verification
Test the full chain
# Should launch Codex normally
bondage verify codex ~/.config/bondage/bondage.conf
bondage chain codex ~/.config/bondage/bondage.conf -- --help
codex --help
Confirm no plaintext keys
# Should return nothing
grep -r "sk-proj\|sk-live" ~/.codex/ ~/.zshrc 2>/dev/null
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| "Invalid API key" | API-key mode selected but envchain namespace empty | envchain --set codex OPENAI_API_KEY |
| Codex starts without the expected profile | Running outside the wrapper | Launch through bondage exec codex ..., not the bare binary |
| Wrapper commands disappeared after a shell restart | Home shell startup depends on an unreadable sourced path | Use a tiny stable ~/.zshrc bootstrap and keep the real logic below it |
"Permission denied" on ~/.codex/ |
nono profile too restrictive | Add ~/.codex to the managed nono profile or bondage nono grants, rerun bondage verify/bondage chain, then restart Codex |
Wrapped launch broke after upgrading nono |
Launcher still pins an old package-manager path | Re-pin the launcher config and rerun bondage verify |