Custom Permissions Rewritten to Read-Only in Windows OS App
What version of the Codex App are you using (From “About Codex” dialog)?
OpenAI.Codex_26.611.8604.0_x64
What subscription do you have?
Enterprise API Key
What platform is your computer?
Microsoft Windows NT 10.0.26200.0 x64
What issue are you seeing?
Summary
Codex Desktop appears to overwrite a custom permission configuration from workspaceWrite to readOnly when a prompt is submitted in an existing local thread.
The issue reproduces after updating to:
OpenAI.Codex_26.611.8604.0_x64
The runtime config parser accepts the configured permission profile, and the thread permission entry can exist as workspaceWrite after app restart/update. However, submitting a prompt rewrites the same thread's persisted permission entry to readOnly. The sandbox runner then receives zero write roots and correctly applies read-only ACLs.
Environment
Codex Desktop package:
OpenAI.Codex_26.611.8604.0_x64
Codex executable path:
C:\Program Files\WindowsApps\OpenAI.Codex_26.611.8604.0_x64__2p2nqsd0c76g0\app\resources\codex.exe
OS:
Windows 11 Professional, 10.0.26200
Thread ID:
019ed65c-61e7-7290-bb5f-290caf86909d
Global state file:
C:\Users\User\.codex\.codex-global-state.json
Config
The global config includes a custom permission profile intended to allow workspace writes by default:
default_permissions = "trusted_workspace_edit"
[allowed_permission_profiles]
":read-only" = true
":workspace" = true
"trusted_workspace_edit" = true
[permissions.trusted_workspace_edit]
extends = ":workspace"
[permissions.trusted_workspace_edit.filesystem.":workspace_roots"]
"." = "write"
".git" = "write"
".agents" = "read"
".codex" = "read"
"**/.env" = "deny"
"**/.env.*" = "deny"
"**/*.env" = "deny"
[permissions.trusted_workspace_edit.network]
enabled = true
allow_local_binding = true
codex doctor --json reported that config.toml parsed successfully:
config.toml parse: ok
CODEX_HOME: C:\Users\User\.codex
filesystem sandbox: restricted
network sandbox: enabled
What steps can reproduce the bug?
Reproduction
- Use custom config mode in Codex Desktop.
- Confirm that the persisted thread permission entry is
workspaceWriteafter app update/restart. - Send a prompt in the same thread.
- Re-check:
C:\Users\User\.codex\.codex-global-state.json
Key:
electron-persisted-atom-state.heartbeat-thread-permissions-by-id.<threadId>
What is the expected behavior?
Expected Result
The thread permission should remain workspace-write, matching the active custom permission profile:
{
"sandboxPolicy": {
"type": "workspaceWrite"
}
}
Actual Result
Immediately after submitting a prompt, the same thread permission entry is rewritten to:
{
"approvalPolicy": "on-request",
"approvalsReviewer": "auto_review",
"sandboxPolicy": {
"type": "readOnly",
"networkAccess": false
}
}
Additional information
The app state also shows:
{
"agentMode": "custom",
"preferredNonFullAccessMode": null
}
Sandbox Log Evidence
After the prompt-time rewrite, the sandbox setup receives zero write roots:
setup refresh: spawning C:\Program Files\WindowsApps\OpenAI.Codex_26.611.8604.0_x64__2p2nqsd0c76g0\app\resources\codex-windows-sandbox-setup.exe
setup refresh: processed 0 write roots (read roots delegated); errors=[]
read-acl-only mode: applying read ACLs
This indicates the sandbox runner is obeying the persisted read-only policy it was given. The likely failure is upstream, in the prompt/send permission writeback path.
Static Analysis Findings
Static inspection of the packaged Desktop webview bundle suggests the prompt-time custom permission path is still using the older flat config schema.
Relevant packaged files:
C:\Program Files\WindowsApps\OpenAI.Codex_26.611.8604.0_x64__2p2nqsd0c76g0\app\resources\app.asar
Relevant bundled modules:
/webview/assets/src-DEDxOnN6.js
/webview/assets/use-permissions-mode-9OJM2a7p.js
/webview/assets/build-start-conversation-params-BWnyzAKG.js
/webview/assets/config-queries-BAeIrVPL.js
/webview/assets/agent-settings-DVuPmCWg.js
In /webview/assets/src-DEDxOnN6.js, minified function Xf maps an agent/permission mode to a concrete permission object:
function Xf(e,t,n){
switch(e){
case `read-only`: return qf();
case `full-access`: return Jf();
case `auto`: return Kf(t);
case `granular`: return Kf(t,void 0,Nf);
case `guardian-approvals`: ...
case `custom`: return Yf(t,n)
}
}
For custom, it delegates to Yf. That function appears to inspect only the legacy flat config fields:
sandbox_mode
approval_policy
sandbox_workspace_write
approvals_reviewer
If sandbox_mode is missing, null, or undefined, Yf falls back to qf(...), which produces:
{
sandboxPolicy: {
type: `readOnly`,
networkAccess: false
}
}
In /webview/assets/use-permissions-mode-9OJM2a7p.js, the prompt-time writeback path calls the same permission resolver and persists the result with:
update-thread-settings-for-next-turn
That matches the observed behavior: the custom profile is initially present as workspaceWrite, but on prompt submission the webview computes and writes a readOnly policy.
An ASAR-aware scan of shipped webview JS found references to:
sandbox_mode
approval_policy
sandbox_workspace_write
approvals_reviewer
but did not find references to:
default_permissions
allowed_permission_profiles
This suggests the Desktop webview permission UI/send path has not been updated to resolve the newer named permission profile schema, even though the runtime config parser accepts it.
Likely Root Cause
The Codex Desktop prompt/send permission writeback path appears to resolve custom permissions from legacy flat config fields rather than from:
default_permissions = "..."
[allowed_permission_profiles]
[permissions.<profile>]
When the active config uses only the named permission profile schema, the Desktop webview's custom path sees no sandbox_mode and falls back to read-only.
Possible Workaround
Adding the legacy flat fields alongside the named permission profile may work around the Desktop prompt-time resolver:
sandbox_mode = "workspace-write"
approval_policy = "on-request"
approvals_reviewer = "auto_review"
[sandbox_workspace_write]
network_access = true
This workaround targets the schema that the packaged Desktop webview appears to read. It does not explain or fix the underlying mismatch between the runtime's named permission profile support and the Desktop webview's prompt-time permission writeback path.
Suggested Fix Area
Please inspect the Desktop webview permission resolver and prompt-time thread settings update path, especially:
/webview/assets/src-DEDxOnN6.js
Xf(...)
Yf(...)
qf(...)
/webview/assets/use-permissions-mode-9OJM2a7p.js
update-thread-settings-for-next-turn
/webview/assets/build-start-conversation-params-BWnyzAKG.js
permission construction for start-conversation
/webview/assets/config-queries-BAeIrVPL.js
read-config-for-host result shape consumed by the webview
The relevant fix is likely to make the Desktop custom-mode resolver use the fully resolved permission profile from config, including default_permissions and [permissions.<profile>], rather than falling back to readOnly when legacy sandbox_mode is absent.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