This is a high-signal reference for common config sections and defaults.
Last verified: February 28, 2026.
Config path resolution at startup:
ZEROCLAW_WORKSPACEoverride (if set)- persisted
~/.zeroclaw/active_workspace.tomlmarker (if present) - default
~/.zeroclaw/config.toml
ZeroClaw logs the resolved config on startup at INFO level:
Config loadedwith fields:path,workspace,source,initialized
CLI commands for config inspection and modification:
zeroclaw config showβ print effective config as JSON (secrets masked)zeroclaw config get <key>β query a value by dot-path (e.g.zeroclaw config get gateway.port)zeroclaw config set <key> <value>β update a value and save toconfig.tomlzeroclaw config schemaβ print JSON Schema (draft 2020-12) to stdout
| Key | Default | Notes |
|---|---|---|
default_provider |
openrouter |
provider ID or alias |
provider_api |
unset | Optional API mode for custom:<url> providers: openai-chat-completions or openai-responses |
default_model |
anthropic/claude-sonnet-4-6 |
model routed through selected provider |
default_temperature |
0.7 |
model temperature |
model_support_vision |
unset (None) |
Vision support override for active provider/model |
Notes:
model_support_vision = trueforces vision support on (e.g. Ollama runningllava).model_support_vision = falseforces vision support off.- Unset keeps the provider's built-in default.
- Environment override:
ZEROCLAW_MODEL_SUPPORT_VISIONorMODEL_SUPPORT_VISION(values:true/false/1/0/yes/no/on/off).
Use named profiles to map a logical provider id to a provider name/base URL and optional profile-scoped credentials.
| Key | Default | Notes |
|---|---|---|
name |
unset | Optional provider id override (for example openai, openai-codex) |
base_url |
unset | Optional OpenAI-compatible endpoint URL |
wire_api |
unset | Optional protocol mode: responses or chat_completions |
model |
unset | Optional profile-scoped default model |
api_key |
unset | Optional profile-scoped API key (used when top-level api_key is empty) |
requires_openai_auth |
false |
Load OpenAI auth material (OPENAI_API_KEY / Codex auth file) |
Notes:
- If both top-level
api_keyand profileapi_keyare present, top-levelapi_keywins. - If top-level
default_modelis still the global OpenRouter default, profilemodelis used as an automatic compatibility override. - Secrets encryption applies to profile API keys when
secrets.encrypt = true.
Example:
default_provider = "sub2api"
[model_providers.sub2api]
name = "sub2api"
base_url = "https://api.example.com/v1"
wire_api = "chat_completions"
model = "qwen-max"
api_key = "sk-profile-key"| 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 |
zeroclaw |
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:
zeroclaw doctor traces --limit 20zeroclaw doctor traces --event tool_call_result --contains \"error\"zeroclaw doctor traces --id <trace-id>
Example:
[observability]
backend = "otel"
otel_endpoint = "http://localhost:4318"
otel_service_name = "zeroclaw"
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:
ZEROCLAW_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
ZEROCLAW_PROVIDERwhen you intentionally want runtime env to override a non-default configured provider. - For OpenAI-compatible Responses fallback transport:
ZEROCLAW_RESPONSES_WEBSOCKET=1forces websocket-first mode (wss://.../responses) for compatible providers.ZEROCLAW_RESPONSES_WEBSOCKET=0forces HTTP-only mode.- Unset = auto (websocket-first only when endpoint host is
api.openai.com, then HTTP fallback if websocket fails).
| Key | Default | Purpose |
|---|---|---|
compact_context |
true |
When true: bootstrap_max_chars=6000, rag_chunk_limit=2. Use for 13B or smaller models |
max_tool_iterations |
20 |
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 |
loop_detection_no_progress_threshold |
3 |
Same tool+args producing identical output this many times triggers loop detection. 0 disables |
loop_detection_ping_pong_cycles |
2 |
AβBβAβB alternating pattern cycle count threshold. 0 disables |
loop_detection_failure_streak |
3 |
Same tool consecutive failure count threshold. 0 disables |
Notes:
- Setting
max_tool_iterations = 0falls back to safe default20. - 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.- Loop detection intervenes before
max_tool_iterationsis exhausted. On first detection the agent receives a self-correction prompt; if the loop persists the agent is stopped early. Detection is result-aware: repeated calls with different outputs (genuine progress) do not trigger. Set any threshold to0to disable that detector.
| 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, ZeroClaw 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 |
~/.zeroclaw/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
zeroclaw estopto engage andzeroclaw estop resumeto clear levels.
| Key | Default | Purpose |
|---|---|---|
block_private_ip |
true |
Block local/private/link-local/multicast addresses by default |
allow_cidrs |
[] |
CIDR ranges allowed to bypass private-IP blocking (100.64.0.0/10, 198.18.0.0/15) |
allow_domains |
[] |
Domain patterns that bypass private-IP blocking before DNS checks (internal.example, *.svc.local) |
allow_loopback |
false |
Permit loopback targets (localhost, 127.0.0.1, ::1) |
require_first_visit_approval |
false |
Require explicit human confirmation before first-time access to unseen domains |
enforce_domain_allowlist |
false |
Require all URL targets to match domain_allowlist (in addition to tool-level allowlists) |
domain_allowlist |
[] |
Global trusted domain allowlist shared across URL tools |
domain_blocklist |
[] |
Global domain denylist shared across URL tools (highest priority) |
approved_domains |
[] |
Persisted first-visit approvals granted by a human operator |
Notes:
- This policy is shared by
browser_open,http_request, andweb_fetch. browserautomation (action = "open") also follows this policy.- Tool-level allowlists still apply.
allow_domains/allow_cidrsonly override private/local blocking. domain_blocklistis evaluated before allowlists; blocked hosts are always denied.- With
require_first_visit_approval = true, unseen domains are denied until added toapproved_domains(or matched bydomain_allowlist). - DNS rebinding protection remains enabled: resolved local/private IPs are denied unless explicitly allowlisted.
- Agents can inspect/update these settings at runtime via
web_access_config(action=get|set|check_url). - In supervised mode,
web_access_configmutations still require normal tool approval unless explicitly auto-approved.
Example:
[security.url_access]
block_private_ip = true
allow_cidrs = ["100.64.0.0/10", "198.18.0.0/15"]
allow_domains = ["internal.example", "*.svc.local"]
allow_loopback = false
require_first_visit_approval = true
enforce_domain_allowlist = false
domain_allowlist = ["docs.rs", "github.com", "*.rust-lang.org"]
domain_blocklist = ["*.malware.test"]
approved_domains = ["example.com"]Runtime workflow (web_access_config):
- Start strict-first mode (deny unknown domains until reviewed):
{
"action": "set",
"require_first_visit_approval": true,
"enforce_domain_allowlist": false
}- Dry-run a target URL before access:
{ "action": "check_url", "url": "https://docs.rs" }- After human confirmation, persist approval for future runs:
{ "action": "set", "add_approved_domains": ["docs.rs"] }- Escalate to strict allowlist-only mode (recommended for production agents):
{
"action": "set",
"enforce_domain_allowlist": true,
"domain_allowlist": ["docs.rs", "github.com", "*.rust-lang.org"]
}- Emergency deny of a domain across all URL tools:
{ "action": "set", "add_domain_blocklist": ["*.malware.test"] }Operational guidance:
- Use
approved_domainsfor iterative onboarding and temporary approvals. - Use
domain_allowlistfor stable long-term trusted domains. - Use
domain_blocklistfor immediate global deny; it always overrides allow rules. - Keep
allow_domainsfocused on private-network bypass cases only (internal.example,*.svc.local).
Environment overrides:
ZEROCLAW_URL_ACCESS_BLOCK_PRIVATE_IP/URL_ACCESS_BLOCK_PRIVATE_IPZEROCLAW_URL_ACCESS_ALLOW_LOOPBACK/URL_ACCESS_ALLOW_LOOPBACKZEROCLAW_URL_ACCESS_REQUIRE_FIRST_VISIT_APPROVAL/URL_ACCESS_REQUIRE_FIRST_VISIT_APPROVALZEROCLAW_URL_ACCESS_ENFORCE_DOMAIN_ALLOWLIST/URL_ACCESS_ENFORCE_DOMAIN_ALLOWLISTZEROCLAW_URL_ACCESS_ALLOW_CIDRS/URL_ACCESS_ALLOW_CIDRS(comma-separated)ZEROCLAW_URL_ACCESS_ALLOW_DOMAINS/URL_ACCESS_ALLOW_DOMAINS(comma-separated)ZEROCLAW_URL_ACCESS_DOMAIN_ALLOWLIST/URL_ACCESS_DOMAIN_ALLOWLIST(comma-separated)ZEROCLAW_URL_ACCESS_DOMAIN_BLOCKLIST/URL_ACCESS_DOMAIN_BLOCKLIST(comma-separated)ZEROCLAW_URL_ACCESS_APPROVED_DOMAINS/URL_ACCESS_APPROVED_DOMAINS(comma-separated)
| Key | Default | Purpose |
|---|---|---|
enabled |
true |
Enable syscall anomaly detection over command output telemetry |
strict_mode |
false |
Emit anomaly when denied syscalls are observed even if in baseline |
alert_on_unknown_syscall |
true |
Alert on syscall names not present in baseline |
max_denied_events_per_minute |
5 |
Threshold for denied-syscall spike alerts |
max_total_events_per_minute |
120 |
Threshold for total syscall-event spike alerts |
max_alerts_per_minute |
30 |
Global alert budget guardrail per rolling minute |
alert_cooldown_secs |
20 |
Cooldown between identical anomaly alerts |
log_path |
syscall-anomalies.log |
JSONL anomaly log path |
baseline_syscalls |
built-in allowlist | Expected syscall profile; unknown entries trigger alerts |
Notes:
- Detection consumes seccomp/audit hints from command
stdout/stderr. - Numeric syscall IDs in Linux audit lines are mapped to common x86_64 names when available.
- Alert budget and cooldown reduce duplicate/noisy events during repeated retries.
max_denied_events_per_minutemust be less than or equal tomax_total_events_per_minute.
Example:
[security.syscall_anomaly]
enabled = true
strict_mode = false
alert_on_unknown_syscall = true
max_denied_events_per_minute = 5
max_total_events_per_minute = 120
max_alerts_per_minute = 30
alert_cooldown_secs = 20
log_path = "syscall-anomalies.log"
baseline_syscalls = ["read", "write", "openat", "close", "execve", "futex"]Lightweight, opt-in adversarial suffix filter that runs before provider calls in channel and gateway message pipelines.
| Key | Default | Purpose |
|---|---|---|
enable_perplexity_filter |
false |
Enable pre-LLM statistical suffix anomaly blocking |
perplexity_threshold |
18.0 |
Character-class bigram perplexity threshold |
suffix_window_chars |
64 |
Trailing character window used for anomaly scoring |
min_prompt_chars |
32 |
Minimum prompt length before filter is evaluated |
symbol_ratio_threshold |
0.20 |
Minimum punctuation ratio in suffix window for blocking |
Notes:
- This filter is disabled by default to preserve baseline latency/behavior.
- The detector combines character-class perplexity with GCG-like token heuristics.
- Inputs are blocked only when anomaly conditions are met; normal natural-language prompts pass.
- Typical per-message overhead is designed to stay under
50msin debug-safe local tests and substantially lower in release builds.
Example:
[security.perplexity_filter]
enable_perplexity_filter = true
perplexity_threshold = 16.5
suffix_window_chars = 72
min_prompt_chars = 40
symbol_ratio_threshold = 0.25Controls outbound credential leak handling for channel replies after tool-output sanitization.
| Key | Default | Purpose |
|---|---|---|
enabled |
true |
Enable outbound credential leak scanning on channel responses |
action |
redact |
Leak handling mode: redact (mask and deliver) or block (do not deliver original content) |
sensitivity |
0.7 |
Leak detector sensitivity (0.0 to 1.0, higher is more aggressive) |
Notes:
- Detection uses the same leak detector used by existing redaction guardrails (API keys, JWTs, private keys, high-entropy tokens, etc.).
action = "redact"preserves current behavior (safe-by-default compatibility).action = "block"is stricter and returns a safe fallback message instead of potentially sensitive content.- When this guard is enabled,
/v1/chat/completionsstreaming responses are safety-buffered and emitted after sanitization to avoid leaking raw token deltas before final scan.
Example:
[security.outbound_leak_guard]
enabled = true
action = "block"
sensitivity = 0.9Delegate 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.2Research phase allows the agent to gather information through tools before generating the main response.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable research phase |
trigger |
never |
Research trigger strategy: never, always, keywords, length, question |
keywords |
["find", "search", "check", "investigate"] |
Keywords that trigger research (when trigger = keywords) |
min_message_length |
50 |
Minimum message length to trigger research (when trigger = length) |
max_iterations |
5 |
Maximum tool calls during research phase |
show_progress |
true |
Show research progress to user |
Notes:
- Research phase is disabled by default (
trigger = never). - When enabled, the agent first gathers facts through tools (grep, file_read, shell, memory search), then responds using the collected context.
- Research runs before the main agent turn and does not count toward
agent.max_tool_iterations. - Trigger strategies:
neverβ research disabled (default)alwaysβ research on every user messagekeywordsβ research when message contains any keyword from the listlengthβ research when message length exceedsmin_message_lengthquestionβ research when message contains '?'
Example:
[research]
enabled = true
trigger = "keywords"
keywords = ["find", "show", "check", "how many"]
max_iterations = 3
show_progress = trueThe agent will research the codebase before responding to queries like:
- "Find all TODO in src/"
- "Show contents of main.rs"
- "How many files in the project?"
| Key | Default | Purpose |
|---|---|---|
kind |
native |
Runtime backend: native, docker, or wasm |
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.
- Deprecated compatibility alias:
runtime.reasoning_levelis still accepted but should be migrated toprovider.reasoning_level. runtime.kind = "wasm"enables capability-bounded module execution and disables shell/process style execution.
| Key | Default | Purpose |
|---|---|---|
tools_dir |
"tools/wasm" |
Workspace-relative directory containing .wasm modules |
fuel_limit |
1000000 |
Instruction budget per module invocation |
memory_limit_mb |
64 |
Per-module memory cap (MB) |
max_module_size_mb |
50 |
Maximum allowed .wasm file size (MB) |
allow_workspace_read |
false |
Allow WASM host calls to read workspace files (future-facing) |
allow_workspace_write |
false |
Allow WASM host calls to write workspace files (future-facing) |
allowed_hosts |
[] |
Explicit network host allowlist for WASM host calls (future-facing) |
Notes:
allowed_hostsentries must be normalizedhostorhost:portstrings; wildcards, schemes, and paths are rejected whenruntime.wasm.security.strict_host_validation = true.- Invocation-time capability overrides are controlled by
runtime.wasm.security.capability_escalation_mode:deny(default): reject escalation above runtime baseline.clamp: reduce requested capabilities to baseline.
| Key | Default | Purpose |
|---|---|---|
require_workspace_relative_tools_dir |
true |
Require runtime.wasm.tools_dir to be workspace-relative and reject .. traversal |
reject_symlink_modules |
true |
Block symlinked .wasm module files during execution |
reject_symlink_tools_dir |
true |
Block execution when runtime.wasm.tools_dir is itself a symlink |
strict_host_validation |
true |
Fail config/invocation on invalid host entries instead of dropping them |
capability_escalation_mode |
"deny" |
Escalation policy: deny or clamp |
module_hash_policy |
"warn" |
Module integrity policy: disabled, warn, or enforce |
module_sha256 |
{} |
Optional map of module names to pinned SHA-256 digests |
Notes:
module_sha256keys must match module names (without.wasm) and use[A-Za-z0-9_-]only.module_sha256values must be 64-character hexadecimal SHA-256 strings.module_hash_policy = "warn"allows execution but logs missing/mismatched digests.module_hash_policy = "enforce"blocks execution on missing/mismatched digests and requires at least one pin.
WASM profile templates:
dev/config.wasm.dev.tomldev/config.wasm.staging.tomldev/config.wasm.prod.toml
| Key | Default | Purpose |
|---|---|---|
reasoning_level |
unset (None) |
Reasoning effort/level override for providers that support explicit levels (currently OpenAI Codex /responses) |
transport |
unset (None) |
Provider transport override (auto, websocket, sse) |
Notes:
- Supported values:
minimal,low,medium,high,xhigh(case-insensitive). - When set, overrides
ZEROCLAW_CODEX_REASONING_EFFORTfor OpenAI Codex requests. - Unset falls back to
ZEROCLAW_CODEX_REASONING_EFFORTif present, otherwise defaults toxhigh. - If both
provider.reasoning_leveland deprecatedruntime.reasoning_levelare set, provider-level value wins. provider.transportis normalized case-insensitively (wsaliases towebsocket;httpaliases tosse).- For OpenAI Codex, default transport mode is
auto(WebSocket-first with SSE fallback). - Transport override precedence for OpenAI Codex:
[[model_routes]].transport(route-specific)PROVIDER_TRANSPORT/ZEROCLAW_PROVIDER_TRANSPORT/ZEROCLAW_CODEX_TRANSPORTprovider.transport- legacy
ZEROCLAW_RESPONSES_WEBSOCKET(boolean)
- Environment overrides replace configured
provider.transportwhen set.
| 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) |
trusted_skill_roots |
[] |
Allowlist of directory roots for symlink targets in workspace/skills/* |
prompt_injection_mode |
full |
Skill prompt verbosity: full (inline instructions/tools) or compact (name/description/location only) |
clawhub_token |
unset | Optional Bearer token for authenticated ClawHub skill downloads |
Notes:
- Security-first default: ZeroClaw does not clone or sync
open-skillsunlessopen_skills_enabled = true. - Environment overrides:
ZEROCLAW_OPEN_SKILLS_ENABLEDaccepts1/0,true/false,yes/no,on/off.ZEROCLAW_OPEN_SKILLS_DIRoverrides the repository path when non-empty.ZEROCLAW_SKILLS_PROMPT_MODEacceptsfullorcompact.
- Precedence for enable flag:
ZEROCLAW_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.- Symlinked workspace skills are blocked by default. Set
trusted_skill_rootsto allow local shared-skill directories after explicit trust review. zeroclaw skills installandzeroclaw skills auditapply a static security audit. Skills that contain script-like files, high-risk shell payload snippets, or unsafe markdown link traversal are rejected.clawhub_tokenis sent asAuthorization: Bearer <token>when downloading from ClawHub. Obtain a token from https://clawhub.ai after signing in. Required if the API returns 429 (rate-limited) or 401 (unauthorized) for anonymous requests.
ClawHub token example:
[skills]
clawhub_token = "your-token-here"| 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. - ZeroClaw 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 tools (browser_open and browser) |
allowed_domains |
[] |
Allowed domains for browser_open and browser (exact/subdomain match, or "*" for all public domains) |
browser_open |
default |
Browser used by browser_open: disable, brave, chrome, firefox, edge (msedge alias), default |
session_name |
unset | Browser session name (for agent-browser automation) |
backend |
agent_browser |
Browser automation backend: "agent_browser", "rust_native", "computer_use", or "auto" |
auto_backend_priority |
[] |
Priority order for backend = "auto" (for example ["agent_browser","rust_native","computer_use"]) |
agent_browser_command |
agent-browser |
Executable/path for agent-browser CLI |
agent_browser_extra_args |
[] |
Extra args prepended to each agent-browser command |
agent_browser_timeout_ms |
30000 |
Timeout per agent-browser action command |
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:
browser_openis a simple URL opener;browseris full browser automation (open/click/type/scroll/screenshot).- 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 |
user_agent |
ZeroClaw/1.0 |
User-Agent header for outbound HTTP requests |
credential_profiles |
{} |
Optional named env-backed auth profiles used by tool arg credential_profile |
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. - Shell
curl/wgetare classified as high-risk and may be blocked by autonomy policy. Preferhttp_requestfor direct HTTP calls. credential_profileslets the harness inject auth headers from environment variables, so agents can call authenticated APIs without raw tokens in tool arguments.
Example:
[http_request]
enabled = true
allowed_domains = ["api.github.com", "api.linear.app"]
[http_request.credential_profiles.github]
header_name = "Authorization"
env_var = "GITHUB_TOKEN"
value_prefix = "Bearer "
[http_request.credential_profiles.linear]
header_name = "Authorization"
env_var = "LINEAR_API_KEY"
value_prefix = ""Then call http_request with:
{
"url": "https://api.github.com/user",
"credential_profile": "github"
}When using credential_profile, do not also set the same header key in args.headers (case-insensitive), or the request will be rejected as a header conflict.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable web_fetch for page-to-text extraction |
provider |
fast_html2md |
Fetch/render backend: fast_html2md, nanohtml2text, firecrawl, tavily |
api_key |
unset | API key for provider backends that require it (e.g. firecrawl, tavily) |
api_url |
unset | Optional API URL override (self-hosted/alternate endpoint) |
allowed_domains |
["*"] |
Domain allowlist ("*" allows all public domains) |
blocked_domains |
[] |
Denylist applied before allowlist |
max_response_size |
500000 |
Maximum returned payload size in bytes |
timeout_secs |
30 |
Request timeout in seconds |
user_agent |
ZeroClaw/1.0 |
User-Agent header for fetch requests |
Notes:
web_fetchis optimized for summarization/data extraction from web pages.- Redirect targets are revalidated against allow/deny domain policy.
- Local/private network targets remain blocked even when
allowed_domains = ["*"].
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable web_search_tool |
provider |
duckduckgo |
Search backend: duckduckgo (ddg alias), brave, firecrawl, tavily, perplexity, exa, jina |
fallback_providers |
[] |
Fallback providers tried in order after primary failure |
retries_per_provider |
0 |
Retry count before switching to next provider |
retry_backoff_ms |
250 |
Delay between retry attempts (milliseconds) |
api_key |
unset | Generic provider key (used by firecrawl/tavily, fallback for dedicated provider keys) |
api_url |
unset | Optional API URL override |
brave_api_key |
unset | Dedicated Brave key (required for provider = "brave" unless api_key is set) |
perplexity_api_key |
unset | Dedicated Perplexity key |
exa_api_key |
unset | Dedicated Exa key |
jina_api_key |
unset | Optional Jina key |
domain_filter |
[] |
Optional domain filter forwarded to supported providers |
language_filter |
[] |
Optional language filter forwarded to supported providers |
country |
unset | Optional country hint for supported providers |
recency_filter |
unset | Optional recency filter for supported providers |
max_tokens |
unset | Optional token budget for providers that support it (for example Perplexity) |
max_tokens_per_page |
unset | Optional per-page token budget for supported providers |
exa_search_type |
auto |
Exa search mode: auto, keyword, neural |
exa_include_text |
false |
Include text payloads in Exa responses |
jina_site_filters |
[] |
Optional site filters for Jina search |
max_results |
5 |
Maximum search results returned (must be 1-10) |
timeout_secs |
15 |
Request timeout in seconds |
user_agent |
ZeroClaw/1.0 |
User-Agent header for search requests |
Notes:
- If DuckDuckGo returns
403/429in your network, switch provider tobrave,perplexity,exa, or configurefallback_providers. web_searchfinds candidate URLs; pair it withweb_fetchfor page content extraction.- Agents can modify these settings at runtime via the
web_search_configtool (action=get|set|list_providers). - In supervised mode,
web_search_configmutations still require normal tool approval unless explicitly auto-approved. - Invalid provider names,
exa_search_type, and out-of-range retry/result/timeout values are rejected during config validation.
Recommended resilient profile:
[web_search]
enabled = true
provider = "perplexity"
fallback_providers = ["exa", "jina", "duckduckgo"]
retries_per_provider = 1
retry_backoff_ms = 300
max_results = 5
timeout_secs = 20Runtime workflow (web_search_config):
- Inspect available providers and current config snapshot:
{ "action": "list_providers" }{ "action": "get" }- Set a primary provider with fallback chain:
{
"action": "set",
"provider": "perplexity",
"fallback_providers": ["exa", "jina", "duckduckgo"]
}- Tune provider-specific options:
{ "action": "set", "exa_search_type": "neural", "exa_include_text": true }{ "action": "set", "jina_site_filters": ["docs.rs", "github.com"] }- Add geo/language/recency filters for region-aware queries:
{
"action": "set",
"country": "US",
"language_filter": ["en"],
"recency_filter": "week"
}Environment overrides:
ZEROCLAW_WEB_SEARCH_ENABLED/WEB_SEARCH_ENABLEDZEROCLAW_WEB_SEARCH_PROVIDER/WEB_SEARCH_PROVIDERZEROCLAW_WEB_SEARCH_MAX_RESULTS/WEB_SEARCH_MAX_RESULTSZEROCLAW_WEB_SEARCH_TIMEOUT_SECS/WEB_SEARCH_TIMEOUT_SECSZEROCLAW_WEB_SEARCH_FALLBACK_PROVIDERS/WEB_SEARCH_FALLBACK_PROVIDERS(comma-separated)ZEROCLAW_WEB_SEARCH_RETRIES_PER_PROVIDER/WEB_SEARCH_RETRIES_PER_PROVIDERZEROCLAW_WEB_SEARCH_RETRY_BACKOFF_MS/WEB_SEARCH_RETRY_BACKOFF_MSZEROCLAW_WEB_SEARCH_DOMAIN_FILTER/WEB_SEARCH_DOMAIN_FILTER(comma-separated)ZEROCLAW_WEB_SEARCH_LANGUAGE_FILTER/WEB_SEARCH_LANGUAGE_FILTER(comma-separated)ZEROCLAW_WEB_SEARCH_COUNTRY/WEB_SEARCH_COUNTRYZEROCLAW_WEB_SEARCH_RECENCY_FILTER/WEB_SEARCH_RECENCY_FILTERZEROCLAW_WEB_SEARCH_MAX_TOKENS/WEB_SEARCH_MAX_TOKENSZEROCLAW_WEB_SEARCH_MAX_TOKENS_PER_PAGE/WEB_SEARCH_MAX_TOKENS_PER_PAGEZEROCLAW_WEB_SEARCH_EXA_SEARCH_TYPE/WEB_SEARCH_EXA_SEARCH_TYPEZEROCLAW_WEB_SEARCH_EXA_INCLUDE_TEXT/WEB_SEARCH_EXA_INCLUDE_TEXTZEROCLAW_WEB_SEARCH_JINA_SITE_FILTERS/WEB_SEARCH_JINA_SITE_FILTERS(comma-separated)ZEROCLAW_BRAVE_API_KEY/BRAVE_API_KEYZEROCLAW_PERPLEXITY_API_KEY/PERPLEXITY_API_KEYZEROCLAW_EXA_API_KEY/EXA_API_KEYZEROCLAW_JINA_API_KEY/JINA_API_KEY
| 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 |
|---|---|---|
enabled |
false |
enable node-control scaffold endpoint (POST /api/node-control) |
auth_token |
null |
optional extra shared token checked via X-Node-Control-Token |
allowed_node_ids |
[] |
allowlist for node.describe/node.invoke ([] accepts any) |
| 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, explicit executable paths, or "*" |
command_context_rules |
[] |
per-command context-aware allow/deny rules (domain/path constraints, optional high-risk override) |
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 |
allow_sensitive_file_reads |
false |
allow file_read on sensitive files/dirs (for example .env, .aws/credentials, private keys) |
allow_sensitive_file_writes |
false |
allow file_write/file_edit on sensitive files/dirs (for example .env, .aws/credentials, private keys) |
auto_approve |
[] |
tool operations always auto-approved |
always_ask |
[] |
tool operations that always require approval |
non_cli_excluded_tools |
built-in denylist (includes shell, process, file_write, ...) |
tools hidden from non-CLI channel tool specs |
non_cli_approval_approvers |
[] |
optional allowlist for who can run non-CLI approval-management commands |
non_cli_natural_language_approval_mode |
direct |
natural-language behavior for approval-management commands (direct, request_confirm, disabled) |
non_cli_natural_language_approval_mode_by_channel |
{} |
per-channel override map for natural-language approval mode |
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.allowed_commandsentries can be command names (for example,"git"), explicit executable paths (for example,"/usr/bin/antigravity"), or"*"to allow any command name/path (risk gates still apply).command_context_rulescan narrow or overrideallowed_commandsfor matching commands:action = "allow"rules are restrictive when present for a command: at least one allow rule must match.action = "deny"rules explicitly block matching contexts.allow_high_risk = trueallows a matching high-risk command to pass the hard block, but supervised mode still requiresapproved=true.
file_readblocks sensitive secret-bearing files/directories by default. Setallow_sensitive_file_reads = trueonly for controlled debugging sessions.file_writeandfile_editblock sensitive secret-bearing files/directories by default. Setallow_sensitive_file_writes = trueonly for controlled break-glass sessions.file_read,file_write, andfile_editrefuse multiply-linked files (hard-link guard) to reduce workspace path bypass risk via hard-link escapes.- 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). - In supervised mode on non-CLI channels, operators can persist human-approved tools with:
- One-step flow:
/approve <tool>. - Two-step flow:
/approve-request <tool>then/approve-confirm <request-id>(same sender + same chat/channel). Both paths write toautonomy.auto_approveand remove the tool fromautonomy.always_ask.
- One-step flow:
- For pending runtime execution prompts (including Telegram inline approval buttons), use:
/approve-allow <request-id>to approve only the current pending request./approve-deny <request-id>to reject the current pending request. This path does not modifyautonomy.auto_approveorautonomy.always_ask.
non_cli_natural_language_approval_modecontrols how strict natural-language approval intents are:direct(default): natural-language approval grants immediately (private-chat friendly).request_confirm: natural-language approval creates a pending request that needs explicit confirm.disabled: natural-language approval commands are rejected; use slash commands only.
non_cli_natural_language_approval_mode_by_channelcan override that mode for specific channels (keys are channel names liketelegram,discord,slack).- Example: keep global
direct, but forcediscord = "request_confirm"for team chats.
- Example: keep global
non_cli_approval_approverscan restrict who is allowed to run approval commands (/approve*,/unapprove,/approvals):*allows all channel-admitted senders.aliceallows senderaliceon any channel.telegram:aliceallows only that channel+sender pair.telegram:*allows any sender on Telegram.*:aliceallowsaliceon any channel.
- By default,
processis excluded on non-CLI channels alongsideshell. To opt in intentionally, remove"process"from[autonomy].non_cli_excluded_toolsinconfig.toml. - Use
/unapprove <tool>to remove persisted approval fromautonomy.auto_approve. /approve-pendinglists pending requests for the current sender+chat/channel scope.- If a tool remains unavailable after approval, check
autonomy.non_cli_excluded_tools(runtime/approvalsshows this list). Channel runtime reloads this list fromconfig.tomlautomatically.
[autonomy]
workspace_only = false
forbidden_paths = ["/etc", "/root", "/proc", "/sys", "~/.ssh", "~/.gnupg", "~/.aws"]
allowed_roots = ["~/Desktop/projects", "/opt/shared-repo"]
[[autonomy.command_context_rules]]
command = "curl"
action = "allow"
allowed_domains = ["api.github.com", "*.example.internal"]
allow_high_risk = true
[[autonomy.command_context_rules]]
command = "rm"
action = "allow"
allowed_path_prefixes = ["/tmp"]
allow_high_risk = true| 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. - Observation memory is available via tool
memory_observe, which stores entries under categoryobservationby default (override withcategorywhen needed).
Example (tool-call payload):
{
"observation": "User asks for brief release notes when CI is green.",
"source": "chat",
"confidence": 0.9
}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 |
max_tokens |
unset | Optional per-route output token cap forwarded to provider APIs |
api_key |
unset | Optional API key override for this route's provider |
transport |
unset | Optional per-route transport override (auto, websocket, sse) |
| 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"
max_tokens = 8192
[[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
zeroclaw 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. - Telegram/Discord/Slack/Mattermost/Lark/Feishu support
[channels_config.<channel>.group_reply]:mode = "all_messages"ormode = "mention_only"allowed_sender_ids = ["..."]to bypass mention gating in groupsallowed_usersallowlist checks still run first
- Telegram/Discord/Lark/Feishu ACK emoji reactions are configurable under
[channels_config.ack_reaction.<channel>]with switchable enable state, custom emoji pools, and conditional rules. - Legacy
mention_onlyflags (Telegram/Discord/Mattermost/Lark) remain supported as fallback only. Ifgroup_reply.modeis set, it takes precedence over legacymention_only. - While
zeroclaw channel startis running, updates todefault_provider,default_model,default_temperature,api_key,api_url, andreliability.*are hot-applied fromconfig.tomlon the next inbound message.
Per-channel ACK reaction policy (<channel>: telegram, discord, lark, feishu).
| Key | Default | Purpose |
|---|---|---|
enabled |
true |
Master switch for ACK reactions on this channel |
strategy |
random |
Pool selection strategy: random or first |
sample_rate |
1.0 |
Probabilistic gate in [0.0, 1.0] for channel fallback ACKs |
emojis |
[] |
Channel-level custom fallback pool (uses built-in pool when empty) |
rules |
[] |
Ordered conditional rules; first matching rule can react or suppress |
Rule object fields ([[channels_config.ack_reaction.<channel>.rules]]):
| Key | Default | Purpose |
|---|---|---|
enabled |
true |
Enable/disable this single rule |
contains_any |
[] |
Match when message contains any keyword (case-insensitive) |
contains_all |
[] |
Match when message contains all keywords (case-insensitive) |
contains_none |
[] |
Match only when message contains none of these keywords |
regex_any |
[] |
Match when any regex pattern matches |
regex_all |
[] |
Match only when all regex patterns match |
regex_none |
[] |
Match only when none of these regex patterns match |
sender_ids |
[] |
Match only these sender IDs ("*" matches all) |
chat_ids |
[] |
Match only these chat/channel IDs ("*" matches all) |
chat_types |
[] |
Restrict to group and/or direct |
locale_any |
[] |
Restrict by locale tag (prefix supported, e.g. zh) |
action |
react |
react to emit ACK, suppress to force no ACK when matched |
sample_rate |
unset | Optional rule-level gate in [0.0, 1.0] (overrides channel sample_rate) |
strategy |
unset | Optional per-rule strategy override |
emojis |
[] |
Emoji pool used when this rule matches |
Example:
[channels_config.ack_reaction.telegram]
enabled = true
strategy = "random"
sample_rate = 1.0
emojis = ["β
", "π", "π₯"]
[[channels_config.ack_reaction.telegram.rules]]
contains_any = ["deploy", "release"]
contains_none = ["dry-run"]
regex_none = ["panic|fatal"]
chat_ids = ["-100200300"]
chat_types = ["group"]
strategy = "first"
sample_rate = 0.9
emojis = ["π"]
[[channels_config.ack_reaction.telegram.rules]]
contains_any = ["error", "failed"]
action = "suppress"
sample_rate = 1.0| 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. ZEROCLAW_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.
BlueBubbles iMessage bridge β webhook receive + REST API send.
| Key | Required | Purpose |
|---|---|---|
server_url |
Yes | BlueBubbles server URL (e.g. http://192.168.1.100:1234) |
password |
Yes | BlueBubbles server password |
allowed_senders |
Optional | Sender allowlist β phone numbers or Apple IDs ([] = allow all when dm_policy = "open") |
ignore_senders |
Optional | Sender handles to silently drop (suppresses echoed outbound) |
webhook_secret |
Optional | Enables Authorization: Bearer <secret> verification on inbound webhooks |
dm_policy |
Optional | "open" (default) | "allowlist" | "disabled" β gates direct messages |
group_policy |
Optional | "open" (default) | "allowlist" | "disabled" β gates group chats |
group_allow_from |
Optional | Chat GUIDs allowed when group_policy = "allowlist". Use ["*"] for all groups |
send_read_receipts |
Optional | Post read receipt after each processed message. Default: true |
Notes:
- Webhook endpoint is
POST /bluebubbles. - Group chat GUIDs contain
;+;; DM GUIDs contain;-;. - See channels-reference.md Β§4.20 for policy behaviour table.
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. ZEROCLAW_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.
Inter-process communication for independent ZeroClaw agents on the same host.
| Key | Default | Purpose |
|---|---|---|
enabled |
false |
Enable IPC tools (agents_list, agents_send, agents_inbox, state_get, state_set) |
db_path |
~/.zeroclaw/agents.db |
Shared SQLite database path (all agents on this host share one file) |
staleness_secs |
300 |
Agents not seen within this window are considered offline (seconds) |
Notes:
- When
enabled = false(default), no IPC tools are registered and no database is created. - All agents that share a
db_pathcan discover each other and exchange messages. - Agent identity is derived from
workspace_dir(SHA-256 hash), not user-supplied.
Example:
[agents_ipc]
enabled = true
db_path = "~/.zeroclaw/agents.db"
staleness_secs = 300- deny-by-default channel allowlists (
[]means deny all) - pairing required on gateway by default
- public bind disabled by default
After editing config:
zeroclaw status
zeroclaw doctor
zeroclaw channel doctor
zeroclaw service restart