Skip to content

Permissions

Permissions control what an agent can do within its session. Herdctl provides fine-grained control over tool access and permission approval modes using a flat configuration structure that maps directly to the Claude Agents SDK. This allows you to create agents with appropriate access levels—from read-only support bots to full-access development agents.

agents/my-agent.yaml
permission_mode: acceptEdits
# Restrict available tools (whitelist)
tools:
- Read
- Write
- Edit
- Bash
- Glob
- Grep
# Pre-approve permissions (skip prompts)
allowed_tools:
- Read
- Write
- Edit
- "Bash(git *)"
- "Bash(npm *)"
- "Bash(pnpm *)"
# Block dangerous patterns
denied_tools:
- WebSearch
- "Bash(rm -rf *)"
- "Bash(sudo *)"

The permission_mode field controls how Claude Code handles permission requests. This maps directly to the Claude Agents SDK’s permission modes.

permission_mode: acceptEdits # default
ModeDescriptionUse Case
defaultRequires approval for everythingMaximum control, manual oversight
acceptEditsAuto-approve file operationsRecommended for most agents
bypassPermissionsAuto-approve everythingTrusted, isolated environments
planPlanning only, no executionResearch agents, dry runs

The most restrictive mode. Every tool use requires explicit approval through herdctl’s permission callback system.

permission_mode: default

When to use:

  • Testing new agents
  • Running untrusted prompts
  • Environments requiring audit trails

Auto-approves file operations (Read, Write, Edit, mkdir, rm, mv, cp) while still requiring approval for other tools like Bash execution. This is the default mode if not specified.

permission_mode: acceptEdits

When to use:

  • Standard development agents
  • Content creation agents
  • Most production use cases

Auto-approves all tool requests without prompting. Use with caution.

permission_mode: bypassPermissions

When to use:

  • Fully trusted agents in isolated environments
  • Docker-isolated agents with resource limits
  • Automated pipelines with pre-validated prompts

Enables planning mode where Claude analyzes and plans but doesn’t execute tools. Useful for understanding what an agent would do.

permission_mode: plan

When to use:

  • Previewing agent behavior before execution
  • Research and analysis agents
  • Generating plans for human review

Control which Claude Code tools an agent can use with tools, allowed_tools, and denied_tools arrays. These are top-level configuration fields.

Use tools to restrict which tools are available to the agent. Only listed tools exist in the agent’s context:

tools:
- Read
- Write
- Edit
- Glob
- Grep

Use allowed_tools to pre-approve permissions for specific tools. These tools won’t prompt for permission:

allowed_tools:
- Read
- Write
- Edit
- Glob
- Grep
- Task
- WebFetch
- "Bash(git *)"
- "Bash(npm *)"

allowed_tools only controls permission prompts, NOT tool availability. To restrict which tools exist, use tools instead.

Explicitly block specific tools:

denied_tools:
- WebSearch
- WebFetch
- "Bash(sudo *)"
- "Bash(rm -rf /)"
ToolDescriptionRisk Level
ReadRead files from filesystemLow
WriteCreate new filesMedium
EditModify existing filesMedium
GlobFind files by patternLow
GrepSearch file contentsLow
BashExecute shell commands (use patterns)High
TaskLaunch subagentsMedium
WebFetchFetch web contentMedium
WebSearchSearch the webMedium
TodoWriteManage task listsLow
AskUserQuestionRequest user inputLow
NotebookEditEdit Jupyter notebooksMedium

Bash commands are controlled using Bash() patterns in the allowed_tools and denied_tools arrays. The pattern inside the parentheses is matched against the command being executed.

Allow specific commands:

allowed_tools:
- "Bash(git *)" # All git commands
- "Bash(npm run *)" # npm run scripts
- "Bash(pnpm *)" # All pnpm commands
- "Bash(node scripts/*)" # Node scripts in scripts/
- "Bash(make build)" # Specific make target

Deny dangerous patterns:

denied_tools:
- "Bash(rm -rf /)"
- "Bash(rm -rf /*)"
- "Bash(sudo *)"
- "Bash(chmod 777 *)"
- "Bash(curl * | bash)"
- "Bash(curl * | sh)"
- "Bash(wget * | bash)"
- "Bash(wget * | sh)"
- "Bash(dd if=*)"
- "Bash(mkfs *)"
- "Bash(> /dev/*)"
FieldWhat it doesExample behavior
toolsWhitelist — only these tools existtools: [Read, Write] → Agent can ONLY use Read and Write
denied_toolsBlocklist — these tools are removeddenied_tools: [Bash] → Agent cannot use Bash
allowed_toolsPermission pre-approval — skip promptsallowed_tools: [Read] → Read won’t require permission

MCP (Model Context Protocol) server tools use the mcp__<server>__<tool> naming convention:

allowed_tools:
- Read
- Edit
- mcp__github__* # All GitHub MCP tools
- mcp__posthog__* # All PostHog MCP tools
- mcp__filesystem__read_file # Specific tool only

Wildcard support:

  • mcp__github__* — Allow all tools from the GitHub MCP server
  • mcp__* — Allow all MCP tools (not recommended)

Full development capabilities with sensible restrictions:

