Reference
office.yaml
The one configuration file that makes an office. It lives at the root of your office repo and holds everything the platform needs to know about your company.
Every company name, domain, org, timezone and limit the platform uses comes from office.yaml or from the policy repo. Nothing company-specific is compiled into the platform itself.
office.yaml never holds secrets. Fields that need one (the forge admin token, the model API key) hold the name of an entry in the office's encrypted secret store instead. Set the value with officectl secrets set.
officectl init writes this file from your answers. The office server reads it at boot (agent-office-server --config office.yaml, default ./office.yaml), and every officectl command finds it in the directory given by --dir, the nearest office.yaml above the current directory, or $AGENT_OFFICE_DIR. The file is validated strictly: an unknown key is an error, so typos surface instead of being ignored. Fields marked default below may be omitted.
Full example#
office:
id: northwind # [a-z][a-z0-9-]+ ; used for repo names
name: Northwind Office
timezone: Europe/Berlin
url: https://office.example.com # public base URL of the web app (also MCP)
docs_url: https://docs.example.com # optional; "Docs" link in the sidebar and on Settings
platform: 0.2.0 # informational; real pin is package.json
owner: { email: owner@example.com, name: Owner Name }
forge:
kind: gitea # gitea | github (github = stub in POC)
url: https://git.example.com
org: northwind
repo_prefix: "vault-"
admin_token_secret: forge.admin_token # name in the secret store
paths:
state: /var/lib/agent-office # DB, ledger, transcripts, secrets
agents_home: /home # agent OS users' homes: <agents_home>/ag-<slug>
host:
os_users: true # one OS user per agent
sandbox: true # Claude Code sandbox on (fail closed if unavailable)
models:
default: claude-sonnet-5-5 # model id passed to the harness
key_secret: models.anthropic_key # secret store name
runs:
max_concurrent: 3
default_max_turns: 40
default_timeout_minutes: 30
ledger:
anchor_every_minutes: 60
auth:
local_passwords: true
oidc: nullThe values above are examples: northwind and example.com are placeholders.
office#
Identity of the deployment.
| Field | Type | Example | Meaning |
|---|---|---|---|
office.id | string, [a-z][a-z0-9-]+ | northwind | Short id. Used to name repos: the policy repo is <id>-policy, the handbook repo is <id>-handbook. |
office.name | string | Northwind Office | Display name in the web UI. |
office.timezone | IANA timezone, default UTC | Europe/Berlin | The office's timezone. Scheduled runs (run.schedule cron) and the daily budget day are counted in it. |
office.url | http(s):// URL | https://office.example.com | Public base URL of the web app. The MCP endpoints (/mcp/agent, /mcp/human), the forge webhook (/api/webhooks/forge) and invite links (/invite/<token>) live under it. A trailing slash is dropped. |
office.docs_url | http(s):// URL, optional | https://docs.example.com | Where your people find documentation for this office (the Agent Office docs, or your own handbook site). When set, the web app shows a Docs link (opens in a new tab) in the sidebar footer and on Settings; when unset, no link is shown. A trailing slash is dropped. |
platform#
| Field | Type | Example | Meaning |
|---|---|---|---|
platform | version string, optional | 0.2.0 | Informational; written by officectl init. The real version pin is the @agent-office/platform dependency in the office repo's package.json. See Upgrades. |
owner#
| Field | Type | Example | Meaning |
|---|---|---|---|
owner.email | owner@example.com | The office owner. This address is always an owner, even if access.yaml is broken or missing. It is the break-glass account. | |
owner.name | string | Owner Name | Display name. |
forge#
The git forge that holds the policy repo, the handbook and every agent's vault.
| Field | Type | Example | Meaning |
|---|---|---|---|
forge.kind | gitea | github, default gitea | gitea | Which forge adapter to use. github is a Stub in this release: the interface exists, but the office runs on Gitea. |
forge.url | URL | https://git.example.com | Base URL of the forge. |
forge.org | organisation name | northwind | The organization that owns all office repos (officectl init defaults it to the office id). |
forge.repo_prefix | lowercase letters, digits, dashes; default vault- | "vault-" | Prefix for vault repos. An agent with slug cos gets the vault vault-cos. |
forge.admin_token_secret | secret name, default forge.admin_token | forge.admin_token | Name of the secret-store entry holding the forge admin token (a Gitea site admin). The office uses it to create the org, teams, repos, agent users, branch protections, webhooks and mail issues. |
paths#
| Field | Type | Example | Meaning |
|---|---|---|---|
paths.state | absolute path, default /var/lib/agent-office | /var/lib/agent-office | The office's private state: database (with the ledger), transcripts, per-run settings, secrets. Mode 0700, owned by the office OS user; officectl doctor checks both. Agents cannot read it. (officectl init suggests /usr/local/var/agent-office on macOS.) |
paths.agents_home | absolute path, default /home | /home | Parent directory for agent OS users' homes. Agent cos lives in <agents_home>/ag-cos. |
host#
How runs are isolated on the machine.
| Field | Type | Example | Meaning |
|---|---|---|---|
host.os_users | boolean, default true | true | Create one OS user (ag-<slug>) per agent and run each agent as its own user. Needs the sudo rule installed by officectl install. false is a development mode: runs execute as the office's own user and the UI flags them as not isolated. |
host.sandbox | boolean, default true | true | Turn on Claude Code's built-in sandbox for every run (bubblewrap on Linux, Seatbelt on macOS) with failIfUnavailable: if the sandbox can't start, the run fails instead of running unsandboxed. false turns it off; officectl doctor warns and the desk shows sandbox off. |
models#
| Field | Type | Example | Meaning |
|---|---|---|---|
models.default | model id, default claude-sonnet-5-5 | claude-sonnet-5-5 | Model passed to the harness (--model) when a personnel file doesn't set model. |
models.key_secret | secret name, default models.anthropic_key | models.anthropic_key | Secret-store entry holding the office's Anthropic API key. A per-agent key is stored as <key_secret>.<slug>, e.g. models.anthropic_key.cos. |
A Claude subscription token is not configured here: it is stored as the fixed secret models.claude_oauth_token. For each run the office uses the first credential that exists: the agent's API key (models.anthropic_key.<slug>), then the office API key (models.anthropic_key), then the subscription token. If none exists, the run is refused with model key missing.
Which credential? A Claude subscription token (claude setup-token) is fine for one person using an office for their own work. When a company runs an office for its business, use Anthropic API keys (a Console workspace with a spend limit; per-agent keys if you want separate limits). Terms: Consumer Terms, Commercial Terms. See Model credentials.
runs#
| Field | Type | Example | Meaning |
|---|---|---|---|
runs.max_concurrent | integer ≥ 1, default 3 | 3 | Most runs executing at once, across all agents. |
runs.default_max_turns | integer ≥ 1, default 40 | 40 | Turn limit for runs whose personnel file doesn't set run.max_turns. |
runs.default_timeout_minutes | integer ≥ 1, default 30 | 30 | Wall-clock limit for runs whose personnel file doesn't set run.timeout_minutes. On timeout the run gets SIGTERM, then SIGKILL after 10 seconds, and is marked killed. |
ledger#
| Field | Type | Example | Meaning |
|---|---|---|---|
ledger.anchor_every_minutes | integer ≥ 1, default 60 | 60 | How often the office commits the ledger's chain head to ledger/anchors.jsonl in the policy repo (skipped when nothing new was ledgered). See Ledger actions. |
auth#
How humans sign in to the web UI.
| Field | Type | Example | Meaning |
|---|---|---|---|
auth.local_passwords | boolean, default true | true | Email + password sign-in. New humans set their password from a one-time invite link (/invite/<token>). false disables password sign-in, and with OIDC not built that leaves no way to sign in. |
auth.oidc | object or null, default null | null | Roadmap Single sign-on through an OIDC provider (issuer, client_id, client_secret_secret). The key is reserved; OIDC sign-in is not built in this release. Leave it null. |