| title | Permissions |
|---|---|
| description | Five permission modes and one rule language that gates every edit, read, and command. |
One setting gates what the agent may write, read, and run. Change it with
shift+tab in chat, --permission-mode per run, or permissions.mode in
aster.yaml.
| Mode | Does |
|---|---|
plan |
Explores and presents a plan. Never edits or runs |
manual |
Asks before every edit and command. Needs the TUI or --stream; headless runs deny instead |
auto |
Edits and runs, pausing on the built-in risky-command list |
edit |
Like auto, but commands are trusted; only a rule stops one. Default |
yolo |
No rules, no sandbox. Asks once before enabling |
No mode allows something a looser one refuses, so stepping up the ladder only ever adds freedom.
allow, ask, and deny hold rules in one language:
| Rule | Matches |
|---|---|
Edit(<glob>) |
Writing a path |
Read(<glob>) |
Reading a path |
Bash(<command>:*) |
A command line starting with <command> |
Bash(<command>) |
That command line exactly |
Edit, Read, Bash |
Everything that tool does |
permissions:
mode: edit
allow: ["Bash(cargo test:*)", "Edit(src/**)", "Read(**/.env)"]
ask: ["Edit(migrations/**)", "Bash(git push:*)"]
deny: ["Bash(npm publish:*)", "Edit(infra/**)"]A Bash rule is matched against the command line and every command inside a
shell script it carries, so Bash(sudo:*) still fires on
bash -lc "cargo build && sudo make install". Leading environment assignments
are ignored, a shell nested in a shell is followed a few levels down, and a
prefix ends at a word boundary, so Bash(rm:*) does not also match rmdir.
denyaskallow- the built-in rules
mode
User rules come before the built-ins, so a single allow entry overrides one of
them without disabling the rest. To read .env files but keep every other
secret protected:
permissions:
allow: ["Read(**/.env)"]Ask before writing, since anything here runs as code later: .git/**,
**/.git/**, .github/workflows/**, .husky/**.
Ask before running, in every mode but edit: privilege escalation (sudo,
doas, su), destructive filesystem operations (rm, rmdir, dd, mkfs,
shred), permission and process control (chmod, chown, chgrp, kill,
killall, pkill), system control (shutdown, reboot, halt, systemctl,
launchctl), and network egress (curl, wget, nc, ssh, scp, rsync).
Pausing on these is the whole difference between auto and edit; an ask
rule of your own fires in edit too.
Refuse to read, since a secret cannot be taken back out of the model's
context: **/.env, **/.env.*, **/*.pem, **/*.key, **/id_rsa*,
**/*.p12, **/*.pfx, **/credentials.json, **/secrets.*.
use_default_rules: false drops all three sets at once.
Outside yolo, commands run in a sandbox: filesystem writes are limited to the repository and temp directories, secrets are stripped from the environment, and the network is reachable with per-domain approval.
Two keys tune the edges:
permissions:
additional_directories: ["~/Downloads"] # readable outside the repo, no prompt
allow_credentials: ["gh:~/.config/gh"] # preauthorize one credential dir for one commandThe sandbox denies ~/.ssh, ~/.aws, ~/.gnupg, ~/.config/gh, and
~/.kube by default and prompts when a command needs one; an
allow_credentials entry skips the prompt for that command-and-directory pair
only. ~/Library/Keychains is never grantable.
A prompt needs a front-end that can answer. Headless runs (-p, --json) have
none, so anything that reaches ask is refused there. Pre-approve it with an
allow rule, or run with --stream and answer approvals on stdin.