Permissions in opencode: allow, ask, deny
IntermediateLast updated: Oct 21, 2026
Every action opencode takes — reading a file, editing code, running a shell command — resolves to one of three outcomes: run it, ask first, or block it. The permission config controls which is which. Since v1.1.1 the legacy boolean tools config is deprecated and merged into permission (still accepted for backwards compatibility).
Actions
Each rule resolves to "allow" (run without approval), "ask" (prompt for approval in the TUI), or "deny" (block the action).
Configure
Set a global default with "*", then override per tool:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"*": "ask",
"shell": "allow",
"edit": "deny"
}
}
Or set everything at once: "permission": "allow".
Granular rules
Most permissions take an object mapping input patterns to actions. The last matching rule wins — put the catch-all "*" first and more specific patterns after it:
{
"$schema": "https://opencode.ai/config.json",
"permission": {
"shell": {
"*": "ask",
"git *": "allow",
"git push *": "deny",
"grep *": "allow"
},
"edit": {
"*": "deny",
"packages/web/src/content/docs/*.mdx": "allow"
},
"webfetch": {
"*": "ask",
"https://en.wikipedia.org/*": "allow"
}
}
}
Wildcards are simple: * matches zero or more characters, ? matches exactly one, everything else is literal. ~ or $HOME at the start of a pattern expands to your home directory. Patterns match the tool input — file paths for read/edit, the glob for glob, the regex for grep, the URL for webfetch, and the parsed command for shell. Match full commands, not just the binary: "grep *" allows grep pattern file.txt, while "grep" alone blocks it once arguments are passed.
Available permissions
| Key | Matches |
|---|---|
read |
File path |
edit |
File path — covers edit, write, patch |
glob |
Glob pattern |
grep |
Regex pattern |
list |
Directory path — the ls tool |
shell |
Parsed commands (e.g. git status --porcelain) |
task |
Subagent type |
skill |
Skill name |
lsp |
LSP queries (non-granular) |
question |
Asking you questions mid-run |
webfetch |
URL |
websearch |
Web search |
external_directory |
Tools touching paths outside the working directory |
doom_loop |
The same tool call repeated 3x with identical input |
Defaults are permissive: most keys are allow, while the two safety guards external_directory and doom_loop default to ask. read allows everything except .env files (*.env and *.env.* denied, *.env.example allowed).
external_directory gates any tool call that touches paths outside where opencode started. Allow trusted roots explicitly:
{
"permission": {
"external_directory": { "~/projects/personal/**": "allow" },
"edit": { "~/projects/personal/**": "deny" }
}
}
Allowed directories inherit workspace defaults — since read defaults to allow, reads just work; layer deny rules where you want restrictions.
What “ask” does
The approval prompt offers three outcomes:
once— approve just this requestalways— approve future requests matching the tool’s suggested patterns for the rest of the session (shell approvals typically suggest a safe prefix likegit status*)reject— deny the request
Auto mode approves anything not explicitly denied: launch with opencode --auto or opencode run --auto "…", or toggle it from the command palette (Enable auto-approve permissions). Deny rules are still enforced. A muted auto indicator shows next to the agent name while it’s active.
Per-agent overrides
Agents get their own permission block, merged with the global config — agent rules take precedence:
{
"agent": {
"plan": {
"permission": {
"edit": { "*": "deny", "~/.opencode/plan/*": "allow" },
"shell": "ask"
}
}
}
}
The built-in plan agent already denies edits outside ~/.opencode/plan/ by default (refused without prompting) and allows the question tool — but it leaves shell commands to your permission config. Watch the ordering: plan’s defaults apply before your top-level permission, so a global "edit": "allow" re-enables edits — pin agent.plan.permission explicitly to keep plan read-only. Markdown-defined agents (~/.config/opencode/agents/*.md) take the same permission: block in frontmatter.
v2 differences
v2 replaces the whole surface: permissions (plural) is an ordered array of { "action", "resource", "effect" } rules — last match wins — instead of the v1 permission object, and config files are opencode.jsonc. Actions rename bash→shell and task→subagent; doom_loop and lsp are not v2 actions. Defaults also shift: v2 asks on .env reads rather than denying them. This guide covers the v1 config.