Skip to content

5.5 Permission Control

💡 One-line summary: Control what AI can and cannot do through permission configuration.

📝 Course Notes

Key knowledge points from this lesson:

Permission Control Course Notes

What You'll Be Able to Do

  • Understand three permission modes
  • Configure global permissions
  • Configure fine-grained rules
  • Configure Agent-level permissions
  • Understand how Bash commands are matched
  • Configure external directory access permissions (external_directory)

Your Current Challenges

  • Worried about AI executing dangerous commands
  • Annoyed by confirmation prompts every time writing files
  • Want to limit certain Agent's capabilities
  • Always getting confirmation prompts when AI accesses files outside the project

When to Use This

  • When you need: Fine-grained control over what AI can do
  • And you don't want: To give AI too much freedom

Permission Modes

ModeDescription
allowAllow execution without asking
askAsk user for confirmation each time
denyDeny execution

Global Permission Configuration

Use permission (note: singular) in opencode.json:

jsonc
{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "*": "ask",          // Default: all operations require confirmation
    "bash": "allow",     // Bash commands auto-approved
    "edit": "deny"       // Deny file editing
  }
}

Set all permissions at once:

jsonc
{
  "permission": "allow"  // All operations auto-approved
}

Fine-Grained Rules (Object Syntax)

Apply different actions based on tool input:

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",                    // Default: require confirmation
      "git *": "allow",              // git commands allowed
      "npm *": "allow",              // npm commands allowed
      "rm *": "deny"                 // deny rm commands
    },
    "edit": {
      "*": "deny",                   // Default: deny editing
      "packages/web/src/content/docs/*.mdx": "allow"  // Only allow editing docs
    }
  }
}

Rule Priority: Rules are evaluated by pattern matching, the last matched rule wins.

A common pattern is to put wildcard "*" rules first, followed by more specific rules.


Wildcards

  • * matches zero or more arbitrary characters
  • ? matches exactly one character
  • Other characters match literally

Available Permissions List

Permissions Supporting Fine-Grained Rules

These permissions can use object syntax to configure different pattern rules:

PermissionDescriptionMatch Content
readRead filesFile path
editFile modification permission (covers edit, write, patch, multiedit)File path
globFile wildcard searchglob pattern
grepContent searchregex pattern
listList directory filesDirectory path
bashRun shell commandsParsed command prefix
taskStart sub-agentSub-agent name
skillLoad skillSkill name
lspRun LSP querySupports fine-grained matching
external_directoryAccess paths outside projectDirectory path

📝 Note: edit permission covers four tool operations: write (create new files), edit (modify files), patch (patch files), multiedit (batch edit).

Permissions Supporting Only Simple Syntax

These permissions can only be set to allow/ask/deny, object syntax not supported:

PermissionDescription
todoreadRead todo list
todowriteUpdate todo list
webfetchFetch URL content (passes URL at runtime for always approval)
websearchWeb search (passes query at runtime for always approval)
codesearchCode search (passes query at runtime for always approval)
doom_loopTriggered when same tool call repeats 3 times

⚠️ Note: plan_enter and plan_exit are only used as default permission configuration for built-in Agents, configuring them in opencode.json has no effect. The question field can be configured by users but generally doesn't need modification.

Source: opencode/packages/opencode/src/config/config.ts:621-652


Default Values

  • Most permissions default to "allow"
  • doom_loop and external_directory default to "ask"
  • .env files default to ask (not outright deny):
jsonc
{
  "permission": {
    "read": {
      "*": "allow",
      "*.env": "deny",
      "*.env.*": "deny",
      "*.env.example": "allow"  // Example files allowed
    }
  }
}

Source: opencode/packages/opencode/src/agent/agent.ts:46-57


Agent Permission Matrix

OpenCode has four main built-in Agents, each with different default permissions:

