Windows (elevated sandbox): approved escalated command still runs inside the sandbox in the CLI, works in Codex Desktop
Summary
On native Windows with [windows] sandbox = "elevated", when the model requests an escalated
command (exec_command with sandbox_permissions: "require_escalated" + prefix_rule) and the
guardian reviewer approves it, the terminal CLI (codex, client codex-tui) executes the command
inside the sandbox anyway (as the CodexSandboxOffline/Online user). The Codex Desktop app
handles the identical request correctly and runs it outside the sandbox.
Result in the CLI: every command that needs the user's credentials fails —gh → Access is denied reading %APPDATA%\GitHub CLI\config.yml;ssh <ssh-alias> → hostname could not be resolved (SSH config/known_hosts in the profile are invisible);git fetch over HTTPS → SEC_E_NO_CREDENTIALS.
Environment
- Windows 11 Home, 10.0.26200
- Codex Desktop (Microsoft Store) 26.820.9563.0, bundled runtime
codex-cli 0.150.0-alpha.8 - Codex CLI (npm
@openai/codex) 0.149.0 — same behaviour observed by the user on 0.150.x before downgrading gh2.94.0, OpenSSH (Windows built-in)~/.codex/config.toml(relevant part):
approval_policy = "on-request"
approvals_reviewer = "guardian_subagent"
sandbox_mode = "workspace-write"
sandbox_workspace_write.network_access = true
[windows]
sandbox = "elevated"
~/.codex/rules/is empty (verified; earlier presence of rules files made no difference to this bug).- Sandbox users
CodexSandboxOffline/CodexSandboxOnlineexist; sandbox audit passes.
Steps to reproduce
- In a project folder, run
codexin PowerShell (terminal CLI). - Ask: "Run
gh auth statusandssh <host> date -u; escalate if needed." - The model calls
exec_command({cmd:"gh auth status", sandbox_permissions:"require_escalated", justification:"…", prefix_rule:["gh","auth","status"]}).
- The guardian reviewer runs (several
codex-auto-reviewturns) and approves. - Observe: the command still executes via the sandbox runner and fails with
Access is deniedfor
GitHub CLI\config.yml; SSH fails with unresolved host alias.
Same steps in Codex Desktop (same machine, same config, same request shape) → command runs
outside the sandbox and succeeds (gh api …, gh pr view … worked at 2026-08-27 13:05–13:18Z).
Evidence (from ~/.codex/logs_2.sqlite and ~/.codex/.sandbox/sandbox.2026-08-27.log)
| thread | client (app_server.client_name) | threads.source | cli_version | result |
|---|---|---|---|---|
| 01a0434a-c8e3-7443-ad7a-b0e1c6809969 | Codex Desktop | vscode | 0.150.0-alpha.8 | escalated gh api … succeeded |
| 01a04372-bab9-7f60-9cc6-9db4fe0ef372 | codex-tui | cli | 0.149.0 | escalated git fetch, gh auth status, ssh … all failed |
| 01a0438b-7f6b-70e1-80cc-6419db84bda5 | codex-tui | cli | 0.149.0 | escalated gh auth status, ssh … failed |
- For the failing threads, each escalated call is followed within ~2 s by guardian
(model=codex-auto-review) turns (2–7 per call), i.e. the approval path ran.
- The sandbox log then shows the command being launched through the sandbox helper
(setup refresh: spawning …codex-windows-sandbox-setup.exe, helper launch resolution: using, then
copied command-runner path …codex-command-runner-0.149.0.exe
START: …powershell.exe -NoProfile -Command …) — i.e. still sandboxed after approval.
- Effective turn policy for the CLI threads (from
feedback_tags):
approval_policy=OnRequest sandbox_policy=WorkspaceWrite { writable_roots: [], network_access: false … }
— note network_access: false although config sets sandbox_workspace_write.network_access = true;
the desktop thread shows network_access: true and a populated writable_roots.
- No
.rulesfile involved:exec_policy … loaded rules from 0 files.
Expected
After the guardian (or user) approves a require_escalated request, the CLI should run the command
outside the Windows sandbox with the user's profile, exactly as Codex Desktop does.
Workarounds found
- Use Codex Desktop for anything needing credentials (escalation works there).
- Or start the CLI with
codex --sandbox danger-full-accessfor that session.
Extra observations (may be separate issues)
- A
prefix_rulewithdecision = "allow"in~/.codex/rules/default.rules(as written by the
desktop app's "Always allow") causes the command to skip approval and run inside the sandbox
— so an "always allow" for gh auth status permanently breaks gh on Windows. A decision = "prompt"
rule behaves the same way after approval.
deny_read_acl_state.jsonin~/.codex/.sandbox/can be corrupted when two threads start at the
same time (two writers; file ends up as {"principals": {}} followed by a second JSON tail), after
which every new thread fails with helper_unknown_error: apply deny-read ACLs.
- With a permission profile containing
"**/.env*" = "deny"in a workspace of ~800k files, every
thread start scans the whole tree (10+ min at 100% CPU), which starves the app-server request queue
(app_server_client_request_queue_rejected) and blanks the desktop UI.