docsbuild agentsconfiguring agents

Configuring agents

The blueprint says what the agent is. Configuration says how it runs on this install — model, access, extension credentials.

An agent's configuration is the install-specific layer — model picks, who can reach it, the credentials its extensions need. Holding it separate from the blueprint keeps the blueprint shippable: one design can run on many installs. A handful of files under config/ carry it: agent.json for runtime settings, acl.json for access control, provisions.json for resource mounts and operator-fillable slots, per-extension subfolders under config/ext/ for credentials and overrides, and mcp-servers.json for env values on external MCP servers.

agent.json — runtime settings

Every field is optional; the server fills in defaults. The two that matter most: the model the agent runs on, and the container network mode.

agent.json

json · operator-set runtime config
{
  "model": "claude-sonnet-4-5",
  "modelOverrides": [
    { "channel": "reflection", "model": "claude-haiku-4-5" }
  ],
  "containerNetwork": "sdk-only",
  "timezone": "America/New_York"
}
  • model — default Claude model the agent runs on. Channels inherit unless overridden.
  • modelOverrides — per-channel (optionally per-phase) model substitutions. Each entry is { channel, phase?, model }. Useful for pinning a cheaper model to a lightweight channel.
  • containerNetwork — egress firewall mode. sdk-only (default) lets the agent reach the model provider and nothing else; full opens the container to the internet; none disables egress entirely. Pair with containerAllowedEndpoints to permit specific domains.
  • timezone — IANA timezone for cron schedules and time-of-day prompts. Falls back to the server's TZ.

Other optional knobs: backup (snapshot interval and retention), fileWatch (preview limits), maxOutputBytes (cap on user-visible output), showSteps / showConsoleSteps (reasoning-stream visibility).

acl.json — who can reach the agent

Access is granted per caller, per channel, as a set of permission bits. An empty allowed map means only the owner can talk to the agent.

acl.json

json · access control list
{
  "owner": "operator",
  "allowed": {
    "research-agent": { "lookup": "q" },
    "u:sam@server": { "default": "io" }
  },
  "rejected": {},
  "reject_message": "Not authorized to reach this agent."
}
  • owner — identity with full authorization. The default "operator" is an inert label — your CLI and admin sessions hold full access without it. Set a u: identity to give that user unconditional push reach; listing and summary reads stay scoped to the channels they are placed in.
  • allowed — nested map: caller identity → channel name → bit string. The caller is a peer agent or a u: user. Channel "*" matches any user-defined channel (not the console-only __* channels).
  • rejected — same shape as allowed, but a hard deny. A caller listed here is blocked outright, distinct from an ungranted caller, whose first contact is held for your approval.
  • reject_message — what a blocked caller sees.

Permission bits, all from this agent's view of the edge:

BitDirectionMeaning
ooutboundthis agent sends free-text into the caller's conversation
qoutboundthis agent queries the peer; reply enters next-turn context
routboundthis agent queries the peer; reply is dropped before context
poutboundthis agent hands its user over to the peer
iinboundthis agent accepts free-text from the caller, and hosts a handed-over user's turn
ainboundthis agent answers queries (q or r) from the peer

Bits describe what this agent does on the edge, not what the peer does. A cross-agent edge needs two entries, one in each agent's acl.json, with complementary bits: sender writes q (or r), receiver writes a, which covers both q and r. For a handover, the sender writes p, and the handed-over user needs free-text access (i) on the receiver, since the pushed-in turn lands as an inbound message. Without both sides, nothing crosses.

🔒 SECURITY
Human callers live in acl.json too. When you approve someone, their grant is written into allowed as a u: entry, the same merged table the agent-to-agent edges use. See Access.

provisions.json — resource mounts and operator slots

📖 JARGON
Several fields below — and in the per-extension and mcp-servers.json sections that follow — are gated by the blueprint. In capabilities.json, a bare value means the field is locked (the author fixed it); { unlocked: true, value: ... } means the operator can override. Writes here to locked fields are silently ignored. See Authoring blueprints for the author-side decision.

Some agents need host-side things the author can't ship — a folder of notes to mount, an extra Python package, a tool to disable on this install. provisions.json is where you fill in those values. The blueprint declares what's needed (in blueprint/props/capabilities.json); provisions binds it to your host.

provisions.json

json · operator-filled deployment values
{
  "resources": {
    "notes_dir": "/Users/alex/notes",
    "scratch":   { "path": "/Users/alex/scratch", "access": "rw" }
  },
  "pip": { "extra_packages": ["pandas"] },
  "additional_disabled_tools": ["bash"]
}
  • resources — host paths bound to resource slots the blueprint declared. Each key matches a slot name; the value is either a bare path string (read-only) or an object with explicit access: "ro" | "rw". Paths are mounted into the agent container at runtime.
  • pip.extra_packages — extra Python packages to install. Only honored if the blueprint marked pip.extra_packages unlocked.
  • additional_disabled_tools — tools to disable on top of what the blueprint already disabled. Only honored if the blueprint marked the field unlocked.

Slots the blueprint marked required: true must be bound here before the agent will start. Optional slots can be left unbound.

Per-extension config and secrets

Each extension the agent uses gets its own subfolder under config/ext/<name>/. Two files live there: config.json for operator overrides of the extension's settings, and secrets.json for credentials.

What an operator can override is set by the blueprint. In blueprint/props/capabilities.json, each extension field is either locked (the value is fixed by the author) or unlocked (the operator can override). Locked fields never accept operator changes; unlocked fields are what config.json writes to. Secrets are always operator-set — blueprints never carry credentials.

config/ext/email/config.json

json · operator overrides
{
  "send_mode": "enabled",
  "read_window_days": 14
}

config/ext/email/secrets.json

json · credentials
{
  "imap_user": "alex@example.com",
  "imap_password": "..."
}
🔒 SECURITY
Files under config/ext/ are not mounted into the container. The extension code runs on the host and reads them there; the agent never sees the raw secret. This is how an extension can hold a credential the LLM can't reach. One exception: the Configure assistant can read these values to help you set them up, and when it does, the value goes to the model (Anthropic), like anything else in that chat. Entering them on the form keeps them out of the model entirely.

mcp-servers.json — env values for external MCP servers

Extensions are the curated path; external MCP servers are the open one. If the blueprint declares any (in capabilities.json::mcp_servers), each server may need env values the author can't ship — API keys, hosts, anything install-specific. mcp-servers.json holds them.

mcp-servers.json

json · operator env for external MCP servers
{
  "github":   { "GITHUB_TOKEN": "ghp_..." },
  "postgres": { "DB_URL": "postgresql://..." }
}

Top-level key is the MCP server's name from capabilities.json; the inner map is env-var name → value. Only unlocked env slots can be written here — locked slots are vendor-hardcoded and rejected at write time. Required unlocked slots must be filled before the agent will start.

  • Authoring blueprints — the companion: everything an agent IS before it's wired to your install.
  • Access — how people reach an agent: a held first contact, then your approval, recorded in acl.json.
  • Capabilities — what extensions are and why they can hold secrets the agent can't see.