Reference
Capability catalog
Every power an agent can be granted in its personnel file, and the concrete enforcement each one compiles into.
A capability is a named power. Agents start with none: anything not listed in the capabilities of their personnel file is denied. Each entry is a bare name (mail.read) or a single-key mapping with parameters (mail.send: { to: ["*"] }). Unknown names, bad parameters and duplicates are validation errors.
The office compiles the list (compile(personnel, office, context) in src/core/capabilities.ts, a pure function) into:
| Output | Where it is enforced | When |
|---|---|---|
| Forge grants | The agent's forge user: read or write collaborator on each listed repo. | Applied by the reconciler when the personnel file changes; stale grants are removed. |
| Token scopes | The forge token minted for one run (run-<runId>), revoked when it ends. | Every run. |
| Sandbox egress | The hosts the run's sandbox may reach. | Every run. |
| Claude Code settings | Permission mode and allow/deny rules, passed to the run with --settings. | Every run. |
| Office MCP tools | Which office tools the agent may call. A call without the capability is denied and ledgered as run.denied. | Every call. |
Capabilities from an approved, still-open approval window are added to the personnel list for runs and MCP calls in that window. See Capabilities for the idea, and Permissions & capabilities for how to change them.
Summary#
| Capability | Parameters | Grants |
|---|---|---|
vault.write | none | Edit and push its own vault. |
mail.send | to (optional) | Start mail threads to colleagues. |
mail.read | none | Read, reply to and close its own mail. |
office.read | none | See colleagues and what they are working on. |
forge.read | repos | Read and clone the listed repos. |
forge.write | repos, via | Push branches and open pull requests on the listed repos. |
hr.propose | none | Propose hires and capability changes as policy PRs. |
artifact.publish | none | Publish deliverables to its desk, committed to its vault. |
approval.request | none | Ask a human to approve something, including a temporary capability. |
net.egress | list of hosts | Reach those hosts from the sandbox. |
shell | profile (optional) | Run a profile's shell commands. |
budget.override | usd (optional) | Approval-only: keep running past the daily budget. |
Every run's forge token has the scopes read:repository and write:issue (mail threads are issues). write:repository is added when the agent has vault.write or forge.write.
vault.write#
capabilities:
- vault.write
Lets the agent change its own vault. Git is allowed as a whole rather than subcommand by subcommand: listing subcommands left agents stuck on harmless calls (git --version, git rev-parse), and the limits that matter are the run token's forge permissions and branch protection. Every hired agent is a write collaborator on its vault (that comes from provisioning, not from this capability); without vault.write its Claude Code settings deny editing the vault.
Compiles into:
- Claude Code settings: allow
Editunder the vault, and git as a whole (Bash(git *)). Without it: denyEditon the vault, and denyBashentirely unless the agent also hasshell,forge.readorforge.write. - Token scopes:
write:repository.
CLAUDE.md and .office/** stay read-only either way: denied in the settings, in the sandbox's denyWrite, and as protected files on the vault's main branch, so the forge rejects a push that changes them.
mail.send#
capabilities:
- mail.send # anyone (same as to: ["*"])
- mail.send: { to: [cos, analyst] } # only these agents
| Parameter | Type | Meaning |
|---|---|---|
to | list | ["*"] for anyone, or agent slugs. Each slug must be an agent in the policy. Omitted = anyone. |
Compiles into:
- Office MCP tools:
mail_send. Mail to a recipient outsidetois refused and ledgered asrun.denied.
mail.read#
capabilities:
- mail.read
Lets the agent list and read the mail in its own inbox, reply, and close threads.
Compiles into:
- Office MCP tools:
mail_inbox,mail_read,mail_reply,mail_close.
office.read#
capabilities:
- office.read
An overview of colleagues: who exists, their roles and status, who is running now and how much open mail each has.
Compiles into:
- Office MCP tools:
colleagues_list,office_overview.
forge.read#
capabilities:
- forge.read: { repos: ["northwind-handbook"] }
| Parameter | Type | Meaning |
|---|---|---|
repos | list of repo names (at least one) | Repos in the office org the agent may read. |
Compiles into:
- Forge grants: read collaborator on each listed repo.
- Claude Code settings: allow git as a whole (
Bash(git *)), so the agent can clone the repos intowork/<repo>inside its vault and inspect them. See Agents that open pull requests. - Office MCP tools:
forge_list_prs.
forge.write#
capabilities:
- forge.write: { repos: ["northwind-office"], via: pull_request }
| Parameter | Type | Meaning |
|---|---|---|
repos | list of repo names (at least one) | Repos the agent may push branches to and open pull requests on. |
via | pull_request (the only value, and the default) | Changes arrive as pull requests. On a repo whose main is protected (the office, handbook and policy repos are, from officectl init), the agent can open PRs but can't merge them. |
Compiles into:
- Forge grants: write collaborator on each listed repo (write wins if a repo is also in
forge.read). - Token scopes:
write:repository. - Claude Code settings: allow git as a whole (
Bash(git *)), and denyBash(git push * main),Bash(git push * HEAD:main)andBash(git push origin main). Those deny rules are a speed bump, not the boundary: a refspec can be spelled many ways. The boundary is branch protection onmain, so protectmainon every repo you grant this on (officectl initdoes it for the policy, office and handbook repos). - Office MCP tools:
forge_open_prandforge_list_prs.
The workflow (clone into work/<repo>, branch <slug>/<topic>, push, forge_open_pr) is in Agents that open pull requests.
hr.propose#
capabilities:
- hr.propose
Lets the agent read personnel files and draft hires and changes. Each proposal is a pull request on the policy repo. It grants nothing by itself: a human approver has to merge it.
Compiles into:
- Office MCP tools:
hr_agent_personnel,hr_propose_hire,hr_propose_change.
artifact.publish#
capabilities:
- artifact.publish
Lets the agent publish deliverables: reports, HTML pages, images, PDFs, CSV or JSON files. The office (not the agent) commits each one to the agent's vault under artifacts/<date>-<title>/ and lists it on the desk and the Artifacts page, for every human who may see the agent. Every role template grants it. See Artifacts.
Compiles into:
- Office MCP tools:
artifact_publish,artifact_list. - Nothing else: no sandbox paths, forge scopes or hosts. The agent never writes the files itself.
approval.request#
capabilities:
- approval.request
Lets the agent ask a human for a decision. The request appears in Needs you in the web UI and in the human MCP tool approvals_list. A lead (approval.decide) approves or denies it; nobody can decide their own request.
A request can be a one-off action, a capability.window (a capability for a limited time, default 4 hours, at most 168), or a budget.override. Approving a window opens it (ledger capability.window.opened): until it ends, the capability is added to the agent's runs and MCP calls. Windows can carry any capability, including the approval-only budget.override.
Compiles into:
- Office MCP tools:
approval_request.
net.egress#
capabilities:
- net.egress: ["api.anthropic.com", "*.example.org"]
A list of host names (wildcards like *.example.org allowed) the run may reach.
Compiles into:
- Sandbox: the hosts are added to the sandbox's allowed domains (
strictAllowlist), together with the forge host and the office host. When the forge or office resolves to an address of the office machine itself, its<ip>:<port>is allowlisted too, because the Linux sandbox otherwise refuses names that resolve to the local host. - Claude Code settings: allow
WebFetch(domain:<host>)for each host, andBash(curl *),Bash(wget *). Withoutnet.egress: denyWebFetch,Bash(curl *)andBash(wget *).WebSearchis always denied (it can't be limited to domains).
shell#
capabilities:
- shell # same as { profile: basic }
- shell: { profile: node }
| Parameter | Type | Meaning |
|---|---|---|
profile | basic (default) | node | python | A named set of commands. |
Compiles into Claude Code settings allow rules (Bash(<command> *)) for:
| Profile | Commands |
|---|---|
basic | mkdir, touch, cp, mv, rm, sed, awk, sort, uniq, cut, tr, jq, date, xargs, tee, ls, cat, head, tail, grep, wc, diff, echo, printf, pwd, basename, dirname |
node | basic plus node, npm, npx |
python | basic plus python3, python, pip, pip3 |
Commands run inside the sandbox, and only allow-listed ones run (autoAllowBashIfSandboxed is off), plus Claude Code's built-in read-only commands. An agent without shell but with vault.write, forge.read or forge.write runs only git and read-only commands. An agent with none of these gets Bash denied entirely.
budget.override#
# not allowed in personnel.yaml: granted only through an approved window
{ "budget.override": { "usd": 5 } }
| Parameter | Type | Meaning |
|---|---|---|
usd | positive number, optional | Extra allowance beyond budget.usd_per_day. Omitted = no cap while the window is open. |
Approval-only: putting it in personnel.yaml is a validation error. An agent asks for it with approval_request (kind: budget.override); while the approved window is open, a hard_stop budget admits runs past the daily limit.
The floor every run gets#
Whatever the capabilities say, every run's compiled Claude Code settings include:
permissions.defaultModefromrun.mode(default,acceptEditsordontAsk).bypassPermissionsis rejected,permissions.disableBypassPermissionsMode: "disable"andpermissions.disableAutoMode: "disable"are set, andblockReadsOutsideWorkingDirectories: true.- Deny
ReadandEditon the office state directory and every other agent's home, andEditon/etc. Reads of/etcby Claude's file tools are refused byblockReadsOutsideWorkingDirectories; sandboxed commands get/etcindenyReadwith only the CA trust paths re-opened (below). - Deny
Bash(sudo *),Bash(su *),Bash(doas *), andEditon the vault'sCLAUDE.mdand.office/. - Deny
WebSearch; denyWebFetch,curl,wgetunlessnet.egressis granted. - Allow exactly the office MCP tools the capabilities unlock (
mcp__office__<tool>); deny the rest. - With
host.sandbox: true: sandbox enabled withfailIfUnavailable: trueandallowUnsandboxedCommands: false,autoAllowBashIfSandboxed: false(sandboxed commands still need an allow rule), network limited (strictAllowlist) tonet.egress+ the forge host + the office host (plus their<ip>:<port>when they resolve to this machine),denyReadon the state directory, other agents' homes and/etc(on macOS also/private/etc),allowReadre-opening only the CA trust paths git, curl and node need for TLS (Linux:/etc/ssl/certs,/etc/ca-certificates,/usr/share/ca-certificates,/etc/pki/tls/certs,/etc/pki/ca-trust; macOS:/etc/ssl,/private/etc/ssl), anddenyWriteon the vault'sCLAUDE.mdand.office. enableAllProjectMcpServers: false.envis empty, except on Linux, whereSSL_CERT_FILEandGIT_SSL_CAINFOpoint at the host's CA bundle (/etc/ssl/certs/ca-certificates.crtor/etc/pki/tls/certs/ca-bundle.crt).
The harness also passes --strict-mcp-config with the office MCP as the only server, and --setting-sources "" so no user, project or local settings file (the vault is agent-writable) can add rules. The run's model credential, its office MCP token and its forge token reach the process as environment variables, piped through stdin, never on the command line.
A system-wide floor is also written to Claude Code's managed settings file by officectl install. It binds every Claude Code session on the host and can't be overridden by --settings: deny Read/Edit on the state directory, bypass and auto mode disabled, sandbox enabled and failing closed with allowUnsandboxedCommands: false, strictAllowlist networking, and denyRead on the state directory.
| OS | Path |
|---|---|
| Linux | /etc/claude-code/managed-settings.json |
| macOS | /Library/Application Support/ClaudeCode/managed-settings.json |