Permission Systems: Who Decides What Runs
Every coding agent must decide: should this command run? Should this file be edited? Should this tool be allowed? This page dissects how 18 agents answer those questions â from simple YOLO modes to ML-powered auto-approval.
Permission spectrum overview
How coding agents decide whether a command runs, a file gets modified, or a tool is invoked is a fundamental architecture question. The 18 agents analyzed here span an enormous range â from simple mode enums to ML-powered classifiers to rule DSLs with pattern matching. Below is a comprehensive comparison across four dimensions: the model type, available modes, auto-approval strategy, and granularity of control.
| Agent | Permission Model | Modes | Auto-Approval | Granularity |
|---|---|---|---|---|
| Claude Code | 4 modes + ML classifier | default, plan, bypass, auto | ML classifier for auto mode | Per-operation + wildcard rules |
| Neovate | 3 modes + 22-item ban list | default, autoEdit, yolo | autoEdit for edits | Per-tool category |
| Codex | execpolicy DSL + approval flow | suggest, auto-edit, full-auto | is_known_safe_command() fast-path |
Per-tool + prefix patterns + network rules |
| OpenCode | allow/deny/ask rules | Pattern-based | Always-once-reject | Per-permission + path/command patterns |
| Crush | TUI permission prompts | Interactive | None | Per-operation, user-present |
| DeerFlow | Tool group filtering + guardrails | Fail-closed default | None | Per-tool-group |
| OpenHands | RISK_LEVELS dict |
Risk-classified | None | Per-tool risk level |
| Hermes | Child agent tool stripping | Parent/child | None | Per-agent-type |
| Dirac | DIRAC_COMMAND_PERMISSIONS + approval |
Default, YOLO | YOLO auto-approves all | Glob pattern permissions |
| Qwen Code | Permission decisions + shell classification | Interactive | Read-only detection | Per-tool, read vs write |
| Pochi | Apply-diff safety params | Interactive | None | Per-operation safety checks |
| Kimi CLI | Hooks-based interception | Hook-driven | Depends on hooks | Hook-level |
| Pi Mono | Extensions-only | None (README: no permission popups) | None | Via extensions only |
| ADK-Rust | RBAC + scope-based tool auth + human-in-loop | Framework callbacks | None | Per-tool-scope + RBAC |
| Goose | ACP protocol permissions | IDE-mediated | IDE decides | Per-tool via ACP |
| Wintermolt | Config-based tool enablement | Config-driven | None | Per-tool enable/disable |
| Zaica | Config-based | Config-driven | None | Per-tool |
| Oh My Pi | Permission layers | Multi-layer | None | Extension-based layers |
Claude Code: The most sophisticated permission system
Claude Code implements the most nuanced permission model in this set: four approval modes, wildcard rules, an ML classifier, and persistent rule storage. It's the only agent that attempts to learn what's safe rather than relying purely on static rules or user judgment.
Four permission modes
| Mode | Behavior | When to use |
|---|---|---|
default |
Prompt for every dangerous operation | General development â safest default |
plan |
Show plan, ask once for approval, then auto-approve all operations in the plan | Known tasks with clear scope â balance of safety and speed |
bypassPermissions |
Auto-approve everything | Only in containers/VMs â never on host |
auto |
ML classifier decides â auto-approve safe operations, prompt for risky ones | Trusted workflows where you want ML to filter noise |
Wildcard rules
Claude Code supports pattern-based permission rules that auto-approve or deny entire families of operations. The two main rule types:
-
Bash(git *)â auto-approves all git commands. This is safe because git operations are inherently scoped to the repository and don't affect system state outside the workspace. -
FileEdit(/src/*)â auto-approves edits under/src/. Useful when you trust the agent to modify source files but want prompts for config files or dotfiles.
PermissionRule type and architecture
The core permission abstraction is the PermissionRule type,
which combines:
- Tool name pattern â which tool the rule applies to
- Path/command pattern â glob or prefix matching on the operation target
- Behavior â
allow,deny, orask
Rules are evaluated against a ToolPermissionContext, an in-memory
cache of permission decisions that avoids re-prompting for the same operation
within a session. Rules persist across sessions via settings.json,
so your Bash(git *) rule survives restarts.
ML classifier integration
In auto mode, the ML classifier runs in parallel with
the permission prompt. If the classifier deems an operation safe, it can
upgrade the decision to auto-approve before the user even responds. This is
the key UX improvement: the user sees fewer prompts because safe operations
are pre-approved by the model, not because they were silenced.
Permission UI components
The BashPermissionRequest component displays to the user:
- The full command to be executed
- Its classification (safe, risky, destructive)
- Sandbox status (running in sandbox or on host)
- A destructive warning via
getDestructiveCommandWarning()for high-risk patterns likerm -rf,dd, ormkfs
Programmatic rule changes flow through PermissionUpdate and
PermissionUpdateDestination, allowing other parts of the system
to modify permission rules at runtime â for instance, the plan mode
temporarily upgrades all planned operations to allow.
Codex: The execpolicy rule engine
Codex takes a different philosophy: rather than modes and classifiers, it
provides a programmable rule DSL through its execpolicy crate.
This gives fine-grained, code-as-policy control over what commands run.
Fast-path allowlist
Before hitting the policy engine, Codex checks
is_known_safe_command() â a hardcoded allowlist of read-only
operations that skip all policy evaluation:
ls, cat, find, git status, head, tail, wc, grep, echo, pwd, date, whoami
This is a critical performance optimization: most agent turns involve many read-only commands, and skipping the full policy engine for them reduces latency and eliminates unnecessary prompts.
Permission profiles
| Profile | Behavior |
|---|---|
suggest |
Prompt the user before any operation â most restrictive |
auto-edit |
Auto-approve file edits, prompt for commands |
full-auto |
Approve all operations â requires sandbox for safety |
Runtime approval flow
The request_permissions tool is Codex's runtime approval
mechanism: the agent explicitly requests permission from the user for an
operation, the user approves or denies, and the decision is cached for
the session. This is different from Claude Code's parallel classifier â
Codex always waits for the user unless the operation matches an allowlist
rule.
execpolicy rule DSL
The execpolicy crate supports:
- Prefix patterns â
"git *"matches any git subcommand - Network rules â allow/deny commands that make network requests
- Command whitelists/blacklists â explicit allow or deny lists
- MCP tool overrides â per-tool permission overrides in
config.toml
Thread-level sandbox isolation
Each thread can have its own sandbox_mode and approval policy.
This means you can run a high-trust thread with full-auto inside
a sandbox, while a lower-trust thread runs with suggest on the
host. The separation of approval policy from sandbox enforcement is Codex's
key architectural insight: they are orthogonal concerns and should be
configured independently.
OpenCode: Pattern-matching permission bus
OpenCode implements a permission system built around a permission bus â an event-driven architecture where permission requests and decisions flow through a central dispatch. This is fundamentally different from the mode-based approaches: every operation publishes a permission request, rules are evaluated in sequence, and a reply is generated.
Rule/Request/Reply architecture
Three core types define the system:
- Rule â a pattern with an associated behavior (
allow,deny,ask) - Request â a permission query from an operation (tool, path, command)
- Reply â the decision:
once(approve this time),always(approve and remember), orreject(deny)
Last-match-wins evaluation
Rules are evaluated sequentially with last-match-wins semantics.
This means you can set a broad allow rule and then add specific
deny rules that override it. The order matters: later rules take
precedence. This is a deliberate design choice â it allows users to start
permissive and then carve out exceptions, rather than starting restrictive
and carving out allowances.
Permission bus and ACP integration
The permission bus publishes events to all subscribers. This enables:
- Distributed permission handling â multiple components can observe and react to permission decisions
- ACP client integration â the Agent Communication Protocol client can surface permission prompts to external UIs
- Audit logging â every permission decision is an event that can be logged
Configuration and rule composition
The fromConfig() function builds rule sets from configuration,
and merge() combines multiple rule sets. The disabled()
function creates a fully permissive rule set â useful for testing or trusted
environments. Rules can be backed by a database for persistence across sessions,
and per-agent overrides allow different agents to have different permission rules
within the same system.
Neovate: Banned-list approach
Neovate takes the most opinionated stance: define what's not allowed, and everything else is fine. This inverted approach is simpler to reason about but requires maintaining a comprehensive ban list.
Three approval modes
| Mode | Behavior |
|---|---|
default |
Prompt for every operation â user decides case-by-case |
autoEdit |
Auto-approve file edits, prompt for commands |
yolo |
Approve everything â including the ban list |
The 22-item banned command list
In default mode, the following commands are always blocked:
alias, aria2c, axel, bash, chrome, curl, curlie, eval,
firefox, fish, http-prompt, httpie, links, lynx, nc,
rm, safari, sh, source, telnet, w3m, wget, xh, zsh
This list is carefully curated: it bans shells (bash, sh,
fish, zsh) to prevent escape to an unrestricted shell,
network tools (curl, wget, lynx, etc.)
to prevent data exfiltration, and destructive commands (rm,
eval, source).
High-risk pattern detection
Beyond static bans, Neovate detects dangerous patterns even in allowed commands:
rm -rf # recursive force delete
sudo # privilege escalation
dd if= # raw disk access
mkfs # filesystem creation
curl | sh # remote code execution
Each pipeline segment is checked individually, so ls | rm -rf /
is caught on the rm -rf segment even though ls is safe.
Quote-aware pipeline parser
The critical security feature is a character-level state machine that tracks quoting context before any security check:
inSingleQuoteâ inside single quotesinDoubleQuoteâ inside double quotesescapingâ after a backslash
splitPipelineSegments() uses this state machine so that
'echo "a|b"' is correctly identified as ONE segment â the pipe
inside the quotes is not a pipeline separator. Similarly,
hasCommandSubstitution() detects $() and backticks
within quoted contexts, preventing command injection via nested substitution.
Per-tool category permissions
Beyond bash, Neovate categorizes all tools into: read,
write, command, network, and
ask. Each category can have independent permission rules,
allowing fine-grained control like "auto-approve reads but prompt for writes."
Other notable permission approaches
Dirac: Glob-pattern permissions + YOLO escape hatch
Dirac uses the DIRAC_COMMAND_PERMISSIONS environment variable
to define glob patterns for allowed commands. The default workflow is
approval-based â every command requires user confirmation. The YOLO mode
is an escape hatch that auto-approves everything, but the codebase also
implements git checkpoints: before any risky operation, a checkpoint is
created so the user can revert if things go wrong.
Crush: TUI permission prompts + SHA-256 loop detection
Crush builds permission prompts directly into its Go terminal UI. Before executing dangerous operations, the user sees an interactive prompt in the TUI. Additionally, SHA-256 loop detection computes signatures for every tool call (tool_name + input + output); if any signature repeats 5 times in a 10-step window, the agent halts as stuck.
DeerFlow: Tool group filtering + guardrail middleware
DeerFlow filters tools into groups (web, file:read, file:write, bash) with a fail-closed default â nothing is allowed unless explicitly permitted. The guardrail middleware is one layer in a 14-layer middleware chain, where permissions are enforced as a distinct concern. This is a defense-in-depth approach: even if one layer fails, others catch the violation.
OpenHands: RISK_LEVELS dictionary
OpenHands classifies every tool with a risk level via a
RISK_LEVELS dictionary. All tool calls carry a
security_risk attribute, and a Jinja2 security risk
assessment template is included in the system prompt. This approach is
more about classification than enforcement â the agent is told the risk
level and expected to act accordingly, but there's no hard block on
high-risk tools.
Hermes: Child agent tool stripping
Hermes removes dangerous tools from child agents at construction time â 5 tools are always stripped from children. This creates a parent/child permission asymmetry: the parent has full tool access, while children operate with a reduced surface area. This is structural safety: rather than prompting or classifying, the dangerous tools simply don't exist in the child's tool set.
ADK-Rust: RBAC + scope-based tool auth + human-in-loop
ADK-Rust implements permission control at the framework level through Role-Based Access Control (RBAC) combined with scope-based tool authorization. Human-in-the-loop callbacks allow the framework to prompt the user for permission decisions. This is not shell-level parsing â it's architectural permission control built into the agent framework itself.
Qwen Code: Read-only detection + shell classification
Qwen Code classifies shell commands and auto-approves those detected as read-only. This is a lightweight version of Claude Code's ML classifier: instead of a full model, it uses simple pattern matching to determine if a command is destructive. Read-only commands skip the prompt; writes require user confirmation.
Pochi: Apply-diff safety parameters
Pochi implements permission checks around its apply-diff operations with safety parameters that validate the proposed changes before execution. This is a content-aware approach: rather than checking the tool name, it examines the actual diff content to determine if the operation is safe.
The YOLO mode problem
Every agent with a YOLO/bypass/auto-approve mode faces the same fundamental tension: developers want zero-friction for trusted work, but any auto-approve mode is a security hole if the model is compromised.
The YOLO tradeoff
YOLO mode is the most dangerous configuration in any coding agent. It removes all permission gates, trusting the model to never execute a destructive command. If the model is compromised via prompt injection, or if it hallucinates a dangerous command, there is no safety net.
Different agents approach this tension differently:
| Agent | YOLO Name | Mitigation |
|---|---|---|
| Claude Code | bypassPermissions |
Documentation warns: only use in containers/VMs |
| Neovate | yolo |
Still checks high-risk patterns (rm -rf, sudo, dd) |
| Codex | full-auto |
Separate approval policy from sandbox â full-auto can still be sandboxed |
| Dirac | YOLO |
Git checkpoints before risky operations |
Claude Code's ML classifier is the most nuanced attempt at balancing safety
versus speed: instead of a binary "prompt everything" or "approve everything,"
it uses a model to make per-operation decisions. Codex's approach of
separating approval policy from sandbox enforcement is also noteworthy â you
can be in full-auto mode but still sandboxed, meaning the agent
runs freely but within an isolated environment.
The real answer
Auto-approve inside sandbox, prompt outside sandbox. This simple rule captures the right balance: frictionless development in a safe environment, careful gates when operating on the host machine.
Permission architecture patterns
Across all 18 agents, five distinct architectural patterns emerge for handling permissions:
Pattern 1 Approval-mode layers
Agents: Neovate, Qwen Code, Dirac, Pochi
A simple mode enum where the user picks a level of trust: prompt-all, auto-edit, or approve-everything. This is the simplest pattern to implement and the easiest to explain to users. The tradeoff is coarse granularity â you can't say "auto-approve git but prompt for builds."
Pattern 2 Policy engines
Agents: Codex, OpenCode
A rule DSL with pattern matching for fine-grained control. Users write
rules like allow git * or deny curl *. This
is the most flexible pattern but requires users to understand rule
syntax and evaluation order. Codex's execpolicy and OpenCode's
permission bus are both examples of this approach.
Pattern 3 AI-assisted classification
Agents: Claude Code
An ML classifier makes per-operation decisions, auto-approving safe operations and prompting for risky ones. This is the most sophisticated approach but also the most complex to implement. The classifier runs in parallel with the permission prompt, upgrading decisions to auto-approve when confidence is high.
Pattern 4 Tool stripping
Agents: Hermes, DeerFlow
Remove dangerous tools from sub-agents entirely. This is structural safety â rather than prompting or classifying, the dangerous tools simply don't exist in the child agent's tool set. Hermes strips 5 tools from children; DeerFlow filters by tool group with a fail-closed default.
Pattern 5 Risk classification
Agents: OpenHands
Classify tools by risk level and apply policies per risk level. The
RISK_LEVELS dictionary maps each tool to a risk category,
and the system prompt includes a security risk assessment template.
This is more advisory than enforced â the agent is told the risk level
and expected to act accordingly.
What you should build
If you're designing a permission system for a new coding agent, here are the key recommendations distilled from analyzing all 18 agents:
Start with Codex's separation of concerns
Separate approval policy from sandbox enforcement. They are orthogonal concerns: approval policy decides whether to run, sandbox decides where it runs. Mixing them leads to configurations that are either too restrictive (prompt for everything in sandbox) or too permissive (auto-approve on host).
Minimum viable permission system
- At least 3 modes: prompt-all (safest), auto-approve-sandboxed (frictionless in isolation), and auto-approve-all (YOLO, for trusted environments). This covers the full spectrum of trust levels.
-
Add wildcard rules early:
Bash(git *)andRead(*)are safe defaults that eliminate prompt fatigue. Git operations are scoped to the repository, and reads can't modify state. These two rules alone eliminate a majority of permission prompts. -
Consider ML classification for auto mode: Even a simple
classifier that detects read-only commands is better than all-or-nothing.
You don't need Claude Code's full ML infrastructure â a pattern matcher
that recognizes
cat,ls,grep, andfindas safe is a meaningful improvement. - Never strip permissions from sub-agents without sandboxing them: If you remove tools from a child agent, also run it in a sandbox. Tool stripping alone is insufficient â the child might still access the filesystem or network through remaining tools.
-
Make permissions persistent across sessions: Store rules in
settings.jsonor a similar persistent format, not just in-memory. Users should not have to re-approvegit *every time they restart the agent. - Show users what's happening: Permission prompts should display the command, its risk level, and sandbox status. Transparency builds trust â users who understand why they're being prompted are more likely to set appropriate rules.
Common mistakes to avoid
- Don't make YOLO the default â the friction of prompt-all is better than the risk of auto-approve-all
- Don't rely solely on ban lists â they're incomplete by definition (you can't ban what you haven't thought of)
- Don't conflate approval policy with sandbox â they solve different problems
- Don't strip tools without also restricting the execution environment
- Don't forget that read-only detection is a cheap, effective classifier
Bottom line
Permission systems are the most visible security feature of any coding agent â they're what the user interacts with on every turn. A well-designed permission system balances safety and developer experience; a poorly designed one is either a security hole or a productivity black hole.
Claude Code's four-mode system with ML classifier represents the most sophisticated attempt at this balance. Codex's separation of approval policy from sandbox enforcement is the most architecturally sound. Neovate's quote-aware pipeline parser is the most defensive bash implementation. And OpenCode's permission bus is the most extensible â it can scale to distributed systems and multi-agent architectures.
The pattern that emerges from all of this: the best permission system is layered. Start with coarse modes, add wildcard rules for common operations, classify operations by risk, sandbox when possible, and persist decisions so users don't face prompt fatigue. No single pattern is sufficient on its own â production readiness requires all of them working together.