Skip to content

config.yaml reference

Deployment-wide settings at ~/.lionclaw/config.yaml. Almost every field here is editable from the dashboard's Config section (admin only), which is the recommended way — the forms validate as you type. A few keys have no form yet and are edited in the file directly; each says so in its description.

Out-of-range numbers are clamped, not rejected. If you write poll_minutes: 9999 where the range is 1–1440, you get 1440 and no error. If you write something unparseable for a field, that field falls back to its default silently. Check the value the dashboard shows back after saving.

Most changes apply on the next tick. The exceptions — the ports and team accounts — need a runtime restart, and the dashboard says so.

Every key and block in this section can also be changed from an AgentFather conversation ("set the disk warning threshold to 85%", "turn magic links off"). AgentFather reads the whole file, patches the field through the same validation a dashboard save gets, and reports honestly which keys were applied and which were ignored (with the reason) — if it says a key was ignored, nothing changed on disk. Each change surfaces an approval card first. Secrets never go in this file — they live in the vault.

Root keys

Key Type Default Meaning
default_agent string "merli" The agent the dashboard chat and the simple chat open first, and the one API calls that name no agent go to.
preferred_model string | null null Default model for new agents.
runtime_api_port integer 8765 The runtime API port. Restart required.
dashboard_port integer 3200 The dashboard port. Restart required.
multi_user_enabled boolean | null null Team accounts. Unset resolves by deployment type — on for managed deployments, off for a manual install. See Multi-user: two different features. Restart required.
ui_mode "advanced" | "simple" "advanced" Single-user installs only. Which interface the operator sees: advanced is the full dashboard, simple is the stripped bot-chat app described in The dashboard. Team deployments ignore this and use each person's own Interface setting in Admin → Users. Edited in config.yaml directly — no dashboard form yet.
highlights_all_agents boolean false Show highlights across every agent instead of the current one.
extra_bind_roots string[] [] Extra host directories agents may reach.
archetype_dirs string[] [] Extra directories to search for agent templates.
egress_allow string[] [] Domains every agent's tools may reach — bare domains like api.github.com, a domain covering its subdomains. Empty means every public destination is allowed. An agent whose own egress_allow is non-empty uses its list instead; an agent with an empty list inherits this one. Governs every outbound request a tool makes — web and HTTP tools, the headless browser, remote-agent calls and every integration toolkit — but not the sandbox shell. Edited in Config → Outbound access; applies to the next tool call. See agent.json5 reference.
egress_deny string[] [] Domains no agent's tools may reach, same format. Checked before the allow list. Every agent's own egress_deny is added to this list — an agent can block more, never less. Edited in Config → Outbound access.
sandbox_tool_sets string[] [] Extra tool bundles to install into the sandbox your agents run commands in. Empty means none — agents still get the standard toolkit (git, Python, Node, jq, sqlite3, ripgrep, tmux, ffmpeg, pandoc, PDF tools, and the GitHub command-line tool gh — present but not signed in, see Sandbox tool bundles). Accepts "chinese" (Alibaba, Tencent, Qiniu and Baidu cloud + storage SDKs, WeChat official APIs, Bilibili, yt-dlp) and "coding" (the claude and codex coding-agent command-line tools, each needing its own sign-in). A bundle installs once, in the background, into a shared area every agent sees, and stays there across restarts — so it costs its download size on your disk (the chinese bundle is around 740 MB). Turning a bundle back off stops future installs but does not delete what it already installed. Edited in config.yaml directly — no dashboard form yet. See Sandbox tool bundles.
sandbox object {} Resource ceilings for the sandbox each agent runs shell commands in: memory (a size like 4g or 2048m, minimum 6m) and cpus (cores, 0.1–64; 0.5 is half a core). Leave either unset for the automatic limit — 25% of the machine's RAM and 50% of its cores, never below 512m and half a core — so a bigger machine gives every agent more room with nothing to configure. These are ceilings, not reservations: an idle agent uses nothing. A single agent can override both in its own agent.json5, and the LIONCLAW_SANDBOX_MEMORY / LIONCLAW_SANDBOX_CPUS environment variables override everything. Edited in Config → Sandbox resources. Changing a limit rebuilds each agent's sandbox on its next start: workspaces and installed tool bundles are kept, anything installed by hand inside the sandbox is not.
checkpoint_backend "postgres" | "sqlite" "postgres" Where conversation state is stored.
checkpoint_db_url string | null null Required when using postgres.
store_backend "postgres" | "sqlite" "postgres"
store_db_url string | null null
mcp_relay_url string | null null Central relay for MCP sign-ins.
instance_id string | null null This deployment's identifier.

Selecting postgres (including by leaving the default) without a URL is an error, not a silent fallback to SQLite. Set the URL, or switch the backend to sqlite for a zero-configuration single-machine install.

Sandbox tool bundles

