OpenCode Permissions Reference

Updated by

Independent reference, not affiliated with Anomaly. Tables are generated from the OpenCode source at v1.18.33 (v1.18.33). Official documentation: opencode.ai.

OpenCode's permission config maps each tool key, such as edit, bash or webfetch, to allow, ask or deny, for the whole tool or per pattern. Rules are checked in order and the last match wins; a request matching nothing is asked. By default everything is allowed, except that .env reads, outside directories and repeated identical calls ask first.

How do OpenCode permissions work?#

Each tool call becomes a request: a permission key plus one or more patterns, such as the file path for edit or the command text for bash. OpenCode lays out one ordered rule list (built-in defaults, then the agent's built-in rules, then your top-level permission, then that agent's own permission block) and, for each pattern, takes the last rule whose key and pattern both match. If any pattern resolves to deny the call fails; if any resolves to ask you are prompted; otherwise it runs.

{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "edit": "ask",
    "bash": {
      "*": "ask",
      "git status *": "allow",
      "git push *": "deny"
    },
    "webfetch": "deny"
  }
}

Key order in your file is preserved, so put the broad "*" pattern first and exceptions after it. "permission": "ask" on its own is shorthand for { "*": "ask" }. A tool whose last rule for pattern * is deny is removed from the model's tool list rather than offered and refused. The legacy tools map still works ("write": false becomes edit: deny), but permission wins where both set the same tool.

Which permission keys exist?#

