2319 lines
63 KiB
Markdown
2319 lines
63 KiB
Markdown
|
|
---
|
|||
|
|
title: "Configuration Reference"
|
|||
|
|
description: "Complete field-by-field reference for ~/.openclaw/openclaw.json"
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Configuration Reference
|
|||
|
|
|
|||
|
|
Every field available in `~/.openclaw/openclaw.json`. For a task-oriented overview, see [Configuration](/gateway/configuration).
|
|||
|
|
|
|||
|
|
Config format is **JSON5** (comments + trailing commas allowed). All fields are optional — OpenClaw uses safe defaults when omitted.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Channels
|
|||
|
|
|
|||
|
|
Each channel starts automatically when its config section exists (unless `enabled: false`).
|
|||
|
|
|
|||
|
|
### DM and group access
|
|||
|
|
|
|||
|
|
All channels support DM policies and group policies:
|
|||
|
|
|
|||
|
|
| DM policy | Behavior |
|
|||
|
|
| ------------------- | --------------------------------------------------------------- |
|
|||
|
|
| `pairing` (default) | Unknown senders get a one-time pairing code; owner must approve |
|
|||
|
|
| `allowlist` | Only senders in `allowFrom` (or paired allow store) |
|
|||
|
|
| `open` | Allow all inbound DMs (requires `allowFrom: ["*"]`) |
|
|||
|
|
| `disabled` | Ignore all inbound DMs |
|
|||
|
|
|
|||
|
|
| Group policy | Behavior |
|
|||
|
|
| --------------------- | ------------------------------------------------------ |
|
|||
|
|
| `allowlist` (default) | Only groups matching the configured allowlist |
|
|||
|
|
| `open` | Bypass group allowlists (mention-gating still applies) |
|
|||
|
|
| `disabled` | Block all group/room messages |
|
|||
|
|
|
|||
|
|
<Note>
|
|||
|
|
`channels.defaults.groupPolicy` sets the default when a provider's `groupPolicy` is unset.
|
|||
|
|
Pairing codes expire after 1 hour. Pending DM pairing requests are capped at **3 per channel**.
|
|||
|
|
Slack/Discord have a special fallback: if their provider section is missing entirely, runtime group policy can resolve to `open` (with a startup warning).
|
|||
|
|
</Note>
|
|||
|
|
|
|||
|
|
### WhatsApp
|
|||
|
|
|
|||
|
|
WhatsApp runs through the gateway's web channel (Baileys Web). It starts automatically when a linked session exists.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
whatsapp: {
|
|||
|
|
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
|||
|
|
allowFrom: ["+15555550123", "+447700900123"],
|
|||
|
|
textChunkLimit: 4000,
|
|||
|
|
chunkMode: "length", // length | newline
|
|||
|
|
mediaMaxMb: 50,
|
|||
|
|
sendReadReceipts: true, // blue ticks (false in self-chat mode)
|
|||
|
|
groups: {
|
|||
|
|
"*": { requireMention: true },
|
|||
|
|
},
|
|||
|
|
groupPolicy: "allowlist",
|
|||
|
|
groupAllowFrom: ["+15551234567"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
web: {
|
|||
|
|
enabled: true,
|
|||
|
|
heartbeatSeconds: 60,
|
|||
|
|
reconnect: {
|
|||
|
|
initialMs: 2000,
|
|||
|
|
maxMs: 120000,
|
|||
|
|
factor: 1.4,
|
|||
|
|
jitter: 0.2,
|
|||
|
|
maxAttempts: 0,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Multi-account WhatsApp">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
whatsapp: {
|
|||
|
|
accounts: {
|
|||
|
|
default: {},
|
|||
|
|
personal: {},
|
|||
|
|
biz: {
|
|||
|
|
// authDir: "~/.openclaw/credentials/whatsapp/biz",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Outbound commands default to account `default` if present; otherwise the first configured account id (sorted).
|
|||
|
|
- Legacy single-account Baileys auth dir is migrated by `openclaw doctor` into `whatsapp/default`.
|
|||
|
|
- Per-account override: `channels.whatsapp.accounts.<id>.sendReadReceipts`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
### Telegram
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
telegram: {
|
|||
|
|
enabled: true,
|
|||
|
|
botToken: "your-bot-token",
|
|||
|
|
dmPolicy: "pairing",
|
|||
|
|
allowFrom: ["tg:123456789"],
|
|||
|
|
groups: {
|
|||
|
|
"*": { requireMention: true },
|
|||
|
|
"-1001234567890": {
|
|||
|
|
allowFrom: ["@admin"],
|
|||
|
|
systemPrompt: "Keep answers brief.",
|
|||
|
|
topics: {
|
|||
|
|
"99": {
|
|||
|
|
requireMention: false,
|
|||
|
|
skills: ["search"],
|
|||
|
|
systemPrompt: "Stay on topic.",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
customCommands: [
|
|||
|
|
{ command: "backup", description: "Git backup" },
|
|||
|
|
{ command: "generate", description: "Create an image" },
|
|||
|
|
],
|
|||
|
|
historyLimit: 50,
|
|||
|
|
replyToMode: "first", // off | first | all
|
|||
|
|
linkPreview: true,
|
|||
|
|
streamMode: "partial", // off | partial | block
|
|||
|
|
draftChunk: {
|
|||
|
|
minChars: 200,
|
|||
|
|
maxChars: 800,
|
|||
|
|
breakPreference: "paragraph", // paragraph | newline | sentence
|
|||
|
|
},
|
|||
|
|
actions: { reactions: true, sendMessage: true },
|
|||
|
|
reactionNotifications: "own", // off | own | all
|
|||
|
|
mediaMaxMb: 5,
|
|||
|
|
retry: {
|
|||
|
|
attempts: 3,
|
|||
|
|
minDelayMs: 400,
|
|||
|
|
maxDelayMs: 30000,
|
|||
|
|
jitter: 0.1,
|
|||
|
|
},
|
|||
|
|
network: { autoSelectFamily: false },
|
|||
|
|
proxy: "socks5://localhost:9050",
|
|||
|
|
webhookUrl: "https://example.com/telegram-webhook",
|
|||
|
|
webhookSecret: "secret",
|
|||
|
|
webhookPath: "/telegram-webhook",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Bot token: `channels.telegram.botToken` or `channels.telegram.tokenFile`, with `TELEGRAM_BOT_TOKEN` as fallback for the default account.
|
|||
|
|
- `configWrites: false` blocks Telegram-initiated config writes (supergroup ID migrations, `/config set|unset`).
|
|||
|
|
- Draft streaming uses Telegram `sendMessageDraft` (requires private chat topics).
|
|||
|
|
- Retry policy: see [Retry policy](/concepts/retry).
|
|||
|
|
|
|||
|
|
### Discord
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
discord: {
|
|||
|
|
enabled: true,
|
|||
|
|
token: "your-bot-token",
|
|||
|
|
mediaMaxMb: 8,
|
|||
|
|
allowBots: false,
|
|||
|
|
actions: {
|
|||
|
|
reactions: true,
|
|||
|
|
stickers: true,
|
|||
|
|
polls: true,
|
|||
|
|
permissions: true,
|
|||
|
|
messages: true,
|
|||
|
|
threads: true,
|
|||
|
|
pins: true,
|
|||
|
|
search: true,
|
|||
|
|
memberInfo: true,
|
|||
|
|
roleInfo: true,
|
|||
|
|
roles: false,
|
|||
|
|
channelInfo: true,
|
|||
|
|
voiceStatus: true,
|
|||
|
|
events: true,
|
|||
|
|
moderation: false,
|
|||
|
|
},
|
|||
|
|
replyToMode: "off", // off | first | all
|
|||
|
|
dm: {
|
|||
|
|
enabled: true,
|
|||
|
|
policy: "pairing",
|
|||
|
|
allowFrom: ["1234567890", "steipete"],
|
|||
|
|
groupEnabled: false,
|
|||
|
|
groupChannels: ["openclaw-dm"],
|
|||
|
|
},
|
|||
|
|
guilds: {
|
|||
|
|
"123456789012345678": {
|
|||
|
|
slug: "friends-of-openclaw",
|
|||
|
|
requireMention: false,
|
|||
|
|
reactionNotifications: "own",
|
|||
|
|
users: ["987654321098765432"],
|
|||
|
|
channels: {
|
|||
|
|
general: { allow: true },
|
|||
|
|
help: {
|
|||
|
|
allow: true,
|
|||
|
|
requireMention: true,
|
|||
|
|
users: ["987654321098765432"],
|
|||
|
|
skills: ["docs"],
|
|||
|
|
systemPrompt: "Short answers only.",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
historyLimit: 20,
|
|||
|
|
textChunkLimit: 2000,
|
|||
|
|
chunkMode: "length", // length | newline
|
|||
|
|
maxLinesPerMessage: 17,
|
|||
|
|
retry: {
|
|||
|
|
attempts: 3,
|
|||
|
|
minDelayMs: 500,
|
|||
|
|
maxDelayMs: 30000,
|
|||
|
|
jitter: 0.1,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Token: `channels.discord.token`, with `DISCORD_BOT_TOKEN` as fallback for the default account.
|
|||
|
|
- Use `user:<id>` (DM) or `channel:<id>` (guild channel) for delivery targets; bare numeric IDs are rejected.
|
|||
|
|
- Guild slugs are lowercase with spaces replaced by `-`; channel keys use the slugged name (no `#`). Prefer guild IDs.
|
|||
|
|
- Bot-authored messages are ignored by default. `allowBots: true` enables them (own messages still filtered).
|
|||
|
|
- `maxLinesPerMessage` (default 17) splits tall messages even when under 2000 chars.
|
|||
|
|
|
|||
|
|
**Reaction notification modes:** `off` (none), `own` (bot's messages, default), `all` (all messages), `allowlist` (from `guilds.<id>.users` on all messages).
|
|||
|
|
|
|||
|
|
### Google Chat
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
googlechat: {
|
|||
|
|
enabled: true,
|
|||
|
|
serviceAccountFile: "/path/to/service-account.json",
|
|||
|
|
audienceType: "app-url", // app-url | project-number
|
|||
|
|
audience: "https://gateway.example.com/googlechat",
|
|||
|
|
webhookPath: "/googlechat",
|
|||
|
|
botUser: "users/1234567890",
|
|||
|
|
dm: {
|
|||
|
|
enabled: true,
|
|||
|
|
policy: "pairing",
|
|||
|
|
allowFrom: ["users/1234567890"],
|
|||
|
|
},
|
|||
|
|
groupPolicy: "allowlist",
|
|||
|
|
groups: {
|
|||
|
|
"spaces/AAAA": { allow: true, requireMention: true },
|
|||
|
|
},
|
|||
|
|
actions: { reactions: true },
|
|||
|
|
typingIndicator: "message",
|
|||
|
|
mediaMaxMb: 20,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Service account JSON: inline (`serviceAccount`) or file-based (`serviceAccountFile`).
|
|||
|
|
- Env fallbacks: `GOOGLE_CHAT_SERVICE_ACCOUNT` or `GOOGLE_CHAT_SERVICE_ACCOUNT_FILE`.
|
|||
|
|
- Use `spaces/<spaceId>` or `users/<userId|email>` for delivery targets.
|
|||
|
|
|
|||
|
|
### Slack
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
slack: {
|
|||
|
|
enabled: true,
|
|||
|
|
botToken: "xoxb-...",
|
|||
|
|
appToken: "xapp-...",
|
|||
|
|
dm: {
|
|||
|
|
enabled: true,
|
|||
|
|
policy: "pairing",
|
|||
|
|
allowFrom: ["U123", "U456", "*"],
|
|||
|
|
groupEnabled: false,
|
|||
|
|
groupChannels: ["G123"],
|
|||
|
|
},
|
|||
|
|
channels: {
|
|||
|
|
C123: { allow: true, requireMention: true, allowBots: false },
|
|||
|
|
"#general": {
|
|||
|
|
allow: true,
|
|||
|
|
requireMention: true,
|
|||
|
|
allowBots: false,
|
|||
|
|
users: ["U123"],
|
|||
|
|
skills: ["docs"],
|
|||
|
|
systemPrompt: "Short answers only.",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
historyLimit: 50,
|
|||
|
|
allowBots: false,
|
|||
|
|
reactionNotifications: "own",
|
|||
|
|
reactionAllowlist: ["U123"],
|
|||
|
|
replyToMode: "off", // off | first | all
|
|||
|
|
thread: {
|
|||
|
|
historyScope: "thread", // thread | channel
|
|||
|
|
inheritParent: false,
|
|||
|
|
},
|
|||
|
|
actions: {
|
|||
|
|
reactions: true,
|
|||
|
|
messages: true,
|
|||
|
|
pins: true,
|
|||
|
|
memberInfo: true,
|
|||
|
|
emojiList: true,
|
|||
|
|
},
|
|||
|
|
slashCommand: {
|
|||
|
|
enabled: true,
|
|||
|
|
name: "openclaw",
|
|||
|
|
sessionPrefix: "slack:slash",
|
|||
|
|
ephemeral: true,
|
|||
|
|
},
|
|||
|
|
textChunkLimit: 4000,
|
|||
|
|
chunkMode: "length",
|
|||
|
|
mediaMaxMb: 20,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- **Socket mode** requires both `botToken` and `appToken` (`SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` for default account env fallback).
|
|||
|
|
- **HTTP mode** requires `botToken` plus `signingSecret` (at root or per-account).
|
|||
|
|
- `configWrites: false` blocks Slack-initiated config writes.
|
|||
|
|
- Use `user:<id>` (DM) or `channel:<id>` for delivery targets.
|
|||
|
|
|
|||
|
|
**Reaction notification modes:** `off`, `own` (default), `all`, `allowlist` (from `reactionAllowlist`).
|
|||
|
|
|
|||
|
|
**Thread session isolation:** `thread.historyScope` is per-thread (default) or shared across channel. `thread.inheritParent` copies parent channel transcript to new threads.
|
|||
|
|
|
|||
|
|
| Action group | Default | Notes |
|
|||
|
|
| ------------ | ------- | ---------------------- |
|
|||
|
|
| reactions | enabled | React + list reactions |
|
|||
|
|
| messages | enabled | Read/send/edit/delete |
|
|||
|
|
| pins | enabled | Pin/unpin/list |
|
|||
|
|
| memberInfo | enabled | Member info |
|
|||
|
|
| emojiList | enabled | Custom emoji list |
|
|||
|
|
|
|||
|
|
### Mattermost
|
|||
|
|
|
|||
|
|
Mattermost ships as a plugin: `openclaw plugins install @openclaw/mattermost`.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
mattermost: {
|
|||
|
|
enabled: true,
|
|||
|
|
botToken: "mm-token",
|
|||
|
|
baseUrl: "https://chat.example.com",
|
|||
|
|
dmPolicy: "pairing",
|
|||
|
|
chatmode: "oncall", // oncall | onmessage | onchar
|
|||
|
|
oncharPrefixes: [">", "!"],
|
|||
|
|
textChunkLimit: 4000,
|
|||
|
|
chunkMode: "length",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Chat modes: `oncall` (respond on @-mention, default), `onmessage` (every message), `onchar` (messages starting with trigger prefix).
|
|||
|
|
|
|||
|
|
### Signal
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
signal: {
|
|||
|
|
reactionNotifications: "own", // off | own | all | allowlist
|
|||
|
|
reactionAllowlist: ["+15551234567", "uuid:123e4567-e89b-12d3-a456-426614174000"],
|
|||
|
|
historyLimit: 50,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Reaction notification modes:** `off`, `own` (default), `all`, `allowlist` (from `reactionAllowlist`).
|
|||
|
|
|
|||
|
|
### iMessage
|
|||
|
|
|
|||
|
|
OpenClaw spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
imessage: {
|
|||
|
|
enabled: true,
|
|||
|
|
cliPath: "imsg",
|
|||
|
|
dbPath: "~/Library/Messages/chat.db",
|
|||
|
|
remoteHost: "user@gateway-host",
|
|||
|
|
dmPolicy: "pairing",
|
|||
|
|
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
|
|||
|
|
historyLimit: 50,
|
|||
|
|
includeAttachments: false,
|
|||
|
|
mediaMaxMb: 16,
|
|||
|
|
service: "auto",
|
|||
|
|
region: "US",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Requires Full Disk Access to the Messages DB.
|
|||
|
|
- Prefer `chat_id:<id>` targets. Use `imsg chats --limit 20` to list chats.
|
|||
|
|
- `cliPath` can point to an SSH wrapper; set `remoteHost` for SCP attachment fetching.
|
|||
|
|
|
|||
|
|
<Accordion title="iMessage SSH wrapper example">
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
#!/usr/bin/env bash
|
|||
|
|
exec ssh -T gateway-host imsg "$@"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
### Multi-account (all channels)
|
|||
|
|
|
|||
|
|
Run multiple accounts per channel (each with its own `accountId`):
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
telegram: {
|
|||
|
|
accounts: {
|
|||
|
|
default: {
|
|||
|
|
name: "Primary bot",
|
|||
|
|
botToken: "123456:ABC...",
|
|||
|
|
},
|
|||
|
|
alerts: {
|
|||
|
|
name: "Alerts bot",
|
|||
|
|
botToken: "987654:XYZ...",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `default` is used when `accountId` is omitted (CLI + routing).
|
|||
|
|
- Env tokens only apply to the **default** account.
|
|||
|
|
- Base channel settings apply to all accounts unless overridden per account.
|
|||
|
|
- Use `bindings[].match.accountId` to route each account to a different agent.
|
|||
|
|
|
|||
|
|
### Group chat mention gating
|
|||
|
|
|
|||
|
|
Group messages default to **require mention** (metadata mention or regex patterns). Applies to WhatsApp, Telegram, Discord, Google Chat, and iMessage group chats.
|
|||
|
|
|
|||
|
|
**Mention types:**
|
|||
|
|
|
|||
|
|
- **Metadata mentions**: Native platform @-mentions. Ignored in WhatsApp self-chat mode.
|
|||
|
|
- **Text patterns**: Regex patterns in `agents.list[].groupChat.mentionPatterns`. Always checked.
|
|||
|
|
- Mention gating is enforced only when detection is possible (native mentions or at least one pattern).
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
messages: {
|
|||
|
|
groupChat: { historyLimit: 50 },
|
|||
|
|
},
|
|||
|
|
agents: {
|
|||
|
|
list: [{ id: "main", groupChat: { mentionPatterns: ["@openclaw", "openclaw"] } }],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`messages.groupChat.historyLimit` sets the global default. Channels can override with `channels.<channel>.historyLimit` (or per-account). Set `0` to disable.
|
|||
|
|
|
|||
|
|
#### DM history limits
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
telegram: {
|
|||
|
|
dmHistoryLimit: 30,
|
|||
|
|
dms: {
|
|||
|
|
"123456789": { historyLimit: 50 },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Resolution: per-DM override → provider default → no limit (all retained).
|
|||
|
|
|
|||
|
|
Supported: `telegram`, `whatsapp`, `discord`, `slack`, `signal`, `imessage`, `msteams`.
|
|||
|
|
|
|||
|
|
#### Self-chat mode
|
|||
|
|
|
|||
|
|
Include your own number in `allowFrom` to enable self-chat mode (ignores native @-mentions, only responds to text patterns):
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
channels: {
|
|||
|
|
whatsapp: {
|
|||
|
|
allowFrom: ["+15555550123"],
|
|||
|
|
groups: { "*": { requireMention: true } },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "main",
|
|||
|
|
groupChat: { mentionPatterns: ["reisponde", "@openclaw"] },
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Commands (chat command handling)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
commands: {
|
|||
|
|
native: "auto", // register native commands when supported
|
|||
|
|
text: true, // parse /commands in chat messages
|
|||
|
|
bash: false, // allow ! (alias: /bash)
|
|||
|
|
bashForegroundMs: 2000,
|
|||
|
|
config: false, // allow /config
|
|||
|
|
debug: false, // allow /debug
|
|||
|
|
restart: false, // allow /restart + gateway restart tool
|
|||
|
|
allowFrom: {
|
|||
|
|
"*": ["user1"],
|
|||
|
|
discord: ["user:123"],
|
|||
|
|
},
|
|||
|
|
useAccessGroups: true,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Command details">
|
|||
|
|
|
|||
|
|
- Text commands must be **standalone** messages with leading `/`.
|
|||
|
|
- `native: "auto"` turns on native commands for Discord/Telegram, leaves Slack off.
|
|||
|
|
- Override per channel: `channels.discord.commands.native` (bool or `"auto"`). `false` clears previously registered commands.
|
|||
|
|
- `channels.telegram.customCommands` adds extra Telegram bot menu entries.
|
|||
|
|
- `bash: true` enables `! <cmd>` for host shell. Requires `tools.elevated.enabled` and sender in `tools.elevated.allowFrom.<channel>`.
|
|||
|
|
- `config: true` enables `/config` (reads/writes `openclaw.json`).
|
|||
|
|
- `channels.<provider>.configWrites` gates config mutations per channel (default: true).
|
|||
|
|
- `allowFrom` is per-provider. When set, it is the **only** authorization source (channel allowlists/pairing and `useAccessGroups` are ignored).
|
|||
|
|
- `useAccessGroups: false` allows commands to bypass access-group policies when `allowFrom` is not set.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Agent defaults
|
|||
|
|
|
|||
|
|
### `agents.defaults.workspace`
|
|||
|
|
|
|||
|
|
Default: `~/.openclaw/workspace`.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.repoRoot`
|
|||
|
|
|
|||
|
|
Optional repository root shown in the system prompt's Runtime line. If unset, OpenClaw auto-detects by walking upward from the workspace.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { repoRoot: "~/Projects/openclaw" } },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.skipBootstrap`
|
|||
|
|
|
|||
|
|
Disables automatic creation of workspace bootstrap files (`AGENTS.md`, `SOUL.md`, `TOOLS.md`, `IDENTITY.md`, `USER.md`, `HEARTBEAT.md`, `BOOTSTRAP.md`).
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { skipBootstrap: true } },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.bootstrapMaxChars`
|
|||
|
|
|
|||
|
|
Max characters per workspace bootstrap file before truncation. Default: `20000`.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { bootstrapMaxChars: 20000 } },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.userTimezone`
|
|||
|
|
|
|||
|
|
Timezone for system prompt context (not message timestamps). Falls back to host timezone.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { userTimezone: "America/Chicago" } },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.timeFormat`
|
|||
|
|
|
|||
|
|
Time format in system prompt. Default: `auto` (OS preference).
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: { defaults: { timeFormat: "auto" } }, // auto | 12 | 24
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.defaults.model`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
models: {
|
|||
|
|
"anthropic/claude-opus-4-6": { alias: "opus" },
|
|||
|
|
"minimax/MiniMax-M2.1": { alias: "minimax" },
|
|||
|
|
},
|
|||
|
|
model: {
|
|||
|
|
primary: "anthropic/claude-opus-4-6",
|
|||
|
|
fallbacks: ["minimax/MiniMax-M2.1"],
|
|||
|
|
},
|
|||
|
|
imageModel: {
|
|||
|
|
primary: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
|
|||
|
|
fallbacks: ["openrouter/google/gemini-2.0-flash-vision:free"],
|
|||
|
|
},
|
|||
|
|
thinkingDefault: "low",
|
|||
|
|
verboseDefault: "off",
|
|||
|
|
elevatedDefault: "on",
|
|||
|
|
timeoutSeconds: 600,
|
|||
|
|
mediaMaxMb: 5,
|
|||
|
|
contextTokens: 200000,
|
|||
|
|
maxConcurrent: 3,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `model.primary`: format `provider/model` (e.g. `anthropic/claude-opus-4-6`). If you omit the provider, OpenClaw assumes `anthropic` (deprecated).
|
|||
|
|
- `models`: the configured model catalog and allowlist for `/model`. Each entry can include `alias` (shortcut) and `params` (provider-specific: `temperature`, `maxTokens`).
|
|||
|
|
- `imageModel`: only used if the primary model lacks image input.
|
|||
|
|
- `maxConcurrent`: max parallel agent runs across sessions (each session still serialized). Default: 1.
|
|||
|
|
|
|||
|
|
**Built-in alias shorthands** (only apply when the model is in `agents.defaults.models`):
|
|||
|
|
|
|||
|
|
| Alias | Model |
|
|||
|
|
| -------------- | ------------------------------- |
|
|||
|
|
| `opus` | `anthropic/claude-opus-4-6` |
|
|||
|
|
| `sonnet` | `anthropic/claude-sonnet-4-5` |
|
|||
|
|
| `gpt` | `openai/gpt-5.2` |
|
|||
|
|
| `gpt-mini` | `openai/gpt-5-mini` |
|
|||
|
|
| `gemini` | `google/gemini-3-pro-preview` |
|
|||
|
|
| `gemini-flash` | `google/gemini-3-flash-preview` |
|
|||
|
|
|
|||
|
|
Your configured aliases always win over defaults.
|
|||
|
|
|
|||
|
|
Z.AI GLM-4.x models automatically enable thinking mode unless you set `--thinking off` or define `agents.defaults.models["zai/<model>"].params.thinking` yourself.
|
|||
|
|
|
|||
|
|
### `agents.defaults.cliBackends`
|
|||
|
|
|
|||
|
|
Optional CLI backends for text-only fallback runs (no tool calls). Useful as a backup when API providers fail.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
cliBackends: {
|
|||
|
|
"claude-cli": {
|
|||
|
|
command: "/opt/homebrew/bin/claude",
|
|||
|
|
},
|
|||
|
|
"my-cli": {
|
|||
|
|
command: "my-cli",
|
|||
|
|
args: ["--json"],
|
|||
|
|
output: "json",
|
|||
|
|
modelArg: "--model",
|
|||
|
|
sessionArg: "--session",
|
|||
|
|
sessionMode: "existing",
|
|||
|
|
systemPromptArg: "--system",
|
|||
|
|
systemPromptWhen: "first",
|
|||
|
|
imageArg: "--image",
|
|||
|
|
imageMode: "repeat",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- CLI backends are text-first; tools are always disabled.
|
|||
|
|
- Sessions supported when `sessionArg` is set.
|
|||
|
|
- Image pass-through supported when `imageArg` accepts file paths.
|
|||
|
|
|
|||
|
|
### `agents.defaults.heartbeat`
|
|||
|
|
|
|||
|
|
Periodic heartbeat runs.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
heartbeat: {
|
|||
|
|
every: "30m", // 0m disables
|
|||
|
|
model: "openai/gpt-5.2-mini",
|
|||
|
|
includeReasoning: false,
|
|||
|
|
session: "main",
|
|||
|
|
to: "+15555550123",
|
|||
|
|
target: "last", // last | whatsapp | telegram | discord | ... | none
|
|||
|
|
prompt: "Read HEARTBEAT.md if it exists...",
|
|||
|
|
ackMaxChars: 300,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `every`: duration string (ms/s/m/h). Default: `30m`.
|
|||
|
|
- Per-agent: set `agents.list[].heartbeat`. When any agent defines `heartbeat`, **only those agents** run heartbeats.
|
|||
|
|
- Heartbeats run full agent turns — shorter intervals burn more tokens.
|
|||
|
|
|
|||
|
|
### `agents.defaults.compaction`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
compaction: {
|
|||
|
|
mode: "safeguard", // default | safeguard
|
|||
|
|
reserveTokensFloor: 24000,
|
|||
|
|
memoryFlush: {
|
|||
|
|
enabled: true,
|
|||
|
|
softThresholdTokens: 6000,
|
|||
|
|
systemPrompt: "Session nearing compaction. Store durable memories now.",
|
|||
|
|
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `mode`: `default` or `safeguard` (chunked summarization for long histories). See [Compaction](/concepts/compaction).
|
|||
|
|
- `memoryFlush`: silent agentic turn before auto-compaction to store durable memories. Skipped when workspace is read-only.
|
|||
|
|
|
|||
|
|
### `agents.defaults.contextPruning`
|
|||
|
|
|
|||
|
|
Prunes **old tool results** from in-memory context before sending to the LLM. Does **not** modify session history on disk.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
contextPruning: {
|
|||
|
|
mode: "cache-ttl", // off | cache-ttl
|
|||
|
|
ttl: "1h", // duration (ms/s/m/h), default unit: minutes
|
|||
|
|
keepLastAssistants: 3,
|
|||
|
|
softTrimRatio: 0.3,
|
|||
|
|
hardClearRatio: 0.5,
|
|||
|
|
minPrunableToolChars: 50000,
|
|||
|
|
softTrim: { maxChars: 4000, headChars: 1500, tailChars: 1500 },
|
|||
|
|
hardClear: { enabled: true, placeholder: "[Old tool result content cleared]" },
|
|||
|
|
tools: { deny: ["browser", "canvas"] },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="cache-ttl mode behavior">
|
|||
|
|
|
|||
|
|
- `mode: "cache-ttl"` enables pruning passes.
|
|||
|
|
- `ttl` controls how often pruning can run again (after the last cache touch).
|
|||
|
|
- Pruning soft-trims oversized tool results first, then hard-clears older tool results if needed.
|
|||
|
|
|
|||
|
|
**Soft-trim** keeps beginning + end and inserts `...` in the middle.
|
|||
|
|
|
|||
|
|
**Hard-clear** replaces the entire tool result with the placeholder.
|
|||
|
|
|
|||
|
|
Notes:
|
|||
|
|
|
|||
|
|
- Image blocks are never trimmed/cleared.
|
|||
|
|
- Ratios are character-based (approximate), not exact token counts.
|
|||
|
|
- If fewer than `keepLastAssistants` assistant messages exist, pruning is skipped.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
See [Session Pruning](/concepts/session-pruning) for behavior details.
|
|||
|
|
|
|||
|
|
### Block streaming
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
blockStreamingDefault: "off", // on | off
|
|||
|
|
blockStreamingBreak: "text_end", // text_end | message_end
|
|||
|
|
blockStreamingChunk: { minChars: 800, maxChars: 1200 },
|
|||
|
|
blockStreamingCoalesce: { idleMs: 1000 },
|
|||
|
|
humanDelay: { mode: "natural" }, // off | natural | custom (use minMs/maxMs)
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Non-Telegram channels require explicit `*.blockStreaming: true` to enable block replies.
|
|||
|
|
- Channel overrides: `channels.<channel>.blockStreamingCoalesce` (and per-account variants). Signal/Slack/Discord/Google Chat default `minChars: 1500`.
|
|||
|
|
- `humanDelay`: randomized pause between block replies. `natural` = 800–2500ms. Per-agent override: `agents.list[].humanDelay`.
|
|||
|
|
|
|||
|
|
See [Streaming](/concepts/streaming) for behavior + chunking details.
|
|||
|
|
|
|||
|
|
### Typing indicators
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
typingMode: "instant", // never | instant | thinking | message
|
|||
|
|
typingIntervalSeconds: 6,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Defaults: `instant` for direct chats/mentions, `message` for unmentioned group chats.
|
|||
|
|
- Per-session overrides: `session.typingMode`, `session.typingIntervalSeconds`.
|
|||
|
|
|
|||
|
|
See [Typing Indicators](/concepts/typing-indicators).
|
|||
|
|
|
|||
|
|
### `agents.defaults.sandbox`
|
|||
|
|
|
|||
|
|
Optional **Docker sandboxing** for the embedded agent. See [Sandboxing](/gateway/sandboxing) for the full guide.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
sandbox: {
|
|||
|
|
mode: "non-main", // off | non-main | all
|
|||
|
|
scope: "agent", // session | agent | shared
|
|||
|
|
workspaceAccess: "none", // none | ro | rw
|
|||
|
|
workspaceRoot: "~/.openclaw/sandboxes",
|
|||
|
|
docker: {
|
|||
|
|
image: "openclaw-sandbox:bookworm-slim",
|
|||
|
|
containerPrefix: "openclaw-sbx-",
|
|||
|
|
workdir: "/workspace",
|
|||
|
|
readOnlyRoot: true,
|
|||
|
|
tmpfs: ["/tmp", "/var/tmp", "/run"],
|
|||
|
|
network: "none",
|
|||
|
|
user: "1000:1000",
|
|||
|
|
capDrop: ["ALL"],
|
|||
|
|
env: { LANG: "C.UTF-8" },
|
|||
|
|
setupCommand: "apt-get update && apt-get install -y git curl jq",
|
|||
|
|
pidsLimit: 256,
|
|||
|
|
memory: "1g",
|
|||
|
|
memorySwap: "2g",
|
|||
|
|
cpus: 1,
|
|||
|
|
ulimits: {
|
|||
|
|
nofile: { soft: 1024, hard: 2048 },
|
|||
|
|
nproc: 256,
|
|||
|
|
},
|
|||
|
|
seccompProfile: "/path/to/seccomp.json",
|
|||
|
|
apparmorProfile: "openclaw-sandbox",
|
|||
|
|
dns: ["1.1.1.1", "8.8.8.8"],
|
|||
|
|
extraHosts: ["internal.service:10.0.0.5"],
|
|||
|
|
binds: ["/home/user/source:/source:rw"],
|
|||
|
|
},
|
|||
|
|
browser: {
|
|||
|
|
enabled: false,
|
|||
|
|
image: "openclaw-sandbox-browser:bookworm-slim",
|
|||
|
|
cdpPort: 9222,
|
|||
|
|
vncPort: 5900,
|
|||
|
|
noVncPort: 6080,
|
|||
|
|
headless: false,
|
|||
|
|
enableNoVnc: true,
|
|||
|
|
allowHostControl: false,
|
|||
|
|
autoStart: true,
|
|||
|
|
autoStartTimeoutMs: 12000,
|
|||
|
|
},
|
|||
|
|
prune: {
|
|||
|
|
idleHours: 24,
|
|||
|
|
maxAgeDays: 7,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
tools: {
|
|||
|
|
sandbox: {
|
|||
|
|
tools: {
|
|||
|
|
allow: [
|
|||
|
|
"exec",
|
|||
|
|
"process",
|
|||
|
|
"read",
|
|||
|
|
"write",
|
|||
|
|
"edit",
|
|||
|
|
"apply_patch",
|
|||
|
|
"sessions_list",
|
|||
|
|
"sessions_history",
|
|||
|
|
"sessions_send",
|
|||
|
|
"sessions_spawn",
|
|||
|
|
"session_status",
|
|||
|
|
],
|
|||
|
|
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Sandbox details">
|
|||
|
|
|
|||
|
|
**Workspace access:**
|
|||
|
|
|
|||
|
|
- `none`: per-scope sandbox workspace under `~/.openclaw/sandboxes`
|
|||
|
|
- `ro`: sandbox workspace at `/workspace`, agent workspace mounted read-only at `/agent`
|
|||
|
|
- `rw`: agent workspace mounted read/write at `/workspace`
|
|||
|
|
|
|||
|
|
**Scope:**
|
|||
|
|
|
|||
|
|
- `session`: per-session container + workspace
|
|||
|
|
- `agent`: one container + workspace per agent (default)
|
|||
|
|
- `shared`: shared container and workspace (no cross-session isolation)
|
|||
|
|
|
|||
|
|
**`setupCommand`** runs once after container creation (via `sh -lc`). Needs network egress, writable root, root user.
|
|||
|
|
|
|||
|
|
**Containers default to `network: "none"`** — set to `"bridge"` if the agent needs outbound access.
|
|||
|
|
|
|||
|
|
**Inbound attachments** are staged into `media/inbound/*` in the active workspace.
|
|||
|
|
|
|||
|
|
**`docker.binds`** mounts additional host directories; global and per-agent binds are merged.
|
|||
|
|
|
|||
|
|
**Sandboxed browser** (`sandbox.browser.enabled`): Chromium + CDP in a container. noVNC URL injected into system prompt. Does not require `browser.enabled` in main config.
|
|||
|
|
|
|||
|
|
- `allowHostControl: false` (default) blocks sandboxed sessions from targeting the host browser.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
Build images:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
scripts/sandbox-setup.sh # main sandbox image
|
|||
|
|
scripts/sandbox-browser-setup.sh # optional browser image
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `agents.list` (per-agent overrides)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "main",
|
|||
|
|
default: true,
|
|||
|
|
name: "Main Agent",
|
|||
|
|
workspace: "~/.openclaw/workspace",
|
|||
|
|
agentDir: "~/.openclaw/agents/main/agent",
|
|||
|
|
model: "anthropic/claude-opus-4-6", // or { primary, fallbacks }
|
|||
|
|
identity: {
|
|||
|
|
name: "Samantha",
|
|||
|
|
theme: "helpful sloth",
|
|||
|
|
emoji: "🦥",
|
|||
|
|
avatar: "avatars/samantha.png",
|
|||
|
|
},
|
|||
|
|
groupChat: { mentionPatterns: ["@openclaw"] },
|
|||
|
|
sandbox: { mode: "off" },
|
|||
|
|
subagents: { allowAgents: ["*"] },
|
|||
|
|
tools: {
|
|||
|
|
profile: "coding",
|
|||
|
|
allow: ["browser"],
|
|||
|
|
deny: ["canvas"],
|
|||
|
|
elevated: { enabled: true },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `id`: stable agent id (required).
|
|||
|
|
- `default`: when multiple are set, first wins (warning logged). If none set, first list entry is default.
|
|||
|
|
- `model`: string form overrides `primary` only; object form `{ primary, fallbacks }` overrides both (`[]` disables global fallbacks).
|
|||
|
|
- `identity.avatar`: workspace-relative path, `http(s)` URL, or `data:` URI.
|
|||
|
|
- `identity` derives defaults: `ackReaction` from `emoji`, `mentionPatterns` from `name`/`emoji`.
|
|||
|
|
- `subagents.allowAgents`: allowlist of agent ids for `sessions_spawn` (`["*"]` = any; default: same agent only).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Multi-agent routing
|
|||
|
|
|
|||
|
|
Run multiple isolated agents inside one Gateway. See [Multi-Agent](/concepts/multi-agent).
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
|
|||
|
|
{ id: "work", workspace: "~/.openclaw/workspace-work" },
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
bindings: [
|
|||
|
|
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
|
|||
|
|
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
|
|||
|
|
],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Binding match fields
|
|||
|
|
|
|||
|
|
- `match.channel` (required)
|
|||
|
|
- `match.accountId` (optional; `*` = any account; omitted = default account)
|
|||
|
|
- `match.peer` (optional; `{ kind: direct|group|channel, id }`)
|
|||
|
|
- `match.guildId` / `match.teamId` (optional; channel-specific)
|
|||
|
|
|
|||
|
|
**Deterministic match order:**
|
|||
|
|
|
|||
|
|
1. `match.peer`
|
|||
|
|
2. `match.guildId`
|
|||
|
|
3. `match.teamId`
|
|||
|
|
4. `match.accountId` (exact, no peer/guild/team)
|
|||
|
|
5. `match.accountId: "*"` (channel-wide)
|
|||
|
|
6. Default agent
|
|||
|
|
|
|||
|
|
Within each tier, the first matching `bindings` entry wins.
|
|||
|
|
|
|||
|
|
### Per-agent access profiles
|
|||
|
|
|
|||
|
|
<Accordion title="Full access (no sandbox)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "personal",
|
|||
|
|
workspace: "~/.openclaw/workspace-personal",
|
|||
|
|
sandbox: { mode: "off" },
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Read-only tools + workspace">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "family",
|
|||
|
|
workspace: "~/.openclaw/workspace-family",
|
|||
|
|
sandbox: { mode: "all", scope: "agent", workspaceAccess: "ro" },
|
|||
|
|
tools: {
|
|||
|
|
allow: [
|
|||
|
|
"read",
|
|||
|
|
"sessions_list",
|
|||
|
|
"sessions_history",
|
|||
|
|
"sessions_send",
|
|||
|
|
"sessions_spawn",
|
|||
|
|
"session_status",
|
|||
|
|
],
|
|||
|
|
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="No filesystem access (messaging only)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "public",
|
|||
|
|
workspace: "~/.openclaw/workspace-public",
|
|||
|
|
sandbox: { mode: "all", scope: "agent", workspaceAccess: "none" },
|
|||
|
|
tools: {
|
|||
|
|
allow: [
|
|||
|
|
"sessions_list",
|
|||
|
|
"sessions_history",
|
|||
|
|
"sessions_send",
|
|||
|
|
"sessions_spawn",
|
|||
|
|
"session_status",
|
|||
|
|
"whatsapp",
|
|||
|
|
"telegram",
|
|||
|
|
"slack",
|
|||
|
|
"discord",
|
|||
|
|
"gateway",
|
|||
|
|
],
|
|||
|
|
deny: [
|
|||
|
|
"read",
|
|||
|
|
"write",
|
|||
|
|
"edit",
|
|||
|
|
"apply_patch",
|
|||
|
|
"exec",
|
|||
|
|
"process",
|
|||
|
|
"browser",
|
|||
|
|
"canvas",
|
|||
|
|
"nodes",
|
|||
|
|
"cron",
|
|||
|
|
"gateway",
|
|||
|
|
"image",
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
See [Multi-Agent Sandbox & Tools](/tools/multi-agent-sandbox-tools) for precedence details.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Session
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
session: {
|
|||
|
|
scope: "per-sender",
|
|||
|
|
dmScope: "main", // main | per-peer | per-channel-peer | per-account-channel-peer
|
|||
|
|
identityLinks: {
|
|||
|
|
alice: ["telegram:123456789", "discord:987654321012345678"],
|
|||
|
|
},
|
|||
|
|
reset: {
|
|||
|
|
mode: "daily", // daily | idle
|
|||
|
|
atHour: 4,
|
|||
|
|
idleMinutes: 60,
|
|||
|
|
},
|
|||
|
|
resetByType: {
|
|||
|
|
thread: { mode: "daily", atHour: 4 },
|
|||
|
|
direct: { mode: "idle", idleMinutes: 240 },
|
|||
|
|
group: { mode: "idle", idleMinutes: 120 },
|
|||
|
|
},
|
|||
|
|
resetTriggers: ["/new", "/reset"],
|
|||
|
|
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
|
|||
|
|
maintenance: {
|
|||
|
|
mode: "warn", // warn | enforce
|
|||
|
|
pruneAfter: "30d",
|
|||
|
|
maxEntries: 500,
|
|||
|
|
rotateBytes: "10mb",
|
|||
|
|
},
|
|||
|
|
mainKey: "main", // legacy (runtime always uses "main")
|
|||
|
|
agentToAgent: { maxPingPongTurns: 5 },
|
|||
|
|
sendPolicy: {
|
|||
|
|
rules: [{ action: "deny", match: { channel: "discord", chatType: "group" } }],
|
|||
|
|
default: "allow",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Session field details">
|
|||
|
|
|
|||
|
|
- **`dmScope`**: how DMs are grouped.
|
|||
|
|
- `main`: all DMs share the main session.
|
|||
|
|
- `per-peer`: isolate by sender id across channels.
|
|||
|
|
- `per-channel-peer`: isolate per channel + sender (recommended for multi-user inboxes).
|
|||
|
|
- `per-account-channel-peer`: isolate per account + channel + sender (recommended for multi-account).
|
|||
|
|
- **`identityLinks`**: map canonical ids to provider-prefixed peers for cross-channel session sharing.
|
|||
|
|
- **`reset`**: primary reset policy. `daily` resets at `atHour` local time; `idle` resets after `idleMinutes`. When both configured, whichever expires first wins.
|
|||
|
|
- **`resetByType`**: per-type overrides (`direct`, `group`, `thread`). Legacy `dm` accepted as alias for `direct`.
|
|||
|
|
- **`mainKey`**: legacy field. Runtime now always uses `"main"` for the main direct-chat bucket.
|
|||
|
|
- **`sendPolicy`**: match by `channel`, `chatType` (`direct|group|channel`, with legacy `dm` alias), or `keyPrefix`. First deny wins.
|
|||
|
|
- **`maintenance`**: `warn` warns the active session on eviction; `enforce` applies pruning and rotation.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Messages
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
messages: {
|
|||
|
|
responsePrefix: "🦞", // or "auto"
|
|||
|
|
ackReaction: "👀",
|
|||
|
|
ackReactionScope: "group-mentions", // group-mentions | group-all | direct | all
|
|||
|
|
removeAckAfterReply: false,
|
|||
|
|
queue: {
|
|||
|
|
mode: "collect", // steer | followup | collect | steer-backlog | steer+backlog | queue | interrupt
|
|||
|
|
debounceMs: 1000,
|
|||
|
|
cap: 20,
|
|||
|
|
drop: "summarize", // old | new | summarize
|
|||
|
|
byChannel: {
|
|||
|
|
whatsapp: "collect",
|
|||
|
|
telegram: "collect",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
inbound: {
|
|||
|
|
debounceMs: 2000, // 0 disables
|
|||
|
|
byChannel: {
|
|||
|
|
whatsapp: 5000,
|
|||
|
|
slack: 1500,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Response prefix
|
|||
|
|
|
|||
|
|
Per-channel/account overrides: `channels.<channel>.responsePrefix`, `channels.<channel>.accounts.<id>.responsePrefix`.
|
|||
|
|
|
|||
|
|
Resolution (most specific wins): account → channel → global. `""` disables and stops cascade. `"auto"` derives `[{identity.name}]`.
|
|||
|
|
|
|||
|
|
**Template variables:**
|
|||
|
|
|
|||
|
|
| Variable | Description | Example |
|
|||
|
|
| ----------------- | ---------------------- | --------------------------- |
|
|||
|
|
| `{model}` | Short model name | `claude-opus-4-6` |
|
|||
|
|
| `{modelFull}` | Full model identifier | `anthropic/claude-opus-4-6` |
|
|||
|
|
| `{provider}` | Provider name | `anthropic` |
|
|||
|
|
| `{thinkingLevel}` | Current thinking level | `high`, `low`, `off` |
|
|||
|
|
| `{identity.name}` | Agent identity name | (same as `"auto"`) |
|
|||
|
|
|
|||
|
|
Variables are case-insensitive. `{think}` is an alias for `{thinkingLevel}`.
|
|||
|
|
|
|||
|
|
### Ack reaction
|
|||
|
|
|
|||
|
|
- Defaults to active agent's `identity.emoji`, otherwise `"👀"`. Set `""` to disable.
|
|||
|
|
- Scope: `group-mentions` (default), `group-all`, `direct`, `all`.
|
|||
|
|
- `removeAckAfterReply`: removes ack after reply (Slack/Discord/Telegram/Google Chat only).
|
|||
|
|
|
|||
|
|
### Inbound debounce
|
|||
|
|
|
|||
|
|
Batches rapid text-only messages from the same sender into a single agent turn. Media/attachments flush immediately. Control commands bypass debouncing.
|
|||
|
|
|
|||
|
|
### TTS (text-to-speech)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
messages: {
|
|||
|
|
tts: {
|
|||
|
|
auto: "always", // off | always | inbound | tagged
|
|||
|
|
mode: "final", // final | all
|
|||
|
|
provider: "elevenlabs",
|
|||
|
|
summaryModel: "openai/gpt-4.1-mini",
|
|||
|
|
modelOverrides: { enabled: true },
|
|||
|
|
maxTextLength: 4000,
|
|||
|
|
timeoutMs: 30000,
|
|||
|
|
prefsPath: "~/.openclaw/settings/tts.json",
|
|||
|
|
elevenlabs: {
|
|||
|
|
apiKey: "elevenlabs_api_key",
|
|||
|
|
baseUrl: "https://api.elevenlabs.io",
|
|||
|
|
voiceId: "voice_id",
|
|||
|
|
modelId: "eleven_multilingual_v2",
|
|||
|
|
seed: 42,
|
|||
|
|
applyTextNormalization: "auto",
|
|||
|
|
languageCode: "en",
|
|||
|
|
voiceSettings: {
|
|||
|
|
stability: 0.5,
|
|||
|
|
similarityBoost: 0.75,
|
|||
|
|
style: 0.0,
|
|||
|
|
useSpeakerBoost: true,
|
|||
|
|
speed: 1.0,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
openai: {
|
|||
|
|
apiKey: "openai_api_key",
|
|||
|
|
model: "gpt-4o-mini-tts",
|
|||
|
|
voice: "alloy",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `auto` controls auto-TTS. `/tts off|always|inbound|tagged` overrides per session.
|
|||
|
|
- `summaryModel` overrides `agents.defaults.model.primary` for auto-summary.
|
|||
|
|
- API keys fall back to `ELEVENLABS_API_KEY`/`XI_API_KEY` and `OPENAI_API_KEY`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Talk
|
|||
|
|
|
|||
|
|
Defaults for Talk mode (macOS/iOS/Android).
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
talk: {
|
|||
|
|
voiceId: "elevenlabs_voice_id",
|
|||
|
|
voiceAliases: {
|
|||
|
|
Clawd: "EXAVITQu4vr4xnSDxMaL",
|
|||
|
|
Roger: "CwhRBWXzGAHq8TQ4Fs17",
|
|||
|
|
},
|
|||
|
|
modelId: "eleven_v3",
|
|||
|
|
outputFormat: "mp3_44100_128",
|
|||
|
|
apiKey: "elevenlabs_api_key",
|
|||
|
|
interruptOnSpeech: true,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID`.
|
|||
|
|
- `apiKey` falls back to `ELEVENLABS_API_KEY`.
|
|||
|
|
- `voiceAliases` lets Talk directives use friendly names.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Tools
|
|||
|
|
|
|||
|
|
### Tool profiles
|
|||
|
|
|
|||
|
|
`tools.profile` sets a base allowlist before `tools.allow`/`tools.deny`:
|
|||
|
|
|
|||
|
|
| Profile | Includes |
|
|||
|
|
| ----------- | ----------------------------------------------------------------------------------------- |
|
|||
|
|
| `minimal` | `session_status` only |
|
|||
|
|
| `coding` | `group:fs`, `group:runtime`, `group:sessions`, `group:memory`, `image` |
|
|||
|
|
| `messaging` | `group:messaging`, `sessions_list`, `sessions_history`, `sessions_send`, `session_status` |
|
|||
|
|
| `full` | No restriction (same as unset) |
|
|||
|
|
|
|||
|
|
### Tool groups
|
|||
|
|
|
|||
|
|
| Group | Tools |
|
|||
|
|
| ------------------ | ---------------------------------------------------------------------------------------- |
|
|||
|
|
| `group:runtime` | `exec`, `process` (`bash` is accepted as an alias for `exec`) |
|
|||
|
|
| `group:fs` | `read`, `write`, `edit`, `apply_patch` |
|
|||
|
|
| `group:sessions` | `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`, `session_status` |
|
|||
|
|
| `group:memory` | `memory_search`, `memory_get` |
|
|||
|
|
| `group:web` | `web_search`, `web_fetch` |
|
|||
|
|
| `group:ui` | `browser`, `canvas` |
|
|||
|
|
| `group:automation` | `cron`, `gateway` |
|
|||
|
|
| `group:messaging` | `message` |
|
|||
|
|
| `group:nodes` | `nodes` |
|
|||
|
|
| `group:openclaw` | All built-in tools (excludes provider plugins) |
|
|||
|
|
|
|||
|
|
### `tools.allow` / `tools.deny`
|
|||
|
|
|
|||
|
|
Global tool allow/deny policy (deny wins). Case-insensitive, supports `*` wildcards. Applied even when Docker sandbox is off.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: { deny: ["browser", "canvas"] },
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `tools.byProvider`
|
|||
|
|
|
|||
|
|
Further restrict tools for specific providers or models. Order: base profile → provider profile → allow/deny.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
profile: "coding",
|
|||
|
|
byProvider: {
|
|||
|
|
"google-antigravity": { profile: "minimal" },
|
|||
|
|
"openai/gpt-5.2": { allow: ["group:fs", "sessions_list"] },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `tools.elevated`
|
|||
|
|
|
|||
|
|
Controls elevated (host) exec access:
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
elevated: {
|
|||
|
|
enabled: true,
|
|||
|
|
allowFrom: {
|
|||
|
|
whatsapp: ["+15555550123"],
|
|||
|
|
discord: ["steipete", "1234567890123"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Per-agent override (`agents.list[].tools.elevated`) can only further restrict.
|
|||
|
|
- `/elevated on|off|ask|full` stores state per session; inline directives apply to single message.
|
|||
|
|
- Elevated `exec` runs on the host, bypasses sandboxing.
|
|||
|
|
|
|||
|
|
### `tools.exec`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
exec: {
|
|||
|
|
backgroundMs: 10000,
|
|||
|
|
timeoutSec: 1800,
|
|||
|
|
cleanupMs: 1800000,
|
|||
|
|
notifyOnExit: true,
|
|||
|
|
applyPatch: {
|
|||
|
|
enabled: false,
|
|||
|
|
allowModels: ["gpt-5.2"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `tools.web`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
web: {
|
|||
|
|
search: {
|
|||
|
|
enabled: true,
|
|||
|
|
apiKey: "brave_api_key", // or BRAVE_API_KEY env
|
|||
|
|
maxResults: 5,
|
|||
|
|
timeoutSeconds: 30,
|
|||
|
|
cacheTtlMinutes: 15,
|
|||
|
|
},
|
|||
|
|
fetch: {
|
|||
|
|
enabled: true,
|
|||
|
|
maxChars: 50000,
|
|||
|
|
maxCharsCap: 50000,
|
|||
|
|
timeoutSeconds: 30,
|
|||
|
|
cacheTtlMinutes: 15,
|
|||
|
|
userAgent: "custom-ua",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `tools.media`
|
|||
|
|
|
|||
|
|
Configures inbound media understanding (image/audio/video):
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
media: {
|
|||
|
|
concurrency: 2,
|
|||
|
|
audio: {
|
|||
|
|
enabled: true,
|
|||
|
|
maxBytes: 20971520,
|
|||
|
|
scope: {
|
|||
|
|
default: "deny",
|
|||
|
|
rules: [{ action: "allow", match: { chatType: "direct" } }],
|
|||
|
|
},
|
|||
|
|
models: [
|
|||
|
|
{ provider: "openai", model: "gpt-4o-mini-transcribe" },
|
|||
|
|
{ type: "cli", command: "whisper", args: ["--model", "base", "{{MediaPath}}"] },
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
video: {
|
|||
|
|
enabled: true,
|
|||
|
|
maxBytes: 52428800,
|
|||
|
|
models: [{ provider: "google", model: "gemini-3-flash-preview" }],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Media model entry fields">
|
|||
|
|
|
|||
|
|
**Provider entry** (`type: "provider"` or omitted):
|
|||
|
|
|
|||
|
|
- `provider`: API provider id (`openai`, `anthropic`, `google`/`gemini`, `groq`, etc.)
|
|||
|
|
- `model`: model id override
|
|||
|
|
- `profile` / `preferredProfile`: auth profile selection
|
|||
|
|
|
|||
|
|
**CLI entry** (`type: "cli"`):
|
|||
|
|
|
|||
|
|
- `command`: executable to run
|
|||
|
|
- `args`: templated args (supports `{{MediaPath}}`, `{{Prompt}}`, `{{MaxChars}}`, etc.)
|
|||
|
|
|
|||
|
|
**Common fields:**
|
|||
|
|
|
|||
|
|
- `capabilities`: optional list (`image`, `audio`, `video`). Defaults: `openai`/`anthropic`/`minimax` → image, `google` → image+audio+video, `groq` → audio.
|
|||
|
|
- `prompt`, `maxChars`, `maxBytes`, `timeoutSeconds`, `language`: per-entry overrides.
|
|||
|
|
- Failures fall back to the next entry.
|
|||
|
|
|
|||
|
|
Provider auth follows standard order: auth profiles → env vars → `models.providers.*.apiKey`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
### `tools.agentToAgent`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
tools: {
|
|||
|
|
agentToAgent: {
|
|||
|
|
enabled: false,
|
|||
|
|
allow: ["home", "work"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### `tools.subagents`
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
subagents: {
|
|||
|
|
model: "minimax/MiniMax-M2.1",
|
|||
|
|
maxConcurrent: 1,
|
|||
|
|
archiveAfterMinutes: 60,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `model`: default model for spawned sub-agents. If omitted, sub-agents inherit the caller's model.
|
|||
|
|
- Per-subagent tool policy: `tools.subagents.tools.allow` / `tools.subagents.tools.deny`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Custom providers and base URLs
|
|||
|
|
|
|||
|
|
OpenClaw uses the pi-coding-agent model catalog. Add custom providers via `models.providers` in config or `~/.openclaw/agents/<agentId>/agent/models.json`.
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
models: {
|
|||
|
|
mode: "merge", // merge (default) | replace
|
|||
|
|
providers: {
|
|||
|
|
"custom-proxy": {
|
|||
|
|
baseUrl: "http://localhost:4000/v1",
|
|||
|
|
apiKey: "LITELLM_KEY",
|
|||
|
|
api: "openai-completions", // openai-completions | openai-responses | anthropic-messages | google-generative-ai
|
|||
|
|
models: [
|
|||
|
|
{
|
|||
|
|
id: "llama-3.1-8b",
|
|||
|
|
name: "Llama 3.1 8B",
|
|||
|
|
reasoning: false,
|
|||
|
|
input: ["text"],
|
|||
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|||
|
|
contextWindow: 128000,
|
|||
|
|
maxTokens: 32000,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Use `authHeader: true` + `headers` for custom auth needs.
|
|||
|
|
- Override agent config root with `OPENCLAW_AGENT_DIR` (or `PI_CODING_AGENT_DIR`).
|
|||
|
|
|
|||
|
|
### Provider examples
|
|||
|
|
|
|||
|
|
<Accordion title="Cerebras (GLM 4.6 / 4.7)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
env: { CEREBRAS_API_KEY: "sk-..." },
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: {
|
|||
|
|
primary: "cerebras/zai-glm-4.7",
|
|||
|
|
fallbacks: ["cerebras/zai-glm-4.6"],
|
|||
|
|
},
|
|||
|
|
models: {
|
|||
|
|
"cerebras/zai-glm-4.7": { alias: "GLM 4.7 (Cerebras)" },
|
|||
|
|
"cerebras/zai-glm-4.6": { alias: "GLM 4.6 (Cerebras)" },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
models: {
|
|||
|
|
mode: "merge",
|
|||
|
|
providers: {
|
|||
|
|
cerebras: {
|
|||
|
|
baseUrl: "https://api.cerebras.ai/v1",
|
|||
|
|
apiKey: "${CEREBRAS_API_KEY}",
|
|||
|
|
api: "openai-completions",
|
|||
|
|
models: [
|
|||
|
|
{ id: "zai-glm-4.7", name: "GLM 4.7 (Cerebras)" },
|
|||
|
|
{ id: "zai-glm-4.6", name: "GLM 4.6 (Cerebras)" },
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Use `cerebras/zai-glm-4.7` for Cerebras; `zai/glm-4.7` for Z.AI direct.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="OpenCode Zen">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "opencode/claude-opus-4-6" },
|
|||
|
|
models: { "opencode/claude-opus-4-6": { alias: "Opus" } },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Set `OPENCODE_API_KEY` (or `OPENCODE_ZEN_API_KEY`). Shortcut: `openclaw onboard --auth-choice opencode-zen`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Z.AI (GLM-4.7)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "zai/glm-4.7" },
|
|||
|
|
models: { "zai/glm-4.7": {} },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Set `ZAI_API_KEY`. `z.ai/*` and `z-ai/*` are accepted aliases. Shortcut: `openclaw onboard --auth-choice zai-api-key`.
|
|||
|
|
|
|||
|
|
- General endpoint: `https://api.z.ai/api/paas/v4`
|
|||
|
|
- Coding endpoint (default): `https://api.z.ai/api/coding/paas/v4`
|
|||
|
|
- For the general endpoint, define a custom provider with the base URL override.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Moonshot AI (Kimi)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
env: { MOONSHOT_API_KEY: "sk-..." },
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "moonshot/kimi-k2.5" },
|
|||
|
|
models: { "moonshot/kimi-k2.5": { alias: "Kimi K2.5" } },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
models: {
|
|||
|
|
mode: "merge",
|
|||
|
|
providers: {
|
|||
|
|
moonshot: {
|
|||
|
|
baseUrl: "https://api.moonshot.ai/v1",
|
|||
|
|
apiKey: "${MOONSHOT_API_KEY}",
|
|||
|
|
api: "openai-completions",
|
|||
|
|
models: [
|
|||
|
|
{
|
|||
|
|
id: "kimi-k2.5",
|
|||
|
|
name: "Kimi K2.5",
|
|||
|
|
reasoning: false,
|
|||
|
|
input: ["text"],
|
|||
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|||
|
|
contextWindow: 256000,
|
|||
|
|
maxTokens: 8192,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
For the China endpoint: `baseUrl: "https://api.moonshot.cn/v1"` or `openclaw onboard --auth-choice moonshot-api-key-cn`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Kimi Coding">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
env: { KIMI_API_KEY: "sk-..." },
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "kimi-coding/k2p5" },
|
|||
|
|
models: { "kimi-coding/k2p5": { alias: "Kimi K2.5" } },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Anthropic-compatible, built-in provider. Shortcut: `openclaw onboard --auth-choice kimi-code-api-key`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Synthetic (Anthropic-compatible)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
env: { SYNTHETIC_API_KEY: "sk-..." },
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "synthetic/hf:MiniMaxAI/MiniMax-M2.1" },
|
|||
|
|
models: { "synthetic/hf:MiniMaxAI/MiniMax-M2.1": { alias: "MiniMax M2.1" } },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
models: {
|
|||
|
|
mode: "merge",
|
|||
|
|
providers: {
|
|||
|
|
synthetic: {
|
|||
|
|
baseUrl: "https://api.synthetic.new/anthropic",
|
|||
|
|
apiKey: "${SYNTHETIC_API_KEY}",
|
|||
|
|
api: "anthropic-messages",
|
|||
|
|
models: [
|
|||
|
|
{
|
|||
|
|
id: "hf:MiniMaxAI/MiniMax-M2.1",
|
|||
|
|
name: "MiniMax M2.1",
|
|||
|
|
reasoning: false,
|
|||
|
|
input: ["text"],
|
|||
|
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|||
|
|
contextWindow: 192000,
|
|||
|
|
maxTokens: 65536,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Base URL should omit `/v1` (Anthropic client appends it). Shortcut: `openclaw onboard --auth-choice synthetic-api-key`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="MiniMax M2.1 (direct)">
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
defaults: {
|
|||
|
|
model: { primary: "minimax/MiniMax-M2.1" },
|
|||
|
|
models: {
|
|||
|
|
"minimax/MiniMax-M2.1": { alias: "Minimax" },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
models: {
|
|||
|
|
mode: "merge",
|
|||
|
|
providers: {
|
|||
|
|
minimax: {
|
|||
|
|
baseUrl: "https://api.minimax.io/anthropic",
|
|||
|
|
apiKey: "${MINIMAX_API_KEY}",
|
|||
|
|
api: "anthropic-messages",
|
|||
|
|
models: [
|
|||
|
|
{
|
|||
|
|
id: "MiniMax-M2.1",
|
|||
|
|
name: "MiniMax M2.1",
|
|||
|
|
reasoning: false,
|
|||
|
|
input: ["text"],
|
|||
|
|
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
|
|||
|
|
contextWindow: 200000,
|
|||
|
|
maxTokens: 8192,
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Set `MINIMAX_API_KEY`. Shortcut: `openclaw onboard --auth-choice minimax-api`.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
<Accordion title="Local models (LM Studio)">
|
|||
|
|
|
|||
|
|
See [Local Models](/gateway/local-models). TL;DR: run MiniMax M2.1 via LM Studio Responses API on serious hardware; keep hosted models merged for fallback.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Skills
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
skills: {
|
|||
|
|
allowBundled: ["gemini", "peekaboo"],
|
|||
|
|
load: {
|
|||
|
|
extraDirs: ["~/Projects/agent-scripts/skills"],
|
|||
|
|
},
|
|||
|
|
install: {
|
|||
|
|
preferBrew: true,
|
|||
|
|
nodeManager: "npm", // npm | pnpm | yarn
|
|||
|
|
},
|
|||
|
|
entries: {
|
|||
|
|
"nano-banana-pro": {
|
|||
|
|
apiKey: "GEMINI_KEY_HERE",
|
|||
|
|
env: { GEMINI_API_KEY: "GEMINI_KEY_HERE" },
|
|||
|
|
},
|
|||
|
|
peekaboo: { enabled: true },
|
|||
|
|
sag: { enabled: false },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `allowBundled`: optional allowlist for bundled skills only (managed/workspace skills unaffected).
|
|||
|
|
- `entries.<skillKey>.enabled: false` disables a skill even if bundled/installed.
|
|||
|
|
- `entries.<skillKey>.apiKey`: convenience for skills declaring a primary env var.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Plugins
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
plugins: {
|
|||
|
|
enabled: true,
|
|||
|
|
allow: ["voice-call"],
|
|||
|
|
deny: [],
|
|||
|
|
load: {
|
|||
|
|
paths: ["~/Projects/oss/voice-call-extension"],
|
|||
|
|
},
|
|||
|
|
entries: {
|
|||
|
|
"voice-call": {
|
|||
|
|
enabled: true,
|
|||
|
|
config: { provider: "twilio" },
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Loaded from `~/.openclaw/extensions`, `<workspace>/.openclaw/extensions`, plus `plugins.load.paths`.
|
|||
|
|
- **Config changes require a gateway restart.**
|
|||
|
|
- `allow`: optional allowlist (only listed plugins load). `deny` wins.
|
|||
|
|
|
|||
|
|
See [Plugins](/tools/plugin).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Browser
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
browser: {
|
|||
|
|
enabled: true,
|
|||
|
|
evaluateEnabled: true,
|
|||
|
|
defaultProfile: "chrome",
|
|||
|
|
profiles: {
|
|||
|
|
openclaw: { cdpPort: 18800, color: "#FF4500" },
|
|||
|
|
work: { cdpPort: 18801, color: "#0066CC" },
|
|||
|
|
remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" },
|
|||
|
|
},
|
|||
|
|
color: "#FF4500",
|
|||
|
|
// headless: false,
|
|||
|
|
// noSandbox: false,
|
|||
|
|
// executablePath: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
|
|||
|
|
// attachOnly: false,
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `evaluateEnabled: false` disables `act:evaluate` and `wait --fn`.
|
|||
|
|
- Remote profiles are attach-only (start/stop/reset disabled).
|
|||
|
|
- Auto-detect order: default browser if Chromium-based → Chrome → Brave → Edge → Chromium → Chrome Canary.
|
|||
|
|
- Control service: loopback only (port derived from `gateway.port`, default `18791`).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## UI
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
ui: {
|
|||
|
|
seamColor: "#FF4500",
|
|||
|
|
assistant: {
|
|||
|
|
name: "OpenClaw",
|
|||
|
|
avatar: "CB", // emoji, short text, image URL, or data URI
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `seamColor`: accent color for native app UI chrome (Talk Mode bubble tint, etc.).
|
|||
|
|
- `assistant`: Control UI identity override. Falls back to active agent identity.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Gateway
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
gateway: {
|
|||
|
|
mode: "local", // local | remote
|
|||
|
|
port: 18789,
|
|||
|
|
bind: "loopback",
|
|||
|
|
auth: {
|
|||
|
|
mode: "token", // token | password
|
|||
|
|
token: "your-token",
|
|||
|
|
// password: "your-password", // or OPENCLAW_GATEWAY_PASSWORD
|
|||
|
|
allowTailscale: true,
|
|||
|
|
},
|
|||
|
|
tailscale: {
|
|||
|
|
mode: "off", // off | serve | funnel
|
|||
|
|
resetOnExit: false,
|
|||
|
|
},
|
|||
|
|
controlUi: {
|
|||
|
|
enabled: true,
|
|||
|
|
basePath: "/openclaw",
|
|||
|
|
// root: "dist/control-ui",
|
|||
|
|
// allowInsecureAuth: false,
|
|||
|
|
// dangerouslyDisableDeviceAuth: false,
|
|||
|
|
},
|
|||
|
|
remote: {
|
|||
|
|
url: "ws://gateway.tailnet:18789",
|
|||
|
|
transport: "ssh", // ssh | direct
|
|||
|
|
token: "your-token",
|
|||
|
|
// password: "your-password",
|
|||
|
|
},
|
|||
|
|
trustedProxies: ["10.0.0.1"],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
<Accordion title="Gateway field details">
|
|||
|
|
|
|||
|
|
- `mode`: `local` (run gateway) or `remote` (connect to remote gateway). Gateway refuses to start unless `local`.
|
|||
|
|
- `port`: single multiplexed port for WS + HTTP. Precedence: `--port` > `OPENCLAW_GATEWAY_PORT` > `gateway.port` > `18789`.
|
|||
|
|
- `bind`: `auto`, `loopback` (default), `lan` (`0.0.0.0`), `tailnet` (Tailscale IP only), or `custom`.
|
|||
|
|
- **Auth**: required by default. Non-loopback binds require a shared token/password. Onboarding wizard generates a token by default.
|
|||
|
|
- `auth.allowTailscale`: when `true`, Tailscale Serve identity headers satisfy auth (verified via `tailscale whois`). Defaults to `true` when `tailscale.mode = "serve"`.
|
|||
|
|
- `tailscale.mode`: `serve` (tailnet only, loopback bind) or `funnel` (public, requires auth).
|
|||
|
|
- `remote.transport`: `ssh` (default) or `direct` (ws/wss). For `direct`, `remote.url` must be `ws://` or `wss://`.
|
|||
|
|
- `gateway.remote.token` is for remote CLI calls only; does not enable local gateway auth.
|
|||
|
|
- `trustedProxies`: reverse proxy IPs that terminate TLS. Only list proxies you control.
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
### OpenAI-compatible endpoints
|
|||
|
|
|
|||
|
|
- Chat Completions: disabled by default. Enable with `gateway.http.endpoints.chatCompletions.enabled: true`.
|
|||
|
|
- Responses API: `gateway.http.endpoints.responses.enabled`.
|
|||
|
|
|
|||
|
|
### Multi-instance isolation
|
|||
|
|
|
|||
|
|
Run multiple gateways on one host with unique ports and state dirs:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
OPENCLAW_CONFIG_PATH=~/.openclaw/a.json \
|
|||
|
|
OPENCLAW_STATE_DIR=~/.openclaw-a \
|
|||
|
|
openclaw gateway --port 19001
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Convenience flags: `--dev` (uses `~/.openclaw-dev` + port `19001`), `--profile <name>` (uses `~/.openclaw-<name>`).
|
|||
|
|
|
|||
|
|
See [Multiple Gateways](/gateway/multiple-gateways).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Hooks
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
hooks: {
|
|||
|
|
enabled: true,
|
|||
|
|
token: "shared-secret",
|
|||
|
|
path: "/hooks",
|
|||
|
|
maxBodyBytes: 262144,
|
|||
|
|
allowedAgentIds: ["hooks", "main"],
|
|||
|
|
presets: ["gmail"],
|
|||
|
|
transformsDir: "~/.openclaw/hooks",
|
|||
|
|
mappings: [
|
|||
|
|
{
|
|||
|
|
match: { path: "gmail" },
|
|||
|
|
action: "agent",
|
|||
|
|
agentId: "hooks",
|
|||
|
|
wakeMode: "now",
|
|||
|
|
name: "Gmail",
|
|||
|
|
sessionKey: "hook:gmail:{{messages[0].id}}",
|
|||
|
|
messageTemplate: "From: {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}",
|
|||
|
|
deliver: true,
|
|||
|
|
channel: "last",
|
|||
|
|
model: "openai/gpt-5.2-mini",
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Auth: `Authorization: Bearer <token>` or `x-openclaw-token: <token>`.
|
|||
|
|
|
|||
|
|
**Endpoints:**
|
|||
|
|
|
|||
|
|
- `POST /hooks/wake` → `{ text, mode?: "now"|"next-heartbeat" }`
|
|||
|
|
- `POST /hooks/agent` → `{ message, name?, agentId?, sessionKey?, wakeMode?, deliver?, channel?, to?, model?, thinking?, timeoutSeconds? }`
|
|||
|
|
- `POST /hooks/<name>` → resolved via `hooks.mappings`
|
|||
|
|
|
|||
|
|
<Accordion title="Mapping details">
|
|||
|
|
|
|||
|
|
- `match.path` matches sub-path after `/hooks` (e.g. `/hooks/gmail` → `gmail`).
|
|||
|
|
- `match.source` matches a payload field for generic paths.
|
|||
|
|
- Templates like `{{messages[0].subject}}` read from the payload.
|
|||
|
|
- `transform` can point to a JS/TS module returning a hook action.
|
|||
|
|
- `agentId` routes to a specific agent; unknown IDs fall back to default.
|
|||
|
|
- `allowedAgentIds`: restricts explicit routing (`*` or omitted = allow all, `[]` = deny all).
|
|||
|
|
- `deliver: true` sends final reply to a channel; `channel` defaults to `last`.
|
|||
|
|
- `model` overrides LLM for this hook run (must be allowed if model catalog is set).
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
### Gmail integration
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
hooks: {
|
|||
|
|
gmail: {
|
|||
|
|
account: "openclaw@gmail.com",
|
|||
|
|
topic: "projects/<project-id>/topics/gog-gmail-watch",
|
|||
|
|
subscription: "gog-gmail-watch-push",
|
|||
|
|
pushToken: "shared-push-token",
|
|||
|
|
hookUrl: "http://127.0.0.1:18789/hooks/gmail",
|
|||
|
|
includeBody: true,
|
|||
|
|
maxBytes: 20000,
|
|||
|
|
renewEveryMinutes: 720,
|
|||
|
|
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
|
|||
|
|
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
|
|||
|
|
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
|
|||
|
|
thinking: "off",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Gateway auto-starts `gog gmail watch serve` on boot when configured. Set `OPENCLAW_SKIP_GMAIL_WATCHER=1` to disable.
|
|||
|
|
- Don't run a separate `gog gmail watch serve` alongside the Gateway.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Canvas host
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
canvasHost: {
|
|||
|
|
root: "~/.openclaw/workspace/canvas",
|
|||
|
|
port: 18793,
|
|||
|
|
liveReload: true,
|
|||
|
|
// enabled: false, // or OPENCLAW_SKIP_CANVAS_HOST=1
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Serves HTML/CSS/JS over HTTP for iOS/Android nodes.
|
|||
|
|
- Injects live-reload client into served HTML.
|
|||
|
|
- Auto-creates starter `index.html` when empty.
|
|||
|
|
- Also serves A2UI at `/__openclaw__/a2ui/`.
|
|||
|
|
- Changes require a gateway restart.
|
|||
|
|
- Disable live reload for large directories or `EMFILE` errors.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Discovery
|
|||
|
|
|
|||
|
|
### mDNS (Bonjour)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
discovery: {
|
|||
|
|
mdns: {
|
|||
|
|
mode: "minimal", // minimal | full | off
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `minimal` (default): omit `cliPath` + `sshPort` from TXT records.
|
|||
|
|
- `full`: include `cliPath` + `sshPort`.
|
|||
|
|
- Hostname defaults to `openclaw`. Override with `OPENCLAW_MDNS_HOSTNAME`.
|
|||
|
|
|
|||
|
|
### Wide-area (DNS-SD)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
discovery: {
|
|||
|
|
wideArea: { enabled: true },
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Writes a unicast DNS-SD zone under `~/.openclaw/dns/`. For cross-network discovery, pair with a DNS server (CoreDNS recommended) + Tailscale split DNS.
|
|||
|
|
|
|||
|
|
Setup: `openclaw dns setup --apply`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Environment
|
|||
|
|
|
|||
|
|
### `env` (inline env vars)
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
env: {
|
|||
|
|
OPENROUTER_API_KEY: "sk-or-...",
|
|||
|
|
vars: {
|
|||
|
|
GROQ_API_KEY: "gsk-...",
|
|||
|
|
},
|
|||
|
|
shellEnv: {
|
|||
|
|
enabled: true,
|
|||
|
|
timeoutMs: 15000,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Inline env vars are only applied if the process env is missing the key.
|
|||
|
|
- `.env` files: CWD `.env` + `~/.openclaw/.env` (neither overrides existing vars).
|
|||
|
|
- `shellEnv`: imports missing expected keys from your login shell profile.
|
|||
|
|
- See [Environment](/help/environment) for full precedence.
|
|||
|
|
|
|||
|
|
### Env var substitution
|
|||
|
|
|
|||
|
|
Reference env vars in any config string with `${VAR_NAME}`:
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
gateway: {
|
|||
|
|
auth: { token: "${OPENCLAW_GATEWAY_TOKEN}" },
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Only uppercase names matched: `[A-Z_][A-Z0-9_]*`.
|
|||
|
|
- Missing/empty vars throw an error at config load.
|
|||
|
|
- Escape with `$${VAR}` for a literal `${VAR}`.
|
|||
|
|
- Works with `$include`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Auth storage
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
auth: {
|
|||
|
|
profiles: {
|
|||
|
|
"anthropic:me@example.com": { provider: "anthropic", mode: "oauth", email: "me@example.com" },
|
|||
|
|
"anthropic:work": { provider: "anthropic", mode: "api_key" },
|
|||
|
|
},
|
|||
|
|
order: {
|
|||
|
|
anthropic: ["anthropic:me@example.com", "anthropic:work"],
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Per-agent auth profiles stored at `<agentDir>/auth-profiles.json`.
|
|||
|
|
- Legacy OAuth imports from `~/.openclaw/credentials/oauth.json`.
|
|||
|
|
- See [OAuth](/concepts/oauth).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Logging
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
logging: {
|
|||
|
|
level: "info",
|
|||
|
|
file: "/tmp/openclaw/openclaw.log",
|
|||
|
|
consoleLevel: "info",
|
|||
|
|
consoleStyle: "pretty", // pretty | compact | json
|
|||
|
|
redactSensitive: "tools", // off | tools
|
|||
|
|
redactPatterns: ["\\bTOKEN\\b\\s*[=:]\\s*([\"']?)([^\\s\"']+)\\1"],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- Default log file: `/tmp/openclaw/openclaw-YYYY-MM-DD.log`.
|
|||
|
|
- Set `logging.file` for a stable path.
|
|||
|
|
- `consoleLevel` bumps to `debug` when `--verbose`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Wizard
|
|||
|
|
|
|||
|
|
Metadata written by CLI wizards (`onboard`, `configure`, `doctor`):
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
wizard: {
|
|||
|
|
lastRunAt: "2026-01-01T00:00:00.000Z",
|
|||
|
|
lastRunVersion: "2026.1.4",
|
|||
|
|
lastRunCommit: "abc1234",
|
|||
|
|
lastRunCommand: "configure",
|
|||
|
|
lastRunMode: "local",
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Identity
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
agents: {
|
|||
|
|
list: [
|
|||
|
|
{
|
|||
|
|
id: "main",
|
|||
|
|
identity: {
|
|||
|
|
name: "Samantha",
|
|||
|
|
theme: "helpful sloth",
|
|||
|
|
emoji: "🦥",
|
|||
|
|
avatar: "avatars/samantha.png",
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Written by the macOS onboarding assistant. Derives defaults:
|
|||
|
|
|
|||
|
|
- `messages.ackReaction` from `identity.emoji` (falls back to 👀)
|
|||
|
|
- `mentionPatterns` from `identity.name`/`identity.emoji`
|
|||
|
|
- `avatar` accepts: workspace-relative path, `http(s)` URL, or `data:` URI
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Bridge (legacy, removed)
|
|||
|
|
|
|||
|
|
Current builds no longer include the TCP bridge. Nodes connect over the Gateway WebSocket. `bridge.*` keys are no longer part of the config schema (validation fails until removed; `openclaw doctor --fix` can strip unknown keys).
|
|||
|
|
|
|||
|
|
<Accordion title="Legacy bridge config (historical reference)">
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"bridge": {
|
|||
|
|
"enabled": true,
|
|||
|
|
"port": 18790,
|
|||
|
|
"bind": "tailnet",
|
|||
|
|
"tls": {
|
|||
|
|
"enabled": true,
|
|||
|
|
"autoGenerate": true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
</Accordion>
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Cron
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
{
|
|||
|
|
cron: {
|
|||
|
|
enabled: true,
|
|||
|
|
maxConcurrentRuns: 2,
|
|||
|
|
sessionRetention: "24h", // duration string or false
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- `sessionRetention`: how long to keep completed cron sessions before pruning. Default: `24h`.
|
|||
|
|
|
|||
|
|
See [Cron Jobs](/automation/cron-jobs).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Media model template variables
|
|||
|
|
|
|||
|
|
Template placeholders expanded in `tools.media.*.models[].args`:
|
|||
|
|
|
|||
|
|
| Variable | Description |
|
|||
|
|
| ------------------ | ------------------------------------------------- |
|
|||
|
|
| `{{Body}}` | Full inbound message body |
|
|||
|
|
| `{{RawBody}}` | Raw body (no history/sender wrappers) |
|
|||
|
|
| `{{BodyStripped}}` | Body with group mentions stripped |
|
|||
|
|
| `{{From}}` | Sender identifier |
|
|||
|
|
| `{{To}}` | Destination identifier |
|
|||
|
|
| `{{MessageSid}}` | Channel message id |
|
|||
|
|
| `{{SessionId}}` | Current session UUID |
|
|||
|
|
| `{{IsNewSession}}` | `"true"` when new session created |
|
|||
|
|
| `{{MediaUrl}}` | Inbound media pseudo-URL |
|
|||
|
|
| `{{MediaPath}}` | Local media path |
|
|||
|
|
| `{{MediaType}}` | Media type (image/audio/document/…) |
|
|||
|
|
| `{{Transcript}}` | Audio transcript |
|
|||
|
|
| `{{Prompt}}` | Resolved media prompt for CLI entries |
|
|||
|
|
| `{{MaxChars}}` | Resolved max output chars for CLI entries |
|
|||
|
|
| `{{ChatType}}` | `"direct"` or `"group"` |
|
|||
|
|
| `{{GroupSubject}}` | Group subject (best effort) |
|
|||
|
|
| `{{GroupMembers}}` | Group members preview (best effort) |
|
|||
|
|
| `{{SenderName}}` | Sender display name (best effort) |
|
|||
|
|
| `{{SenderE164}}` | Sender phone number (best effort) |
|
|||
|
|
| `{{Provider}}` | Provider hint (whatsapp, telegram, discord, etc.) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Config includes (`$include`)
|
|||
|
|
|
|||
|
|
Split config into multiple files:
|
|||
|
|
|
|||
|
|
```json5
|
|||
|
|
// ~/.openclaw/openclaw.json
|
|||
|
|
{
|
|||
|
|
gateway: { port: 18789 },
|
|||
|
|
agents: { $include: "./agents.json5" },
|
|||
|
|
broadcast: {
|
|||
|
|
$include: ["./clients/mueller.json5", "./clients/schmidt.json5"],
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Merge behavior:**
|
|||
|
|
|
|||
|
|
- Single file: replaces the containing object.
|
|||
|
|
- Array of files: deep-merged in order (later overrides earlier).
|
|||
|
|
- Sibling keys: merged after includes (override included values).
|
|||
|
|
- Nested includes: up to 10 levels deep.
|
|||
|
|
- Paths: relative (to the including file), absolute, or `../` parent references.
|
|||
|
|
- Errors: clear messages for missing files, parse errors, and circular includes.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
_Related: [Configuration](/gateway/configuration) · [Configuration Examples](/gateway/configuration-examples) · [Doctor](/gateway/doctor)_
|