Permissions
A card without permissions can draw in its box, keep storage, read its settings, set its header, exchange nothing with anybody. Everything else is a permission you declare in the manifest and the user grants.
How grants work:
- Per package, checked on every request. The user grants a permission to your card package (every copy of it on every canvas). The app checks the grant on every single call, in the app — not in the SDK, not in your card.
- Required ones are all-or-nothing at install. The install dialog lists them high-risk first. Declining means not installing.
- Optional ones are asked at runtime (
"optional": true). Callcard.permissions.request()while the card is on screen; the app asks in its own window-wide dialog, and the user can say no. - Some imply others.
fs.writeincludesfs.read;agents.output,agents.promptandterminals.writeincludeagents.read. - Users can revoke any permission in Settings → Custom cards. The card’s frames reload with
the smaller grant and get a
permissions.changedevent; code defensively. - An update that adds permissions or network hosts needs the user’s consent before it applies. Permissions you drop are revoked.
- Arrows are consent for data. Permissions that reach another card, agent or terminal work only with cards connected to yours by an arrow — either direction, unless said otherwise.
A missing permission makes the call fail with PERMISSION_DENIED (error.permission names it).
At a glance
| Permission | Risk | Unlocks |
|---|---|---|
agents.read | Low | agents.list/get, status/turn/changed events, the agent status port |
agents.output | High | Reading screens and replies of connected agents and terminals |
agents.prompt | High | Sending prompts to connected AI agents |
terminals.write | High | Running commands in connected terminals |
cards.connected | Medium | Reading and changing connected notes, todo lists, task boards, stickies |
network + hosts | Medium | net.fetch to the listed hosts |
network.local | High | net.fetch to servers on this computer |
fs.read | High | Reading files in the workspace folder |
fs.write | High | Writing files in the workspace folder (implies fs.read) |
clipboard.write | Low | Copying text to the clipboard |
canvas.spawn | Low | Adding up to 4 cards next to itself |
background | Low | Staying alive while nobody looks |
usage.read | Low | Token usage and cost of the workspace |
Each permission
agents.read — low
Users see: See the agents on this canvas. Their names, which tool they run, and whether they are working, waiting for you or finished.
Unlocks: card.agents.list() / get(), the events agents.status,
agents.turn, agents.changed, and the built-in agent status output port.
Scope: AI agents and terminals of the card’s own workspace. Other cards are not agents and are not listed.
agents.output — high
Users see: Read what agents and terminals connected to it print. Everything on the screens of agent and terminal cards you connect to it with an arrow, including anything secret shown there.
Unlocks: card.agents.readScreen(), card.agents.lastReply(), live output with
card.agents.onOutput(), the agent reply and terminal exit output ports.
Scope: arrow-connected agents and terminals only. Implies agents.read.
agents.prompt — high
Users see: Give instructions to agents connected to it. Type and send prompts to AI agents you connect to it with an arrow. An agent can edit files and run commands, so this card can make it do anything the agent can do.
Unlocks: card.agents.prompt() and the agent prompt input port.
Scope: arrow-connected AI agents (not terminals). Prompts go through the prompt queue and
the workspace budget, at most 6 a minute per card (counted together for the method
and the agent’s prompt port); each one shows on the arrow and
in the arrow log. To an agent in dangerous mode every prompt waits for the
user’s OK on the card. Implies agents.read.
terminals.write — high
Users see: Run commands in terminals connected to it. Type and run commands in terminal cards you connect to it with an arrow — anything you could run yourself.
Unlocks: card.terminals.run() and write(), the terminal
command input port.
Scope: arrow-connected terminal cards (bash, PowerShell, cmd). At most 30 commands a minute per
card, counted together for run and the terminal’s command port; write at most 60 a minute.
Each command shows on the arrow. Implies agents.read.
cards.connected — medium
Users see: Read and change cards connected to it. Notes, checklists, task boards and stickies you connect to it with an arrow.
Unlocks: the built-in card ports of note, todo list, task board and sticky — append to a note, add tasks, read a checklist.
Scope: arrow-connected cards of those four kinds.
network — medium, needs hosts
Users see: Connect to api.github.com, *.example.com. Send and receive data from these
internet addresses only. Anything the card can see may be sent there.
Unlocks: card.net.fetch() to those hosts over https.
Scope: exactly the declared host patterns, public addresses only — a host that resolves to a private, loopback or cloud-metadata address is refused, on every redirect too. A card cannot ask for “any host” in version 1.
network.local — high
Users see: Connect to servers on this computer. Reach programs listening on localhost, such as dev servers and local databases.
Unlocks: card.net.fetch() to localhost, 127.0.0.1 and ::1, over http or https.
Scope: any port except NeuroSquad’s own servers: its MCP server, the remote access server, the developer link server, the debugging port of every browser card’s Chrome, and the development server of the app itself. Secret placeholders are never filled in for requests to this computer.
fs.read — high
Users see: Read files in the workspace folder. Any file in this workspace’s project folder,
including secrets stored in files like .env.
Unlocks: card.fs.stat/list/read*/watch() and the workspace’s absolute
path in card.workspace.
Scope: the workspace folder. Paths are relative to it; .. and absolute paths are refused, and
a link that points outside the folder is refused too. If the workspace folder is the user’s home
folder, the root of a drive, or contains NeuroSquad’s own data folder, every file call is refused.
fs.write — high
Users see: Change files in the workspace folder. Create, overwrite and move files to the trash
in this workspace’s project folder. Agents in this workspace read and run these files, so this card
can make them run commands. Protected: .git, agent settings (.claude, .mcp.json, CLAUDE.md,
AGENTS.md and similar) and NeuroSquad’s own data.
Unlocks: card.fs.writeText/writeBytes/mkdir/trash().
Scope: as fs.read, minus protected paths: any .git, agent
and editor settings, CI workflows. There is no hard delete — trash moves to the OS
trash. Implies fs.read.
clipboard.write — low
Users see: Copy to your clipboard. Replace what is on your clipboard with text from the card.
Unlocks: card.copyText(), once a second. Reading the
clipboard is not possible.
canvas.spawn — low
Users see: Add cards next to itself. Place up to 4 new cards beside it on the canvas.
Unlocks: card.spawn() — another copy of your card, or a note,
todo list, task board or sticky, connected to it with an arrow.
Scope: 4 cards per card, 4 a minute, within the workspace’s card cap.
background — low
Users see: Keep running when you are not looking. Stay active while its workspace is hidden. Uses more memory and battery.
Unlocks: the card is not suspended when hidden.
Scope: at most 8 such cards run in the background app-wide; beyond that, the least recently seen are suspended anyway.
usage.read — low
Users see: See token usage and costs. How many tokens the agents on this workspace used and what they cost.
Unlocks: card.usage().
Scope: the card’s own workspace.
What the install dialog lists besides permissions
Derived from the manifest, not permissions: the names of the tools the card offers to connected agents, and how many input and output ports it has. Optional permissions appear under It may ask later for.
Ask for the least you need. Every high-risk line makes careful users think twice, and a missing
reason makes them guess. If a feature needs a strong permission only sometimes, make it
optional and ask when the user reaches for that feature.