---
title: "Permissions"
description: "Tune the authorization boundary for reads, writes and commands."
---

Bi's default posture is that **every tool call needs your approval**. The default level is "approve", so every call — `read` included — raises a prompt. That is deliberate: you get the boundary first, and you decide which operations deserve to stop bothering you.

Rules, not a lower default, are what make it convenient.

## Evaluation order

A tool call passes through three steps and stops at the first match:

1. **Auto-approve rules** (`auto_approve_rules`) — a match passes immediately, no prompt
2. **Tiered rules** (`rules`) — a match uses that rule's level to decide pass or prompt
3. **Default level** (`default_level`) — the fallback when neither matched

Rules therefore have priority: a precise auto-approve rule overrides a broader tiered rule.

## The two kinds of rule

The permission config lives in `.bi/permissions.json`, and you can also edit it from the settings UI. Its shape is:

```json
{
  "enabled": true,
  "default_level": 2,
  "timeout_seconds": 0,
  "auto_approve_rules": [],
  "rules": []
}
```

Setting `enabled` to `false` switches the whole approval mechanism off and tool calls stop being checked. A `timeout_seconds` of 0 means no timeout.

**Auto-approve rules** carry only `tool`, `pattern` and `message`, and mean exactly one thing: match, then pass.

```json
{
  "enabled": true,
  "default_level": 2,
  "auto_approve_rules": [
    { "tool": "exec", "pattern": "command=(git status|git diff)", "message": "read-only inspection" },
    { "tool": "read", "pattern": "path=.*\\.go$", "message": "reading source" }
  ],
  "rules": []
}
```

**Tiered rules** add a `level` field. A level below "approve" (numerically under 2) passes; equal or above prompts:

```json
{
  "tool": "exec",
  "pattern": "command=.*",
  "level": 3,
  "message": "running a command needs your approval"
}
```

## Writing patterns

Tool arguments are folded into a `k=v k=v` match string, with field names sorted alphabetically first. Write patterns against that shape:

- `command=.*npm.*`
- `path=.*\.md$`
- `command=.*git\s+push.*`

This keeps you from matching against escaped raw JSON text, which is fragile and very easy to misconfigure.

## Levels

| Level | Meaning |
| --- | --- |
| 0 | Log |
| 1 | Auto |
| 2 | Approve — the default |
| 3 | Sensitive |

## How this differs from intercept rules

Both gate operations, but the mechanism differs:

- A **permission rule** match raises a **prompt** you can allow immediately
- An **[intercept rule](/en/docs/advanced/intercept-rules)** match **rejects outright**, with no confirmation step

Use permission rules when you want "a human must agree"; use intercept rules when you want "no matter how you click, this is off limits".

## Workspace boundary

The agent's file operations are confined to the workspace root you selected, and out-of-bounds paths are rejected. This is independent of permission rules and cannot be relaxed by configuration.
