Start typing to search the documentation.

Docs navigation

Permissions

Permissions control whether an agent may perform an action on a resource. V2 configuration uses the permissions field and an ordered array of rules.

Rule schema

Each rule has three required string fields:

{
  "$schema": "https://opencode.ai/config.json",
  "permissions": [
    { "action": "*", "resource": "*", "effect": "ask" },
    { "action": "read", "resource": "*", "effect": "allow" },
    { "action": "read", "resource": "*.env", "effect": "deny" },
    { "action": "shell", "resource": "git status *", "effect": "allow" },
    { "action": "shell", "resource": "git push *", "effect": "deny" },
    { "action": "edit", "resource": "packages/docs/*.mdx", "effect": "allow" },
  ],
}
  • action matches a tool permission action.
  • resource matches the value the tool is trying to use, such as a path, command, URL, query, or agent ID.
  • effect is "allow", "deny", or "ask".

allow proceeds without prompting, deny blocks the operation, and ask waits for a user decision. If no rule matches, the result is ask.

Matching and order

Both action and resource support simple wildcards:

  • * matches zero or more characters, including /.
  • ? matches exactly one character.
  • All other characters are literal.

Matches cover the entire value. Slashes are normalized, and matching is case-insensitive on Windows. For shell convenience, a pattern ending in " *" also matches the command without arguments: "git status *" matches both git status and git status --short.

The last matching rule wins. Put broad rules first and exceptions later. Rules from lower-priority configuration files are loaded first. OpenCode then appends all global rules before agent-specific rules, so a matching agent rule overrides a global rule.

Some operations check several resources at once, such as a patch touching multiple files. OpenCode denies the operation if any resource resolves to deny; otherwise it asks if any resolves to ask; otherwise it allows it.

Actions and resources

V2 action names are strings, so plugins may introduce additional actions. The current built-in actions use these resources:

ActionResource matched
readLocation-relative path for an internal file or directory; canonical absolute path for an external target
editTarget path for edit, write, and patch; all three tools share this action
globThe requested glob pattern
grepThe requested regular expression, not the search path
shellThe complete raw shell command string
subagentThe target agent ID
skillThe skill ID
question*
webfetchThe requested URL
websearchThe search query
external_directoryA canonical external directory boundary, normally ending in /*
<server>_<tool>* for an MCP tool; unsupported characters in both names become _
execute*; controls availability of the Code Mode dispatcher, while each nested tool still enforces its own permission

doom_loop and lsp are not current V2 Core permission actions.

External directories

A path outside both the active Location and its non-root project worktree requires a separate external_directory decision before the tool’s own read or edit decision. This applies to external paths used by read, edit, write, and patch, and to an external shell working directory.

{
  "$schema": "https://opencode.ai/config.json",
  "permissions": [
    {
      "action": "external_directory",
      "resource": "~/projects/reference/*",
      "effect": "allow",
    },
    {
      "action": "read",
      "resource": "~/projects/reference/*",
      "effect": "allow",
    },
    {
      "action": "edit",
      "resource": "~/projects/reference/*",
      "effect": "deny",
    },
  ],
}

For external_directory, read, and edit resources, a leading ~, ~/, $HOME, or $HOME/ is expanded when configuration loads. Shell resources are raw command text and are not home-expanded.

Relative mutation paths may traverse outside the active Location while remaining inside its project worktree. Paths outside both boundaries require external_directory approval. Explicit external paths are canonicalized before matching, so authorize only trusted directory boundaries.

Experimental shell scanner

Set experimental.portable_shell_scanner to true to test the portable shell permission scanner. The default remains the tree-sitter scanner.

{
  "$schema": "https://opencode.ai/config.json",
  "experimental": {
    "portable_shell_scanner": true,
  },
}

When enabled, shell commands are analyzed only by the portable scanner. Tree-sitter is not used as a fallback or a second opinion. If the scanner cannot analyze a command, the shell tool reports a scanner error rather than silently retrying with Tree-sitter. These failures are experimental parser gaps to fix, not permission denials.

The flag changes parser selection, not permission policy. Existing rules, saved approval patterns, and best-effort directory inference continue to apply. There is no additional approval mode or blanket unknown-directory restriction. With the flag disabled, the existing Tree-sitter path is unchanged.

Defaults

Every agent, including custom agents, starts with ordered defaults that allow tools, ask for external directories, ask for .env reads, and allow .env.example reads. Shipped agents then add their own policies:

AgentEffective default policy
buildAllows most actions; asks for external directories and .env reads; allows questions and entering plan mode; denies exiting plan mode
planUses the same base, allows questions and exiting plan mode, and denies edits except OpenCode plan files
generalUses the base policy but cannot launch another subagent; questions and plan transitions remain denied
exploreDenies everything except read, glob, grep, webfetch, and websearch; cannot launch subagents and asks for external directories
Hidden maintenance agentsDeny all actions

The base read rules are ordered as follows:

[
  { "action": "read", "resource": "*", "effect": "allow" },
  { "action": "read", "resource": "*.env", "effect": "ask" },
  { "action": "read", "resource": "*.env.*", "effect": "ask" },
  { "action": "read", "resource": "*.env.example", "effect": "allow" },
]

OpenCode also permits its managed tool-output, shell-output, temporary, and global configuration directories. These exceptions apply only to the external-directory boundary for every agent; the underlying action still uses its own permission rules. The environment instructions identify the temporary directory available for work outside the workspace. Later global and agent-specific rules can override these defaults.

Agent overrides

Configure shared policy at the top level and append narrower rules to a named agent under agents.<id>.permissions:

{
  "$schema": "https://opencode.ai/config.json",
  "permissions": [
    { "action": "shell", "resource": "*", "effect": "ask" },
    { "action": "shell", "resource": "git diff *", "effect": "allow" },
    { "action": "shell", "resource": "git status *", "effect": "allow" },
  ],
  "agents": {
    "reviewer": {
      "description": "Review code without changing it",
      "mode": "subagent",
      "permissions": [
        { "action": "edit", "resource": "*", "effect": "deny" },
        { "action": "shell", "resource": "git diff *", "effect": "allow" },
        { "action": "shell", "resource": "git status *", "effect": "allow" },
      ],
    },
  },
}

Agent rules do not replace the global array; they are appended after it. A custom subagent executes with its own permissions, not a permission subset derived from the parent agent.

Approval choices

When an ask rule matches, clients can reply with:

  • Allow once (once): approve only the pending request.
  • Allow always (always): approve this request and save the patterns proposed by the tool for the current project.
  • Reject (reject): reject the request. Rejecting also rejects other pending permission requests in the same session; clients may attach feedback.

Saved approvals are durable and project-scoped. They are additional allow rules, but they can never override a configured deny. The proposed saved pattern may be broader than the displayed resource: several tools propose *, shell proposes command prefixes, and skills and subagents propose their IDs. Review the confirmation carefully and remove saved approvals that are no longer needed.

Non-interactive clients must decide how to handle requests that require approval; explicit deny rules remain enforced.