A Small Codex Startup Menu for Choosing How Much I Trust a Session
How I use Codex's native profiles plus a tiny zsh wrapper to choose a sandbox and approval posture before the agent starts.
I have been using Codex enough that the startup defaults started to matter.
Not every session has the same risk profile. Sometimes I am working in a repo I trust and want Codex to edit files freely inside the workspace. Sometimes I am opening a new repo and only want read-only inspection. Sometimes I want a lower-friction session where sandbox failures should fail closed instead of asking me to approve an escape hatch.
Codex already has the important pieces built in: sandbox modes, approval policies, profiles, and /status. I did not want to replace any of that. I wanted a small decision point before Codex starts:
What kind of session am I about to run?
So I added a tiny cx wrapper around codex.
The problem
The built-in permission controls are good, but I was relying too much on memory.
I could always run:
codex --sandbox workspace-write --ask-for-approval on-request
or:
codex --profile maximum-safety
or launch Codex and adjust permissions after the fact.
But that means the decision happens either through command-line recall or after I am already inside the session. I wanted the safety choice to be explicit at launch, in plain English, without changing how Codex itself works.
The two levers
For my local workflow, the two settings that matter most are:
sandbox_mode: what Codex is technically allowed to touch.approval_policy: when Codex should stop and ask before taking an action.
My everyday default is:
sandbox_mode = "workspace-write"
approval_policy = "on-request"
That gives Codex write access inside the workspace, keeps network access restricted, and asks before actions that need escalation outside the sandbox.
I keep MCP, browser, and database tools on explicit approval separately. Those tools can affect real external systems, so I do not want them hidden behind a low-friction shell-command mode.
The profiles
I added named profiles to ~/.codex/config.toml.
# Default safety posture for shell commands across sessions:
# keep command execution sandboxed to the workspace, and ask only when a task
# needs an explicit escalation outside that sandbox.
sandbox_mode = "workspace-write"
approval_policy = "on-request"
# Maximum safety: useful for unknown repos, code review, or "look but don't touch"
# sessions. Codex can read files but needs approval to edit, run commands, or
# access the network.
[profiles.maximum-safety]
sandbox_mode = "read-only"
approval_policy = "on-request"
# More cautious than the default while still allowing workspace edits. Codex asks
# before commands outside its trusted/read-only set.
[profiles.command-cautious]
sandbox_mode = "workspace-write"
approval_policy = "untrusted"
# Low friction while still sandboxed. Codex won't ask for approval; anything that
# cannot run inside the workspace sandbox fails instead of escalating.
[profiles.low-friction]
sandbox_mode = "workspace-write"
approval_policy = "never"
# Non-interactive read-only mode for inspection/CI-style runs. Codex never asks
# for approval and cannot make changes.
[profiles.read-only-never]
sandbox_mode = "read-only"
approval_policy = "never"
Those are not new permissions. They are just saved combinations of Codex’s native settings.
The tiny wrapper
Then I added a cx function to my ~/.zshrc.
# Codex profile picker. Keeps `codex` itself unchanged.
# Profiles are defined in ~/.codex/config.toml.
cx() {
local choice profile
echo "Codex startup mode:"
echo " 1) Everyday trusted repo edit workspace; ask before escalation"
echo " default workspace-write + on-request"
echo " 2) New/risky repo review read-only; ask before edits or commands"
echo " maximum-safety read-only + on-request"
echo " 3) Trusted repo, cautious edit workspace; ask before untrusted commands"
echo " command-cautious workspace-write + untrusted"
echo " 4) Trusted repo, low friction edit/run in sandbox; never ask"
echo " low-friction workspace-write + never"
echo " 5) Read-only, no prompts inspect only; fail instead of asking"
echo " read-only-never read-only + never"
printf "Select mode [1]: "
read -r choice
case "${choice:-1}" in
1|default)
command codex "$@"
;;
2|maximum-safety)
command codex --profile maximum-safety "$@"
;;
3|command-cautious)
command codex --profile command-cautious "$@"
;;
4|low-friction)
command codex --profile low-friction "$@"
;;
5|read-only-never)
command codex --profile read-only-never "$@"
;;
q|quit|exit)
return 130
;;
*)
echo "Unknown Codex mode: $choice" >&2
return 2
;;
esac
}
I intentionally did not override codex. The original command still works exactly as shipped. cx is just the version I run when I want a startup checkpoint.
How I choose
The menu maps to human situations, not just technical flags.
Everyday trusted repo
This is my default. I know the repo, I expect edits, and I am comfortable letting Codex run normal sandboxed commands. It still asks before escalation.
New/risky repo review
This is for unfamiliar code, third-party projects, or anything where I want Codex to inspect first and touch later. It starts read-only.
Trusted repo, cautious
This is for workspaces I trust, but where I want more prompts around command execution. Codex can edit the workspace, but commands outside the trusted set require approval.
Trusted repo, low friction
This is for focused sessions in repos I know well. Codex can operate inside the sandbox without prompting. If something needs to leave the sandbox, it fails instead of asking.
Read-only, no prompts
This is mostly for inspection or CI-style use. It is intentionally narrow: read files, do not edit, do not ask.
Five options is probably the upper limit. If I do not use read-only-never over time, I will remove it. The goal is less friction, not a new decision tree.
Verifying the mode
After launch, I run:
/status
That shows the applied permission mode:
Permissions: Custom (workspace-write, on-request)
or:
Permissions: Custom (read-only, on-request)
The word Custom is expected because these settings come from my config rather than a built-in preset.
For untrusted, Codex may show the internal name unless-trusted in lower-level debug output. That is the same idea: trusted read-style commands can run, while untrusted commands require approval.
Why this is not over-engineering
This only feels worth doing because it is small.
The wrapper is not a permissions system. Codex is still enforcing the sandbox. Codex is still deciding when an action needs approval. Codex’s own /status command is still the source of truth.
All cx does is make me answer a useful question before the session starts.
That matters because the wrong default can be subtle. If I launch into an unfamiliar repo with my normal trusted-workspace posture, I may only notice after I have already asked Codex to inspect and act. Starting with a short menu makes the trust decision explicit.
What I would like upstream
I do not think this needs to be a big feature. The useful upstream version would be something like:
codex --choose-profile
or a config option like:
profile_prompt = true
If multiple profiles exist, Codex could optionally ask which one to use before the session starts. That would preserve the existing profile model while making launch-time intent easier.
Until then, a shell function is enough.
References
The official Codex docs cover the pieces this uses:
My takeaway is simple: use the native controls, keep the wrapper thin, and make the trust decision before the agent starts.