Start typing to search the documentation.

Docs navigation

Permissions

Permissions control whether an agent may perform an action on a resource.

Configure

A common setup is to ask before running shell commands, allow routine Git inspection, and always block pushes. Add these ordered rules to opencode.jsonc:

{
  "$schema": "https://opencode.ai/config.json",
  "permissions": [
    { "action": "shell", "resource": "*", "effect": "ask" },
    { "action": "shell", "resource": "git status *", "effect": "allow" },
    { "action": "shell", "resource": "git diff *", "effect": "allow" },
    { "action": "shell", "resource": "git push *", "effect": "deny" },
  ],
}

The last matching rule wins, so the specific exceptions follow the broad rule.

Rules

Each rule requires three string fields:

FieldMeaning
actionTool permission action
resourceValue being used, such as a path, command, URL, query, skill ID, or agent ID
effectallow, deny, or ask
{
  "permissions": [
    { "action": "read", "resource": "*", "effect": "allow" },
    { "action": "read", "resource": "*.env", "effect": "deny" },
  ],
}
EffectResult
allowContinue without prompting
denyBlock the operation
askWait for a decision from the client

If no rule matches, OpenCode uses ask.

Matching

Actions and resources use simple whole-value wildcards:

PatternMatch
*Zero or more characters, including /
?Exactly one character
OtherThe literal character
{
  "permissions": [
    { "action": "edit", "resource": "packages/docs/*.mdx", "effect": "allow" },
  ],
}

This pattern matches the entire normalized path. Backslashes are normalized to slashes, and matching is case-insensitive on Windows.

A shell pattern ending in * also matches the command without arguments:

{ "action": "shell", "resource": "git status *", "effect": "allow" }

This matches both git status and git status --short.

OpenCode combines rules in order and uses the last match. Lower-priority configuration is loaded first, global rules are appended next, and agent rules are appended last.

{
  "permissions": [
    { "action": "read", "resource": "*", "effect": "allow" },
    { "action": "read", "resource": "secrets/*", "effect": "deny" },
  ],
}

Operations may check several resources, such as a patch that touches multiple files. Any deny denies the operation; otherwise any ask asks; otherwise the operation is allowed.

Actions

V2 action names are strings, so plugins may define more actions. Built-in tools currently use these actions and resources:

ActionResource
readLocation-relative internal path or canonical absolute external path
editTarget path for edit, write, and patch
globRequested glob pattern
grepRequested regular expression, not the search path
shellScanner-produced command string; compound commands may produce several
subagentTarget agent ID
skillSkill ID
question*
webfetchRequested URL
websearchSearch query
external_directoryCanonical external directory boundary, normally ending in /*
<server>_<tool>* for an MCP tool; unsupported characters in both names become _
execute*; controls Code Mode availability, while nested tools enforce their rules

For example, allow one skill and deny all other skills:

{
  "permissions": [
    { "action": "skill", "resource": "*", "effect": "deny" },
    { "action": "skill", "resource": "effect", "effect": "allow" },
  ],
}

doom_loop and lsp are not current V2 Core permission actions.

Directories

A path outside both the active Location and its non-root project worktree needs external_directory approval before its read or edit approval.

{
  "$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" },
  ],
}

This applies to external paths used by read, edit, write, and patch. Shell checks its external working directory and directories inferred by its scanner before checking shell resources.

For external_directory, read, and edit, a leading ~, ~/, $HOME, or $HOME/ is expanded when configuration loads:

{ "action": "read", "resource": "$HOME/reference/*", "effect": "allow" }

Shell resources remain raw command text and are not home-expanded.

Relative mutation paths may leave the active Location while remaining inside its project worktree. Explicit external paths are canonicalized before matching, so authorize only trusted directory boundaries.

Scanner

Enable the experimental portable shell scanner with:

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

The portable scanner replaces the default tree-sitter scanner; tree-sitter is not a fallback or second opinion. A command the portable scanner cannot analyze returns a scanner error, not a permission denial.

The flag changes only parser selection. Existing permission rules, saved approval patterns, and best-effort directory inference still apply. There is no extra approval mode or blanket restriction for unknown directories.

Defaults

Every agent, including custom agents, starts with this ordered base policy:

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

Shipped agents append these policies:

AgentAdditional policy
buildAllows questions
planAllows questions; denies edits except files under ~/.opencode/plan
generalDenies questions and launching subagents
exploreDenies everything except reads, globs, grep, web fetches, and web searches; asks for external directories and .env reads
titleDenies all actions
summaryDenies all actions
compactionKeeps the base policy

OpenCode also allows external-directory access to its managed tool-output, shell-output, temporary, and global configuration directories. The underlying read, edit, or other action still uses its own rules. Later global and agent rules can override these defaults.

Agents

Put shared rules at the top level and narrower rules under agents.<id>.permissions:

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

Agent rules are appended after global rules; they do not replace the global array. A custom subagent uses its own permissions, not a subset of its parent’s permissions.

Approvals

When a rule resolves to ask, clients can reply with:

ChoiceReplyResult
Allow onceonceApprove only the pending request
Allow alwaysalwaysApprove it and save the tool’s proposed patterns for the project
RejectrejectReject it and every other pending permission request in that session

For example, choosing Allow always for git status --short may save a shell prefix that covers later git status commands:

shell: git status * → allow

Saved approvals are durable, project-scoped allow rules. They never override a configured deny. Tools choose the proposed saved pattern: some propose *, shell proposes command prefixes, and skills and subagents propose their IDs. Review broad approvals and remove those no longer needed.

Clients may attach feedback when rejecting. Non-interactive clients must decide how to handle approval requests; configured deny rules always remain enforced.

Policies

A policy can hard-deny a permission check after these rules and saved approvals run. It turns allow or ask into deny and never grants access.

{
  "experimental": {
    "policies": [{ "action": "permission", "resource": "shell:sudo *", "effect": "deny" }],
  },
}

Global and Console-managed policies override project configuration, which is how an organization blocks a command that a repository would allow.