Reference
Permission catalog (humans)
The roles humans hold, the scopes they apply to, and the minimum role for every action in the web UI and the human MCP.
Humans get roles on scopes, set in access.yaml. Agents get capabilities instead; see the Capability catalog.
Roles#
viewer < operator < lead < admin < owner
Each role includes everything below it. The office owner from office.yaml is always owner on office, even if access.yaml is missing or broken.
Scopes#
| Scope | Covers |
|---|---|
office | All agents and everything else. |
dept:<name> | Agents whose department is <name>. |
agent:<slug> | One agent. |
How a check resolves#
For an action on a target, the office takes the highest role among the human's grants whose scope covers the target, and compares it to the permission's minimum role:
can(human, permission, target?) -> boolean
officecovers every target.dept:xcovers agents withdepartment: x.agent:scovers agents.
Example: Dana holds lead on dept:engineering and nothing else. She can kill a run of an engineering agent (run.kill needs lead), but can only see agents elsewhere if she also holds a role that covers them.
Checks come in three shapes, declared per route or tool: against one agent (its slug and department, as above); anywhere, for list pages and list tools, which need the permission on some scope and then show only the items each grant covers; and office, which counts only grants on office (the Audit and Settings pages, the ledger tools, access proposals).
The catalog#
The complete list (PERMISSIONS in src/core/rbac.ts). The Allows text is what the People and Account pages show.
| Permission | Minimum role | Allows | Used by |
|---|---|---|---|
office.read | viewer | See the office floor: agents, their status and what they are doing | Floor / and its 2D/3D data GET /api/floor, live updates /events; MCP office_overview, agents_list |
agent.read | viewer | Open an agent's desk: charter, capabilities, runs, mail | Desk /agents/<slug>; the chat page /agents/<slug>/chat[/<id>], GET /api/chats, GET /api/chats/<id> (your own threads only); MCP agent_get |
mail.read | viewer | Read mail threads with agents | /mail, /mail/<slug>/<number> |
artifact.read | viewer | See and download the reports and files agents publish | Artifacts /artifacts, /artifacts/<id>, /artifacts/<id>/raw/<file>, GET /api/artifacts, the Artifacts card on the Desk |
run.read | viewer | See runs and their timelines | /runs, /runs/<id>; MCP runs_list, run_get |
approval.read | viewer | See pending approvals | Needs you /needs-you; MCP approvals_list |
people.read | viewer | See who has access to the office | /people |
account.manage | viewer | Manage your own account and MCP tokens | /account, token create and revoke, theme (POST /account/theme) |
agent.chat | operator | Chat with agents | Sending, new threads, rename, pulling a queued message back, and Stop on the run answering your own thread (/api/chats*, the chat page's form); MCP agent_chat |
mail.send | operator | Send mail to agents | /mail/new, replies on /mail; MCP mail_send |
run.start | operator | Start runs manually | Give … a one-off task on the Desk (a manual run) |
run.transcript.read | lead | Read full run transcripts | /runs/<id>/transcript, full event contents in run_get; live chat text (delta events on /events) of that agent's runs, besides the thread's owner |
run.kill | lead | Stop running runs | Stop run on a run page (stopping the run of your own chat thread needs only agent.chat) |
approval.decide | lead | Approve or deny agent requests | Decide on Needs you, and End now on an open window (POST /windows/<id>/revoke); MCP approval_decide |
hire.propose | lead | Propose hires and capability changes (opens a policy PR) | /hire; MCP hire_propose |
agent.suspend | admin | Suspend or resume agents | Suspend / Resume on the Desk (opens a policy PR) |
access.propose | admin | Propose access changes for people (opens a policy PR) | Changes and invite links on /people |
ledger.read | admin | Read the audit ledger | Audit /audit; MCP ledger_query, ledger_verify |
settings.read | admin | See office settings and health checks | /settings |
verify.run | admin | Run the security verification suite | Declared; no web route or MCP tool uses it yet. Run the suite with officectl verify on the host. |
One permission per door#
Every HTTP route declares exactly one permission, or is explicitly public with a reason: sign-in pages (/login, /logout, /invite/<token>), /healthz, static assets, the forge webhook (authenticated by HMAC) and the two MCP endpoints (authenticated by bearer token inside the handler). Registering a route without a declaration throws, and the test suite checks it. Every human MCP tool also declares exactly one permission.
Chat threads are personal#
On top of the permission, a chat thread is checked for ownership. Reading a thread needs agent.read on the agent and the thread being yours; sending, renaming, pulling a queued message back and stopping the thread's run need agent.chat and the thread being yours. Someone else's thread is a 404, not a 403, so thread ids don't reveal who chats with whom. Stop is deliberately narrow: it stops only the run answering your thread; any other run needs run.kill. See Chat with an agent.