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 | ✓ | ✓ | N/A | — |
| Interactive approval prompts | ✓ | ✓ | N/A | — |
| Global + workspace scopes | ✓ | ✓ | N/A | — |
| Sandbox execution without per-action prompts | — | — | ✓ | — |
On Web, the permissions.yaml model and interactive approvals do not apply: the agent runs inside an isolated cloud sandbox instead of prompting per action, so those rows are marked N/A rather than unsupported.
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.
| Scope | Location | Allowed effects |
|---|---|---|
| Kiro | Hardcoded security invariants (cannot be changed by configuration) | deny, ask |
| administration | Enterprise permission policies in managed-settings.json | 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.
Policy presets are named, composable sets of session-scope allow rules. Clients that create agent sessions over the Agent Client Protocol (ACP), such as editor integrations, review bots, and CI harnesses, use them to seed a session with a well-defined capability profile without writing individual rules from scratch.
Presets are requested by an ACP client in the _meta.kiro.policyPreset field of a session/new or session/load request. Multiple presets can be combined; their rules are merged as a union.
{ "_meta": { "kiro": { "policyPreset": ["edit-workspace", "dev-shell"] } } }
Preset rules are seeded into the session when the client opens it, separately from the rules you configured in permissions.yaml. This is why a session's active rules can include entries you never wrote.
Requesting an unknown preset ID rejects the session request. There is no silent fallback to a default policy.
| Preset ID | What it allows |
|---|---|
allow-all | Every capability except sandbox_network (equivalent to capability: all, effect: allow) |
edit-workspace | fs_read and fs_write within ./** only |
read-workspace | fs_read within ./** only |
read-all | fs_read anywhere, plus web_fetch and web_search |
read-only-shell | Read-only shell commands: system info plus git, cargo, npm, docker, kubectl, and rustup queries |
dev-shell | Read-only shell commands (the read-only-shell set) plus git write commands; build tools cargo, npm, bun, yarn; workspace ops mkdir, touch, mv, cp; inspection commands ls, cat, grep, echo |
Presets are allow-only profiles: they remove prompts for the operations a session is meant to perform, and everything else keeps the default of asking for approval. Pick the narrowest profile that matches the session's job.
| Session profile | Presets | What you get |
|---|---|---|
| Code review or audit | read-workspace | Workspace files are read silently. Writes and shell commands still ask, so the session prompts before any change. |
| Investigation and research | read-all | File reads anywhere on disk, plus web_fetch and web_search, for sessions that trace an issue across code and external documentation without needing write access. |
| Diagnostics and triage | read-workspace + read-only-shell | The agent reads workspace files and queries git, docker, kubectl, and other tool state. Nothing grants a write, so every change still asks. |
| Autonomous build-and-test loop | edit-workspace + dev-shell | The agent edits workspace files, runs git write commands and the standard build tools, and creates or moves files in the workspace without a prompt per action. Writes outside the workspace and unlisted commands still ask. |
| Disposable sandbox | allow-all | Every capability is allowed. Use it where the environment itself is the boundary, such as a container that is discarded after the run. |
Each profile is requested the same way: the preset IDs go in the policyPreset array of the session request. A code review bot opens its session with:
{ "_meta": { "kiro": { "policyPreset": ["read-workspace"] } } }
A diagnostics session pairs workspace reads with read-only shell queries:
{ "_meta": { "kiro": { "policyPreset": ["read-workspace", "read-only-shell"] } } }
A CI harness that edits, builds, and tests combines the two write-capable presets:
{ "_meta": { "kiro": { "policyPreset": ["edit-workspace", "dev-shell"] } } }
deny rules and the Kiro scope invariants below still apply regardless of which profile is active.
The same profiles map onto common integration patterns. An editor integration that embeds Kiro typically requests edit-workspace so in-workspace file edits do not prompt. A code review bot requests read-workspace and nothing more. A CI or evaluation harness driving the agent programmatically combines edit-workspace with dev-shell, then adds its own deny rules for anything the pipeline must never touch.
Presets extend what a session is allowed to do, but they cannot override higher-precedence rules. The deny-overrides algorithm described in the Scopes section above still applies in full:
~/.kiro/settings/, .kiro/settings/, ~/.kiro/workspace-roots/) block the action regardless of any preset..git/**, .kiro/agents/**, .kiro/hooks/**, .kiroignore) still prompt, even when a preset grants broad write access.deny rule you have configured at the user, workspace, or administration scope wins over a preset allow.sandbox_network capability is not part of the all meta-capability, so no preset, including allow-all, changes sandbox network gating.Subagents inherit the parent session's full rule set via deny-wins intersection: every allow, deny, and ask from the parent applies, so a more restrictive rule on either side always wins.
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, not changeable by configuration) 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. Allow and Deny are available for the current invocation. Kiro also shows persistent choices when it can derive a saved rule that will work for the requested command:
| 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 |
If Kiro cannot verify a working saved rule, it hides Always allow and Always deny and explains why in the prompt.
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