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

# Permissions

> What each one allows, and how approval works

Most of what an expansion does needs no permission: pages, sidebar buttons, editors, item types, settings tabs, themes, events. None of that reaches outside the app.

Four things do, and each is declared in `meta.json` and approved by the user.

| Permission      | What the user is told                       | Unlocks                                           |
| --------------- | ------------------------------------------- | ------------------------------------------------- |
| `project:read`  | Read the items and files in your projects   | `sdk.project.items`, `sdk.project.readFile`       |
| `project:write` | Change the items and files in your projects | `sdk.project.writeFile`, `sdk.project.updateItem` |
| `network`       | Connect to the internet                     | `fetch`                                           |
| `clipboard`     | Read and write your clipboard               | `sdk.clipboard.*`                                 |

```json theme={null}
{ "permissions": ["project:read", "project:write"] }
```

<Note>
  A permission not in this list is a manifest error, so a typo is caught at load rather than at the call.
</Note>

## How approval works

<Steps>
  <Step title="Needs approval">
    An expansion that declares permissions loads as **Needs approval** and does not run.
  </Step>

  <Step title="Review permissions">
    The user presses *Review permissions* on the Expansions page and sees the plain-language lines above.
  </Step>

  <Step title="Approved">
    On approval the expansion starts. The grant is remembered.
  </Step>
</Steps>

An update that adds a permission asks again, listing only the new ones. The user can take approval back at any time with *Revoke permissions*, which stops the expansion and returns it to Needs approval.

## Being refused

A call without its permission throws `SdkPermissionError` and logs:

```
project.writeFile blocked: needs "project:write" (not in meta.json)
```

`fetch` without `network` rejects with a message naming your expansion. Nothing is half-done; the call simply does not happen.

```ts theme={null}
import sdk, { SdkPermissionError } from 'sdk'

try {
  await sdk.project.writeFile('notes.txt', text)
} catch (err) {
  if (err instanceof SdkPermissionError) sdk.notify('This expansion is missing a permission', { type: 'error' })
}
```

## Asking for less

Declare only what you use. Every permission is a line the user reads before saying yes, and an expansion that asks for nothing starts immediately. If a feature needs a permission, consider putting the feature behind a button rather than making the whole expansion wait on approval.

## What the environment gives you anyway

Inside your code you have standard JavaScript (`Promise`, `Map`, `JSON`, `Intl`, `URL`, `TextEncoder`, `crypto`, …), `console`, and timers that are cleaned up when your expansion unloads.

You do not have `window`, `document`, `require`, `process`, `localStorage` or the app's internals. `fetch` exists only with `network`.

<Warning>
  This is a controlled environment rather than a hard sandbox: it keeps well-behaved expansions on the SDK and makes going around it a deliberate act, and NMCrate review is the gate for the rest. Do not treat it as a security boundary when deciding what to ship.
</Warning>
