Almost everything about a ZeroClaw agent is decided in one file. The runtime's trait-based architecture means the model, the memory backend, the channels and the security boundaries are all selected rather than coded โ and config.toml is where that selection happens.
This guide walks through each section, then gives complete working examples you can adapt.
Configuration keys move between versions in a project this young. Run
zeroclaw doctorafter any edit โ it validates the file and tells you which key it did not understand, which is faster than guessing.
Where the file lives
Configuration sits in ZeroClaw's config directory, created by zeroclaw onboard. On Linux and macOS this is typically under ~/.config/zeroclaw/; on Windows, under your user profile's application data directory. To find it on your system:
zeroclaw status
The same directory holds the encryption key file used for secrets. Keep the whole directory out of version control, and set restrictive permissions on it:
chmod 700 ~/.config/zeroclaw
TOML in sixty seconds
If you have not used TOML: [section] headers group keys, key = "value" assigns strings, arrays use square brackets, and [section.subsection] nests. Strings need quotes; numbers and booleans do not. A # starts a comment.
[provider]
name = "openrouter"
temperature = 0.7
stream = true
models = ["primary", "fallback"]
The single most common mistake is an unquoted string. model = llama3 is a syntax error; model = "llama3" is what you meant.
The [provider] section
Where the thinking happens. ZeroClaw speaks the OpenAI-compatible API shape, so this section works the same whether you are pointing at a commercial API or a local server.
[provider]
name = "openrouter"
model = "anthropic/claude-sonnet-4"
api_key_env = "OPENROUTER_API_KEY"
base_url = "https://openrouter.ai/api/v1"
temperature = 0.7
max_tokens = 4096
nameโ the provider implementation to use.modelโ the model identifier, in whatever form your provider expects. Provider-specific: OpenRouter wantsvendor/model, Ollama wants a local tag such asqwen3:4b.base_urlโ the API endpoint. Changing this alone is usually enough to redirect to a different OpenAI-compatible service.api_key_envโ the environment variable holding the key. Prefer this to inlining a key, and preferzeroclaw onboardto both, since onboarding encrypts the credential at rest.temperatureโ sampling randomness. For agents that call tools, keep this low. Creative variance is a liability when the output is a command rather than prose.max_tokensโ response ceiling. Also a cost ceiling, which is the more useful way to think about it.
Pointing at a local Ollama server instead:
[provider]
name = "ollama"
model = "qwen3:4b"
base_url = "http://localhost:11434/v1"
temperature = 0.3
No API key, no per-token cost, no outbound network requirement.
The [memory] section
What the agent knows between sessions. ZeroClaw's memory engine has no external dependency โ there is no vector database to operate alongside it.
[memory]
backend = "sqlite"
path = "/home/agent/workspace/.memory/agent.db"
auto_recall = true
vector_weight = 0.7
keyword_weight = 0.3
max_results = 10
backendโsqlitefor structured storage and the best retrieval performance,markdownfor human-readable files you can inspect and edit by hand, or an ephemeral mode that forgets everything on exit.auto_recallโ when true, relevant context is retrieved and injected automatically rather than being manually assembled into each prompt. This is the feature that makes memory useful rather than decorative.vector_weight/keyword_weightโ the hybrid search balance. Vector similarity finds conceptually related memories; keyword matching finds exact terms. The 0.7/0.3 default favours meaning over literal matching. Raise the keyword weight if your agent deals in precise identifiers โ ticket numbers, SKUs, filenames โ where exact matching matters more than semantic similarity.max_resultsโ how many memories to pull into context. Higher means better recall and larger, slower, more expensive prompts.
The markdown backend is worth considering while you are still developing. Being able to open a file and read exactly what your agent remembers is a debugging advantage that outweighs the retrieval performance difference at small scale.
The [security] section
The section to get right before the agent does anything real.
[security]
workspace = "/home/agent/workspace"
allowed_commands = ["git", "ls", "cat"]
require_pairing = true
autonomy_level = "supervised"
workspaceโ the only directory the agent may touch. Everything outside is inaccessible.allowed_commandsโ deny-by-default shell execution. Only these executables can run.require_pairingโ new channel connections must present a pairing code.autonomy_levelโ supervised modes require approval before actions execute.
Two warnings that deserve repeating. Allowlisting git effectively grants code execution, because git hooks run arbitrary commands โ the same is true of cargo, npm and make. And the workspace path should be a directory created for the agent, never your home directory. Our security guide covers the reasoning and the failure modes in full.
The [channels] section
How humans reach the agent. Each channel is configured independently and several can run at once.
[channels.telegram]
enabled = true
token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = ["123456789"]
[channels.web]
enabled = true
bind = "127.0.0.1"
port = 3000
[channels.whatsapp]
enabled = false
allowed_users is a second gate alongside pairing โ even a paired connection is rejected if the account is not on the list. For a personal agent, listing your own user ID and nothing else is the correct configuration.
Note the bind address on the web channel. 127.0.0.1 accepts local connections only. Changing it to 0.0.0.0 exposes the dashboard to your entire network, and that is a deliberate decision rather than a default โ see the web UI and gateway guide before making it.
The [tools] section
What the agent can do beyond producing text.
[tools]
enabled = ["file_read", "file_write", "shell", "http"]
timeout_seconds = 30
[tools.http]
allowed_domains = ["api.github.com"]
Enable only what a task genuinely requires. http is the one to think hardest about: an agent with unrestricted network access can send the contents of its workspace anywhere, which undoes much of what workspace scoping bought you. allowed_domains narrows that considerably.
timeout_seconds bounds how long a single tool call may run โ the control that prevents one wedged command from hanging the agent indefinitely.
The [identity] section
The agent's persona and behavioural definition.
[identity]
format = "aieos"
path = "/home/agent/workspace/identity.json"
ZeroClaw supports AIEOS, a portable JSON specification describing traits, psychology, linguistics and motivations, and it also reads OpenClaw's Markdown identity files (IDENTITY.md, SOUL.md) directly. If you are migrating, point at the existing files rather than converting them:
[identity]
format = "markdown"
path = "/home/agent/workspace/IDENTITY.md"
Complete example: a local, private agent
Everything on your own hardware. No API key, no outbound requests, nothing leaves the machine.
[provider]
name = "ollama"
model = "qwen3:4b"
base_url = "http://localhost:11434/v1"
temperature = 0.3
max_tokens = 2048
[memory]
backend = "sqlite"
path = "/home/agent/workspace/.memory/agent.db"
auto_recall = true
vector_weight = 0.7
keyword_weight = 0.3
[security]
workspace = "/home/agent/workspace"
allowed_commands = ["ls", "cat"]
require_pairing = true
autonomy_level = "supervised"
[channels.telegram]
enabled = true
token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = ["123456789"]
[tools]
enabled = ["file_read", "file_write"]
timeout_seconds = 30
Note what is absent: no shell tool, no http tool, a two-command allowlist. This agent can read and write files in one directory and talk to you. That is a deliberately small surface, and it is the right starting point.
Complete example: a hosted-model development agent
More capable, for work you are supervising directly.
[provider]
name = "openrouter"
model = "anthropic/claude-sonnet-4"
api_key_env = "OPENROUTER_API_KEY"
base_url = "https://openrouter.ai/api/v1"
temperature = 0.2
max_tokens = 8192
[memory]
backend = "markdown"
path = "/home/agent/projects/.memory"
auto_recall = true
max_results = 15
[security]
workspace = "/home/agent/projects/current"
allowed_commands = ["git", "cargo", "ls", "cat", "grep"]
require_pairing = true
autonomy_level = "supervised"
[channels.web]
enabled = true
bind = "127.0.0.1"
port = 3000
[tools]
enabled = ["file_read", "file_write", "shell"]
timeout_seconds = 120
[tools.http]
allowed_domains = ["api.github.com", "crates.io"]
The longer timeout accommodates builds. git and cargo are allowlisted because the job requires them โ and that means this agent effectively has code execution, so it runs supervised, in a project directory, as a user that owns nothing else.
Validating changes
zeroclaw doctor # validate configuration and check connectivity
zeroclaw status # show the runtime's current view of its config
zeroclaw channel doctor # check channel health specifically
If doctor reports a key it does not recognise, check it against your installed version rather than assuming the documentation is current โ this includes the documentation on this page.
Practical tips
Keep the file in version control, keep secrets out. Commit config.toml with api_key_env references; never commit the key file or the config directory itself.
Change one thing at a time. When an agent starts behaving strangely after five simultaneous edits, you have no way to identify which one caused it.
Comment your reasoning. In six months, # low temperature: this agent calls tools will explain a decision that would otherwise look arbitrary.
Keep separate configs per agent. Distinct workspaces, distinct allowlists, distinct memory stores. A shared configuration means a compromise of the least careful agent reaches the most sensitive one.
Related reading
- ZeroClaw security โ the reasoning behind the
[security]section - Installing ZeroClaw โ getting to the point where you have a config file
- Web UI, gateway and tunnel โ the
[channels.web]section in depth - Skills and plugins โ extending what the agent can do