Reference
Config cheat sheet, tool list, chat commands, gotchas, and useful commands.
Tool List
| Tool | Description |
|---|---|
exec | Execute shell commands |
bash | Bash shell access |
process | Process management (spawn, kill) |
read | Read files |
write | Write/create files |
edit | Edit existing files |
apply_patch | Apply unified diffs |
web_search | Search the web (Brave/Perplexity) |
web_fetch | Fetch URL content |
browser | Browser automation (Playwright) |
canvas | Interactive artifact rendering |
sessions_list | List active sessions |
sessions_history | Read session history |
sessions_send | Delegate work to another agent’s session (agent-to-agent); NOT for message relay — use message with explicit target instead |
sessions_spawn | Spawn background sub-agent task |
session_status | Check session status |
memory_search | Semantic/hybrid search across memory files. Requires memorySearch config. See Phase 2 |
memory_get | Retrieve a specific memory entry by date or path |
message | Send messages to channels. With explicit target (JID/phone/channel ID), sends directly to any chat regardless of current session — the right tool for proactive group delivery |
cron | Schedule recurring tasks |
gateway | Gateway control (restart, config, status) |
nodes | Remote node operations |
generate_image | Generate images from text prompts (image-gen plugin) |
vm_screenshot | Capture VM screen as PNG image (computer-use plugin) |
vm_exec | Run shell command inside Lume VM (computer-use plugin) |
vm_click | Click at screen coordinates in VM (computer-use plugin) |
vm_type | Type text into focused VM application (computer-use plugin) |
vm_key | Press key or key combination in VM (computer-use plugin) |
vm_launch | Launch macOS application in VM (computer-use plugin) |
vm_scroll | Scroll screen up or down in VM (computer-use plugin) |
Tool Groups
| Group | Tools |
|---|---|
group:runtime | exec, bash, process |
group:fs | read, write, edit, apply_patch |
group:sessions | sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status |
group:memory | memory_search, memory_get |
group:web | web_search, web_fetch |
group:ui | browser, canvas |
group:automation | cron, gateway — can be used individually: deny gateway while keeping cron for conversational job management |
group:messaging | message |
group:nodes | nodes |
group:openclaw | All built-in tools |
Version note:
group:memoryis not recognized by the gateway in v2026.2.15. Use individual tools (memory_search,memory_get) in tool allow/deny lists until this is fixed upstream.
Config Quick Reference
Most Important Keys
{
// Agent definitions
agents: {
defaults: {
sandbox: { mode: "off|non-main|all" },
memorySearch: {
enabled: true, provider: "local",
// fallback: "none", // Don't fall back to remote on local failure
query: {
hybrid: {
enabled: true, vectorWeight: 0.7, textWeight: 0.3,
// mmr: { enabled: true, lambda: 0.7 } // Deduplicate similar results
},
},
// temporalDecay: { enabled: true, halfLifeDays: 30 },
cache: { enabled: true, maxEntries: 50000 }
},
compaction: {
memoryFlush: { enabled: true, softThresholdTokens: 4000 },
// reserveTokens: 8000, // Reserve tokens for post-compaction response (added 2026.2.21)
// keepRecentTokens: 8000, // Preserve recent context across compaction (added 2026.2.21)
},
subagents: {
maxConcurrent: 8,
// maxSpawnDepth: 2, // Max nesting depth for nested sub-agents; default 2 as of 2026.2.21 (added 2026.2.16)
// maxChildrenPerAgent: 10, // Max concurrent children per parent agent (added 2026.2.16)
// announceTimeoutMs: 60000, // Announce call timeout in ms (added 2026.2.22)
}
},
list: [{
id: "main", default: true, workspace: "...",
tools: { deny: [] },
subagents: { allowAgents: ["search"] }
}]
},
// Channel routing
bindings: [{ agentId: "...", match: { channel: "..." } }],
// Chat commands
commands: { bash: false, config: false, debug: false, restart: false },
// Tool restrictions
tools: {
profile: "full", // Shorthand: "minimal" | "coding" | "messaging" | "full"
deny: [],
elevated: { enabled: false },
exec: {
host: "sandbox", // Route exec to sandbox container (when sandbox.mode: "all")
// safeBinTrustedDirs: ["/usr/local/bin"], // Trusted dirs for safeBins path resolution (added 2026.2.22)
},
web: { search: { enabled: true, provider: "brave", apiKey: "..." } },
agentToAgent: { enabled: false, allow: [], maxPingPongTurns: 2 }
},
// Skills
skills: { allowBundled: ["coding-agent", "github", "healthcheck"] },
// Session isolation
session: { dmScope: "per-channel-peer" },
// Channel config
channels: {
// modelByChannel: { "whatsapp": "claude-sonnet-4-5", "signal": "..." }, // Per-channel model overrides (added 2026.2.21)
whatsapp: { dmPolicy: "pairing", allowFrom: [], groupPolicy: "allowlist", groups: { "*": { requireMention: true } } },
signal: { enabled: true, account: "+...", cliPath: "signal-cli", dmPolicy: "pairing" },
googlechat: { enabled: true, serviceAccountFile: "...", audienceType: "app-url", audience: "https://..." }
},
// Gateway (mode: "local" required for startup)
gateway: { mode: "local", bind: "loopback", port: 18789, auth: { mode: "token", token: "..." }, reload: { mode: "auto" } },
// Network discovery
discovery: { mdns: { mode: "minimal" } },
// Plugin allowlist
plugins: { allow: ["whatsapp", "channel-guard", "content-guard"] },
// Logging
logging: {
redactSensitive: "tools",
// maxFileBytes: 524288000, // Log file size cap before writes suppressed; default 500 MB (added 2026.2.22)
},
// Cron — scheduled tasks (added 2026.2.16: webhookToken, notify)
cron: {
// webhookToken: "...", // Auth token for external cron webhook triggers
// notify: { channel: "whatsapp", peer: "+..." }, // Deliver cron output to a channel
jobs: [/* ... */]
}
}DM Policy Options
| Value | Behavior |
|---|---|
pairing | Unknown senders get 8-char code (expires 1hr) |
allowlist | Only pre-approved senders |
open | Anyone (requires allowFrom: ["*"]) |
disabled | Ignore all DMs |
Group Policy & Mention Gating
Group messages are evaluated in three layers:
groupPolicy— top-level gate at channel root- Group allowlists —
groupskeys,groupAllowFrom - Mention gating —
requireMentioninsidegroups,/activationcommand
groupPolicy | Behavior |
|---|---|
allowlist | Only groups listed in groups or groupAllowFrom (default) |
open | All groups allowed; mention gating still applies |
disabled | Block all group messages |
The groups object
Keys are group IDs (or "*" for all groups). Values configure per-group behavior. Keys double as an allowlist — a group ID present as a key is implicitly allowed.
requireMention must be inside the groups object — see gotcha #14
for details and per-channel notes.
{
channels: {
whatsapp: {
groupPolicy: "allowlist",
groups: {
"*": { requireMention: true }, // Default: all groups, mention-gated
"120363XXX@g.us": { requireMention: false }, // Specific group: always respond
"120363YYY@g.us": { tools: { deny: ["exec"] } } // Per-group tool restrictions
}
}
}
}Mention patterns
WhatsApp uses native @mention data (mentionedJids). Google Chat uses native @mention data (when botUser is configured). Signal and other channels without native mentions need regex patterns:
{
agents: {
list: [{
id: "signal",
groupChat: {
mentionPatterns: ["@openclaw", "hey openclaw"] // Case-insensitive regexes
}
}]
}
}Global fallback (all agents/channels): messages.groupChat.mentionPatterns.
Replying to a bot message counts as an implicit mention on WhatsApp, Google Chat, Telegram, Slack, Discord, and Teams.
Common patterns
| Goal | Config |
|---|---|
| All groups, mention-gated | groups: { "*": { requireMention: true } } |
| Specific groups only | groups: { "<jid>": { requireMention: true } } (no "*" key) |
| Disable all groups | groupPolicy: "disabled" |
| All groups, always respond | groups: { "*": { requireMention: false } } |
| Sender allowlist in groups | groupAllowFrom: ["+1555..."] |
Session Scope Options
See Session Management for the full deep-dive on session keys, lifecycle, compaction, and pruning.
| Value | Behavior |
|---|---|
main | All DMs share one session |
per-peer | One session per sender (across channels) |
per-channel-peer | One session per sender per channel (recommended) |
per-account-channel-peer | Most isolated |
Sandbox Modes
| Mode | Behavior |
|---|---|
off | No sandboxing — all sessions run on host |
non-main | Only the agent:<id>:main session runs unsandboxed; all other sessions sandboxed |
all | Every session sandboxed |
Any other value (e.g. "tools") is invalid — causes config validation failure and a gateway crash loop.
What counts as “non-main”: WhatsApp DMs, group sessions, cron runs, and subagent sessions all have session keys that don’t match agent:<id>:main. They are all sandboxed with mode: "non-main". Only the Control UI / CLI main session runs unsandboxed.
Default Sandbox Tool Allow List
When a session is sandboxed, a separate tool policy applies — distinct from and in addition to the agent-level tool policy.
Default allow: exec, process, read, write, edit, apply_patch, image, sessions_list, sessions_history, sessions_send, sessions_spawn, subagents, session_status
Implicitly denied (not in default allow): message, browser, memory_search, memory_get, web_search, web_fetch, agents_list
The default allow list is a closed set — a tool not listed is denied in sandboxed sessions, even if the agent-level policy allows it.
To add message, browser, or memory tools to sandboxed sessions (e.g., so a WhatsApp DM session can send to groups or access memory), override the allow list. This config goes at the global tools level (applies to all agents) or per-agent under agents.list[].tools.sandbox.tools:
{
"tools": {
"sandbox": {
"tools": {
"allow": [
"exec", "process", "read", "write", "edit", "apply_patch", "image",
"sessions_list", "sessions_history", "sessions_send", "sessions_spawn",
"subagents", "session_status",
"message", "browser",
"memory_search", "memory_get"
]
}
}
}
}Gotchas:
tools.sandbox.tools.alsoAllowdoes not work with the default policy (mergeAlsoAllowPolicy()requires an explicit baseallowarray). Must useallowwith the full list.sandbox.toolsat agent level is invalid config. Correct path:agents.list[].tools.sandbox.tools(under the agent’stoolskey).- Verify with:
openclaw sandbox explain --agent <id> --session "<session-key>"
For sandbox architecture, container lifecycle, and config options, see Architecture — Docker Sandbox Architecture .
Sandbox Scope & Access Guide
Different agents need different sandbox configurations. Here’s when to use each combination:
| Use Case | scope | workspaceAccess | mode | Rationale |
|---|---|---|---|---|
| Channel agents (whatsapp, signal) | agent | rw | non-main | Need workspace for memory writes; sandbox provides network isolation |
| Search agent | — | — | off | No filesystem tools; tool policy provides isolation. Unsandboxed to avoid #9857 |
| Main agent (recommended ) | agent | rw | non-main | Channel sessions sandboxed on network: "openclaw-egress"; Control UI session on host (operator trusted). See egress allowlisting |
| Main agent (unsandboxed) | — | — | off | Operator interface; full host access (no Docker isolation) |
| Computer (optional hardened variant ) | agent | rw | all | Separate exec + browser agent, network: "openclaw-egress" |
| Ephemeral tasks | session | none | all | Container destroyed when session ends; no persistent state |
Config Includes ($include)
Split large configs into multiple files — useful for multi-agent setups:
{
gateway: { port: 18789 },
agents: { $include: "./agents.json5" }, // Single file: replaces
broadcast: { $include: ["./a.json5", "./b.json5"] } // Array: deep-merged in order
}Paths are relative to the including file. Nested includes supported (up to 10 levels).
Config Validation
Config is strictly validated — unknown keys, malformed types, or invalid values cause the gateway to refuse to start. Run openclaw doctor to diagnose, openclaw doctor --fix to auto-repair.
Use openclaw config validate (added in 2026.3.2) to check the config file before starting the gateway:
openclaw config validate # Human-readable output
openclaw config validate --json # Machine-readable JSON with error detailsUse openclaw config file (added in 2026.3.1) to print the active config file path:
openclaw config file # Prints e.g. /Users/openclaw/.openclaw/openclaw.jsonEnvironment Files
OpenClaw reads .env files (non-overriding) from: CWD .env → ~/.openclaw/.env → config env block. Alternative to putting secrets in plist/systemd env vars.
Tool Policy Precedence
OpenClaw applies tool restrictions in an 8-layer cascade:
| Layer | Source | Example |
|---|---|---|
| 1 | Tool profile | tools.profile: "coding" |
| 2 | Provider tool profile | tools.byProvider.anthropic.profile |
| 3 | Global tool policy | tools.deny: ["gateway"] |
| 4 | Provider tool policy | tools.byProvider.anthropic.deny |
| 5 | Agent tool policy | agents.list[].tools.deny: ["exec"] |
| 6 | Agent provider policy | agents.list[].tools.byProvider.anthropic.deny |
| 7 | Sandbox tool policy | tools.sandbox.tools |
| 8 | Subagent tool policy | tools.subagents.tools |
Critical: Global deny (layer 3) overrides agent-level allow (layer 5). A tool in tools.deny cannot be re-enabled by an agent’s tools.allow. For tools needed by some agents but not others (e.g., web_search for a search agent), deny per-agent instead of globally. See Phase 5
for the correct isolation pattern.
HTTP API tool restrictions: When sessions are created via the HTTP API (/api/v1/chat/completions), OpenClaw additionally denies sessions_spawn, sessions_send, gateway, and whatsapp_login — regardless of agent tool policy. This is a platform-level safety fence for external API callers. It does not apply to embedded agent sessions (session key agent:<id>:main). Override with gateway.tools.allow if needed.
Cron Jobs
sessionTarget
Controls how the cron job executes.
| Value | Behavior |
|---|---|
"isolated" | Fresh throwaway session per run. Agent receives a prompt, executes it, returns a response. Supports channel delivery via delivery. |
"main" | Injects a short event into the agent’s existing main session (agent:<id>:main). Appended to the ongoing conversation — agent has full prior context. Only supports delivery.mode: "webhook". |
Use "isolated" for tasks (“search for news and write a report”) — each run starts clean. Session key: agent:main:cron:<job-id>:run:<uuid>.
Use "main" for reminders and nudges (“you have a meeting in 10 minutes”) — the event lands in the agent’s running session with full context. Requires payload.kind: "systemEvent" with a text field.
wakeMode
| Value | Behavior |
|---|---|
"now" | Triggers an immediate heartbeat — job runs right away |
"next-heartbeat" | Queues for the agent’s next scheduled heartbeat cycle |
With heartbeat.every: "30m", "next-heartbeat" can delay up to 30 minutes. Use "now" for time-sensitive jobs; "next-heartbeat" for jobs where a delay is acceptable.
delivery
| Mode | Behavior |
|---|---|
"announce" | Agent’s final response delivered to a channel. Requires delivery.to (e.g. WhatsApp group JID). Agent can reply ANNOUNCE_SKIP to suppress. |
"none" | Job runs silently — no delivery. |
"webhook" | POST result to an HTTP URL. Works with both isolated and main. |
When delivery is configured on isolated jobs, the runtime appends delivery instructions and disables the message tool (disableMessageTool: true) to prevent duplicate sends. The agent should not call message directly — the runtime delivers the final response automatically.
delivery.bestEffort: true — suppresses errors if delivery fails (e.g. WhatsApp disconnected).
Cron and group:automation
group:automation expands to ["cron", "gateway"]. These can be allowed/denied individually:
{ "deny": ["gateway"] } // deny gateway only — keeps cron for conversational job management
{ "allow": [..., "cron"] } // allow cron explicitly in an allowlist configAlternatives to the cron tool for job management: Control UI (web), openclaw cron create/list/edit CLI, or direct edit of cron/jobs.json.
Chat Commands
Users can send / commands directly in WhatsApp or Signal chats. Commands must be sent as standalone messages (not inline with other text).
Core Commands
| Command | What it does | Access |
|---|---|---|
/help | List available commands | All authorized senders |
/reset (/new [model]) | Fresh session; optionally switch model | All authorized |
/status | Session info (model, tokens, cost) | All in DMs; owner-only in groups |
/whoami | Show sender identity | All authorized |
/compact | Compact message history | All authorized |
/stop | Abort current operation | All authorized |
/activation mention|always | Toggle group mention gating | Owner-only |
Directives (Session Modifiers)
Directives change session behavior. Standalone = persists to session. Inline in a normal message = one-shot hint, stripped before the model sees it.
| Directive | Controls |
|---|---|
/think off|low|medium|high | Thinking/reasoning depth |
/elevated off|on|ask|full | Host execution mode (escapes sandbox) |
/model <name> | Switch model mid-session |
Dangerous Commands (Disabled by Default)
| Command | Config gate | Risk |
|---|---|---|
/bash <cmd> or ! <cmd> | commands.bash: true | Host shell access |
/config | commands.config: true | Runtime config changes |
/debug | commands.debug: true | Runtime overrides |
/restart | commands.restart: true | Service disruption |
These are owner-only even when enabled. Tool policy still applies — /elevated can’t override tools in tools.deny.
Full command reference: docs.openclaw.ai/tools/slash-commands
Google Chat: Slash commands work in Google Chat DMs and spaces. For Google Chat setup and known issues (e.g., DM routing), see Google Chat Channel Setup .
Gotchas & Non-Obvious Behaviors
Tool Policy
denyalways beatsallowat the same level. If a tool is in both lists, it’s denied.allowis exclusive — ifallowis non-empty, everything not listed is blocked. An emptyallowlist means “allow everything not denied.”Tool policy is a hard stop — chat commands like
/execcannot override denied tools.Global
denyoverrides agent-levelallow— a tool intools.denycannot be re-enabled at the agent level. For tools needed by some agents (e.g.,web_search), deny per-agent instead of globally.profile: "full"means no restrictions —tools.profile: "full"expands to an empty allow/deny set, bypassing all profile-level filtering entirely. Unlike"coding"(which includes a curated allow list),"full"is a footgun: every tool is reachable unless explicitly denied. Prefer explicittools.allowlists over"full"when you need broad access.group:uideny includesbrowser— if an agent allowsbrowserbut deniesgroup:ui, browser is silently disabled. Denycanvasindividually instead when browser should remain available.execallowlists don’t catch shell builtins — allowlists match resolved binary paths only. Shell builtins (cd,export,source) bypass the check entirely.echois both a shell builtin and a standalone binary (/bin/echo) — behavior differs between them, and the builtin version varies by shell. If this matters, denyexecat the agent level.
Agents & Sessions
Never share
agentDirbetween agents — causes auth collisions and session corruption.MEMORY.mdloads in main sessions only (not groups or shared contexts) — don’t put security-critical instructions there.Binding precedence is most-specific wins — a peer-level binding beats a channel-level one.
elevatedmode is per-session, not permanent — buttools.elevated.enabled: falseblocks it globally.Session transcripts contain full message history and tool output — treat them as sensitive. Prune regularly if retention isn’t needed.
Channels
Signal linked devices see everything — the primary phone gets all bot messages. No filtering possible.
pairingcodes expire after 1 hour with max 3 pending per channel.requireMentionmust be inside thegroupsobject, not at channel root — placing it atchannels.whatsapp.requireMentioncauses a Zod validation error. Correct:channels.whatsapp.groups: { "*": { requireMention: true } }. On Signal, also configurementionPatternsinagents.list[].groupChat.mentionPatterns(no native @mention support). On Google Chat, setbotUserin the channel config for reliable mention detection in spaces.Google Chat requires both channel config and plugin — missing either
channels.googlechatorplugins.entries.googlechat.enabled: truecauses a 405 error on the webhook endpoint.Google Chat per-space rate limit is 60/min (1 write/sec) — the 600/min figure in some documentation applies only to data import operations, not normal messaging.
Placeholder
allowFromvalues cause silent message drops —allowFrom: ["+46XXXXXXXXX"]or any non-matching number silently drops all incoming messages with no error or log warning. Always replace placeholders with real phone numbers.Empty env vars cause config validation failure —
${BRAVE_API_KEY}as an empty string triggersEX_CONFIG(exit 78). Use a non-empty placeholder like"not-configured"for optional keys not yet provisioned.
Sandbox & Docker
Sandbox tool policy is a separate layer from agent tool policy — even if an agent allows
messageorbrowser, those tools are blocked in sandboxed sessions unless explicitly listed intools.sandbox.tools.allow. The default sandbox allow list does not includemessage,browser,memory_search,memory_get, orweb_fetch. WhatsApp DM sessions (and all non-main sessions) are sandboxed withmode: "non-main"or"all". Usetools.sandbox.tools.allowwith the full list including any tools your sandboxed sessions need.alsoAllowdoes not work with the default policy — must specify the completeallowlist.Sandbox image must be built before use — the default
openclaw-sandbox:bookworm-slimis rawdebian:bookworm-slimwith no packages. Runcd $(npm root -g)/openclaw && ./scripts/sandbox-setup.shbefore enabling Docker sandboxing. Without this, all exec calls inside the sandbox fail (sh: 1: git: not found).exec host not allowedwhen sandboxed — exec calls targeting the gateway host fail withexec host not allowed (requested gateway; configure tools.exec.host=sandbox to allow). Add"tools": { "exec": { "host": "sandbox" } }to route exec to the sandbox container by default. Withmode: "non-main"(recommended), this only affects sandboxed sessions (channel DMs/groups/cron) — the Control UI session runs on host and exec goes to host naturally. Withmode: "all", the Control UI session is also sandboxed and all exec calls hit this. Prefermode: "non-main"— the operator’s Control UI session should run on host.Sandbox
network: "none"blocks package installs —setupCommandrequiresnetwork: "bridge"andreadOnlyRoot: false, which weakens sandbox isolation. Prefer custom images for production — tools are pre-installed, so secure defaults are preserved.Bind mounts pierce sandbox filesystem — always use
:rosuffix. Never binddocker.sock.
Cron
delivery.tonotdelivery.target— the cron delivery destination field isdelivery.to(e.g. a WhatsApp group JID). The fielddelivery.targetdoes not exist — using it silently delivers to the wrong destination (DM instead of group). Always usedelivery.to.--announce(delivery mode) requires a channel — creating a cron job withopenclaw cron add --announcebut no--channelsucceeds at creation time, but every run fails at the delivery step withcron delivery target is missing. The error repeats every run and clutters logs. Always pair--announcewith--channel <whatsapp|signal>(and a peer if required by your dmPolicy). If there is nothing to report, have the agent replyANNOUNCE_SKIPto suppress delivery.Cron jobs can no longer notify via ad hoc agent sends (2026.3.11+) — isolated cron delivery was tightened so cron jobs can no longer notify through the
messagetool or fallback main-session summaries outside the officialdeliverymechanism. If you have legacy cron jobs that relied on the agent callingmessagedirectly to report results, migrate them todelivery.mode: "announce"with an explicitdelivery.totarget. Runopenclaw doctor --fixto auto-migrate legacy cron storage and delivery metadata.
Config & Gateway
gateway.modeis required — the gateway refuses to start unlessgateway.mode: "local"is set in config. Use--allow-unconfiguredfor ad-hoc/dev runs.Config validation is strict — unknown keys, malformed types, or invalid values cause the gateway to refuse to start. Run
openclaw doctorto diagnose.Environment variable substitution only matches
[A-Z_][A-Z0-9_]*— lowercase vars won’t resolve. Missing vars throw errors at config load.openclaw gateway stop/restartworks with LaunchAgent but not LaunchDaemon — OpenClaw’s built-in gateway commands (openclaw gateway stop,openclaw gateway restart) manage LaunchAgents (gui/<uid>domain) and systemd user services. The default LaunchAgent setup works with these commands. If you use the hardened LaunchDaemon alternative (systemdomain) or systemd system service, these commands won’t find it — uselaunchctl bootout/bootstraporsystemctl restartdirectly. Additionally,KeepAlive: true(launchd) orRestart=always(systemd) causes the service manager to immediately respawn a killed process, which can race with OpenClaw’s own restart logic.
Plugins
Plugin changes require a gateway restart — plugin source files (
.ts) are loaded at startup. Config hot-reload does NOT reload plugins. After updating a plugin in~/.openclaw/extensions/, restart the gateway.Broken tool results poison session history — if a plugin returns malformed content blocks (wrong format, missing fields), the broken entry persists in the session
.jsonlfile. Every subsequent message replays it, causing the same error even after the plugin is fixed. Fix: delete the affected session file. Identify it by grepping for the error pattern, then remove:# Find sessions with broken image blocks (example) grep -l 'media_type' ~/.openclaw/agents/*/sessions/*.jsonl # Delete the affected session file — next message creates a fresh oneImage content blocks are model-visible only — tool result image blocks let the LLM see the image but are NOT forwarded as media to channels. To deliver images via WhatsApp/Signal/Google Chat, include a
MEDIA:<path>directive in a text content block. OpenClaw’ssplitMediaFromOutput()scans text for these directives and attaches matching files as media.OpenClaw uses a flat image content block format —
{type: "image", data: "<base64>", mimeType: "image/png"}. This differs from the Anthropic API format ({type: "image", source: {type: "base64", media_type, data}}). Plugins must use the flat format; OpenClaw converts to API format before sending to the LLM.plugins.allowandenabled: falseare independent checks — both must pass for a plugin to load. A plugin inplugins.allowwith"enabled": falseis fully blocked: the file is never imported,register()is never called. Check precedence:deny→allow→enabled. You can safely pre-populateplugins.allowwith trusted plugin IDs and control which ones actually load viaenabled.Plugin tools are available by default — enabling a plugin that registers tools (image-gen →
generate_image, computer-use →vm_*) makes those tools callable by any agent not blocking them. Agents usingtools.alloware safe (unlisted tools are blocked). Agents using onlytools.denymust explicitly deny plugin tools they shouldn’t have.Plugin-generated temp files accumulate — plugins that save images via
MEDIA:pattern write to$TMPDIR. macOS clears/tmpon reboot, but long-running servers accumulate files. Consider a cron job:find /tmp/openclaw-image-gen -mtime +1 -delete.
Memory
Remote memory search providers need a separate API key — the embedding key (e.g.,
OPENAI_API_KEYfor OpenAI embeddings) is not the same as your AI provider key (ANTHROPIC_API_KEY). Both must be set.Local memory search requires native build approval — run
npx pnpm approve-buildsthennpx pnpm rebuild node-llama-cpp(from the OpenClaw install directory). Without this,memory_searchfalls back to a remote provider (if configured) or returns no results.Memory search auto-reindexes on provider/model change — OpenClaw tracks the embedding provider, model, and chunking params in the index. Changing any of these triggers an automatic reindex. Run
openclaw memory indexto force an immediate rebuild.Daily memory files are auto-loaded for today + yesterday only — older files are only accessible via
memory_search. If search isn’t configured, the agent can’t recall anything beyond yesterday.
Version Compatibility
Features below require the listed version or later. Check yours with openclaw --version.
| Version | Feature | Details |
|---|---|---|
| 2026.1.29 | Control UI token fix | Security vulnerability (CVSS 8.8) patched — update immediately. See Phase 3 |
| 2026.2.1 | before_tool_call hook | Required for content-guard and network-guard plugins |
| 2026.2.3-1 | Security audit baseline | Version used in the worked audit example |
| 2026.2.9 | xAI (Grok) provider | New search provider option |
| 2026.2.12 | Channel bindings regression | #15176 — bindings to non-default agents broken. Not relevant for recommended 2-agent config (all channels route to main) |
| 2026.2.15 | sessions_spawn sandbox bug | #9857
— sessions_spawn breaks when both agents are sandboxed with per-agent tools. Workaround: run search agent unsandboxed |
| 2026.2.16 | Security hardening + plugin hooks + subagent limits | CSP enforcement, workspace path sanitization, web_fetch response size cap (tools.web.fetch.maxResponseBytes, default 5 MB), dangerous Docker config rejection, llm_input/llm_output plugin hooks, maxSpawnDepth/maxChildrenPerAgent for nested subagents, Unicode-aware FTS, timezone-aware memory dates, per-agent QMD scoping, Telegram token auto-redaction |
| 2026.2.19 | Gateway auth auto-generation, hook/plugin path containment, SSRF hardening | See Phase 3 version note |
| 2026.2.21 | Shell exec env injection blocking, sandbox browser hardening, Tailscale auth scoping | See Phase 3 version note |
| 2026.2.22 | Exec safeBin path pinning, openclaw config get redaction, group policy fail-closed | See Phase 3 version note |
| 2026.2.23 | browser.ssrfPolicy.dangerouslyAllowPrivateNetwork (breaking rename), HSTS support, exec obfuscation detection, openclaw sessions cleanup with disk-budget controls | See Phase 3 version note |
| 2026.2.26 | openclaw secrets workflow (audit, configure, apply, reload), openclaw agents bindings/bind/unbind | External secrets management — see Phase 6 |
| 2026.3.1 | Gateway health endpoints (/health, /healthz, /ready, /readyz), agents.*.heartbeat.lightContext | Health endpoints useful for Docker/Kubernetes health checks |
| 2026.3.2 | openclaw config validate, tools.profile: "messaging" new default, pdf tool, Telegram streaming defaults to partial, LaunchAgent Umask key | See Phase 3 version note for security details |
| 2026.3.12 | Workspace plugin auto-load disabled, exec approval Unicode/obfuscation hardening, /config+/debug owner-only, device pairing bootstrap tokens, gateway auth scope fixes, agents.defaults.compaction.postIndexSync, sessions_yield for orchestrators | See Phase 3 version note; multiple CVEs patched |
| 2026.3.13-1 | Node.js 22.16.0+ minimum enforced, OPENCLAW_TZ Docker timezone support, Signal groups schema support, Docker build context token leak fix, session key :dm: corrected to :direct: | Recovery release (npm version remains 2026.3.13) |
Plugins
| Plugin | Purpose | Required env var |
|---|---|---|
whatsapp | WhatsApp channel (bundled) | — |
signal | Signal channel (bundled) | — |
googlechat | Google Chat channel (bundled) | GOOGLE_CHAT_SERVICE_ACCOUNT_FILE |
content-guard | LLM-based injection scanning at sessions_send boundary (search→main) | OPENROUTER_API_KEY |
channel-guard | Inbound message injection scanning for WhatsApp/Signal/Google Chat | OPENROUTER_API_KEY |
file-guard | Path-based file access protection (no_access, read_only, no_delete) | — (deterministic) |
network-guard | Application-level domain allowlisting for network tool calls | — (deterministic, no model) |
command-guard | Regex-based dangerous command blocking | — (no external deps) |
image-gen | Generate images from text prompts via OpenRouter | OPENROUTER_API_KEY |
computer-use | VM computer interaction (Lume) | — (WebSocket to cua-computer-server) |
The content-guard plugin intercepts sessions_send calls at the search→main trust boundary, classifying message content for prompt injection using an LLM (claude-haiku-4-5 via OpenRouter). It identifies search-agent traffic by the params.sessionKey of the sessions_send call (sessions targeting agent:search:*); all other sessions_send calls (e.g. proactive group delivery) are skipped without scanning. Note: event.agentId is not populated by the runtime for before_tool_call events — rely on params.sessionKey for caller identification. The channel-guard plugin scans incoming WhatsApp/Signal/Google Chat messages before agent processing using an OpenRouter LLM classifier with warn/block thresholds, fail-closed by default (failOpen: false). Both are included in the recommended configuration
.
The file-guard, network-guard, and command-guard plugins provide deterministic enforcement — no ML model, no external dependencies. file-guard enforces path-based file protection with three levels (no_access, read_only, no_delete). network-guard enforces application-level domain allowlisting for web_fetch and exec tool calls. command-guard blocks dangerous shell commands (rm -rf, fork bombs, force push, etc.) via regex. All three are included in the hardened multi-agent
configuration and can optionally be added to any deployment. See Phase 5
for overview and the extension docs
for full configuration.
Plugin Hooks
Plugins can register handlers for these lifecycle hooks:
| Hook | When it fires | Example use |
|---|---|---|
before_tool_call | Before a tool executes | content-guard: classify sessions_send content |
message_received | Incoming channel message (WhatsApp/Signal/Google Chat) | channel-guard: scan for injection |
llm_input | Before prompt is sent to the model (added 2026.2.16) | Input logging, token counting, content filtering |
llm_output | After model response received (added 2026.2.16) | Output logging, response filtering, compliance checks |
Note:
after_tool_resultis not yet wired —before_tool_call+ pre-fetch is the current workaround for content scanning.
The image-gen plugin registers a generate_image tool that agents can call to create images from text prompts. Uses OpenRouter’s unified API — supports FLUX, Gemini, GPT, and Sourceful models. See extensions/image-gen/
for source.
The computer-use plugin registers 7 vm_* tools for controlling a macOS Lume VM via WebSocket connection to cua-computer-server. Requires Apple Silicon Mac with Lume. See extensions/computer-use/
for setup and Phase 8
for deployment.
Plugin Installation
Plugin directories must be named to match the manifest ID in openclaw.plugin.json (e.g., content-guard/, not openclaw-content-guard/). The name field in package.json should also match the manifest ID.
Manual installation (recommended — openclaw plugins install may fail to resolve dependencies or link manifests correctly; see the OpenClaw changelog
for current plugin CLI status):
cp -r extensions/content-guard ~/.openclaw/extensions/content-guard
cp -r extensions/channel-guard ~/.openclaw/extensions/channel-guardThe gateway discovers plugins from ~/.openclaw/extensions/ at startup. Each plugin directory must contain openclaw.plugin.json. Plugin code is loaded once at startup — changes to deployed plugins require a gateway restart (config hot-reload does NOT reload plugins).
Discovery precedence: Plugins are discovered in order: workspace-level (
.openclaw/extensions/in workspace), user-level (~/.openclaw/extensions/), then bundled. First match wins.
CLI installation (when available):
openclaw plugins install --link /path/to/plugin// openclaw.json — plugins section
{
plugins: {
entries: {
"content-guard": {
enabled: true,
config: {
model: "anthropic/claude-haiku-4-5",
maxContentLength: 50000, // Truncate content sent to model
timeoutMs: 15000, // Classification timeout
}
},
"channel-guard": {
enabled: true,
config: {
model: "anthropic/claude-haiku-4-5",
maxContentLength: 10000,
timeoutMs: 10000,
failOpen: false, // Block messages if classifier unavailable
warnThreshold: 0.4, // Score to inject advisory
blockThreshold: 0.8 // Score to hard-block message
}
},
"image-gen": {
enabled: true,
config: {
apiKey: "${OPENROUTER_API_KEY}",
defaultModel: "openai/gpt-5-image-mini",
defaultAspectRatio: "1:1",
defaultImageSize: "2K",
timeoutMs: 60000
}
}
}
}
}Useful Commands
# Setup & channels
openclaw setup # First-time setup (creates ~/.openclaw/)
openclaw channels login # Link a channel (QR code for WhatsApp)
openclaw channels login --account <id> # Link a specific account
openclaw channels logout # Unlink channel
# Gateway management (foreground / LaunchAgent)
openclaw start # Start gateway in foreground
openclaw health # Gateway health check
openclaw status # Gateway status
openclaw dashboard # Open browser UI
openclaw logs # View logs
# Gateway management — LaunchDaemon (macOS) / systemd (Linux)
# scripts/docker-isolation/gateway.sh wraps these for both platforms + multi-instance
bash scripts/gateway.sh start [instance] # Start
bash scripts/gateway.sh stop [instance] # Stop
bash scripts/gateway.sh restart [instance] # Restart
bash scripts/gateway.sh status [instance] # Status
bash scripts/gateway.sh reload [instance] # Reload config (SIGUSR1, no restart)
bash scripts/gateway.sh logs <instance> # Tail logs (instance required for multi)
# Diagnostics & security
openclaw doctor # Diagnose config issues
openclaw doctor --fix # Auto-apply config migrations/repairs
openclaw doctor --generate-gateway-token # Generate a secure token
openclaw security audit # Security scan
openclaw security audit --deep # Deep scan (requires running gateway)
openclaw security audit --fix # Auto-apply safe guardrails
openclaw config validate # Validate config file before startup (2026.3.2+)
openclaw config validate --json # Machine-readable validation output
openclaw config file # Print active config file path (2026.3.1+)
# Memory
openclaw memory status # Index size, provider, last indexed
openclaw memory status --deep # Probe vector + embedding availability
openclaw memory status --deep --index # Reindex if store is dirty
openclaw memory index # Build/rebuild search index
openclaw memory index --agent <id> # Rebuild index for specific agent
openclaw memory search "<query>" # Search memory from terminal
openclaw memory search --query "<query>" # Equivalent long-form (2026.2.24+)
# Session management
openclaw sessions list # List active sessions
openclaw sessions reset # Reset all sessions
openclaw sessions cleanup # Clean up disk + orphaned sessions (2026.2.23+)
openclaw sessions cleanup --fix-missing # Prune store entries with missing transcripts (2026.2.26+)
# Agent routing
openclaw agents bindings # List account-scoped route bindings (2026.2.26+)
openclaw agents bind <agent> <channel> # Bind an agent to a channel account
openclaw agents unbind <agent> <channel> # Remove a channel binding
# Secrets management (2026.2.26+)
openclaw secrets audit # Find hardcoded secrets in config
openclaw secrets configure # Configure secrets provider
openclaw secrets apply # Write SecretRefs to config
openclaw secrets reload # Activate secrets snapshot without restart
# Pairing
openclaw pairing list <channel> # List pending pairing requests
openclaw pairing approve <channel> <code> # Approve a pairing code
# Updates
openclaw update # Built-in updater
curl -fsSL https://openclaw.ai/install.sh | bash # Alternative: install script