When an agent runs a shell command on a managed deployment, it runs inside an isolated sandbox rather than on the machine itself. That sandbox comes with a standard toolkit — git, Python and pip, Node and npm, jq, sqlite3, ripgrep, tmux, ffmpeg, pandoc, PDF tools, zstd, and the GitHub (gh) and Google (gog) command-line tools, present but not signed in (see below) — which covers most of what agents actually do. (tmux is there so a long job outlives the command that started it: an agent can start a build in a detached session and read its output back on a later turn, and you can do the same from the Terminal tab without a browser reconnect killing it. zstd is there so tar --zstd can unpack the .tar.zst archives many projects now ship releases as.) Both python and python3 work, so a command copied from documentation runs as written — and that holds on every kind of install, not just inside the sandbox: python and pip always resolve for an agent's shell commands, with a python3-only system simply forwarding to python3. The Terminal tab's shell sees the same tools an agent does (anything an agent pip installs or npm install -gs is on its PATH), and ships vim and nano so you can open and edit files there directly.

Two heavier bundles are available but off by default, because each one costs real disk on your machine and most deployments never touch them:

Bundle What it adds Rough size
chinese Alibaba, Tencent, Qiniu and Baidu cloud + storage SDKs, the Tencent Cloud CLI, WeChat official APIs (Official Account, WeCom, Pay, MiniProgram), Bilibili, yt-dlp ~740 MB
coding The claude and codex coding-agent command-line tools ~620 MB

