macOS Codex global-state atomic writer recreates private state files as 0644
Summary
On macOS, the Codex desktop app repeatedly recreates$CODEX_HOME/.codex-global-state.json and its .bak file as 0644. A manualchmod 0600 is not persistent because the next normal state save atomically
replaces both paths.
Environment
- Codex app
26.721.81911, build5973 - bundled CLI
0.146.0-alpha.3.1 - macOS
27.0build26A5388g, arm64 - observed umask
0022
Reproduction
- With Codex running, record the mode of the two global-state files.
- Set both exact paths to
0600. - Allow a normal app state update.
- Observe changed birthtime/mtime and mode
0644on both replacement files. - Repeat a normal state update; the replacements are again
0644.
No content needs to be inspected to reproduce this.
Root cause
The installed global-state writer creates a unique sibling temporary file,
writes UTF-8 data without an explicit file mode, and renames it over the main
or backup destination. Under umask 0022, default creation yields 0644;
rename preserves that mode.
An isolated bundled-Node test produces:
default=0644 explicit_after_rename=0600 umask=0022
Expected behavior
The temporary, main and backup global-state files should be owner-only 0600
on POSIX systems and should remain so across every atomic rewrite.
Suggested fix
Pass an explicit creation mode in both writer paths:
writeFileSync(tempPath, data, { encoding: "utf8", mode: 0o600 });
await writeFile(tempPath, data, { encoding: "utf8", mode: 0o600 });
The temporary filename is unique for each write, so the mode is applied at
creation and is preserved by the atomic rename.
Regression test
Under umask 0022, exercise synchronous and asynchronous saves for the main
and backup files. Assert 0600 after repeated atomic replacements, unchanged
serialized data/JSON parsing, and no Windows regression.
Privacy note
This report intentionally omits the file contents, account identifiers, paths
containing a username, tokens and other private application state.