---
title: "Capabilities and permissions"
description: "The shipped capability model — expandCapabilities, the capability hierarchy, PermissionError, and the honest framing that capabilities are intent, not a sandbox."
canonical: https://amnesia-docs.pages.dev/api/reference/capabilities/
source: "src/content/docs/api/reference/capabilities.md"
---

# Capabilities and permissions

:::tip[Shipped — public-stable]
The capability model — `expandCapabilities`, the capability hierarchy, and `PermissionError` — is part
of the first-release shipped surface.
:::

:::caution[Capabilities are intent, not a security boundary]
Capabilities are an **intent declaration**, a **best-effort, in-memory audit**, and an
**accident-prevention** layer. They are **not** a sandbox, isolation, or security guarantee. Obsidian
provides no sandbox, and the documented global handle is admin-scoped and ungated, so a consumer can
bypass capability scoping entirely. Never market or rely on this model as "secure", "sandboxed", or
"isolated".
:::

## The capability set

Capabilities are declared when you [`connect()`](/api/reference/access/#connect--a-scoped-audited-handle).
The live set has six members:

| Capability | Grants |
| --- | --- |
| `read-state` | Read reactive state. |
| `read-document` | Read the open document (implies `read-state`). |
| `write-annotations` | Create/modify annotations (implies `read-document`, `read-state`). |
| `write-bookmarks` | Create/modify bookmarks (implies `read-state`). |
| `write-library` | Modify the library (implies `read-state`). |
| `admin` | Everything above. |

Tokens outside this set (for example `collections:read`, `export:execute`, `modes:read`) are **not**
part of the live capability set and cannot be granted today.

:::note
`write-bookmarks` and `write-library` gate the `commands.bookmarks` and `commands.library` command
surfaces, which are currently
[experimental](/api/reference/experimental-surfaces/#commandslibrary--commandshighlights--commandsbookmarks).
The capability **tokens** are part of the shipped six-member set, but the command surfaces they gate are
not yet shipped — a shipped capability token is not a shipped command surface.
:::

## Hierarchy

Capabilities expand hierarchically — granting a higher capability implies the lower ones:

```text
admin
 ├─ write-library      ─┐
 ├─ write-bookmarks    ─┤─→ read-state
 ├─ write-annotations  ─→ read-document ─→ read-state
 ├─ read-document      ─→ read-state
 └─ read-state
```

## `expandCapabilities`

```ts
expandCapabilities(capabilities: Capability[]): Capability[]
```

Expands a list of requested capabilities into the full implied set. Granting `['write-annotations']`
expands to `['write-annotations', 'read-document', 'read-state']`. This is the function that determines
what a scoped handle is allowed to do.

## `PermissionError`

When a capability-gated method is called without the required capability, the facade throws a
`PermissionError`. Handle it where you call gated methods:

```js
try {
  await scoped.commands.mupdf.createAnnotation(/* ... */);
} catch (err) {
  if (err instanceof PermissionError) {
    // The scoped handle was not granted write-annotations.
  }
}
```

`PermissionError` is the deterministic signal that a gated call was not permitted **for an honest caller
routed through a facade**. It does not constrain a consumer who reaches underlying services directly —
that is the accident-prevention, not security-boundary, framing above.

## What capabilities each shipped surface needs

| Surface | Typical capability |
| --- | --- |
| `commands.notes` reads | `read-state` |
| `commands.notes` writes | `write-annotations` |
| `commands.links` reads | `read-state` |
| `commands.links` writes | `write-annotations` |
| `commands.mupdf` reads (text/region/page) | `read-document` |
| `commands.mupdf` annotate | `write-annotations` |

## Related

- Guide: [Connect to the Amnesia API](/api/guides/connect-to-the-api/)
- [Access the API](/api/reference/access/) — where `connect()` and the global are documented.

<sub>Reference verified as of 2026-06-28.</sub>