Tool/Permissionbuildplangeneralexplore
File read
File edit❌ (only .opencode/plans/*.md allowed)
Shell commands
Web search/fetch
Code search
TODO management
Question tool
Plan enter/exit
External directory access⚠️ (default ask)⚠️ (ask, only .opencode/plans/* allowed)⚠️ (default ask)⚠️ (ask, only GLOB allowed)
📝 Note: External Directory Permission Details

All Agents default to requiring user confirmation (ask) when accessing external directories, but some Agents have exceptions:

  • build: Default ask
  • plan: Default ask, but allows access to .opencode/plans/* directory
  • general: Default ask
  • explore: Default ask, but allows GLOB path access
📝 Note: Detailed Agent Descriptions

Build Agent (Default)

  • Mode: primary
  • Permissions: All allowed
  • Purpose: Default development Agent, all tools available

Plan Agent

  • Mode: primary
  • Permissions: edit defaults to deny, only allows editing .opencode/plans/*.md plan files
  • Purpose: Read-only planning, doesn't modify code, ensures focused thinking during analysis phase

General Agent

  • Mode: subagent
  • Permissions: TODO tools denied (todoread/todowrite: deny)
  • Purpose: General research, multi-step tasks

Explore Agent

  • Mode: subagent
  • Permissions: All tools denied by default ("*": "deny"), only allows grep/glob/list/bash/read/webfetch/websearch/codesearch
  • Purpose: Quick code exploration

Source: opencode/packages/opencode/src/agent/agent.ts:92-155


Plan Mode Special Rules

When AI attempts to access files outside the project working directory, the external_directory permission check is triggered.

Trigger Scenarios

The following tools trigger this permission when accessing paths outside the project:

ToolTrigger Condition
readReading files outside project
editEditing files outside project
writeWriting files outside project
patchPatching files outside project
bashCommands involving paths outside project (e.g., cd .., rm /tmp/file)

Detection Logic

OpenCode uses relative paths to determine if a file is inside the project:

typescript
// Source: opencode/packages/opencode/src/util/filesystem.ts:25-27
export function contains(parent: string, child: string) {
  return !relative(parent, child).startsWith("..")
}

If the relative path starts with .., the file is outside the project directory.

Default Behavior

jsonc
{
  "permission": {
    "external_directory": "ask"  // Default: ask for confirmation each time for non-GLOB paths
  }
}

Common Configuration: Allow External Folder Access

If you want OpenCode to access external folders without requiring authorization each time, add the following configuration:

jsonc
// opencode.json
{
  "permission": {
    "external_directory": "allow"
  }
}

This is one of the most common configurations, especially suitable for:

  • Frequently accessing configuration directories like ~/.config/
  • Project depends on files in other directories
  • Using monorepo but only starting OpenCode in a subdirectory

Fine-Grained Control (By Path)

external_directory supports object syntax for path-based configuration:

jsonc
{
  "permission": {
    "external_directory": {
      "*": "ask",                    // Default: require confirmation
      "/tmp/*": "allow",             // /tmp directory allowed
      "/home/user/shared/*": "allow", // Shared directory allowed
      "/etc/*": "deny"               // System config denied
    }
  }
}

Configuration Methods Summary

MethodConfiguration LocationExample
Global config~/.config/opencode/opencode.json"external_directory": "allow"
Project configproject_root/opencode.json"external_directory": "allow"
Environment variableOPENCODE_PERMISSIONexport OPENCODE_PERMISSION='{"external_directory": "allow"}'
Agent levelagent.xxx.permissionSee example below

Agent Level Configuration

jsonc
{
  "agent": {
    "file-manager": {
      "description": "File management Agent, can access external directories",
      "permission": {
        "external_directory": "allow"
      }
    },
    "safe-agent": {
      "description": "Safe Agent, cannot access external directories",
      "permission": {
        "external_directory": "deny"
      }
    }
  }
}

How Bash Commands Are Matched

OpenCode parses Bash commands into readable command prefixes before matching. Parsing rules are based on the command's "arity" (number of arguments).

Parsing Examples

Input CommandParsed Match Pattern
git checkout maingit checkout
npm installnpm install
npm run devnpm run dev
docker compose updocker compose up
rm -rf node_modulesrm

Common Command Arity

Command PrefixArityDescription
git2Matches git <subcommand>
npm2Matches npm <subcommand>
npm run3Matches npm run <script>
docker2Matches docker <subcommand>
docker compose3Matches docker compose <subcommand>
rm1Only matches rm

Source: opencode/packages/opencode/src/permission/arity.ts

Practical Configuration Examples

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",
      "git status": "allow",
      "git diff": "allow",
      "git log*": "allow",
      "npm run*": "allow",
      "rm*": "deny"           // Deny all delete operations
    }
  }
}

Ask Behavior

When OpenCode prompts for approval, it provides three options:

OptionBehavior
onceApprove only this request
alwaysApprove future requests matching the suggested pattern (current session)
rejectReject the request

How always Works

When selecting always, OpenCode uses the tool's suggested safe pattern to approve subsequent requests.

For example, when approving git status --porcelain, the suggested pattern might be git status*, so all subsequent git status related commands will be auto-approved.

Feedback on Rejection

When user rejects, they can choose:

  • Direct rejection: AI receives "user rejected this tool call" and will try other methods
  • With feedback: Can tell AI why rejected, guiding it to adjust direction

Agent-Level Permissions

Permissions can be overridden per agent, agent rules take precedence over global configuration:

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",
      "git status": "allow"
    }
  },
  "agent": {
    "deploy": {
      "permission": {
        "bash": {
          "*": "ask",
          "git status": "allow",
          "git push": "allow"     // Only deploy agent can push
        }
      }
    }
  }
}

Markdown Agent Configuration

Configure permissions in Markdown agents:

markdown
---
description: Read-only code review
mode: subagent
permission:
  edit: deny
  bash: ask
  webfetch: deny
---

Only analyze code and suggest changes, don't execute any modifications.

Note: Agent permissions merge with global permissions, rules in Agent take precedence.


Deprecated Configuration Migration

Since v1.1.1, the old tools configuration has been deprecated, migrate to permission:

jsonc
// ❌ Old syntax (deprecated, but still backward compatible)
{
  "tools": {
    "bash": true,
    "edit": false
  }
}

// ✅ New syntax
{
  "permission": {
    "bash": "allow",
    "edit": "deny"
  }
}

Old configuration still works, OpenCode will automatically convert to new format.


Security Best Practices

1. Protect Sensitive Files

jsonc
{
  "permission": {
    "read": {
      "*": "allow",
      "*.env": "deny",
      "*.env.*": "deny",
      "*.key": "deny",
      "*.pem": "deny",
      "credentials/*": "deny"
    }
  }
}

.env File Protection

OpenCode protects all .env and .env.* files by default, but allows direct access to .env.example (example files don't contain sensitive information).

2. Restrict Dangerous Commands

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",
      "git *": "allow",
      "npm *": "allow",
      "rm -rf *": "deny",
      "sudo *": "ask",
      "chmod *": "ask"
    }
  }
}

3. Enterprise Environment Configuration

Configure strict permissions in global config file ~/.config/opencode/opencode.json:

jsonc
{
  "permission": {
    "external_directory": "deny",
    "bash": "ask",
    "websearch": "deny",
    "webfetch": "deny"
  }
}

4. Development Environment Configuration

Configure relaxed permissions in project .opencode/opencode.json:

jsonc
{
  "permission": {
    "read": {
      "*": "allow"
    },
    "edit": {
      "src/*": "allow",
      "test/*": "allow",
      "*.md": "allow"
    },
    "external_directory": "allow"
  }
}

Permission Troubleshooting

Problem: Operation Unexpectedly Denied

  1. Check if in Plan mode (Plan mode forbids editing source code)
  2. Check permission configuration in opencode.json
  3. Check if file pattern matching was triggered (check .env and other sensitive file rules)

Problem: Frequent Confirmation Prompts

  1. Use "Always allow" to reduce repeated confirmations
  2. Add allow rules in configuration to override defaults
  3. Use wildcards to match a category of operations

Problem: Permission Configuration Not Taking Effect

  1. Check if JSON syntax is correct
  2. Confirm configuration file path (project vs global)
  3. Ensure using permission (singular) not permissions (plural)
  4. Restart OpenCode for configuration to take effect

Common Pitfalls

SymptomCauseSolution
Permission config not workingUsed permissions (plural)Use permission (singular)
Command unexpectedly blockedRule order issueLast matched rule wins, put * first
Cannot read .envDenied by defaultExplicitly add allow rule
todowrite: {...} errorOnly supports simple syntaxChange to todowrite: "allow"
git push matched as gitIncomplete command configConfigure "git push": "allow"
Agent permissions not workingWrong nesting levelEnsure under agent.name.permission
Always prompts for external file accessexternal_directory defaults to askSet "external_directory": "allow"
Want to deny certain external pathsNeed fine-grained controlUse object syntax to configure by path

Lesson Summary

You learned:

  1. Three permission modes (allow/ask/deny)
  2. Using permission configuration (singular)
  3. Distinguishing permissions supporting object syntax vs simple syntax
  4. Complete list of permission fields (read, edit, glob, grep, list, bash, task, skill, lsp, external_directory, todowrite, todoread, webfetch, websearch, codesearch, doom_loop, question)
    • Note: write, patch, multiedit tools use edit permission
  5. Permission matrix for four built-in Agents (build/plan/general/explore)
  6. Bash command Arity parsing mechanism
  7. always pattern matching behavior
  8. Agent-level permission override
  9. External directory access permission (external_directory) configuration methods
  10. Security best practices (protect sensitive files, restrict dangerous commands, enterprise/development environment configuration)
  11. Permission troubleshooting (operation denied, frequent confirmation prompts, configuration not working)


Next Lesson Preview

In the next lesson, we'll learn about theme and keyboard shortcut customization.