Codex Desktop silently rewrites autonomy settings, causing projectless .codex EPERM prompts

Open 💬 2 comments Opened Jul 13, 2026 by johnsilvavlogs
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

Codex Desktop silently rewrote the machine-wide autonomy tuple in ~/.codex/config.toml from:

approval_policy = "never"
sandbox_mode = "danger-full-access"
approvals_reviewer = "user"

to:

approval_policy = "on-request"
sandbox_mode = "workspace-write"
approvals_reviewer = "guardian_subagent"

Fresh projectless threads then inherited the restricted profile. Their generated workspace root was writable, but the reserved .codex child was explicitly read-only, so ordinary goal-artifact creation failed with Operation not permitted and agents requested escalation for basic local setup.

This is separate from #32039 (projectless root symlink validation). The projectless root was already repaired to a real local directory and task creation itself succeeded.

Environment

  • Codex Desktop: 26.707.61608 (build 5200)
  • Codex CLI: 0.144.3
  • macOS: 26.5.2 (build 25F84)
  • Architecture: Apple Silicon (arm64)

Evidence

  • A pre-incident config backup has never / danger-full-access / user.
  • The rewritten config.toml had a filesystem birth/modification time of 2026-07-13 07:10:14 -0300, the exact second one affected projectless thread was created.
  • Other bounded config backups all retain the intended tuple.
  • Two affected projectless thread turn_context records show:
  • approval_policy: on-request
  • sandbox_policy.type: workspace-write
  • managed permission profile
  • workspace root writable
  • workspace .codex read-only
  • The first ordinary mkdir -p .codex/<goal> in each affected thread returned Operation not permitted.
  • Direct create_goal calls later succeeded; the approval request was caused by the filesystem profile/path collision, not the goal API itself.
  • After restoring never / danger-full-access / user, two fresh projectless canaries started with permission_profile: disabled, created .codex/<goal>/goal.md and plan.md, activated/completed goals, made ordinary local edits, and finished with no approval prompt.
  • No installed LaunchAgent or local maintenance script writes these settings. The exact writer is not represented by a session-level patch event, so attribution to a specific Desktop code path remains uncertain.

Observed sequence

  1. Configure ~/.codex/config.toml with never / danger-full-access / user.
  2. Use Codex Desktop to create projectless threads.
  3. Observe that config.toml has been atomically replaced with on-request / workspace-write / guardian_subagent.
  4. In a fresh projectless thread, attempt ordinary workspace-local state under .codex/<name>.
  5. Observe EPERM and an escalation request.

The rewrite was not reproduced on every subsequent projectless creation, so this may depend on a settings migration or a one-time Desktop persistence path.

Expected behavior

Codex Desktop updates must preserve explicit user autonomy settings in ~/.codex/config.toml. Creating a projectless task must not silently replace them.

If Desktop intentionally migrates or overrides these fields, it should:

  • expose the change clearly;
  • preserve an explicit existing user choice;
  • avoid making a projectless workspace's required local state path read-only; and
  • never turn ordinary projectless goal setup into a user approval prompt.

Local mitigation

I restored the intended tuple and installed a narrow event-driven guard that backs up the config and repairs only the exact known-bad triple. This protects the local workflow but does not fix the underlying Desktop write.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #32338

Powered by Codex Action

johnsilvavlogs · 1 month ago

I found and verified a second persistence layer that explains the recurrence after the original config.toml repair.

Corrected root cause

Codex Desktop does not rely only on ~/.codex/config.toml for new UI-created tasks. The renderer persists a separate local-host mode at:

~/.codex/.codex-global-state.json
electron-persisted-atom-state.agent-mode-by-host-id.local

The signed Desktop bundle maps:

  • full-access -> :danger-full-access + danger-full-access + never + user
  • guardian-approvals -> :workspace + workspace-write + on-request

The UI new-task/projectless path serializes that renderer selection as explicit permissions in start-conversation; those overrides take precedence over the still-correct config.toml values.

This also explains why a thread-tool canary was misleading: it bypassed the real renderer new-task path.

Real UI evidence

On the current signed unified app (26.707.62119, build 5211; bundled CLI 0.144.2):

  • Real UI projectless thread 019f5c94... began two turns as never / danger-full-access / permission_profile=disabled.
  • A subsequent thread_settings_applied event switched the unchanged thread to on-request / workspace-write / auto_review / permission_profile=managed; visual history shows the permission chip changing from Full access to Approve for me.
  • A later UI-created projectless thread then began restricted while the global TOML still contained never / danger-full-access / user.
  • After setting the live UI back to Full access, a fresh New task -> Don't work in a project Ultragoal canary began as never / danger-full-access / disabled, created .codex/<goal>/goal.md and plan.md, called create_goal, wrote and verified an ordinary file outside its generated workspace, completed the goal, and produced no approval request.

The iCloud/projectless-root repair is unrelated: good and bad tasks use real sibling directories with identical filesystem/trust properties, and one thread flipped permission profiles without any cwd change.

Durable local mitigation

I now pin all three locally controlled boundaries:

  1. config.toml: never / danger-full-access / user
  2. Renderer atom: agent-mode-by-host-id.local = full-access
  3. Managed requirements: default_permissions = ":danger-full-access" and only :danger-full-access in allowed_permission_profiles (with the required read-only sandbox fallback in allowed_sandbox_modes)

The managed requirement is loaded through com.openai.codex:requirements_toml_base64; a fresh bundled app-server configRequirements/read returns the intended allow-list/default, and config/read resolves to never / danger-full-access / user. An event-driven LaunchAgent watches the config, renderer state, requirement source, and preference. A live counterfactual from guardian-approvals was automatically restored to full-access with a backup and exit code 0.

The requirement applies to all local threads, not only projectless ones, and the already-running Desktop/app-server process must restart before the permission menu is fully constrained. The immediate UI default and fresh projectless canary are already healthy.

Product fix requested

Please make this precedence explicit and safe:

  • do not let a migrated/stale renderer atom silently override an explicit unrestricted config for fresh projectless tasks;
  • distinguish a per-thread permission change from the global new-task default;
  • surface which layer owns the effective profile; and
  • avoid turning ordinary projectless .codex goal setup into approval friction.