> ## Documentation Index
> Fetch the complete documentation index at: https://docs.seriora.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Permissions

> The gate under every tool call: modes, remembered approvals, and what counts as a write.

Every tool call passes a gate before it runs, on every OS. The gate is the base safety layer, and
nothing else in seri reaches around it.

## Modes

| Mode           | Reads, greps, globs | Writes and commands         |
| -------------- | ------------------- | --------------------------- |
| `approve-each` | run                 | ask first. **The default.** |
| `auto`         | run                 | run                         |
| `read-only`    | run                 | blocked outright            |

`/mode` takes no argument and cycles `read-only` to `approve-each` to `auto` and back around.
Shift-Tab does the same without typing. There is no `/mode auto` form, and typing one is a
command error rather than a mode change. The current mode is shown in the session.

**`approve-each` is the default, not `read-only`,** and that was a measured decision rather than
a preference. A fresh session defaulting to `read-only` and given a write task was blocked
repeatedly and produced nothing: five denials, then a turn that ended with no tool call and no
file created. What keeps `approve-each` from being an every-call prompt is the remembered
approval below.

A task that needs to write in `read-only` reports the block rather than failing the run, and the
run still exits successfully. Cycle out of it and ask again.

## What counts as a write

The classification is per tool, and it is deliberately asymmetric.

| Class | Tools                                      |
| ----- | ------------------------------------------ |
| read  | `read_file`, `grep`, `glob`                |
| write | `write_file`, `edit`, `bash`, `powershell` |

**A name the gate does not recognise is treated as a write.** That direction is the point. MCP
opens the tool set to third parties, so "absent from the write list" stops being evidence of
safety. An unfamiliar tool costs you an approval instead of being waved through in all three
modes.

Two names are exceptions, and neither writes. `dispatch_subagents` hands the child the parent's
own permission mode, so a subagent's `bash` re-enters this same gate at the same mode rather than
escaping it. `skill` reads one file you put in `.seri/skills/` yourself.

See [Tools](/reference/tools) for what each one does.

## Remembering an answer

An approval prompt can remember your answer. "Always" is narrower than it sounds, in two
directions at once. It is scoped to **that one tool**, and to **the project you granted it in**.

Grants live in `permissions.yaml` in your profile root, in two tiers.

| Tier        | Written by                       | Applies to                   |
| ----------- | -------------------------------- | ---------------------------- |
| `projects:` | answering "always" at the prompt | the one worktree you were in |
| `global:`   | you, by hand                     | every project                |

Answering "always" only ever writes the project tier. Nothing in seri promotes a grant to
`global:`, so an approval you gave in one repository does not follow you into the next one.

The project is keyed on the resolved worktree path, case-folded on Windows and macOS, where the
same directory routinely has two spellings.

Only three kinds of grant can persist at all, and the list is closed.

| Grant                | Persists                    |
| -------------------- | --------------------------- |
| `write_file`, `edit` | yes, by name                |
| an MCP tool          | yes, bound to a fingerprint |
| `bash`, `powershell` | never                       |

**`bash` and `powershell` cannot be approved permanently at all.** A grant keyed on a tool name
says nothing about what a shell command will do, so an entry reading `bash` would hand over the
shell for good. seri refuses such an entry on read as well as on write, so hand-editing one into
the file does not work either.

An MCP grant is stored as `mcp_<server>_<tool>@<digest>`. The digest fingerprints that tool's
name, description and input schema at the moment you approved it, because a server is a third
party and those three together are the whole contract you were shown. Change the tool on the
server's end and the digest stops matching, so seri asks again rather than trusting your old
answer about a tool you never actually saw.

## Reviewing what you granted

`/permissions` lists the `write_file` and `edit` grants in effect for the project you are in, and
labels each one by the tier it came from.

| Label        | Tier        | Removable from the panel |
| ------------ | ----------- | ------------------------ |
| persisted    | `projects:` | yes                      |
| pre-approved | `global:`   | no                       |

A tool with no grant in either tier is left out rather than shown as a third state, and grants
belonging to other projects are not listed.

<Warning>
  **An MCP grant does not appear in `/permissions`.** The prompt offers "always" for an MCP tool and
  the store accepts it, but the panel only ever renders the two built-in names, so a permanent MCP
  approval can be made and then neither seen nor revoked there. Edit `permissions.yaml` in your
  profile root to remove one.
</Warning>

## Skipping the gate

`--dangerously-skip-permissions` runs every tool with no prompt for the whole process.
Cycling to `auto` is the same idea inside the session, with a keystroke to get back out of it.

<Warning>
  Both are for attended, high-trust work. Neither is a default, and neither is remembered for you.
</Warning>

## What the gate cannot override

A `PreToolUse` hook runs **before** the gate. A hook that blocks a call blocks it in every mode,
`auto` and `--dangerously-skip-permissions` included, and you are never asked to approve a call a
hook is about to refuse. See [Hooks](/guides/hooks).
