This is a high-signal reference for common config sections and defaults.
Last verified: February 21, 2026.
Config path resolution at startup:
ZEROBUILD_WORKSPACEoverride (if set)- persisted
~/.zerobuild/active_workspace.tomlmarker (if present) - default
~/.zerobuild/config.toml
ZeroBuild logs the resolved config on startup at INFO level:
Config loadedwith fields:path,workspace,source,initialized
Schema export command:
zerobuild config schema(prints JSON Schema draft 2020-12 to stdout)
| Key | Default | Notes |
|---|---|---|
default_provider |
openrouter |
provider ID or alias |
default_model |
anthropic/claude-sonnet-4-6 |
model routed through selected provider |
default_temperature |
0.7 |
model temperature |
| Key | Default | Purpose |
|---|---|---|
backend |
none |
Observability backend: none, noop, log, prometheus, otel, opentelemetry, or otlp |
otel_endpoint |
http://localhost:4318 |
OTLP HTTP endpoint used when backend is otel |
otel_service_name |
zerobuild |
Service name emitted to OTLP collector |
runtime_trace_mode |
none |
Runtime trace storage mode: none, rolling, or full |
runtime_trace_path |
state/runtime-trace.jsonl |
Runtime trace JSONL path (relative to workspace unless absolute) |
runtime_trace_max_entries |
200 |
Maximum retained events when runtime_trace_mode = "rolling" |
Notes:
backend = "otel"uses OTLP HTTP export with a blocking exporter client so spans and metrics can be emitted safely from non-Tokio contexts.- Alias values
opentelemetryandotlpmap to the same OTel backend. - Runtime traces are intended for debugging tool-call failures and malformed model tool payloads. They can contain model output text, so keep this disabled by default on shared hosts.
- Query runtime traces with:
zerobuild doctor traces --limit 20zerobuild doctor traces --event tool_call_result --contains \"error\"zerobuild doctor traces --id <trace-id>
Example:
[observability]
backend = "otel"
otel_endpoint = "http://localhost:4318"
otel_service_name = "zerobuild"
runtime_trace_mode = "rolling"
runtime_trace_path = "state/runtime-trace.jsonl"
runtime_trace_max_entries = 200Provider selection can also be controlled by environment variables. Precedence is:
ZEROBUILD_PROVIDER(explicit override, always wins when non-empty)PROVIDER(legacy fallback, only applied when config provider is unset or stillopenrouter)default_providerinconfig.toml
Operational note for container users:
- If your
config.tomlsets an explicit custom provider likecustom:https://.../v1, a defaultPROVIDER=openrouterfrom Docker/container env will no longer replace it. - Use
ZEROBUILD_PROVIDERwhen you intentionally want runtime env to override a non-default configured provider.
| Key | Default | Purpose |
|---|---|---|
compact_context |
false |
When true: bootstrap_max_chars=6000, rag_chunk_limit=2. Use for 13B or smaller models |
max_tool_iterations |
10 |
Maximum tool-call loop turns per user message across CLI, gateway, and channels |
max_history_messages |
50 |
Maximum conversation history messages retained per session |
parallel_tools |
false |
Enable parallel tool execution within a single iteration |
tool_dispatcher |
auto |
Tool dispatch strategy |
Notes:
- Setting
max_tool_iterations = 0falls back to safe default10. - If a channel message exceeds this value, the runtime returns:
Agent exceeded maximum tool iterations (<value>). - In CLI, gateway, and channel tool loops, multiple independent tool calls are executed concurrently by default when the pending calls do not require approval gating; result order remains stable.
parallel_toolsapplies to theAgent::turn()API surface. It does not gate the runtime loop used by CLI, gateway, or channel handlers.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable OTP gating for sensitive actions/domains |
method |
totp |
OTP method (totp, pairing, cli-prompt) |
token_ttl_secs |
30 |
TOTP time-step window in seconds |
cache_valid_secs |
300 |
Cache window for recently validated OTP codes |
gated_actions |
["shell","file_write","browser_open","browser","memory_forget"] |
Tool actions protected by OTP |
gated_domains |
[] |
Explicit domain patterns requiring OTP (*.example.com, login.example.com) |
gated_domain_categories |
[] |
Domain preset categories (banking, medical, government, identity_providers) |
Notes:
- Domain patterns support wildcard
*. - Category presets expand to curated domain sets during validation.
- Invalid domain globs or unknown categories fail fast at startup.
- When
enabled = trueand no OTP secret exists, ZeroBuild generates one and prints an enrollment URI once.
Example:
[security.otp]
enabled = true
method = "totp"
token_ttl_secs = 30
cache_valid_secs = 300
gated_actions = ["shell", "browser_open"]
gated_domains = ["*.chase.com", "accounts.google.com"]
gated_domain_categories = ["banking"]| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable emergency-stop state machine and CLI |
state_file |
~/.zerobuild/estop-state.json |
Persistent estop state path |
require_otp_to_resume |
true |
Require OTP validation before resume operations |
Notes:
- Estop state is persisted atomically and reloaded on startup.
- Corrupted/unreadable estop state falls back to fail-closed
kill_all. - Use CLI command
zerobuild estopto engage andzerobuild estop resumeto clear levels.
Delegate sub-agent configurations. Each key under [agents] defines a named sub-agent that the primary agent can delegate to.
| Key | Default | Purpose |
|---|---|---|
provider |
required | Provider name (e.g. "ollama", "openrouter", "anthropic") |
model |
required | Model name for the sub-agent |
system_prompt |
unset | Optional system prompt override for the sub-agent |
api_key |
unset | Optional API key override (stored encrypted when secrets.encrypt = true) |
temperature |
unset | Temperature override for the sub-agent |
max_depth |
3 |
Max recursion depth for nested delegation |
agentic |
false |
Enable multi-turn tool-call loop mode for the sub-agent |
allowed_tools |
[] |
Tool allowlist for agentic mode |
max_iterations |
10 |
Max tool-call iterations for agentic mode |
Notes:
agentic = falsepreserves existing single prompt→response delegate behavior.agentic = truerequires at least one matching entry inallowed_tools.- The
delegatetool is excluded from sub-agent allowlists to prevent re-entrant delegation loops.
[agents.researcher]
provider = "openrouter"
model = "anthropic/claude-sonnet-4-6"
system_prompt = "You are a research assistant."
max_depth = 2
agentic = true
allowed_tools = ["web_search", "http_request", "file_read"]
max_iterations = 8
[agents.coder]
provider = "ollama"
model = "qwen2.5-coder:32b"
temperature = 0.2| Key | Default | Purpose |
|---|---|---|
reasoning_enabled |
unset (None) |
Global reasoning/thinking override for providers that support explicit controls |
Notes:
reasoning_enabled = falseexplicitly disables provider-side reasoning for supported providers (currentlyollama, via request fieldthink: false).reasoning_enabled = trueexplicitly requests reasoning for supported providers (think: trueonollama).- Unset keeps provider defaults.
| Key | Default | Purpose |
|---|---|---|
open_skills_enabled |
false |
Opt-in loading/sync of community open-skills repository |
open_skills_dir |
unset | Optional local path for open-skills (defaults to $HOME/open-skills when enabled) |
prompt_injection_mode |
full |
Skill prompt verbosity: full (inline instructions/tools) or compact (name/description/location only) |
Notes:
- Security-first default: ZeroBuild does not clone or sync
open-skillsunlessopen_skills_enabled = true. - Environment overrides:
ZEROBUILD_OPEN_SKILLS_ENABLEDaccepts1/0,true/false,yes/no,on/off.ZEROBUILD_OPEN_SKILLS_DIRoverrides the repository path when non-empty.ZEROBUILD_SKILLS_PROMPT_MODEacceptsfullorcompact.
- Precedence for enable flag:
ZEROBUILD_OPEN_SKILLS_ENABLED→skills.open_skills_enabledinconfig.toml→ defaultfalse. prompt_injection_mode = "compact"is recommended on low-context local models to reduce startup prompt size while keeping skill files available on demand.- Skill loading and
zerobuild skills installboth apply a static security audit. Skills that contain symlinks, script-like files, high-risk shell payload snippets, or unsafe markdown link traversal are rejected.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable Composio managed OAuth tools |
api_key |
unset | Composio API key used by the composio tool |
entity_id |
default |
Default user_id sent on connect/execute calls |
Notes:
- Backward compatibility: legacy
enable = trueis accepted as an alias forenabled = true. - If
enabled = falseorapi_keyis missing, thecomposiotool is not registered. - ZeroBuild requests Composio v3 tools with
toolkit_versions=latestand executes tools withversion="latest"to avoid stale default tool revisions. - Typical flow: call
connect, complete browser OAuth, then runexecutefor the desired tool action. - If Composio returns a missing connected-account reference error, call
list_accounts(optionally withapp) and pass the returnedconnected_account_idtoexecute.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable cost tracking |
daily_limit_usd |
10.00 |
Daily spending limit in USD |
monthly_limit_usd |
100.00 |
Monthly spending limit in USD |
warn_at_percent |
80 |
Warn when spending reaches this percentage of limit |
allow_override |
false |
Allow requests to exceed budget with --override flag |
Notes:
- When
enabled = true, the runtime tracks per-request cost estimates and enforces daily/monthly limits. - At
warn_at_percentthreshold, a warning is emitted but requests continue. - When a limit is reached, requests are rejected unless
allow_override = trueand the--overrideflag is passed.
| Key | Default | Purpose |
|---|---|---|
format |
openclaw |
Identity format: "openclaw" (default) or "aieos" |
aieos_path |
unset | Path to AIEOS JSON file (relative to workspace) |
aieos_inline |
unset | Inline AIEOS JSON (alternative to file path) |
Notes:
- Use
format = "aieos"with eitheraieos_pathoraieos_inlineto load an AIEOS / OpenClaw identity document. - Only one of
aieos_pathoraieos_inlineshould be set;aieos_pathtakes precedence.
| Key | Default | Purpose |
|---|---|---|
max_images |
4 |
Maximum image markers accepted per request |
max_image_size_mb |
5 |
Per-image size limit before base64 encoding |
allow_remote_fetch |
false |
Allow fetching http(s) image URLs from markers |
Notes:
- Runtime accepts image markers in user messages with syntax:
[IMAGE:<source>]. - Supported sources:
- Local file path (for example
[IMAGE:/tmp/screenshot.png])
- Local file path (for example
- Data URI (for example
[IMAGE:data:image/png;base64,...]) - Remote URL only when
allow_remote_fetch = true - Allowed MIME types:
image/png,image/jpeg,image/webp,image/gif,image/bmp. - When the active provider does not support vision, requests fail with a structured capability error (
capability=vision) instead of silently dropping images.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable browser_open tool (opens URLs without scraping) |
allowed_domains |
[] |
Allowed domains for browser_open (exact/subdomain match, or "*" for all public domains) |
session_name |
unset | Browser session name (for agent-browser automation) |
backend |
agent_browser |
Browser automation backend: "agent_browser", "rust_native", "computer_use", or "auto" |
native_headless |
true |
Headless mode for rust-native backend |
native_webdriver_url |
http://127.0.0.1:9515 |
WebDriver endpoint URL for rust-native backend |
native_chrome_path |
unset | Optional Chrome/Chromium executable path for rust-native backend |
| Key | Default | Purpose |
|---|---|---|
endpoint |
http://127.0.0.1:8787/v1/actions |
Sidecar endpoint for computer-use actions (OS-level mouse/keyboard/screenshot) |
api_key |
unset | Optional bearer token for computer-use sidecar (stored encrypted) |
timeout_ms |
15000 |
Per-action request timeout in milliseconds |
allow_remote_endpoint |
false |
Allow remote/public endpoint for computer-use sidecar |
window_allowlist |
[] |
Optional window title/process allowlist forwarded to sidecar policy |
max_coordinate_x |
unset | Optional X-axis boundary for coordinate-based actions |
max_coordinate_y |
unset | Optional Y-axis boundary for coordinate-based actions |
Notes:
- When
backend = "computer_use", the agent delegates browser actions to the sidecar atcomputer_use.endpoint. allow_remote_endpoint = false(default) rejects any non-loopback endpoint to prevent accidental public exposure.- Use
window_allowlistto restrict which OS windows the sidecar can interact with.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable http_request tool for API interactions |
allowed_domains |
[] |
Allowed domains for HTTP requests (exact/subdomain match, or "*" for all public domains) |
max_response_size |
1000000 |
Maximum response size in bytes (default: 1 MB) |
timeout_secs |
30 |
Request timeout in seconds |
Notes:
- Deny-by-default: if
allowed_domainsis empty, all HTTP requests are rejected. - Use exact domain or subdomain matching (e.g.
"api.example.com","example.com"), or"*"to allow any public domain. - Local/private targets are still blocked even when
"*"is configured.
| Key | Default | Purpose |
|---|---|---|
host |
127.0.0.1 |
bind address |
port |
42617 |
gateway listen port |
require_pairing |
true |
require pairing before bearer auth |
allow_public_bind |
false |
block accidental public exposure |
| Key | Default | Purpose |
|---|---|---|
level |
supervised |
read_only, supervised, or full |
workspace_only |
true |
reject absolute path inputs unless explicitly disabled |
allowed_commands |
required for shell execution | allowlist of executable names |
forbidden_paths |
built-in protected list | explicit path denylist (system paths + sensitive dotdirs by default) |
allowed_roots |
[] |
additional roots allowed outside workspace after canonicalization |
max_actions_per_hour |
20 |
per-policy action budget |
max_cost_per_day_cents |
500 |
per-policy spend guardrail |
require_approval_for_medium_risk |
true |
approval gate for medium-risk commands |
block_high_risk_commands |
true |
hard block for high-risk commands |
auto_approve |
[] |
tool operations always auto-approved |
always_ask |
[] |
tool operations that always require approval |
Notes:
level = "full"skips medium-risk approval gating for shell execution, while still enforcing configured guardrails.- Access outside the workspace requires
allowed_roots, even whenworkspace_only = false. allowed_rootssupports absolute paths,~/..., and workspace-relative paths.- Shell separator/operator parsing is quote-aware. Characters like
;inside quoted arguments are treated as literals, not command separators. - Unquoted shell chaining/operators are still enforced by policy checks (
;,|,&&,||, background chaining, and redirects).
[autonomy]
workspace_only = false
forbidden_paths = ["/etc", "/root", "/proc", "/sys", "~/.ssh", "~/.gnupg", "~/.aws"]
allowed_roots = ["~/Desktop/projects", "/opt/shared-repo"]| Key | Default | Purpose |
|---|---|---|
backend |
sqlite |
sqlite, lucid, markdown, none |
auto_save |
true |
persist user-stated inputs only (assistant outputs are excluded) |
embedding_provider |
none |
none, openai, or custom endpoint |
embedding_model |
text-embedding-3-small |
embedding model ID, or hint:<name> route |
embedding_dimensions |
1536 |
expected vector size for selected embedding model |
vector_weight |
0.7 |
hybrid ranking vector weight |
keyword_weight |
0.3 |
hybrid ranking keyword weight |
Notes:
- Memory context injection ignores legacy
assistant_resp*auto-save keys to prevent old model-authored summaries from being treated as facts.
Use route hints so integrations can keep stable names while model IDs evolve.
| Key | Default | Purpose |
|---|---|---|
hint |
required | Task hint name (e.g. "reasoning", "fast", "code", "summarize") |
provider |
required | Provider to route to (must match a known provider name) |
model |
required | Model to use with that provider |
api_key |
unset | Optional API key override for this route's provider |
| Key | Default | Purpose |
|---|---|---|
hint |
required | Route hint name (e.g. "semantic", "archive", "faq") |
provider |
required | Embedding provider ("none", "openai", or "custom:<url>") |
model |
required | Embedding model to use with that provider |
dimensions |
unset | Optional embedding dimension override for this route |
api_key |
unset | Optional API key override for this route's provider |
[memory]
embedding_model = "hint:semantic"
[[model_routes]]
hint = "reasoning"
provider = "openrouter"
model = "provider/model-id"
[[embedding_routes]]
hint = "semantic"
provider = "openai"
model = "text-embedding-3-small"
dimensions = 1536Upgrade strategy:
- Keep hints stable (
hint:reasoning,hint:semantic). - Update only
model = "...new-version..."in the route entries. - Validate with
zerobuild doctorbefore restart/rollout.
Natural-language config path:
- During normal agent chat, ask the assistant to rewire routes in plain language.
- The runtime can persist these updates via tool
model_routing_config(defaults, scenarios, and delegate sub-agents) without manual TOML editing.
Example requests:
Set conversation to provider kimi, model moonshot-v1-8k.Set coding to provider openai, model gpt-5.3-codex, and auto-route when message contains code blocks.Create a coder sub-agent using openai/gpt-5.3-codex with tools file_read,file_write,shell.
Automatic model hint routing — maps user messages to [[model_routes]] hints based on content patterns.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable automatic query classification |
rules |
[] |
Classification rules (evaluated in priority order) |
Each rule in rules:
| Key | Default | Purpose |
|---|---|---|
hint |
required | Must match a [[model_routes]] hint value |
keywords |
[] |
Case-insensitive substring matches |
patterns |
[] |
Case-sensitive literal matches (for code fences, keywords like "fn ") |
min_length |
unset | Only match if message length ≥ N chars |
max_length |
unset | Only match if message length ≤ N chars |
priority |
0 |
Higher priority rules are checked first |
[query_classification]
enabled = true
[[query_classification.rules]]
hint = "reasoning"
keywords = ["explain", "analyze", "why"]
min_length = 200
priority = 10
[[query_classification.rules]]
hint = "fast"
keywords = ["hi", "hello", "thanks"]
max_length = 50
priority = 5Top-level channel options are configured under channels_config.
| Key | Default | Purpose |
|---|---|---|
message_timeout_secs |
300 |
Base timeout in seconds for channel message processing; runtime scales this with tool-loop depth (up to 4x) |
Examples:
[channels_config.telegram][channels_config.discord][channels_config.whatsapp][channels_config.linq][channels_config.nextcloud_talk][channels_config.email][channels_config.nostr]
Notes:
- Default
300sis optimized for on-device LLMs (Ollama) which are slower than cloud APIs. - Runtime timeout budget is
message_timeout_secs * scale, wherescale = min(max_tool_iterations, 4)and a minimum of1. - This scaling avoids false timeouts when the first LLM turn is slow/retried but later tool-loop turns still need to complete.
- If using cloud APIs (OpenAI, Anthropic, etc.), you can reduce this to
60or lower. - Values below
30are clamped to30to avoid immediate timeout churn. - When a timeout occurs, users receive:
⚠️ Request timed out while waiting for the model. Please try again. - Telegram-only interruption behavior is controlled with
channels_config.telegram.interrupt_on_new_message(defaultfalse). When enabled, a newer message from the same sender in the same chat cancels the in-flight request and preserves interrupted user context. - While
zerobuild channel startis running, updates todefault_provider,default_model,default_temperature,api_key,api_url, andreliability.*are hot-applied fromconfig.tomlon the next inbound message.
| Key | Default | Purpose |
|---|---|---|
private_key |
required | Nostr private key (hex or nsec1… bech32); encrypted at rest when secrets.encrypt = true |
relays |
see note | List of relay WebSocket URLs; defaults to relay.damus.io, nos.lol, relay.primal.net, relay.snort.social |
allowed_pubkeys |
[] (deny all) |
Sender allowlist (hex or npub1…); use "*" to allow all senders |
Notes:
- Supports both NIP-04 (legacy encrypted DMs) and NIP-17 (gift-wrapped private messages). Replies mirror the sender's protocol automatically.
- The
private_keyis a high-value secret; keepsecrets.encrypt = true(the default) in production.
See detailed channel matrix and allowlist behavior in channels-reference.md.
WhatsApp supports two backends under one config table.
Cloud API mode (Meta webhook):
| Key | Required | Purpose |
|---|---|---|
access_token |
Yes | Meta Cloud API bearer token |
phone_number_id |
Yes | Meta phone number ID |
verify_token |
Yes | Webhook verification token |
app_secret |
Optional | Enables webhook signature verification (X-Hub-Signature-256) |
allowed_numbers |
Recommended | Allowed inbound numbers ([] = deny all, "*" = allow all) |
WhatsApp Web mode (native client):
| Key | Required | Purpose |
|---|---|---|
session_path |
Yes | Persistent SQLite session path |
pair_phone |
Optional | Pair-code flow phone number (digits only) |
pair_code |
Optional | Custom pair code (otherwise auto-generated) |
allowed_numbers |
Recommended | Allowed inbound numbers ([] = deny all, "*" = allow all) |
Notes:
- WhatsApp Web requires build flag
whatsapp-web. - If both Cloud and Web fields are present, Cloud mode wins for backward compatibility.
Linq Partner V3 API integration for iMessage, RCS, and SMS.
| Key | Required | Purpose |
|---|---|---|
api_token |
Yes | Linq Partner API bearer token |
from_phone |
Yes | Phone number to send from (E.164 format) |
signing_secret |
Optional | Webhook signing secret for HMAC-SHA256 signature verification |
allowed_senders |
Recommended | Allowed inbound phone numbers ([] = deny all, "*" = allow all) |
Notes:
- Webhook endpoint is
POST /linq. ZEROBUILD_LINQ_SIGNING_SECREToverridessigning_secretwhen set.- Signatures use
X-Webhook-SignatureandX-Webhook-Timestampheaders; stale timestamps (>300s) are rejected. - See channels-reference.md for full config examples.
Native Nextcloud Talk bot integration (webhook receive + OCS send API).
| Key | Required | Purpose |
|---|---|---|
base_url |
Yes | Nextcloud base URL (e.g. https://cloud.example.com) |
app_token |
Yes | Bot app token used for OCS bearer auth |
webhook_secret |
Optional | Enables webhook signature verification |
allowed_users |
Recommended | Allowed Nextcloud actor IDs ([] = deny all, "*" = allow all) |
Notes:
- Webhook endpoint is
POST /nextcloud-talk. ZEROBUILD_NEXTCLOUD_TALK_WEBHOOK_SECREToverrideswebhook_secretwhen set.- See nextcloud-talk-setup.md for setup and troubleshooting.
Hardware wizard configuration for physical-world access (STM32, probe, serial).
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Whether hardware access is enabled |
transport |
none |
Transport mode: "none", "native", "serial", or "probe" |
serial_port |
unset | Serial port path (e.g. "/dev/ttyACM0") |
baud_rate |
115200 |
Serial baud rate |
probe_target |
unset | Probe target chip (e.g. "STM32F401RE") |
workspace_datasheets |
false |
Enable workspace datasheet RAG (index PDF schematics for AI pin lookups) |
Notes:
- Use
transport = "serial"withserial_portfor USB-serial connections. - Use
transport = "probe"withprobe_targetfor debug-probe flashing (e.g. ST-Link). - See hardware-peripherals-design.md for protocol details.
Higher-level peripheral board configuration. Boards become agent tools when enabled.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable peripheral support (boards become agent tools) |
boards |
[] |
Board configurations |
datasheet_dir |
unset | Path to datasheet docs (relative to workspace) for RAG retrieval |
Each entry in boards:
| Key | Default | Purpose |
|---|---|---|
board |
required | Board type: "nucleo-f401re", "rpi-gpio", "esp32", etc. |
transport |
serial |
Transport: "serial", "native", "websocket" |
path |
unset | Path for serial: "/dev/ttyACM0", "/dev/ttyUSB0" |
baud |
115200 |
Baud rate for serial |
[peripherals]
enabled = true
datasheet_dir = "docs/datasheets"
[[peripherals.boards]]
board = "nucleo-f401re"
transport = "serial"
path = "/dev/ttyACM0"
baud = 115200
[[peripherals.boards]]
board = "rpi-gpio"
transport = "native"Notes:
- Place
.md/.txtdatasheet files named by board (e.g.nucleo-f401re.md,rpi-gpio.md) indatasheet_dirfor RAG retrieval. - See hardware-peripherals-design.md for board protocol and firmware notes.
- deny-by-default channel allowlists (
[]means deny all) - pairing required on gateway by default
- public bind disabled by default
After editing config:
zerobuild status
zerobuild doctor
zerobuild channel doctor
zerobuild service restart