Skip to content

5.2c Agent Permissions & Security ​

Precisely control what Agents can and cannot do.

📝 Course Notes ​

Key concepts from this lesson:

Agent Permissions & Security Notes

What You'll Learn ​

  • Understand the permission system architecture
  • Configure bash/edit/task/skill permissions
  • Design secure Agent systems
  • Implement the principle of least privilege

Permission System Architecture ​

Three Permission Actions ​

ActionDescriptionEffect
allowAllowExecute directly, no confirmation needed
askAskShow confirmation dialog, user decides
denyDenyRefuse to execute, Agent receives error

Permission Configuration Hierarchy ​

Default Permissions (defined in source code)
    ↓ overrides
Global Config permission
    ↓ overrides
Agent-level permission

Later configurations override earlier ones.

Source: config.ts:418-447, agent.ts:194

Rule Priority: Last Match Wins ​

This is the most important rule! When multiple rules match, the last matching rule wins.

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",           // Rule 1: All commands need confirmation
      "git *": "allow",     // Rule 2: git commands allowed
      "git push*": "deny"   // Rule 3: git push denied
    }
  }
}

Executing git push origin main:

  1. Matches Rule 1 (*) → ask
  2. Matches Rule 2 (git *) → allow
  3. Matches Rule 3 (git push*) → deny
  4. Final result: deny (Rule 3 is last)

Source: agents.mdx:473, permissions.mdx:70


Configurable Permission Types ​

PermissionMatch TargetDescription
readFile pathRead files
editFile pathAll file modifications (edit/write/patch/multiedit)
globglob patternFile search
grepRegex patternContent search
listDirectory pathList directory contents
bashCommand stringExecute shell commands
tasksubagent nameCall sub-agents
skillskill nameLoad skills
lsp-LSP queries (currently no fine-grained support)
todoread-Read Todo list
todowrite-Update Todo list
webfetchURLFetch web content
websearchQuery stringWeb search
codesearchQuery stringCode search
external_directory-Access paths outside project directory
doom_loop-Detect repeated calls (same tool called 3 times with same input)

Source: config.ts:418-447


Permission Configuration Syntax ​

Simple Syntax: Single Action ​

jsonc
{
  "permission": {
    "edit": "allow",      // All file edits allowed
    "bash": "ask",        // All commands need confirmation
    "webfetch": "deny"    // Web fetching denied
  }
}

Global Setting ​

jsonc
{
  "permission": "allow"   // All permissions allowed
}

Object Syntax: Fine-grained Control ​

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",              // Default: need confirmation
      "git status": "allow",   // git status allowed
      "git log*": "allow",     // git log commands allowed
      "rm -rf*": "deny"        // rm -rf denied
    }
  }
}

Wildcards ​

SymbolMeaningExample
*Match any characters (0 or more)git * matches git status, git log
?Match single characterfile?.txt matches file1.txt

bash Permission Details ​

The bash permission matches the parsed command string.

Common Configuration ​

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",                    // Default: need confirmation

      // Git commands
      "git status": "allow",
      "git log*": "allow",
      "git diff*": "allow",
      "git branch*": "allow",
      "git checkout*": "ask",        // Branch switch needs confirmation
      "git push*": "ask",            // Push needs confirmation
      "git reset --hard*": "deny",   // Hard reset denied

      // Package management
      "npm install*": "allow",
      "npm run*": "allow",
      "npm publish*": "deny",        // Publishing denied

      // Dangerous commands
      "rm -rf*": "deny",
      "sudo*": "deny",
      "chmod 777*": "deny"
    }
  }
}

Best Practice for Plan Agent ​

jsonc
{
  "agent": {
    "plan": {
      "permission": {
        "bash": {
          "*": "deny",               // Default: deny all
          "git log*": "allow",       // Read-only commands allowed
          "git diff*": "allow",
          "git status": "allow",
          "ls*": "allow",
          "cat*": "allow",
          "head*": "allow",
          "tail*": "allow"
        }
      }
    }
  }
}

edit Permission Details ​

The edit permission controls all file modification operations, including:

  • edit tool
  • write tool
  • patch tool
  • multiedit tool

Common Configuration ​

jsonc
{
  "permission": {
    "edit": {
      "*": "allow",                    // Default: allow

      // Sensitive files
      "*.env": "deny",
      "*.env.*": "deny",
      "*.env.example": "allow",        // Example files allowed
      ".env.local": "deny",

      // System files
      "package-lock.json": "deny",     // Don't modify lock files
      "pnpm-lock.yaml": "deny",
      "yarn.lock": "deny",

      // Directories
      "node_modules/*": "deny",
      ".git/*": "deny",
      "dist/*": "deny"
    }
  }
}

Read-only Agent Configuration ​

jsonc
{
  "agent": {
    "readonly-auditor": {
      "description": "Read-only code audit, no file modifications",
      "mode": "subagent",
      "permission": {
        "edit": "deny"                 // Deny all edits
      }
    }
  }
}