There are two ways to turn one on. The easiest, and the only one that needs no file access, is to ask AgentFather: "enable the coding tool set for the sandbox" — it patches the deployment config for you and tells you what it did. Or edit sandbox_tool_sets in ~/.lionclaw/config.yaml yourself (this key has no form in the dashboard's Config section yet):

sandbox_tool_sets:
  - chinese
  - coding

The install runs once, in the background, the next time an agent starts a sandbox — nothing blocks and nothing restarts. It lands in a shared area that every agent on the deployment sees and that survives restarts and updates, so you pay for it once.

Two things worth knowing. The coding command-line tools each need their own sign-in (a Claude subscription or API key; a ChatGPT or OpenAI login) — installing them does not authenticate them. And removing a bundle from the list stops future installs but leaves what it already installed in place; Olano will not delete tools you may still be using. If you want that space back, clear the bundle's folder from the Files tab under sandbox/tools.

Agents can also install packages themselves at any time (pip install, npm install -g) — those land in the same shared area, and show up under Olano home in the Overview disk card.

Command-line tools in the sandbox are not signed in, and Olano never signs them in. The sandbox holds no credential — not the GitHub connection, not a vault key, nothing — so gh, gog, and anything you or an agent install there start out logged out and stay that way. That is deliberate: it is what keeps a stray command from reading a token it should never see. Whatever is installed in the sandbox beyond the standard toolkit is yours: unsupported, unauthenticated, and used at your own risk. You may sign a tool in yourself from the Terminal tab (gh auth login, say); that sign-in is your own, it is stored inside the sandbox only, and it lasts until the sandbox is rebuilt — a resource-limit change or an update does that. An agent will never do it for you and will never ask you for a token to do it. For everything the sandbox's tools would have reached, use the built-in integrations instead: the GitHub tools for repositories, issues and pull requests, the git toolkit for cloning and pushing private repositories (agent.json5 reference), and the Connections page (account menu → Setup) to sign in once for all of them.

cognition: — Cortex defaults for the fleet

Key Type Default Meaning
enabled boolean false Master switch for the Cortex engine, off until you turn it on. While it is off, an agent that does not set its own cognition.enabled runs no background cognition at all: no scheduled phases, no distilling of conversations into memory, and no cortex_* tools in chat. Agents can override in either direction (true runs Cortex for that one agent while the fleet stays off). Editable in Cortex → Autonomy → Cortex engine, "All agents" column.
autonomous boolean true Whether agents act on their own initiative.
auto_approve boolean true Whether background work skips the approval queue.
intensity "relaxed" | "balanced" | "aggressive" "relaxed" How much background work: each phase's schedule and the daily cap on deep runs (6 / 12 / 24). An unknown value reads as "relaxed". Editable in Cortex → Autonomy → Cadence, "All agents" column.
fast_model string see note Model for light background work.
deep_model string | null see note Model for heavy background work.
worker_max_iterations integer 100 Step budget for one background reasoning run, for every agent that does not set its own. Counted in internal steps — about 4 per round of thinking plus tool use, so 100 allows roughly 25 rounds. A run that exceeds it stops with "hit its step limit" and its work is discarded. Whole number from 10 to 400; out-of-range values are pulled to the nearest end. Editable in Cortex → Autonomy → Step budget, "All agents" column.
worker_timeout_seconds integer 1200 Wall-clock seconds one background reasoning run may take, for every agent that does not set its own. Whole number from 30 to 7200 (default 1200 = 20 minutes). Raise it alongside worker_max_iterations — a large step budget behind a small time limit just fails on time instead. Editable in Cortex → Autonomy → Time limit, "All agents" column.
worker_max_concurrency integer 2 How many background reasoning runs may be talking to a model at the same time across the whole deployment — every agent's Cortex phases, self-learning and Pulse actions share the pool. A run that finds the pool full waits its turn; the wait does not count against its time limit. Whole number from 1 to 16; out-of-range values are pulled to the nearest end. Raise it on a large fleet with generous provider limits; lower it to 1 if a provider keeps answering "rate limit". Ordinary chat is never held by this.
features.self_learning boolean true Distil conversations into memory.
features.brainstorming boolean true
features.idea_generation boolean true
features.prototyping boolean true
features.skills_optimization boolean true
features.workspace_optimization boolean true
features.memory_optimization boolean true
features.pulse boolean true

On a managed deployment, leaving fast_model and deep_model unset resolves them to olano:turbo and olano:pro — so Cortex works out of the box on credits with no provider account. Setting either explicitly always wins; clearing it in the dashboard returns it to the deployment's own default rather than to a hardcoded provider model.

Resolution rule for every cognition setting: the agent wins when it has an opinion, the deployment applies when it doesn't.

default_models: — where the default:* aliases point

Key Type Default Meaning
turbo string unset The concrete provider:model behind default:turbo.
pro string unset The concrete provider:model behind default:pro.
max string unset The concrete provider:model behind default:max.

Editable in Config → Default models. An unset (or cleared) key falls back to the deployment's own built-in target — the matching Olano tier on a managed deployment, a bring-your-own-key model elsewhere. A target must be a concrete model: another default:* alias is rejected (aliases never chain), and so are the utility groups. Changes apply to each agent on its next boot or reload. See Deployment default models (default:*) for how the aliases behave.

heartbeat:

Key Type Default Meaning
enabled boolean true Agents wake periodically to check HEARTBEAT.md.
interval_minutes integer 60 Clamped 1–1440.

issues: — how agents pick up work

Key Type Default Meaning
auto_process boolean true Agents pick up issues assigned to them automatically.
poll_minutes integer 15 Clamped 1–1440.

revisions: — automatic version history

Key Type Default Meaning
enabled boolean true Keep automatic version history.
include_memory boolean true Include memory files. Turn off if you have data-deletion obligations — memory files carry distilled personal data.
debounce_seconds integer 45 How long to wait for edits to settle before committing. Clamped 5–3600.
max_file_kb integer 1024 Skip files larger than this. Clamped 16–65536.

usage_budgets: — token ceilings and budget warnings

Every deployment carries six usage budgets: four token ceilings (input and output, each per-agent and deployment-wide) and two cost ceilings. A turn is refused once one is exhausted, and the refusal names the budget that stopped it. All six are shown in Config → Usage budgets with their current usage, and in Admin → Deployment alongside the rest of your entitlements.

The token ceilings exist to stop a runaway loop, not to meter you, so this block lets you move them. Note what they count: every token an agent spends, including calls made on your own provider keys — so a token budget can stop a bring-your-own-key agent even though nothing was billed to Olano. If that is not what you want for a particular agent, raise or lift the ceiling here.

The two cost budgets are not listed below and cannot be set here or in the file: they cap what Olano itself pays upstream, and they come down with your plan. Managed spend is separately limited by your credit balance, so lifting a token ceiling cannot make a deployment spend credits it does not have.

Each token key takes three shapes, and they mean different things:

You write It means
(key absent) Use your plan's ceiling. This is the default.
a number, e.g. 50000000000 Use this ceiling instead.
unlimited No ceiling at all for this budget.

The long form {value: 50000000000, window: monthly} is also accepted if you want a different reset window (daily, weekly, monthly, lifetime); windows reset in UTC — daily at midnight, weekly on Mondays, monthly on the 1st.

Key Type Default Meaning
input_tokens_per_agent integer | unlimited plan's Input-token ceiling for any single agent.
input_tokens_total integer | unlimited plan's Input-token ceiling across the whole deployment.
output_tokens_per_agent integer | unlimited plan's Output-token ceiling for any single agent.
output_tokens_total integer | unlimited plan's Output-token ceiling across the whole deployment.
alerts_enabled boolean true Raise a dashboard notification as a budget fills. Turning this off silences every budget notification, including the one saying a budget is spent and turns are being refused.
alert_percents integer[] [50, 60, 70, 80, 90, 95, 99] Percentages that raise a notification. Each fires once per budget per window; a turn that jumps several at once raises only the highest. Values must be 1–99 — 100% is always announced and needs no entry. Applies to all six budgets, cost included.
usage_budgets:
  input_tokens_per_agent: 50000000000
  output_tokens_total: unlimited
  alert_percents: [50, 75, 90, 99]

On an unmanaged install nothing is counted or enforced at all, and the Config card says so — the ceilings only apply once a deployment is licensed.

disk_monitor:

Key Type Default Meaning
enabled boolean true
path string "/" Which filesystem to watch.
check_interval_minutes integer 30 Clamped 5–1440.
warn_percent integer 80 Warn at this usage. Clamped 50–99.
cleanup_percent integer 85 Start cleaning at this usage. Clamped 50–99, and never below warn_percent.

disk_monitor.cleanup:

Key Type Default Meaning
enabled boolean true
actions string[] all actions Which cleanups may run — everything is on by default. olano_caches, revisions_gc, olano_stale_images, docker_image_prune, npm_cache, journald_vacuum are safe (caches and old logs only); docker_prune is more aggressive (removes ALL unused Docker images/containers) — untick it if other software on the server relies on cached Docker data. Unknown names are ignored; the old name lionclaw_caches still works.
cooldown_minutes integer 360 Clamped 30–10080.
force_interval_days integer 0 Run periodically regardless of usage. 0 = only on threshold. Clamped 0–90.
journald_vacuum_size string "200M" Size to keep, e.g. 500M, 2G.

disk_monitor.scan: — the daily "what is using my disk" report.

Key Type Default Range
enabled boolean true
interval_hours integer 24 1–168
depth integer 2 1–4
agent_depth integer 2 0–4
time_budget_seconds integer 600 30–3600
throttle_ms integer 20 0–1000
max_entries integer 400 50–2000

mcp_health: — keeping MCP connections alive

Key Type Default Meaning
enabled boolean true
check_interval_minutes integer 10 Clamped 1–1440.
refresh_tokens boolean true Renew sign-ins before they expire. Leave this on — a token that lapses while the agent is idle can become unrecoverable.
refresh_window_minutes integer 20 How far ahead of expiry to renew. Clamped 1–720.
auto_reconnect boolean true Retry servers that timed out or errored, with backoff. A server that needs a sign-in you have not given is never retried — reconnecting cannot invent consent.

context_window: — compaction and oversized tool results

A conversation cannot grow past the model's context window, so two things bound it. Compaction: when a thread reaches the trigger, everything older than the keep budget is summarised into one paragraph, the full text of what was summarised is saved under the agent workspace's .lionclaw/conversation_history/ folder (the agent can read it back with its file tools), and from then on the model sees the summary plus the kept tail. /compact in the chat does the same on demand; it becomes available at half the trigger and reports how many messages it folded. A model can only remember what is in the kept tail, so an agent asked "which tools did you use?" right after a compaction answers from the summary. MCP result offload: a result from an MCP server larger than the limit is saved to .lionclaw/large_tool_results/<call id> in the workspace and replaced in the conversation by a head-and-tail preview plus the file path; the agent reads the rest a part at a time. One page of search results is roughly the default limit, so a search-heavy task no longer spends a quarter of the window on one call.

The keep and trigger each have two spellings: a share of the window when the model's window is known (the model catalog lists it, or the provider reports it), and an absolute token count used only when it is not (a model newer than the catalog, a self-hosted one). Both keep values must be below their trigger; the block corrects a keep at or above it, the dashboard refuses it. Changes reach an agent at its next start or reload. Every field can be overridden per agent (agent.json5 reference) and edited in Config → Context window.

Key Type Default Meaning
keep_fraction number 0.35 Share of the window kept verbatim after a compaction. Clamped 0.05–0.8. Lower frees more per compaction; higher keeps more of the session.
keep_tokens integer 40000 The keep budget in tokens, when the window is unknown. Clamped 2000–2000000.
trigger_fraction number 0.85 Share of the window at which compaction fires automatically. Clamped 0.3–0.98.
trigger_tokens integer 170000 The trigger in tokens, when the window is unknown. Clamped 8000–4000000. Keep it below the smallest window your unknown models really have.
mcp_result_token_limit integer 5000 MCP results above this many tokens (about four characters each) are offloaded. 0 turns the cap off. Clamped 0–200000. Results from Olano's own tools use a separate 20000-token offload.

remote_agents: — fleet rules for talking to outside agents (A2A)

The per-agent remote_agents list (agent.json5 reference) decides which external agents an agent may reach; this block sets the rules every such call obeys.

Key Type Default Meaning
enabled boolean true Master switch. Off ⇒ no agent can reach any remote agent; the tools answer with an explanatory error rather than disappearing.
allow_ad_hoc_discovery boolean false Whether a2a_discover_agent may read the Agent Card of a URL that is not in the calling agent's list. Sending a message is never ad hoc, in any mode.
max_reply_chars integer 20000 Replies longer than this are truncated before the agent reads them. Clamped 500–500000.
max_message_chars integer 20000 Longer outbound messages are refused. Clamped 500–500000.

external_access: — letting outside systems reach your agents

Home-wide switches and policy for the inbound surfaces: the MCP endpoint at <public URL>/api/mcp, the per-agent A2A endpoints at <public URL>/api/a2a/agents/<agent>, and Olano's own OAuth sign-in for clients that cannot paste a token. Both surfaces are off by default; each agent still has to opt in on its External access tab (agent.json5 reference). Edited from Config → External access, which also lists the tokens and OAuth clients. See Beyond the deployment: A2A and MCP.

Key Type Default Meaning
mcp.enabled boolean false Serve the MCP endpoint. Off answers 404 to everyone whatever the agents say. Applies immediately.
a2a.enabled boolean false Serve the A2A endpoints. Off answers 404. Applies immediately.
require_https boolean true Refuse to serve any of this when the deployment's public URL is plain http (tokens travel in a header). Loopback and private-network URLs are exempt for development.
token_ttl_days integer 90 Default lifetime of a bearer token minted without an explicit expiry; 0 = never expires. Clamped 0–3650.
a2a_task_retention_days integer 7 How long the record of each inbound A2A call (task id, status, message, reply) stays retrievable by the caller through the protocol's task lookup before it is deleted. Only the A2A envelope: the conversation itself stays in the agent's thread and transcript, and the Agent to Agent view keeps its own log. Clamped 1–3650.
allowed_hosts string[] [] Extra Host header values accepted by the DNS-rebinding protection, on top of the public URL's host and loopback (host:* allows any port). Only needed when the endpoints are reached under another hostname.
allowed_origins string[] [] Extra browser Origin values to accept. Server-to-server clients send none.
oauth.enabled boolean true Olano's own OAuth 2.1 authorization server at <public URL>/api/oauth. It only answers while a surface above is on. Off ⇒ OAuth-only clients cannot connect; pasted tokens still work.
oauth.dynamic_registration boolean true Accept RFC 7591 client registrations so an application can sign up by itself before its first sign-in. Registration grants nothing; the consent page does.
oauth.access_token_minutes integer 60 Lifetime of the access tokens the sign-in issues. Clamped 5–1440.
oauth.refresh_token_days integer 30 How long a client may keep renewing without a new consent; every refresh rotates the token. Clamped 1–365.
jwt.issuer string "" With jwt.jwks_url, also accept access tokens issued by your own authorization server (Auth0, Keycloak, Okta, Entra…).
jwt.jwks_url string "" Where that issuer publishes its signing keys.
jwt.audience string "" The audience such a token must carry. Empty means the URL of the endpoint the token is presented to, which is what the standard requires: <public URL>/api/mcp for MCP, and the agent's own <public URL>/api/a2a/agents/<agent> for A2A (<public URL>/api/a2a/agents is accepted there too). A value set here replaces both.
jwt.required_scopes string[] [] Scopes every such token must include, in addition to the agent scopes (agent:<name> / agents:*) that decide reach.

catalog_sync:

Key Type Default Meaning
enabled boolean true Keep the model, MCP and agent catalogs current.
manifest_poll_minutes integer 60 Clamped 15–1440.

credits: — how costs are displayed

Key Type Default Meaning
display "usd" | "credits" "usd" How costs are shown. Display only — the underlying accounting is unchanged.

A magic link is a short-lived, PIN-protected page an agent sends into a chat so someone with no dashboard account (and no server access) can finish one task on their own phone: sign in to a service, connect a messenger, edit an agent's instructions, hand over a credential, or edit a file. The link is single-use, and the agent is told the outcome in the same chat when it lands.

Dashboard: Config → Magic links (and the mint UI under Connections).

Renamed from handshake:. A config still using the old key keeps working — it is read as a fallback — and the first save from the dashboard rewrites it under magic_links:.

Key Type Default Meaning
enabled boolean true Allow agents to send magic links. Off ⇒ nothing can mint one and existing links stop opening.
ttl_minutes integer 15 Link lifetime. Clamped 2–1440.
pin "required" | "off" "required" Whether links also need a PIN. A forwarded or leaked link is useless without it.
max_pin_attempts integer 5 Wrong PINs before the link burns itself. Clamped 1–10.
allow_groups boolean false Allow links to be minted from group chats, where every member sees the link and its PIN.
owner_login.enabled boolean false Let an agent send the owner a magic link that signs them into the dashboard.
owner_login.auto_bind boolean false On a single-admin deployment, skip the one-time password step.
owner_login.session_hours integer 24 Clamped 1–720.

How the outcome comes back. Whichever chat the link was sent from is told when it lands — you never have to ask "did that work?" or refresh anything. On a messenger the agent posts a ✅ or ⚠️ message saying what connected and whether the agent still needs a restart. In the dashboard chat the same result appears as a card in the same conversation:

  • when the connection is already live — connecting messengers restarts the agent for you, and signing in to an MCP server reloads its tools — the card says so and there is nothing to do;
  • when the agent has to restart before it can use what you just added (a new API key, a newly connected service), the card says that and offers a Restart button. Dashboard admins get the button; anyone else gets a line asking an admin to restart it.

The card stays in the conversation, so reopening the thread later — or opening it in another tab — still shows what happened. A link that expired or failed reports that instead, and never offers a restart.

Editing a file over a magic link. An agent can send a link that opens one file in an editable box (magic_link_request_file_edit, or /olano connect edit <path>, or the Connections mint form). A path relative to the agent's workspace edits that agent's own file; an absolute path edits a file anywhere the server can write, and only an admin agent or the dashboard may send one of those. The file must already exist, be text, and be under 256 KB; saving replaces the whole file with what the recipient submits. Whoever opens the link sees the file's current contents, so it should only go to someone meant to read that file.

prompt_refresh:

Key Type Default Meaning
enabled boolean true Pick up edits to identity/instruction files without a restart.
check_interval_minutes integer 2 Clamped 1–1440.
notify_threads boolean true Tell active conversations that the prompt changed.

speech: — voice in and out

Two identical blocks, speech.stt (speech to text) and speech.tts (text to speech):

Key Type Default Meaning
provider "disabled" | "olano" | "openai" | "groq" | "qwen" | "custom" "disabled"
model string ""
url string "" For provider: custom.
api_key_name string "" Which vault entry holds the key.
voice string "" TTS only.
audio_format string "" TTS only: aac, flac, m4a, mp3, opus, pcm, wav.

On a managed deployment, an stt block with no provider key at all resolves to olano — voice notes transcribe on credits with no setup. An explicit disabled always wins. tts never turns itself on: voice replies stay off on every install until you pick a provider here or in Config → Speech (on a managed deployment, picking olano runs them on credits).

Transcription applies to voice notes on every messenger that carries them, WhatsApp included. If a voice note on one channel is answered as though nothing was said while the same words work elsewhere, the audio never reached the transcriber — not a speech setting. See Channels and connectors for the WhatsApp-specific case.

media: — vision and image generation

Key Type Default Meaning
vision string "" "" = decide automatically, "olano" = force the managed route, or a "provider:model" pin for your own key.
image string "" Same, for image generation and editing.
Key Type Default Meaning
provider "" | "olano" | "tavily" | "disabled" "" (auto) tavily always needs TAVILY_API_KEY in the vault.

rate_limits: — message caps

Key Type Default Meaning
messages.per_minute integer | null null null = unlimited. An explicit 0 blocks entirely.
messages.per_hour integer | null null
messages.per_day integer | null null
per_platform.<platform> same three keys {} Per-connector overrides, e.g. per_platform.telegram.per_minute.

An agent's own security.rate_limits overrides these for that agent.

playground: — the prompt sandbox

Key Type Default Meaning
enabled boolean false Show the Playground tab. Off also skips generating its assets.

self_serve_agents: — let people make their own bots

On by default: every signed-in member gets a "New bot" button (most visibly in the simple view) and can create bots of their own, within the quota below. Admins can always create agents regardless of this block — nothing here applies to them.

Key Type Default Meaning
enabled boolean true When true (the default), a non-admin member may create an agent for themselves. They become its owner, so they can edit and delete the one they made — and nothing else changes about what a member may do. Set false to make agent creation admin-only.
max_per_user integer 3 How many agents one person may own before further creation is refused. Clamped to 1–100. Admins are not counted or limited.
allowed_archetypes string[] [] Which templates a member may build from. Empty means any template, which is the default. A non-empty list is strict: only those templates, and building from scratch is refused too — so use it when you want people choosing from a menu you curated rather than describing a bot freehand. Names are the template ids shown in the New bot picker.

All three knobs are editable from the dashboard under Config → Member bot creation — changes apply on save, no restart. A member who creates a bot gets a bot that runs on your deployment and spends your credits; max_per_user is the ceiling on how much of that any one person can start, and enabled: false closes the door entirely if that is not a trade your deployment wants.

agent_autonomy: — what agents may change about themselves

Deployment-wide defaults for chat self-configuration: when someone asks a bot in conversation to "hook yourself up to Notion", "switch to a cheaper model", "rewrite your instructions" or "add a research helper", these switches decide whether the bot has the tools to do it. Everything is on by default. Each key is a boolean; the block is editable from Config → Agent self-configuration, and a single agent can override any switch in either direction from its own page (Agents → agent → Chat self-configuration, three-state with Inherit). Changes apply to the agent's next tool call — no restart.

Key What the agent may do when true (the default)
explore_archetypes Search and read the template library (read-only).
change_model Switch its own model, from the models this deployment can actually reach. Applies after it restarts.
modify_prompt Rewrite its own system prompt (full replacement; recorded in Config → Revisions under the agent's name).
restart_self Restart itself so saved changes take effect — scheduled just after its reply, so the answer isn't cut off.
add_subagents Add specialist subagents to itself, from scratch or drafted from a template.
connect_mcps Connect services (MCP servers and built-in toolkits). Find every supported way to reach a service (self_find_integration), add or remove an MCP server or a built-in toolkit on itself, and start the sign-in. OAuth and sign-in codes happen in the user's own browser; API keys go through the vault. Also lets it read an outside project's documentation before anyone installs it (self_inspect_external).
connect_platforms Wire messaging channels onto itself — token platforms (Telegram, Discord, Slack) and WhatsApp by a QR code shown right in the chat.
manage_skills List the skills it has and switch individual ones on or off for itself — the same switches on its Skills tab, writing the same disabled_skills list. It can never install, edit or delete a skill this way, and switching off a common skill leaves every other agent's copy alone. Applies after it restarts.
magic_links Send magic links — the magic_link_* tools: sign a person in to a service or MCP server, collect an API key straight into the vault, connect messengers by QR or token, let someone edit its instructions or a file, open the dashboard on their phone. The default way a bot connects itself to anything, so it ships on. Off removes the whole family from the bot; a single link tool can also be switched off on its Tools tab. Owners only, group chats refused, and an approval card on the stricter profiles; Config → Magic links stays the hard off switch for the whole deployment and separately gates the dashboard link.
external_access Publish itself for outside callers (MCP / A2A) and mint access tokens — self_set_external_access and magic_link_mint_access_token. The tools additionally refuse anyone who is not an administrator, so this only decides whether a bot holds them at all. Off removes them from the bot; the External access tab still works for admins.

Four guardrails apply regardless of these switches. Only people who own the bot can make it change itself (see below). Every change is written through the same validated path the dashboard uses, so a broken edit is refused rather than half-saved. Every agent.json5 change lands in Config → Revisions attributed to the acting agent, so it can be reviewed and rolled back. And on the stricter security profiles each self-change first raises a human approval card. These switches govern the agent acting on itself only — they grant nothing to humans and nothing over other agents.

Who may ask is a separate question from what the bot may change. The switches above decide what the bot is capable of; ownership decides who is allowed to trigger it. Anything that writes — model, instructions, subagents, MCP services, messaging channels, restart, sending a magic link — is limited to:

  • an owner of that bot (whoever creates a bot owns it; more owners are granted under Admin → Access, where the roles are Owner and User),
  • any deployment admin, and
  • on a messaging channel, a sender on that channel's allowed users list — which is what keeps "pair yourself with WhatsApp" workable from a phone.

Everyone else who may chat with the bot gets a plain refusal naming the remedy, and keeps the read-only half: it can still list the models it could switch to, the services it could connect, and the templates it could draw from. So a colleague you shared a bot with can explore what it could become and then ask you to approve it, rather than silently reconfiguring your bot.

Two cases are refused outright, with no way to grant them: an agent asking another agent to reconfigure it, and a scheduled or background run trying to reconfigure the agent while nobody is watching.

One built-in exception: an agent in customer-service mode (per-customer isolation — a support bot serving strangers on a shared channel) inherits off for every capability, whatever the deployment default says. A stranger must not be able to talk a support bot into rewiring itself. You can still enable an individual capability for such an agent deliberately, with an explicit On in its own Chat self-configuration card.

groups: — multi-bot conversations

On by default, and editable from the dashboard at Config → Group chats — no file editing needed. While it is off, group threads do not exist and every group address on the API answers "not found"; turning it off never deletes a group, and turning it back on restores every group and transcript untouched.

Key Type Default Meaning
enabled boolean true Group threads: one conversation several bots share, where you can address any member. Replies stream in word by word, the same as a one-to-one chat. Whether the bots then answer each other is the separate autonomous switch below.
min_members integer 2 Fewest bots in a group.
max_members integer 6 Most bots in a group.
autonomous boolean true A second, separate switch, also on by default. With it on, a bot whose reply names another member by @name hands that member the next turn — so the bots carry on with each other without you in between, inside the two limits below. With it off, you post, the bots you named answer, and the thread waits for you.
max_hops integer 3 How many bot-to-bot turns one message of yours may set off. Clamped to 1–10. When it runs out the thread stops and says so in the conversation.
cascade_timeout_seconds integer 180 How long the whole chain may run before it stops itself, in seconds. Clamped to 10–1800.

autonomous is the one to think about. It is what makes a group a conversation rather than a switchboard, and it is also the setting that spends model calls nobody is watching, because one message from you can start a chain of bots answering each other. That is why the two limits exist, why the switch is separate from enabled, and why every stop — hop limit, timeout, or two bots going back and forth — is written into the conversation instead of the thread just going quiet. If you would rather relay every hand-off yourself, turn it off at Config → Group chats; the group surface stays exactly as it is. Raise max_hops only once you have watched a few chains and know what they cost you, and raise cascade_timeout_seconds with it — more turns under the same time limit only converts "out of turns" into "out of time".

If you are upgrading from a release where this switch was off by default and your config.yaml never mentioned it, your groups start doing this after the upgrade. Set autonomous: false there, or switch it off in the dashboard, to keep the old behaviour.

Using groups. A Group chats section appears in the agent list of both interfaces — in the full dashboard, under your agents in the sidebar. Click a group to open it; right-click it to rename it; use New group chat… at the foot of the section (or right-click its heading) to start one, picking the agents in the order you want them to answer. Each group has a details panel — the ⓘ button in its header — for renaming it, adding an agent, removing one, or deleting the whole group; on a phone the panel fills the screen. Order matters: the first agent in the roster answers anything that doesn't name someone. The Add an agent button in a one-to-one chat's header is the other way in: it turns that conversation into a group (see the Chat section).

Addressing one agent, or several. Start a message with @ and an agent's name to put it to that agent; typing @ opens a list of the members to pick from. You can name more than one — "@ana @luis @ravi what would each of you do here?" — and then each of them takes its own turn, in the order you named them, and each one can see what the ones before it said, so they add to each other instead of writing the same answer three times. Name nobody and the group's first agent answers. A line under the message box says which of those is about to happen — "ana answers", "ana, luis answer, each in turn" — and it changes as you type, so there is nothing to set before sending.

Agents you did not name still read everything. They stay out of the way unless they are named — and any agent can bring a teammate in by writing @their-name in its own reply, which is what turns a group into a conversation rather than a switchboard. With autonomous on (the default) that mention wakes the other agent for the next turn; with it off, the mention marks who should pick the work up and the thread waits for you. When an agent answers a teammate that called on it, its message carries a small "↩ name" tag beside the "→ name" one, so you can always tell who is answering whom — and answering is not handing over: the chain only continues when a reply names somebody else.

Every agent in a group is told, on every turn, who else is in the room and what each of them does (their description from the agent's own profile), that a message naming somebody else is context to read rather than a cue to answer, and which ways it has of reaching the others — so the group behaves like a room of colleagues rather than several agents that happen to share a page.

Watching them work. The group header has the same Quiet / Normal / Verbose control as a one-to-one chat, and it starts on Normal rather than Quiet: Normal shows one line per tool an agent uses — including it calling another agent or handing work to one of its own helpers — and Verbose adds each tool's full input and result plus the helper transcripts. In a group that activity is usually the point, which is why the starting level differs. The choice is remembered in your browser and applies to every group.

The reply is laid out in the order it happened: what the agent said before it went to work, then what it did, then what it said afterwards — rather than a pile of tool lines above one block of text. That order is kept, so reopening the group later shows the same conversation you watched.

Files an agent makes. When an agent saves a file and names it in its reply, the group shows it: a picture appears inline, anything else gets a card with Download and Open in Files, and the file's name in the text becomes a link. Each agent has its own workspace, so a link goes to the files of the agent that made it. A file the agent later deletes stops being offered rather than leaving a dead link behind.

When one agent asks another one directly, the transcript says so while it waits: a line reading "maria is thinking…", with that agent's own face beside it, appears under the member doing the asking and clears the moment the answer comes back. It shows at every level, Quiet included, because it is about who is talking rather than about tools — and without it a group looks frozen for as long as the other agent takes. What that agent says comes back inside the asking agent's reply, not as its own message in the group; an agent pulled in with @their name is the other route, and that one does post its own message.

Renaming a group. Right-click a group in the sidebar and choose Rename, or open the group's info panel and use the pencil beside its name (that is the route on a phone, which has no right-click). One-to-one conversations rename the same way in both views: right-click the chat under its agent in the sidebar (or in the simple view's rail), use the pencil on a row of the chat page's conversation list, or type /rename followed by the name you want. A name is only a label — renaming changes nothing about the conversation, who is in it, or what the agents remember of it. The name shows everywhere the conversation is listed the moment it is saved.

Turning a one-to-one chat into a group. In any conversation, Add an agent in the header brings other agents in. The agent you were already talking to leads the new group, so it keeps answering by default, and the recent history is copied across labelled with who said what — so the agents you add can read what was discussed instead of starting blind. Long conversations are trimmed to the most recent messages and the group says so in the transcript. You can switch that copying off if you would rather start clean. Two things are worth knowing: your original one-to-one chat is left exactly as it was (this creates a separate conversation), and what an agent privately remembers of that chat does not transfer — every member, including the original one, starts the group with only what the transcript shows.

Letting bots use groups themselves. Adding "groups" to an agent's tools: list gives it four abilities: list the groups it belongs to, read a group's recent messages, post into a group (optionally addressing one member with @name), and create a new group for the person it is talking to. A bot does not need these to take part in a group it is already in — replying and @mentioning a teammate work with no tools at all; these are for reaching a group it is not currently being spoken to in. A bot can only see and speak in groups it is a member of, its own name is always stamped as the author, and whether an addressed member replies on its own still follows autonomous and its limits — a bot's post starts exactly the same bounded exchange a message of yours would, never more. Creating a group works only in a live conversation with a signed-in person (the group becomes theirs, in their sidebar); from a scheduled run the bot is told to ask a human instead.