Actions: ask, allow, deny
KeyAcceptsPattern is matched againstDefault (build agent)
readaction or {pattern: action}file path relative to the worktree*: allow; *.env: ask; *.env.*: ask; *.env.example: allow
editaction or {pattern: action}file path relative to the worktree (edit, write and apply_patch)allow (from `*`)
globaction or {pattern: action}the glob patternallow (from `*`)
grepaction or {pattern: action}the search regexallow (from `*`)
listaction or {pattern: action}no tool asks for this key in this releaseallow (from `*`)
bashaction or {pattern: action}each command in the parsed command lineallow (from `*`)
taskaction or {pattern: action}the subagent nameallow (from `*`)
external_directoryaction or {pattern: action}directory outside the worktree, as `<dir>/*`*: ask; <data>/tool-output/*, <tmp>/*, <skill dirs>/*, <reference dirs>/*: allow
todowriteaction onlyalways `*`allow (from `*`)
questionaction onlywhether the agent may ask you a questiondeny
webfetchaction onlythe URLallow (from `*`)
websearchaction onlythe search queryallow (from `*`)
lspaction or {pattern: action}always `*`allow (from `*`)
doom_loopaction onlythe tool name, after 3 identical calls in a rowask
skillaction or {pattern: action}the skill nameallow (from `*`)
<server>_<tool>action or {pattern: action}always `*`; one key per MCP toolallow (from `*`)

Keys marked "action only" take allow, ask or deny and no pattern map. MCP tools have no shared key: each is checked under its own <server>_<tool> name, so "github_*": "ask" covers every tool of a server named github. The question, plan_enter and plan_exit keys control the ask-the-user and plan-mode tools.

How do bash patterns match?#

* matches any run of characters, including spaces and /, and ? matches one character; everything else is literal and the pattern must match the whole value. A pattern ending in * also matches the bare command, so git status * covers git status too. Backslashes are treated as /, and on Windows matching ignores case. A leading ~/ or $HOME in a pattern expands to your home directory.

OpenCode parses the command line and checks every command in it separately, using that command's full text, so npm test && git push needs both npm test and git push to be allowed. cd, pushd, popd and their PowerShell forms are not checked. For file commands (rm, cp, mv, mkdir, touch, chmod, chown, cat, cd and their PowerShell equivalents), arguments that point outside the project raise an external_directory request for their directories before the bash check.

Choosing "Allow always" on a bash prompt saves a prefix rule built from a table of how many words make up each command's name:

136 command prefixes in the arity table
Command approvedRule "Always" adds
git checkout maingit checkout *
npm run devnpm run dev *
npm install reactnpm install *
rm -rf distrm *
python -m venv envpython -m *
docker compose up -ddocker compose up *
ls -lals *

What are the default OpenCode permissions?#

PermissionPatternAction
**allow
doom_loop*ask
external_directory*ask
external_directory<data>/tool-output/*, <tmp>/*, <skill dirs>/*, <reference dirs>/*allow
question*deny
plan_enter*deny
plan_exit*deny
read*allow
read*.envask
read*.env.*ask
read*.env.exampleallow

<data> is OpenCode's data directory (~/.local/share/opencode by default) and <tmp> its folder in the system temp directory. Every agent also allows external_directory for <data>/tool-output/*, where truncated tool output is stored, unless your config denies that pattern.

How do I set permissions per agent?#

Put a permission block inside the agent's entry. Its rules are appended after the top-level ones, so for that agent they win:

{
  "permission": { "bash": "ask" },
  "agent": {
    "build": { "permission": { "bash": { "*": "ask", "npm test *": "allow" } } },
    "plan": { "permission": { "webfetch": "deny" } }
  }
}

The built-in agents add these rules after the defaults and before your config:

AgentModeHiddenRules added after the defaults
buildprimarynoquestion: allow; plan_enter: allow
planprimarynoquestion: allow; plan_exit: allow; task general: deny; external_directory <data>/plans/*: allow; edit: deny; edit .opencode/plans/*.md: allow; edit <data>/plans/*.md: allow
generalsubagentnotodowrite: deny
exploresubagentno*: deny; grep: allow; glob: allow; list: allow; bash: allow; webfetch: allow; websearch: allow; read: allow; external_directory: ask; external_directory <data>/tool-output/*, <tmp>/*, <skill dirs>/*, <reference dirs>/*: allow
compactionprimaryyes*: deny
titleprimaryyes*: deny
summaryprimaryyes*: deny

A subagent started through the task tool keeps the parent session's deny rules and external_directory rules, and is denied todowrite and task unless its own configuration mentions them.

What does "ask" do?#

--auto: auto-approve permissions that are not explicitly denied (dangerous!). --yolo and --dangerously-skip-permissions are hidden aliases.
CommandAn ask rule, by defaultWith --auto
opencodeprompts you: Allow once, Allow always or Rejectanswered "once" automatically; also toggled from the command palette ("Enable auto-approve permissions")
opencode runrejected automatically ("permission requested: ...; auto-rejecting")answered "once" automatically

"Allow once" approves this call. "Allow always" approves it and adds allow rules for the request's patterns (for bash, the prefix patterns above), which also releases other pending requests in the session that those rules now cover; the rules are held in memory and never written to config. "Reject" fails the call and rejects every other pending request in the same session; rejecting with a message sends that message to the model as feedback. deny rules are never prompted, so --auto cannot override them.

doom_loop fires when the model makes the same tool call with identical input three times in a row. OPENCODE_PERMISSION accepts a JSON object that is merged into permission after every config file.

How are OpenCode 2 permissions different?#

OpenCode 2 replaces the permission object with a permissions array of rules, each with action, resource and effect (allow, deny or ask), evaluated with the same last-match-wins rule. It also renames actions: bash becomes shell and task becomes subagent. OpenCode 1.x refuses a config that contains permissions, at the top level or inside an agent, with "V2 permissions are not supported by OpenCode V1". The OpenCode 2 permissions docs cover the new schema.

Frequently asked questions#

How do OpenCode permissions work?#

Each tool call is checked against an ordered list of rules: defaults, the agent's built-in rules, your permission block, then the agent's own permission. The last rule whose tool key and pattern both match decides: allow runs the call, ask prompts you, deny fails it. A call that matches no rule is asked.

How do I allow all permissions in OpenCode?#

Set "permission": "allow", which becomes { "*": "allow" }, or start OpenCode with --auto, which answers every prompt with "Allow once". In the TUI the command palette entry "Enable auto-approve permissions" toggles the same mode mid-session. Neither overrides a deny rule, and the default ask rules for .env files and outside directories stop prompting under --auto.

Is there an OpenCode --dangerously-skip-permissions flag?#

Yes, as a hidden alias. opencode and opencode run accept --auto, described as "auto-approve permissions that are not explicitly denied (dangerous!)", and treat --yolo and --dangerously-skip-permissions the same way. Without --auto, opencode run rejects every permission prompt automatically, so non-interactive runs need either --auto or explicit allow rules.

How do I set permissions for a specific OpenCode agent?#

Add permission inside agent.<name> in opencode.json, for example "agent": { "plan": { "permission": { "bash": "deny" } } }. Agent rules are appended after the top-level permission, so they win for that agent only. Markdown agents in .opencode/agent/ accept the same permission field in their frontmatter.

How do I let OpenCode access directories outside the project?#

Allow them under external_directory, which defaults to ask: "external_directory": { "~/projects/shared/*": "allow" }. The pattern is matched against the outside directory followed by /*, and ~/ expands to your home directory. read, edit, write, apply_patch, glob, grep and lsp targets outside the worktree, and bash file arguments pointing outside it, all go through this key before their own check.

What changed in OpenCode 2 permissions?#

OpenCode 2 uses a permissions array of { "action", "resource", "effect" } rules instead of the permission object, renames bash to shell and task to subagent, and keeps last-match-wins ordering. The two formats are not interchangeable: OpenCode 1.x rejects a config containing permissions, and the v2 docs tell you not to use permission, bash or task.

Sources

Release-by-release changes: OpenCode version tracker. All OpenCode pages: OpenCode reference index.