permission_mode: acceptEdits
allowed_tools:
- Read
- Write
- Edit
- Glob
- Grep
- Task
- TodoWrite
- "Bash(git *)"
- "Bash(npm *)"
- "Bash(pnpm *)"
- "Bash(node *)"
- "Bash(npx *)"
- "Bash(tsc *)"
- "Bash(eslint *)"
- "Bash(prettier *)"
- "Bash(vitest *)"
- "Bash(jest *)"
denied_tools:
- "Bash(rm -rf /)"
- "Bash(rm -rf /*)"
- "Bash(sudo *)"
- "Bash(chmod 777 *)"

Can read and search but cannot modify:

permission_mode: default
# Only these tools exist
tools:
- Read
- Glob
- Grep
- WebFetch
# No need for denied_tools when using tools whitelist

Can read/write files, no shell access:

permission_mode: acceptEdits
# Whitelist only content tools
tools:
- Read
- Write
- Edit
- Glob
- Grep
- WebFetch
- WebSearch
- TodoWrite
# Bash and Task don't exist for this agent

Maximum permissions in a Docker container:

permission_mode: bypassPermissions
# No tools restriction - agent has all tools
docker:
enabled: true
base_image: node:20-slim

Auto-approve but limit available tools:

permission_mode: bypassPermissions
# Only these tools are available
tools:
- Read
- Write
- Edit
- Bash
# allowed_tools has no effect with bypassPermissions

Plan and research without execution:

permission_mode: plan
allowed_tools:
- Read
- Glob
- Grep
- WebFetch
- WebSearch

Can only perform git operations:

permission_mode: acceptEdits
allowed_tools:
- Read
- Glob
- Grep
- "Bash(git status)"
- "Bash(git diff *)"
- "Bash(git log *)"
- "Bash(git add *)"
- "Bash(git commit *)"
- "Bash(git push *)"
- "Bash(git pull *)"
- "Bash(git checkout *)"
- "Bash(git branch *)"
- "Bash(git merge *)"
- "Bash(gh pr *)"
- "Bash(gh issue *)"
denied_tools:
- "Bash(git push --force *)"
- "Bash(git push -f *)"
- "Bash(git reset --hard *)"

Begin with minimal permissions and expand as needed:

# Start here
permission_mode: default
allowed_tools:
- Read
- Glob
- Grep
# Add more as you verify behavior
EnvironmentRecommended Mode
Development/Testingdefault
Production (standard)acceptEdits
Production (Docker isolated)bypassPermissions
Research/Previewplan

Always deny dangerous patterns in denied_tools:

denied_tools:
# Destructive commands
- "Bash(rm -rf /)"
- "Bash(rm -rf /*)"
- "Bash(rm -rf ~)"
- "Bash(rm -rf ~/*)"
- "Bash(rm -rf .)"
- "Bash(rm -rf ./*)"
# Privilege escalation
- "Bash(sudo *)"
- "Bash(su *)"
- "Bash(doas *)"
# Remote code execution
- "Bash(curl * | bash)"
- "Bash(curl * | sh)"
- "Bash(wget * | bash)"
- "Bash(wget * | sh)"
- "Bash(eval *)"
# System damage
- "Bash(dd if=*)"
- "Bash(mkfs *)"
- "Bash(fdisk *)"
- "Bash(> /dev/*)"
- "Bash(chmod -R 777 *)"
# Fork bomb
- "Bash(:(){ :|:& };:)"

Only allow necessary MCP tools:

allowed_tools:
# Specific MCP tools, not wildcards
- mcp__github__create_issue
- mcp__github__list_issues
- mcp__github__create_pull_request
# NOT: mcp__github__*

Combine Docker isolation with permissions:

permission_mode: bypassPermissions
docker:
enabled: true
base_image: node:20-slim

Restrict workspace access when possible:

workspace:
root: ~/herdctl-workspace/project-a
# Agent can only access this directory

Review agent permissions periodically:

Terminal window
# Show effective permissions for an agent
herdctl config show --agent my-agent --section permissions

Agent permissions inherit from fleet defaults and can be overridden:

# herdctl.yaml (fleet defaults)
defaults:
permission_mode: acceptEdits
denied_tools:
- WebSearch
- "Bash(sudo *)"
agents/trusted-agent.yaml
# Override mode
permission_mode: bypassPermissions
# Add to allowed tools
allowed_tools:
- WebSearch # Override fleet denial
# Inherits denied_tools from fleet

Inheritance rules:

  1. Agent settings override fleet defaults
  2. denied_tools takes precedence over allowed_tools
  3. Denied bash patterns always apply (never removed by inheritance)

Validate your permission configuration:

Terminal window
# Validate specific agent
herdctl validate agents/my-agent.yaml
# Validate entire fleet
herdctl validate
# Show merged permissions
herdctl config show --agent my-agent --section permissions

// Top-level permission fields (not nested)
permission_mode?: "default" | "acceptEdits" | "bypassPermissions" | "plan"
tools?: string[]
allowed_tools?: string[]
denied_tools?: string[]
FieldTypeDefaultDescription
permission_modestring"acceptEdits"Permission approval mode
toolsstring[]Tool availability whitelist - only these tools exist
allowed_toolsstring[]Tools that skip permission prompts (including Bash() patterns)
denied_toolsstring[]Tools explicitly blocked (including Bash() patterns)

Bash commands are specified using Bash(<pattern>) syntax:

PatternDescription
Bash(git *)Allow any git command
Bash(npm run build)Allow specific npm script
Bash(node scripts/*)Allow node scripts in specific directory
Bash(sudo *)(Deny) Block all sudo commands
Bash(rm -rf /)(Deny) Block dangerous rm command