The default model is the one setting both kinds can hold:
model in settings.json wins over model in config.toml, and -m overrides both. config.toml can exist in four places and settings.json in two, as described below. display.json exists only as ~/.o4/display.json. Every file is optional. o4 runs with its defaults when none exist.
Config files and precedence
o4 readsconfig.toml from these places. When two files set the same single value, such as default_tier or [watch], the one higher in the list wins:
Command-line flags apply on top of the merged files for that run. For example,
-m picks the model whatever the files say, and --sandbox fixes the sandbox for the run and ignores default_tier (see Sandbox).
The project files are read from the directory you start o4 in (or the one given with -C), and only after you have trusted that workspace. Until then, o4 doesn’t open them. The same goes for the project’s .o4/settings.json, .o4/.mcp.json, hooks in .o4.md, and the project’s plugins, skills and sub-agents. Your own files in ~/.o4 and the managed file always load, and project instructions such as AGENTS.md are read either way.
Some values combine across files instead of one file winning:
[[permissions.rules]]and[[hooks]]entries from all files add up, together with hooks from the project’s.o4.md.- The
[sandbox]listsallowedDomains,allowedPathsandescape_hatch_binariesadd up.default_tierand[sandbox.proxy]come from the highest file that sets them. [keybindings]combines per action, with the higher file winning for an action both files set.[router]combines per key, and[router.capacity]per provider.auto_approve_plansis on if any file turns it on.
[watch] and [scout], come whole from the highest file that has them.
A few sections are read only from your own files, so a repository can’t set them: [daemon] and [codemode] come from ~/.o4/config.toml and ~/.o4/managed/config.toml, and [resume] only from ~/.o4/config.toml. o4 ignores these sections in project files and logs a warning.
Here’s a small ~/.o4/config.toml:
Managed policy
~/.o4/managed/config.toml is for an administrator, for example to add deny rules or hooks that users can’t override. o4 never writes to it, and commands in the sandbox can’t read or change it. Because deny rules from any file win over allow rules, a managed deny rule can’t be undone by a user or project allow rule.
When a file is broken
If a config file exists but can’t be read or parsed, o4 stops with an error that names the file instead of starting without its rules. Files larger than 1 MiB are rejected. o4 also refuses symlinked config files, and project config files that resolve outside the project.settings.json
~/.o4/settings.json holds your preferences. /config and the first-run setup wizard write it; you can also edit it by hand. It includes:
- the default model (
model), reasoning level (reasoning) and default provider (default_provider) - API keys you entered in o4 (
api_keys) - the permission default (
permission_default) - appearance:
theme,nerd_fonts,scrollbar,reduce_motionand similar - memory, suggestions, notifications and telemetry switches
- compaction thresholds and background-task limits
- language servers (
lsp_servers) and formatters (formatters)
settings.json with 0600 permissions because it can contain API keys. Commands in the sandbox can’t read it.
A project can have its own .o4/settings.json. In a trusted workspace, each key set there overrides your own, with these exceptions:
api_keys,lsp_serversandformatterscombine per entry. An entry for the same provider, language server or file extension comes from the project file.plugin_grantsalways come from your own file, so a repository can’t grant capabilities to plugins.
settings.json entirely. /config shows · project next to a value the project file overrides. /config itself always saves to ~/.o4/settings.json.
If settings.json isn’t valid JSON, or is larger than 1 MiB, o4 ignores the whole file and uses its defaults. See the configuration reference for every key.
The /config screen
Run/config to open the settings screen. To open a tab directly, pass its name, for example /config appearance. The tab names are general, model, reasoning, providers, appearance, keys, mcp, plugins, agents, skills and tools. /config providers add opens the form for adding a custom provider.
Keys
Rows work in one of three ways:
- Change in place.
Entertoggles an on/off setting or moves to the next value, and o4 saves the change to~/.o4/settings.json(or~/.o4/display.json) right away. API key is the only row you type into:Enteropens a text field,Enteragain saves it, andEsccancels. - Open another screen.
Enteropens a picker, browser or list, or runs a command such as/router status. Most of these close/configfirst. - Show details.
Enteropens a read-only details view. PressBackspaceto go back to the list. The sample rows under Preview on the Appearance tab work this way too; see Themes for what they show.
.o4/settings.json is marked · project.
You can open /config while the model is working. Changes are saved right away. Appearance and display changes, notifications and the permission default also take effect at once. Other changes, such as the reasoning level, the default provider or auto memory, take effect when the turn finishes, and o4 shows Settings saved; model and reasoning changes apply when the current turn finishes. A model you pick from the Model row is saved as your default right away, and any switch to it waits until the turn finishes. Import Codex auth and Import Claude Code auth are refused until the turn finishes.
Tabs and rows
Suggestions and notifications
Suggestions is off by default. When it’s on, o4 shows suggested prompts above the input: when a session starts, based on uncommitted changes in git and on failures from the lasttest_run, and after each turn, based on the reply. Up to two show at a time. Press 1 or 2 to pick one, then Enter or Tab on an empty prompt to send it. Typing or Esc dismisses them. After a turn, o4 can also show a dimmed hint ending in [Tab] in the empty prompt: Enter sends it, Tab puts it in the prompt box for editing. See Writing prompts.
Notifications is on by default. o4 sends a terminal notification when a turn finishes and when a tool call needs your approval. It uses the OSC 9 escape sequence in Ghostty, iTerm2, kitty, WezTerm and Warp, and the terminal bell in other terminals. The row switches all notifications on or off. To choose events, set notifications in settings.json to a list of agent-turn-complete and approval-requested.
Notification condition decides when notifications are sent: unfocused (the default) sends them only while the terminal window isn’t focused, and always sends them every time.
Display preferences
Timestamps, the thinking display, animations and screen reader output are saved in~/.o4/display.json, separate from settings.json. Change them in /config (the Appearance and Reasoning tabs); see Themes and display. There is no project display.json, so these four apply in every project. If the file is missing or isn’t valid JSON, o4 uses the defaults.
The ~/.o4 directory
o4 keeps everything it stores for you under~/.o4. The most useful entries:
The location is always
~/.o4 in your home directory; there is no setting or environment variable to move it.
Inside a project, o4 uses a .o4/ folder for project-level files: config.toml, config.local.toml, settings.json, instructions.md, .mcp.json, and skills/, agents/ and plugins/. It also keeps per-project caches there, such as the code index in .o4/index/.