task Permission: Control Subagent Calls ​

The task permission controls which subagents an Agent can call.

How It Works ​

When task: deny is set:

  1. The subagent is completely removed from the Task tool's description
  2. The model won't attempt to call it (because it can't see it)

Note: Users can still manually call any subagent via @agent-name. The task permission only affects automatic Agent calls.

Source: agents.mdx:557-565

Configuration Example ​

jsonc
{
  "agent": {
    "safe-orchestrator": {
      "description": "Security orchestrator, can only call specified subagents",
      "mode": "primary",
      "permission": {
        "task": {
          "*": "deny",                   // Deny all
          "docs-writer": "allow",        // Allow docs
          "code-reviewer": "allow",      // Allow review
          "dangerous-agent": "deny"      // Explicitly deny
        }
      }
    }
  }
}

Using Wildcards ​

jsonc
{
  "agent": {
    "orchestrator": {
      "permission": {
        "task": {
          "*": "deny",
          "safe-*": "allow",            // All safe-* allowed
          "internal/*": "allow",        // Nested directory allowed
          "code-reviewer": "ask"        // Needs confirmation
        }
      }
    }
  }
}

TaskTool Parameter Details ​

The complete parameter definition for the Task tool:

ParameterTypeRequiredDescription
descriptionstringYesTask description (3-5 words), used as sub-session title
promptstringYesTask prompt for the sub-agent to execute
subagent_typestringYesSub-agent name to call (must be non-primary agent)
session_idstringNoContinue an existing sub-session
commandstringNoCommand that triggered this task (for debugging)

Execution Flow ​

The TaskTool workflow:

Main Agent (Build)
    ↓
    1. Permission Check
       - Check if caller has task permission
       - Filter accessible subagents
       ↓
    2. Create Sub-session
       - Create independent session under main session
       - Title: description + (@subagent subagent)
       - Apply restricted permissions (todowrite/todoread/task)
       ↓
    3. Call Sub-agent
       - Sub-agent executes in independent session
       - Context only contains passed prompt
       - Listen to PartUpdated events for progress
       ↓
    4. Return Result
       - Collect all tool call summaries
       - Generate conversation summary
       - Return to Main Agent

Key Point: Sub-agents run in independent Sessions and cannot see the Main Agent's conversation history. You must provide complete context when calling.

Default Restrictions for Sub-agents ​

To prevent infinite recursion, sub-agents (whether called via task or @ manually) have the following hardcoded restrictions:

RestrictionDescriptionReason
todowrite: denyDeny writing to todo listPrevent sub-agent from interfering with Main Agent task management
todoread: denyDeny reading todo listSame as above
task: denyDeny calling other sub-agentsPrevent infinite recursion

Practical Usage Examples ​

Configure to Allow Specific Sub-agents ​

jsonc
{
  "agent": {
    "orchestrator": {
      "description": "Task orchestration Agent, can call specialized sub-agents",
      "mode": "primary",
      "permission": {
        "task": {
          "docs-writer": "allow",      // Allow docs writing
          "code-reviewer": "allow",    // Allow code review
          "general": "allow",          // Allow general tasks
          "*": "deny"                  // Deny others
        }
      }
    }
  }
}

Agent Internal TaskTool Call ​

markdown
# Pseudo-code example
Main Agent receives: Help me write API documentation

1. Analyze task type → Determine docs-writer sub-agent is needed
2. Call TaskTool:
   - description: "Write API documentation"
   - prompt: "Write documentation for the following functions..."
   - subagent_type: "docs-writer"
3. Sub-agent executes → Returns documentation content
4. Main Agent receives result → Continues conversation

Continue Sub-session ​

When a sub-agent needs to execute in steps, you can pass session_id to continue previous work:

TaskTool(
  description: "Complete documentation",
  prompt: "Check documentation completeness and fill in missing content",
  subagent_type: "docs-writer",
  session_id: "abc123"  // Continue previous session
)

Source: packages/opencode/src/tool/task.ts:23-193


skill Permission: Control Skill Loading ​

The skill permission controls which skills an Agent can load.

Configuration Example ​

jsonc
{
  "agent": {
    "restricted-agent": {
      "description": "Restricted Agent, can only use specified skills",
      "mode": "subagent",
      "permission": {
        "skill": {
          "*": "deny",                   // Deny all skills
          "docs-writer": "allow",        // Only allow docs skill
          "translator": "allow"
        }
      }
    }
  }
}

Source: skill.ts:15-21


Built-in Security Rules ​

OpenCode has some default security rules configured:

.env File Protection ​

jsonc
// Built-in default configuration
{
  "permission": {
    "read": {
      "*": "allow",
      "*.env": "deny",          // .env files denied
      "*.env.*": "deny",        // .env.xxx also denied
      "*.env.example": "allow"  // Example files allowed
    }
  }
}

Source: agent.ts:51-56

doom_loop Detection ​

