# Permissions

VeilCLI uses a layered allow/deny permission system to control which tools an agent can use in each mode.

---

## Overview

When an agent tries to call a tool, the runtime checks a permission chain from most-specific to least-specific:

```
agent.modes.<mode>.permissions  (most specific — wins)
    ↓
agent.modes.<mode>.tools / disallowedTools
    ↓
settings.json permissions        (project-wide default)
    ↓
built-in defaults                (deny everything not listed)
```

The **first matching rule wins**.

---

## Permission Fields

### `allow`

An array of tool names that are explicitly permitted. Use `["*"]` as a wildcard to allow all tools.

```json
"permissions": {
  "allow": ["read_file", "list_dir", "bash"]
}
```

### `deny`

An array of tool names that are explicitly blocked. **`deny` always wins over `allow`** — if a tool appears in both, it is denied.

```json
"permissions": {
  "allow": ["*"],
  "deny": ["bash", "write_file"]
}
```

### `ask`

*(Settings-level only — not available in agent.json)*

Tools intended to require human approval before executing. **Current behavior:** `ask` is treated as *allow* — interactive approval is not yet implemented. Use `deny` for anything that must not run unattended.

```json
"permissions": {
  "allow": ["*"],
  "ask": ["write_file", "edit_file", "bash"]
}
```

---

## Where to Set Permissions

### 1. Global default — `settings.json`

Applies to all agents and all modes unless overridden:

```json
{
  "permissions": {
    "allow": ["read_file", "list_dir", "grep", "glob", "web_search"],
    "deny": [],
    "ask": ["bash", "write_file"]
  }
}
```

### 2. Per-mode in `agent.json`

Overrides the global default for a specific mode of a specific agent:

```json
{
  "modes": {
    "chat": {
      "enabled": true,
      "permissions": {
        "allow": ["read_file", "list_dir", "bash", "write_file"],
        "deny": []
      }
    }
  }
}
```

### 3. Tool whitelists and blacklists in `agent.json`

`tools` and `disallowedTools` provide an alternative syntax:

```json
{
  "modes": {
    "chat": {
      "enabled": true,
      "tools": ["read_file", "list_dir"],
      "disallowedTools": ["bash"]
    }
  }
}
```

`tools` = controls which tools are shown to the LLM (tool list in the API call)  
`disallowedTools` = hides tools from the LLM  
`permissions.allow`/`deny` = controls which tools are allowed to *execute* at runtime (checked at call time, supports glob patterns like `"bash(rm *)"`)

You can use `tools`/`disallowedTools`, `permissions`, or both together.

---

## Common Patterns

### Allow everything (development / trusted environment)

```json
{
  "permissions": {
    "allow": ["*"],
    "deny": [],
    "ask": []
  }
}
```

### Read-only agent (safe for untrusted work)

```json
{
  "modes": {
    "chat": {
      "enabled": true,
      "permissions": {
        "allow": ["read_file", "list_dir", "grep", "glob", "web_search", "web_fetch"],
        "deny": []
      }
    }
  }
}
```

### Full access except destructive tools

```json
{
  "permissions": {
    "allow": ["*"],
    "deny": ["bash", "write_file", "edit_file"]
  }
}
```

### Require approval for write operations

```json
{
  "permissions": {
    "allow": ["*"],
    "deny": [],
    "ask": ["write_file", "edit_file", "bash"]
  }
}
```

### Orchestrator agent (multi-agent tools only)

```json
{
  "modes": {
    "chat": {
      "enabled": true,
      "permissions": {
        "allow": ["agent_spawn", "agent_message", "log_write"],
        "deny": []
      }
    }
  }
}
```

---

## Permission Denied Behaviour

When a tool is denied, the agent receives a tool result of:

```
Permission denied for tool "bash"
```

The agent loop continues — the LLM can choose to try another approach.

---

## Agent Restrictions

In addition to tool permissions, you can restrict which sub-agents an agent is allowed to spawn:

```json
{
  "modes": {
    "chat": {
      "allowedAgents": ["coder", "writer"],
      "disallowedAgents": ["admin"]
    }
  }
}
```

`allowedAgents` — whitelist: only these agents can be spawned  
`disallowedAgents` — blacklist: these agents are blocked

If both are empty, all agents are allowed (subject to the target agent's own `chat` mode config).

---

## Quick Reference

| Field | Location | Description |
|-------|----------|-------------|
| `permissions.allow` | settings.json or agent.json modes | Tools allowed to execute at runtime (or `["*"]`). Supports glob patterns. |
| `permissions.deny` | settings.json or agent.json modes | Tools blocked from executing at runtime. Wins over allow. Supports glob patterns. |
| `permissions.ask` | **settings.json only** | Tools requiring human approval before executing |
| `tools` | agent.json modes | Tools shown to the LLM (whitelist — LLM cannot call tools not in this list) |
| `disallowedTools` | agent.json modes | Tools hidden from the LLM (blacklist) |
| `allowedAgents` | agent.json modes | Sub-agents this agent may spawn |
| `disallowedAgents` | agent.json modes | Sub-agents blocked from spawning |