When the same tool is called 3 times consecutively with identical input, doom_loop detection is triggered.

jsonc
{
  "permission": {
    "doom_loop": "ask"    // Default: prompt user for confirmation
  }
}

external_directory Protection ​

When an Agent attempts to access paths outside the project directory:

jsonc
{
  "permission": {
    "external_directory": "ask"    // Default: prompt user for confirmation
  }
}

Implicit Restrictions for Sub-agents ​

Besides configured permissions, sub-agents (whether subagent mode or called all mode) have the following hardcoded restrictions:

  1. Todo Tools Disabled

    • Sub-agents can never use todowrite and todoread.
    • This prevents sub-agents from interfering with Main Agent's task list management.
  2. Primary Agent Exclusive Tools Disabled

    • Tools configured in primary_tools cannot be used by sub-agents.
  3. Task Nesting Restriction

    • Sub-agents cannot call other sub-agents by default (unless explicitly granted task permission).
    • Example: explore cannot call general because its default permission is *: deny.

    Why this design?

    • Prevent infinite recursion: Avoid circular call chains between sub-agents that never complete
    • Control complexity: Make task execution paths more predictable and easier to debug
    • Resource management: Each sub-agent call creates a new session, consuming tokens and compute
    • Separation of concerns: Sub-agents should focus on one thing, orchestration is for the main agent

Agent-level Permission Override ​

Permissions set in Agent configuration override global permissions.

JSON Configuration ​

jsonc
{
  "permission": {
    "bash": {
      "*": "ask",
      "git status": "allow"
    }
  },
  "agent": {
    "build": {
      "permission": {
        "bash": {
          "git push": "allow"       // build agent additionally allows push
        }
      }
    },
    "plan": {
      "permission": {
        "bash": {
          "*": "deny",              // plan agent denies all commands
          "git log*": "allow"       // except viewing logs
        }
      }
    }
  }
}

Markdown Configuration ​

markdown
---
description: Read-only audit Agent
mode: subagent
permission:
  edit: deny
  bash:
    "*": deny
    "git log*": allow
    "git diff*": allow
  webfetch: deny
---

Only analyzes code, makes no modifications.

Security Best Practices ​

1. Principle of Least Privilege ​

Only grant the minimum permissions an Agent needs to complete its task.

jsonc
// ❌ Bad: Too permissive
{
  "agent": {
    "my-agent": {
      "permission": "allow"
    }
  }
}

// ✅ Good: Explicitly list needed permissions
{
  "agent": {
    "my-agent": {
      "permission": {
        "read": "allow",
        "edit": {
          "docs/*": "allow"
        },
        "bash": "deny"
      }
    }
  }
}

2. Explicitly List Allowed Commands ​

jsonc
// ❌ Bad: Allow all, then deny dangerous ones
{
  "permission": {
    "bash": {
      "*": "allow",
      "rm -rf*": "deny"
    }
  }
}

// ✅ Good: Deny all, then allow needed ones
{
  "permission": {
    "bash": {
      "*": "deny",
      "git status": "allow",
      "npm test": "allow"
    }
  }
}

3. Set Sensitive Operations to ask ​

jsonc
{
  "permission": {
    "bash": {
      "*": "allow",
      "git push*": "ask",        // Push needs confirmation
      "npm publish*": "ask",     // Publish needs confirmation
      "docker *": "ask"          // Docker operations need confirmation
    }
  }
}

4. Regularly Review Permission Configuration ​

Checklist:

  • [ ] Are there permissions no longer needed?
  • [ ] Are all sensitive operations set to ask?
  • [ ] Are new Agent permissions reasonable?

Common Pitfalls ​

SymptomCauseSolution
Permission not workingWrong rule orderPut * first, specific rules after
Subagent still callableUser @ calls are unrestrictedtask permission only affects Task tool
bash command match failsMatches parsed commandCheck actual command format (with arguments)
.env still readableCustom rule overrode defaultRemember to keep .env deny rule
Permissions too strictSet *: deny forgot to allow necessary onesAdd allow rules one by one

Relationship with 5.5 Permission Control ​

This chapter focuses on Agent-level permission configuration.

For global permission configuration and more details, see 5.5 Permission Control.


Lesson Summary ​

You learned:

  1. Permission System Architecture: Three actions, configuration hierarchy, last match wins
  2. 12+ Permission Types: bash, edit, task, skill, etc.
  3. Fine-grained Control: Using object syntax and wildcards
  4. TaskTool Mechanism: Sub-agent calls, parameter definition, execution flow
  5. Sub-agent Restrictions: todowrite/todoread/task disabled to prevent infinite recursion
  6. Built-in Security Rules: .env protection, doom_loop, external_directory
  7. Security Best Practices: Least privilege, explicit allow, sensitive operations ask

Next Lesson Preview ​

With permissions configured, there are more advanced techniques: tool interface design, pass-through parameters, debugging methods.

Next Lesson: 5.2d Agent Advanced Techniques