2025-12-09 17:51:05 +00:00
---
summary: "All configuration options for ~/.clawdis/clawdis.json with examples"
read_when:
- Adding or modifying config fields
---
2025-12-13 13:25:49 +00:00
<!-- {% raw %} -->
2025-12-03 15:45:32 +00:00
# Configuration 🔧
2025-12-13 13:25:49 +00:00
CLAWDIS reads an optional **JSON5** config from `~/.clawdis/clawdis.json` (comments + trailing commas allowed).
2025-12-03 15:45:32 +00:00
2025-12-17 11:29:12 +01:00
If the file is missing, CLAWDIS uses safe-ish defaults (embedded Pi agent + per-sender sessions + workspace `~/clawd` ). You usually only need a config to:
2026-01-02 12:59:47 +01:00
- restrict who can trigger the bot (`whatsapp.allowFrom` , `telegram.allowFrom` , etc.)
2026-01-02 17:25:33 -03:00
- control group mention behavior (`whatsapp.groups` , `telegram.groups` , `discord.guilds` , `routing.groupChat` )
2025-12-24 00:22:57 +00:00
- customize message prefixes (`messages` )
2026-01-02 17:25:33 -03:00
- set the agent's workspace (`agent.workspace` )
2025-12-24 00:22:57 +00:00
- tune the embedded agent (`agent` ) and session behavior (`session` )
2026-01-02 17:25:33 -03:00
- set the agent's identity (`identity` )
2025-12-03 15:45:32 +00:00
2026-01-03 16:04:19 +01:00
## Schema + UI hints
The Gateway exposes a JSON Schema representation of the config via `config.schema` for UI editors.
The Control UI renders a form from this schema, with a **Raw JSON** editor as an escape hatch.
Hints (labels, grouping, sensitive fields) ship alongside the schema so clients can render
better forms without hard-coding config knowledge.
2025-12-13 13:25:49 +00:00
## Minimal config (recommended starting point)
2025-12-03 15:45:32 +00:00
2025-12-13 13:25:49 +00:00
```json5
2025-12-03 15:45:32 +00:00
{
2025-12-23 23:45:20 +00:00
agent: { workspace: "~/clawd" },
2026-01-02 12:59:47 +01:00
whatsapp: { allowFrom: ["+15555550123"] }
2025-12-03 15:45:32 +00:00
}
```
2026-01-03 21:35:44 +01:00
Build the default image once with:
```bash
scripts/sandbox-setup.sh
```
2026-01-02 17:25:33 -03:00
## Self-chat mode (recommended for group control)
To prevent the bot from responding to WhatsApp @-mentions in groups (only respond to specific text triggers):
```json5
{
agent: { workspace: "~/clawd" },
whatsapp: {
// Allowlist is DMs only; including your own number enables self-chat mode.
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
},
routing: {
groupChat: {
mentionPatterns: ["@clawd ", "reisponde"]
}
}
}
```
2025-12-13 13:25:49 +00:00
## Common options
2025-12-03 15:45:32 +00:00
2025-12-14 04:21:27 +00:00
### `identity`
Optional agent identity used for defaults and UX. This is written by the macOS onboarding assistant.
If set, CLAWDIS derives defaults (only when you haven’ t set them explicitly):
2025-12-24 00:22:57 +00:00
- `messages.responsePrefix` from `identity.emoji`
- `routing.groupChat.mentionPatterns` from `identity.name` (so “@Samantha ” works in groups)
2025-12-14 04:21:27 +00:00
```json5
{
identity: { name: "Samantha", theme: "helpful sloth", emoji: "🦥" }
}
```
2026-01-01 19:14:14 +01:00
### `wizard`
Metadata written by CLI wizards (`onboard` , `configure` , `doctor` , `update` ).
```json5
{
wizard: {
lastRunAt: "2026-01-01T00:00:00.000Z",
lastRunVersion: "2.0.0-beta5",
lastRunCommit: "abc1234",
lastRunCommand: "configure",
lastRunMode: "local"
}
}
```
2025-12-03 15:45:32 +00:00
### `logging`
2025-12-13 13:25:49 +00:00
- Default log file: `/tmp/clawdis/clawdis-YYYY-MM-DD.log`
- If you want a stable path, set `logging.file` to `/tmp/clawdis/clawdis.log` .
2025-12-21 13:23:42 +00:00
- Console output can be tuned separately via:
- `logging.consoleLevel` (defaults to `info` , bumps to `debug` when `--verbose` )
- `logging.consoleStyle` (`pretty` | `compact` | `json` )
2025-12-13 13:25:49 +00:00
```json5
{
2025-12-21 13:23:42 +00:00
logging: {
level: "info",
file: "/tmp/clawdis/clawdis.log",
consoleLevel: "info",
2025-12-21 13:34:13 +00:00
consoleStyle: "pretty"
2025-12-21 13:23:42 +00:00
}
2025-12-13 13:25:49 +00:00
}
```
2025-12-03 15:45:32 +00:00
2026-01-02 12:59:47 +01:00
### `whatsapp.allowFrom`
2025-12-03 15:45:32 +00:00
2026-01-02 17:25:33 -03:00
Allowlist of E.164 phone numbers that may trigger WhatsApp auto-replies (DMs only).
If empty, the default allowlist is your own WhatsApp number (self-chat mode).
2025-12-03 15:45:32 +00:00
2025-12-13 13:25:49 +00:00
```json5
{
2026-01-03 01:27:37 +01:00
whatsapp: {
allowFrom: ["+15555550123", "+447700900123"],
textChunkLimit: 4000 // optional outbound chunk size (chars)
}
2025-12-13 13:25:49 +00:00
}
2025-12-03 15:45:32 +00:00
```
2026-01-02 17:25:33 -03:00
### `routing.groupChat`
Group messages default to **require mention** (either metadata mention or regex patterns). Applies to WhatsApp, Telegram, Discord, and iMessage group chats.
2026-01-02 22:23:00 +01:00
2026-01-02 17:25:33 -03:00
**Mention types:**
- **Metadata mentions**: Native platform @-mentions (e.g., WhatsApp tap-to-mention). Ignored in WhatsApp self-chat mode (see `whatsapp.allowFrom` ).
- **Text patterns**: Regex patterns defined in `mentionPatterns` . Always checked regardless of self-chat mode.
2026-01-02 22:23:00 +01:00
```json5
{
2026-01-02 17:25:33 -03:00
routing: {
groupChat: {
mentionPatterns: ["@clawd ", "clawdbot", "clawd"],
historyLimit: 50
2026-01-02 22:23:00 +01:00
}
}
}
```
2026-01-02 17:25:33 -03:00
Mention gating defaults live per provider (`whatsapp.groups` , `telegram.groups` , `imessage.groups` , `discord.guilds` ).
2025-12-03 15:45:32 +00:00
2026-01-02 17:25:33 -03:00
To respond **only** to specific text triggers (ignoring native @-mentions):
2025-12-13 13:25:49 +00:00
```json5
{
2026-01-02 17:25:33 -03:00
whatsapp: {
// Include your own number to enable self-chat mode (ignore native @-mentions).
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } }
},
2025-12-24 00:22:57 +00:00
routing: {
2025-12-13 13:25:49 +00:00
groupChat: {
2026-01-02 17:25:33 -03:00
// Only these text patterns will trigger responses
mentionPatterns: ["reisponde", "@clawd "]
2025-12-13 13:25:49 +00:00
}
}
}
```
2025-12-13 02:34:11 +00:00
2025-12-26 13:35:44 +01:00
### `routing.queue`
Controls how inbound messages behave when an agent run is already active.
```json5
{
routing: {
queue: {
2026-01-03 04:26:36 +01:00
mode: "collect", // steer | followup | collect | steer-backlog (steer+backlog ok) | interrupt (queue=steer legacy)
debounceMs: 1000,
cap: 20,
drop: "summarize", // old | new | summarize
2025-12-26 13:35:44 +01:00
bySurface: {
2026-01-03 04:26:36 +01:00
whatsapp: "collect",
telegram: "collect",
2026-01-03 18:44:07 +01:00
discord: "collect",
2026-01-03 04:26:36 +01:00
imessage: "collect",
webchat: "collect"
2025-12-26 13:35:44 +01:00
}
}
}
}
```
2025-12-26 16:54:53 +00:00
### `web` (WhatsApp web provider)
WhatsApp runs through the gateway’ s web provider. It starts automatically when a linked session exists.
Set `web.enabled: false` to keep it off by default.
```json5
{
web: {
enabled: true,
heartbeatSeconds: 60,
reconnect: {
initialMs: 2000,
maxMs: 120000,
factor: 1.4,
jitter: 0.2,
maxAttempts: 0
}
}
}
```
### `telegram` (bot transport)
2026-01-02 11:41:08 +00:00
Clawdis starts Telegram only when a `telegram` config section exists. The bot token is resolved from `TELEGRAM_BOT_TOKEN` or `telegram.botToken` .
2025-12-26 16:54:53 +00:00
Set `telegram.enabled: false` to disable automatic startup.
```json5
{
telegram: {
enabled: true,
botToken: "your-bot-token",
2026-01-02 17:25:33 -03:00
requireMention: true,
2025-12-26 16:54:53 +00:00
allowFrom: ["123456789"],
mediaMaxMb: 5,
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook",
webhookSecret: "secret",
webhookPath: "/telegram-webhook"
}
}
```
2025-12-15 10:11:18 -06:00
### `discord` (bot transport)
Configure the Discord bot by setting the bot token and optional gating:
```json5
{
discord: {
2025-12-26 16:54:53 +00:00
enabled: true,
2025-12-15 10:11:18 -06:00
token: "your-bot-token",
2026-01-01 23:58:35 +01:00
mediaMaxMb: 8, // clamp inbound media size
2026-01-02 17:41:47 -06:00
actions: { // tool action gates (false disables)
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
2026-01-02 00:11:03 -06:00
slashCommand: { // user-installed app slash commands
enabled: true,
name: "clawd",
sessionPrefix: "discord:slash",
ephemeral: true
},
2026-01-02 10:32:21 +01:00
dm: {
enabled: true, // disable all DMs when false
2026-01-02 11:15:52 +01:00
allowFrom: ["1234567890", "steipete"], // optional DM allowlist (ids or names)
groupEnabled: false, // enable group DMs
groupChannels: ["clawd-dm"] // optional group DM allowlist
2026-01-02 10:32:21 +01:00
},
2026-01-02 11:15:52 +01:00
guilds: {
"123456789012345678": { // guild id (preferred) or slug
slug: "friends-of-clawd",
requireMention: false, // per-guild default
2026-01-03 18:48:10 +00:00
reactionNotifications: "own", // off | own | all | allowlist
2026-01-02 11:15:52 +01:00
users: ["987654321098765432"], // optional per-guild user allowlist
channels: {
general: { allow: true },
help: { allow: true, requireMention: true }
}
}
},
historyLimit: 20 // include last N guild messages as context
2025-12-15 10:11:18 -06:00
}
}
```
2026-01-02 11:41:08 +00:00
Clawdis starts Discord only when a `discord` config section exists. The token is resolved from `DISCORD_BOT_TOKEN` or `discord.token` (unless `discord.enabled` is `false` ). Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands.
2026-01-02 11:15:52 +01:00
Guild slugs are lowercase with spaces replaced by `-` ; channel keys use the slugged channel name (no leading `#` ). Prefer guild ids as keys to avoid rename ambiguity.
2026-01-03 12:31:50 -06:00
Reaction notification modes:
- `off` : no reaction events.
2026-01-03 18:48:10 +00:00
- `own` : reactions on the bot's own messages (default).
2026-01-03 12:31:50 -06:00
- `all` : all reactions on all messages.
2026-01-03 18:48:10 +00:00
- `allowlist` : reactions from `guilds.<id>.users` on all messages (empty list disables).
2026-01-03 01:27:37 +01:00
2026-01-02 01:11:04 +01:00
### `imessage` (imsg CLI)
Clawdis spawns `imsg rpc` (JSON-RPC over stdio). No daemon or port required.
```json5
{
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
allowFrom: ["+15555550123", "user@example .com", "chat_id:123"],
includeAttachments: false,
mediaMaxMb: 16,
service: "auto",
region: "US"
}
}
```
Notes:
- Requires Full Disk Access to the Messages DB.
- The first send will prompt for Messages automation permission.
- Prefer `chat_id:<id>` targets. Use `imsg chats --limit 20` to list chats.
2025-12-23 23:45:20 +00:00
### `agent.workspace`
2025-12-13 02:34:11 +00:00
2025-12-17 11:29:12 +01:00
Sets the **single global workspace directory** used by the agent for file operations.
2025-12-13 02:34:11 +00:00
2025-12-17 11:29:12 +01:00
Default: `~/clawd` .
2025-12-13 02:34:11 +00:00
2025-12-17 11:29:12 +01:00
```json5
{
2025-12-23 23:45:20 +00:00
agent: { workspace: "~/clawd" }
2025-12-17 11:29:12 +01:00
}
```
2025-12-14 04:21:27 +00:00
2026-01-03 21:35:44 +01:00
If `agent.sandbox` is enabled, non-main sessions can override this with their
own per-session workspaces under `agent.sandbox.workspaceRoot` .
2025-12-24 00:22:57 +00:00
### `messages`
Controls inbound/outbound prefixes and timestamps.
```json5
{
messages: {
messagePrefix: "[clawdis]",
responsePrefix: "🦞",
2026-01-03 01:27:37 +01:00
timestampPrefix: "Europe/London"
2025-12-24 00:22:57 +00:00
}
}
```
2025-12-29 23:21:05 +01:00
### `talk`
Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID` when unset.
2025-12-30 01:57:45 +01:00
`apiKey` falls back to `ELEVENLABS_API_KEY` (or the gateway’ s shell profile) when unset.
2025-12-30 11:35:29 +01:00
`voiceAliases` lets Talk directives use friendly names (e.g. `"voice":"Clawd"` ).
2025-12-29 23:21:05 +01:00
```json5
{
talk: {
voiceId: "elevenlabs_voice_id",
2025-12-30 11:35:29 +01:00
voiceAliases: {
Clawd: "EXAVITQu4vr4xnSDxMaL",
Roger: "CwhRBWXzGAHq8TQ4Fs17"
},
2025-12-29 23:21:05 +01:00
modelId: "eleven_v3",
outputFormat: "mp3_44100_128",
2025-12-30 01:57:45 +01:00
apiKey: "elevenlabs_api_key",
2025-12-29 23:21:05 +01:00
interruptOnSpeech: true
}
}
```
2025-12-23 23:45:20 +00:00
### `agent`
2025-12-17 11:29:12 +01:00
2025-12-26 00:43:44 +01:00
Controls the embedded agent runtime (model/thinking/verbose/timeouts).
2025-12-23 23:45:20 +00:00
`allowedModels` lets `/model` list/filter and enforce a per-session allowlist
(omit to show the full catalog).
2025-12-26 23:26:14 +00:00
`modelAliases` adds short names for `/model` (alias -> provider/model).
2025-12-13 02:34:11 +00:00
```json5
{
2025-12-23 23:45:20 +00:00
agent: {
2025-12-26 00:16:29 +01:00
model: "anthropic/claude-opus-4-5",
2025-12-23 23:45:20 +00:00
allowedModels: [
"anthropic/claude-opus-4-5",
"anthropic/claude-sonnet-4-1"
],
2025-12-26 23:26:14 +00:00
modelAliases: {
Opus: "anthropic/claude-opus-4-5",
Sonnet: "anthropic/claude-sonnet-4-1"
},
2025-12-23 23:45:20 +00:00
thinkingDefault: "low",
verboseDefault: "off",
timeoutSeconds: 600,
mediaMaxMb: 5,
2025-12-26 01:13:13 +01:00
heartbeat: {
2025-12-26 02:35:21 +01:00
every: "30m",
target: "last"
2025-12-26 01:13:13 +01:00
},
2025-12-25 23:50:52 +01:00
maxConcurrent: 3,
2025-12-25 17:58:19 +00:00
bash: {
2026-01-03 20:15:02 +00:00
backgroundMs: 10000,
2025-12-25 17:58:19 +00:00
timeoutSec: 1800,
cleanupMs: 1800000
},
2025-12-23 23:45:20 +00:00
contextTokens: 200000
2025-12-24 00:22:57 +00:00
}
2025-12-13 02:34:11 +00:00
}
```
2026-01-03 16:45:53 +01:00
Block streaming:
- `agent.blockStreamingDefault` : `"on"` /`"off"` (default on).
2026-01-03 17:10:47 +01:00
- `agent.blockStreamingBreak` : `"text_end"` or `"message_end"` (default: text_end).
2026-01-03 16:45:53 +01:00
- `agent.blockStreamingChunk` : soft chunking for streamed blocks. Defaults to
800– 1200 chars, prefers paragraph breaks (`\n\n` ), then newlines, then sentences.
Example:
```json5
{
agent: {
blockStreamingChunk: { minChars: 800, maxChars: 1200 }
}
}
```
2025-12-26 00:43:44 +01:00
`agent.model` should be set as `provider/model` (e.g. `anthropic/claude-opus-4-5` ).
2025-12-26 23:26:14 +00:00
If `modelAliases` is configured, you may also use the alias key (e.g. `Opus` ).
2025-12-26 00:43:44 +01:00
If you omit the provider, CLAWDIS currently assumes `anthropic` as a temporary
deprecation fallback.
2025-12-31 11:36:57 +01:00
Z.AI models are available as `zai/<model>` (e.g. `zai/glm-4.7` ) and require
`ZAI_API_KEY` (or legacy `Z_AI_API_KEY` ) in the environment.
2025-12-26 00:16:29 +01:00
2025-12-26 01:13:13 +01:00
`agent.heartbeat` configures periodic heartbeat runs:
2025-12-26 01:34:46 +01:00
- `every` : duration string (`ms` , `s` , `m` , `h` ); default unit minutes. Omit or set
2025-12-26 01:13:13 +01:00
`0m` to disable.
- `model` : optional override model for heartbeat runs (`provider/model` ).
2026-01-02 01:11:04 +01:00
- `target` : optional delivery channel (`last` , `whatsapp` , `telegram` , `discord` , `imessage` , `none` ). Default: `last` .
2025-12-26 02:35:21 +01:00
- `to` : optional recipient override (E.164 for WhatsApp, chat id for Telegram).
2025-12-26 03:02:11 +01:00
- `prompt` : optional override for the heartbeat body (default: `HEARTBEAT` ).
2025-12-26 01:13:13 +01:00
2025-12-25 17:58:19 +00:00
`agent.bash` configures background bash defaults:
2026-01-03 20:15:02 +00:00
- `backgroundMs` : time before auto-background (ms, default 10000)
2025-12-25 17:58:19 +00:00
- `timeoutSec` : auto-kill after this runtime (seconds, default 1800)
- `cleanupMs` : how long to keep finished sessions in memory (ms, default 1800000)
2025-12-25 23:50:52 +01:00
`agent.maxConcurrent` sets the maximum number of embedded agent runs that can
execute in parallel across sessions. Each session is still serialized (one run
per session key at a time). Default: 1.
2026-01-03 21:35:44 +01:00
### `agent.sandbox`
Optional per-session **Docker sandboxing** for the embedded agent. Intended for
non-main sessions so they cannot access your host system.
Defaults (if enabled):
- one container per session
- Debian bookworm-slim based image
- workspace per session under `~/.clawdis/sandboxes`
- auto-prune: idle > 24h OR age > 7d
- tools: allow only `bash` , `process` , `read` , `write` , `edit` (deny wins)
2026-01-03 22:11:43 +01:00
- optional sandboxed browser (Chromium + CDP, noVNC observer)
2026-01-03 21:35:44 +01:00
```json5
{
agent: {
sandbox: {
mode: "non-main", // off | non-main | all
perSession: true,
workspaceRoot: "~/.clawdis/sandboxes",
docker: {
image: "clawdis-sandbox:bookworm-slim",
containerPrefix: "clawdis-sbx-",
workdir: "/workspace",
readOnlyRoot: true,
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "bridge",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
setupCommand: "apt-get update & & apt-get install -y git curl jq"
},
2026-01-03 22:11:43 +01:00
browser: {
enabled: false,
image: "clawdis-sandbox-browser:bookworm-slim",
containerPrefix: "clawdis-sbx-browser-",
cdpPort: 9222,
vncPort: 5900,
noVncPort: 6080,
headless: false,
enableNoVnc: true
},
2026-01-03 21:35:44 +01:00
tools: {
allow: ["bash", "process", "read", "write", "edit"],
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"]
},
prune: {
idleHours: 24, // 0 disables idle pruning
maxAgeDays: 7 // 0 disables max-age pruning
}
}
}
}
```
2026-01-03 22:11:43 +01:00
Build the default sandbox image once with:
```bash
scripts/sandbox-setup.sh
```
Build the optional browser image with:
```bash
scripts/sandbox-browser-setup.sh
```
When `agent.sandbox.browser.enabled=true` , the browser tool uses a sandboxed
Chromium instance (CDP). If noVNC is enabled (default when headless=false),
the noVNC URL is injected into the system prompt so the agent can reference it.
This does not require `browser.enabled` in the main config; the sandbox control
URL is injected per session.
2025-12-23 02:48:57 +01:00
### `models` (custom providers + base URLs)
Clawdis uses the **pi-coding-agent** model catalog. You can add custom providers
(LiteLLM, local OpenAI-compatible servers, Anthropic proxies, etc.) by writing
`~/.clawdis/agent/models.json` or by defining the same schema inside your
Clawdis config under `models.providers` .
When `models.providers` is present, Clawdis writes/merges a `models.json` into
`~/.clawdis/agent/` on startup:
- default behavior: **merge** (keeps existing providers, overrides on name)
- set `models.mode: "replace"` to overwrite the file contents
2025-12-26 00:43:44 +01:00
Select the model via `agent.model` (provider/model).
2025-12-23 02:48:57 +01:00
```json5
{
2025-12-26 00:43:44 +01:00
agent: { model: "custom-proxy/llama-3.1-8b" },
2025-12-23 02:48:57 +01:00
models: {
mode: "merge",
providers: {
"custom-proxy": {
baseUrl: "http://localhost:4000/v1",
apiKey: "LITELLM_KEY",
api: "openai-completions",
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
}
]
}
}
}
}
```
2025-12-27 00:48:15 +00:00
### Local models (LM Studio) — recommended setup
Best current local setup (what we’ re running): **MiniMax M2.1** on a beefy Mac Studio
via **LM Studio** using the **Responses API** .
```json5
{
agent: {
model: "Minimax",
allowedModels: [
"anthropic/claude-opus-4-5",
"lmstudio/minimax-m2.1-gs32"
],
modelAliases: {
Opus: "anthropic/claude-opus-4-5",
Minimax: "lmstudio/minimax-m2.1-gs32"
}
},
models: {
mode: "merge",
providers: {
lmstudio: {
baseUrl: "http://127.0.0.1:1234/v1",
apiKey: "lmstudio",
api: "openai-responses",
models: [
{
id: "minimax-m2.1-gs32",
name: "MiniMax M2.1 GS32",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 196608,
maxTokens: 8192
}
]
}
}
}
}
```
Notes:
- LM Studio must have the model loaded and the local server enabled (default URL above).
- Responses API enables clean reasoning/output separation; WhatsApp sees only final text.
- Adjust `contextWindow` /`maxTokens` if your LM Studio context length differs.
2025-12-23 02:48:57 +01:00
Notes:
- Supported APIs: `openai-completions` , `openai-responses` , `anthropic-messages` ,
`google-generative-ai`
- Use `authHeader: true` + `headers` for custom auth needs.
- Override the agent config root with `CLAWDIS_AGENT_DIR` (or `PI_CODING_AGENT_DIR` )
if you want `models.json` stored elsewhere.
2025-12-24 00:22:57 +00:00
### `session`
2025-12-17 11:29:12 +01:00
Controls session scoping, idle expiry, reset triggers, and where the session store is written.
```json5
{
2025-12-24 00:22:57 +00:00
session: {
scope: "per-sender",
idleMinutes: 60,
resetTriggers: ["/new", "/reset"],
store: "~/.clawdis/sessions/sessions.json",
2026-01-03 23:44:38 +01:00
mainKey: "main",
sendPolicy: {
rules: [
{ action: "deny", match: { surface: "discord", chatType: "group" } }
],
default: "allow"
}
2025-12-17 11:29:12 +01:00
}
}
```
2025-12-14 02:59:31 +00:00
2026-01-03 23:44:38 +01:00
Fields:
- `sendPolicy.default` : `allow` or `deny` fallback when no rule matches.
- `sendPolicy.rules[]` : match by `surface` (provider), `chatType` (`direct|group|room` ), or `keyPrefix` (e.g. `cron:` ). First deny wins; otherwise allow.
2026-01-01 10:07:31 +01:00
### `skills` (skills config)
2025-12-20 12:22:15 +01:00
2026-01-01 10:07:31 +01:00
Controls bundled allowlist, install preferences, extra skill folders, and per-skill
overrides. Applies to **bundled** skills and `~/.clawdis/skills` (workspace skills
still win on name conflicts).
2025-12-20 12:22:15 +01:00
2026-01-01 10:07:31 +01:00
Fields:
- `allowBundled` : optional allowlist for **bundled** skills only. If set, only those
bundled skills are eligible (managed/workspace skills unaffected).
- `load.extraDirs` : additional skill directories to scan (lowest precedence).
- `install.preferBrew` : prefer brew installers when available (default: true).
- `install.nodeManager` : node installer preference (`npm` | `pnpm` | `yarn` , default: npm).
- `entries.<skillKey>` : per-skill config overrides.
Per-skill fields:
2025-12-20 12:23:53 +00:00
- `enabled` : set `false` to disable a skill even if it’ s bundled/installed.
2025-12-20 12:22:15 +01:00
- `env` : environment variables injected for the agent run (only if not already set).
- `apiKey` : optional convenience for skills that declare a primary env var (e.g. `nano-banana-pro` → `GEMINI_API_KEY` ).
Example:
```json5
{
skills: {
2026-01-01 10:07:31 +01:00
allowBundled: ["brave-search", "gemini"],
load: {
extraDirs: [
"~/Projects/agent-scripts/skills",
"~/Projects/oss/some-skill-pack/skills"
]
2025-12-20 12:22:15 +01:00
},
2026-01-01 10:07:31 +01:00
install: {
preferBrew: true,
nodeManager: "npm"
},
entries: {
"nano-banana-pro": {
apiKey: "GEMINI_KEY_HERE",
env: {
GEMINI_API_KEY: "GEMINI_KEY_HERE"
}
},
peekaboo: { enabled: true },
sag: { enabled: false }
}
2025-12-20 12:26:58 +00:00
}
}
```
2025-12-13 15:15:09 +00:00
### `browser` (clawd-managed Chrome)
Clawdis can start a **dedicated, isolated** Chrome/Chromium instance for clawd and expose a small loopback control server.
Defaults:
- enabled: `true`
2025-12-13 15:29:39 +00:00
- control URL: `http://127.0.0.1:18791` (CDP uses `18792` )
2026-01-01 22:44:52 +01:00
- CDP URL: `http://127.0.0.1:18792` (control URL + 1)
2025-12-13 15:15:09 +00:00
- profile color: `#FF4500` (lobster-orange)
2025-12-13 17:37:00 +00:00
- Note: the control server is started by the running gateway (Clawdis.app menubar, or `clawdis gateway` ).
2025-12-13 15:15:09 +00:00
```json5
{
browser: {
enabled: true,
2025-12-13 15:29:39 +00:00
controlUrl: "http://127.0.0.1:18791",
2026-01-01 22:44:52 +01:00
// cdpUrl: "http://127.0.0.1:18792", // override for remote CDP
2025-12-13 15:15:09 +00:00
color: "#FF4500 ",
// Advanced:
// headless: false,
2026-01-01 22:44:52 +01:00
// noSandbox: false,
// executablePath: "/usr/bin/chromium",
// attachOnly: false, // set true when tunneling a remote CDP to localhost
2025-12-13 15:15:09 +00:00
}
}
```
2025-12-30 04:14:36 +01:00
### `ui` (Appearance)
Optional accent color used by the native apps for UI chrome (e.g. Talk Mode bubble tint).
If unset, clients fall back to a muted light-blue.
```json5
{
ui: {
seamColor: "#FF4500 " // hex (RRGGBB or #RRGGBB )
}
}
```
2025-12-20 02:08:04 +00:00
### `gateway` (Gateway server mode + bind)
Use `gateway.mode` to explicitly declare whether this machine should run the Gateway.
Defaults:
- mode: **unset** (treated as “do not auto-start”)
- bind: `loopback`
2026-01-03 11:46:58 +01:00
- port: `18789` (single port for WS + HTTP)
2025-12-20 02:08:04 +00:00
```json5
{
gateway: {
mode: "local", // or "remote"
2026-01-03 11:46:58 +01:00
port: 18789, // WS + HTTP multiplex
2025-12-20 02:08:04 +00:00
bind: "loopback",
2026-01-03 17:54:52 +01:00
// controlUi: { enabled: true, basePath: "/clawdis" }
2026-01-02 13:46:48 +01:00
// auth: { mode: "token", token: "your-token" } // token is for multi-machine CLI access
2025-12-21 00:44:39 +00:00
// tailscale: { mode: "off" | "serve" | "funnel" }
2025-12-20 02:08:04 +00:00
}
}
```
2026-01-03 17:54:52 +01:00
Control UI base path:
- `gateway.controlUi.basePath` sets the URL prefix where the Control UI is served.
- Examples: `"/ui"` , `"/clawdis"` , `"/apps/clawdis"` .
- Default: root (`/` ) (unchanged).
2025-12-20 02:08:04 +00:00
Notes:
- `clawdis gateway` refuses to start unless `gateway.mode` is set to `local` (or you pass the override flag).
2026-01-03 11:46:58 +01:00
- `gateway.port` controls the single multiplexed port used for WebSocket + HTTP (control UI, hooks, A2UI).
- Precedence: `--port` > `CLAWDIS_GATEWAY_PORT` > `gateway.port` > default `18789` .
2025-12-20 02:08:04 +00:00
2025-12-21 00:44:39 +00:00
Auth and Tailscale:
2025-12-23 13:13:09 +00:00
- `gateway.auth.mode` sets the handshake requirements (`token` or `password` ).
2026-01-02 13:46:48 +01:00
- `gateway.auth.token` stores the shared token for token auth (used by the CLI on the same machine).
2025-12-21 00:44:39 +00:00
- When `gateway.auth.mode` is set, only that method is accepted (plus optional Tailscale headers).
- `gateway.auth.password` can be set here, or via `CLAWDIS_GATEWAY_PASSWORD` (recommended).
- `gateway.auth.allowTailscale` controls whether Tailscale identity headers can satisfy auth.
- `gateway.tailscale.mode: "serve"` uses Tailscale Serve (tailnet only, loopback bind).
- `gateway.tailscale.mode: "funnel"` exposes the dashboard publicly; requires auth.
- `gateway.tailscale.resetOnExit` resets Serve/Funnel config on shutdown.
2026-01-01 20:10:50 +01:00
Remote client defaults (CLI):
- `gateway.remote.url` sets the default Gateway WebSocket URL for CLI calls when `gateway.mode = "remote"` .
- `gateway.remote.token` supplies the token for remote calls (leave unset for no auth).
2026-01-02 00:17:54 +01:00
- `gateway.remote.password` supplies the password for remote calls (leave unset for no auth).
2026-01-01 20:10:50 +01:00
2026-01-03 23:34:18 +01:00
macOS app behavior:
- Clawdis.app watches `~/.clawdis/clawdis.json` and switches modes live when `gateway.mode` or `gateway.remote.url` changes.
- If `gateway.mode` is unset but `gateway.remote.url` is set, the macOS app treats it as remote mode.
- When you change connection mode in the macOS app, it writes `gateway.mode` (and `gateway.remote.url` in remote mode) back to the config file.
2026-01-01 20:10:50 +01:00
```json5
{
gateway: {
mode: "remote",
remote: {
url: "ws://gateway.tailnet:18789",
2026-01-02 00:17:54 +01:00
token: "your-token",
password: "your-password"
2026-01-01 20:10:50 +01:00
}
}
}
```
2026-01-03 19:52:24 +00:00
### `gateway.reload` (Config hot reload)
The Gateway watches `~/.clawdis/clawdis.json` (or `CLAWDIS_CONFIG_PATH` ) and applies changes automatically.
Modes:
- `hybrid` (default): hot-apply safe changes; restart the Gateway for critical changes.
- `hot` : only apply hot-safe changes; log when a restart is required.
- `restart` : restart the Gateway on any config change.
- `off` : disable hot reload.
```json5
{
gateway: {
reload: {
mode: "hybrid",
debounceMs: 300
}
}
}
```
#### Hot reload matrix (files + impact)
Files watched:
- `~/.clawdis/clawdis.json` (or `CLAWDIS_CONFIG_PATH` )
Hot-applied (no full gateway restart):
- `hooks` (webhook auth/path/mappings) + `hooks.gmail` (Gmail watcher restarted)
- `browser` (browser control server restart)
- `cron` (cron service restart + concurrency update)
- `agent.heartbeat` (heartbeat runner restart)
- `web` (WhatsApp web provider restart)
- `telegram` , `discord` , `signal` , `imessage` (provider restarts)
- `agent` , `models` , `routing` , `messages` , `session` , `whatsapp` , `logging` , `skills` , `ui` , `talk` , `identity` , `wizard` (dynamic reads)
Requires full Gateway restart:
- `gateway` (port/bind/auth/control UI/tailscale)
- `bridge`
- `discovery`
- `canvasHost`
- Any unknown/unsupported config path (defaults to restart for safety)
2026-01-03 11:46:58 +01:00
### Multi-instance isolation
To run multiple gateways on one host, isolate per-instance state + config and use unique ports:
- `CLAWDIS_CONFIG_PATH` (per-instance config)
- `CLAWDIS_STATE_DIR` (sessions/creds/logs)
- `agent.workspace` (memories)
- `gateway.port` (unique per instance)
Example:
```bash
CLAWDIS_CONFIG_PATH=~/.clawdis/a.json \
CLAWDIS_STATE_DIR=~/.clawdis-a \
clawdis gateway --port 19001
```
2025-12-24 14:32:55 +00:00
### `hooks` (Gateway webhooks)
Enable a simple HTTP webhook surface on the Gateway HTTP server.
Defaults:
- enabled: `false`
- path: `/hooks`
2025-12-24 19:39:36 +00:00
- maxBodyBytes: `262144` (256 KB)
2025-12-24 14:32:55 +00:00
```json5
{
hooks: {
enabled: true,
token: "shared-secret",
2025-12-24 19:39:36 +00:00
path: "/hooks",
presets: ["gmail"],
transformsDir: "~/.clawdis/hooks",
mappings: [
{
match: { path: "gmail" },
action: "agent",
wakeMode: "now",
name: "Gmail",
sessionKey: "hook:gmail:{{messages[0].id}}",
messageTemplate:
"From: {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}",
},
],
2025-12-24 14:32:55 +00:00
}
}
```
Requests must include the hook token:
- `Authorization: Bearer <token>` **or**
- `x-clawdis-token: <token>` **or**
- `?token=<token>`
Endpoints:
- `POST /hooks/wake` → `{ text, mode?: "now"|"next-heartbeat" }`
- `POST /hooks/agent` → `{ message, name?, sessionKey?, wakeMode?, deliver?, channel?, to?, thinking?, timeoutSeconds? }`
2025-12-24 19:39:36 +00:00
- `POST /hooks/<name>` → resolved via `hooks.mappings`
2025-12-24 14:32:55 +00:00
`/hooks/agent` always posts a summary into the main session (and can optionally trigger an immediate heartbeat via `wakeMode: "now"` ).
2025-12-24 19:39:36 +00:00
Mapping notes:
- `match.path` matches the sub-path after `/hooks` (e.g. `/hooks/gmail` → `gmail` ).
- `match.source` matches a payload field (e.g. `{ source: "gmail" }` ) so you can use a generic `/hooks/ingest` path.
- Templates like `{{messages[0].subject}}` read from the payload.
- `transform` can point to a JS/TS module that returns a hook action.
Gmail helper config (used by `clawdis hooks gmail setup` / `run` ):
```json5
{
hooks: {
gmail: {
account: "clawdbot@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,
2025-12-24 21:56:21 +00:00
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
2025-12-24 19:39:36 +00:00
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
}
}
}
```
2026-01-03 03:03:49 +01:00
Gateway auto-start:
- If `hooks.enabled=true` and `hooks.gmail.account` is set, the Gateway starts
`gog gmail watch serve` on boot and auto-renews the watch.
- Set `CLAWDIS_SKIP_GMAIL_WATCHER=1` to disable the auto-start (for manual runs).
2026-01-03 12:41:44 +00:00
- Avoid running a separate `gog gmail watch serve` alongside the Gateway; it will
fail with `listen tcp 127.0.0.1:8788: bind: address already in use` .
2026-01-03 03:03:49 +01:00
2025-12-24 21:56:21 +00:00
Note: when `tailscale.mode` is on, Clawdis defaults `serve.path` to `/` so
Tailscale can proxy `/gmail-pubsub` correctly (it strips the set-path prefix).
2025-12-20 17:31:09 +01:00
### `canvasHost` (LAN/tailnet Canvas file server + live reload)
2025-12-18 11:36:46 +01:00
2025-12-20 17:31:09 +01:00
The Gateway serves a directory of HTML/CSS/JS over HTTP so iOS/Android nodes can simply `canvas.navigate` to it.
2025-12-18 11:36:46 +01:00
Default root: `~/clawd/canvas`
2025-12-20 17:31:09 +01:00
Default port: `18793` (chosen to avoid the clawd browser CDP port `18792` )
2025-12-20 22:25:09 +01:00
The server listens on the **bridge bind host** (LAN or Tailnet) so nodes can reach it.
2025-12-18 11:36:46 +01:00
2025-12-18 23:32:48 +01:00
The server:
2025-12-18 11:36:46 +01:00
- serves files under `canvasHost.root`
- injects a tiny live-reload client into served HTML
2025-12-20 17:31:09 +01:00
- watches the directory and broadcasts reloads over a WebSocket endpoint at `/__clawdis/ws`
2025-12-18 23:32:48 +01:00
- auto-creates a starter `index.html` when the directory is empty (so you see something immediately)
2025-12-20 22:25:09 +01:00
- also serves A2UI at `/__clawdis__/a2ui/` and is advertised to nodes as `canvasHostUrl`
(always used by nodes for Canvas/A2UI)
2025-12-18 11:36:46 +01:00
```json5
{
canvasHost: {
2025-12-20 17:31:09 +01:00
root: "~/clawd/canvas",
port: 18793
2025-12-18 11:36:46 +01:00
}
}
```
2025-12-18 23:32:48 +01:00
Disable with:
- config: `canvasHost: { enabled: false }`
- env: `CLAWDIS_SKIP_CANVAS_HOST=1`
2025-12-18 13:18:33 +01:00
### `bridge` (node bridge server)
2025-12-17 17:01:10 +01:00
2025-12-18 13:18:33 +01:00
The Gateway can expose a simple TCP bridge for nodes (iOS/Android), typically on port `18790` .
2025-12-17 17:01:10 +01:00
Defaults:
- enabled: `true`
- port: `18790`
- bind: `lan` (binds to `0.0.0.0` )
Bind modes:
- `lan` : `0.0.0.0` (reachable on any interface, including LAN/Wi‑ Fi and Tailscale)
- `tailnet` : bind only to the machine’ s Tailscale IP (recommended for Vienna ⇄ London)
- `loopback` : `127.0.0.1` (local only)
- `auto` : prefer tailnet IP if present, else `lan`
```json5
{
bridge: {
enabled: true,
port: 18790,
bind: "tailnet"
}
}
```
### `discovery.wideArea` (Wide-Area Bonjour / unicast DNS‑ SD)
When enabled, the Gateway writes a unicast DNS-SD zone for `_clawdis-bridge._tcp` under `~/.clawdis/dns/` using the standard discovery domain `clawdis.internal.`
To make iOS/Android discover across networks (Vienna ⇄ London), pair this with:
- a DNS server on the gateway host serving `clawdis.internal.` (CoreDNS is recommended)
- Tailscale **split DNS** so clients resolve `clawdis.internal` via that server
One-time setup helper (gateway host):
```bash
clawdis dns setup --apply
```
```json5
{
discovery: { wideArea: { enabled: true } }
}
```
2025-12-13 13:25:49 +00:00
## Template variables
2025-12-03 15:45:32 +00:00
2025-12-24 00:22:57 +00:00
Template placeholders are expanded in `routing.transcribeAudio.command` (and any future templated command fields).
2025-12-03 15:45:32 +00:00
| Variable | Description |
|----------|-------------|
2025-12-13 13:25:49 +00:00
| `{{Body}}` | Full inbound message body |
| `{{BodyStripped}}` | Body with group mentions stripped (best default for agents) |
| `{{From}}` | Sender identifier (E.164 for WhatsApp; may differ per surface) |
| `{{To}}` | Destination identifier |
| `{{MessageSid}}` | Provider message id (when available) |
2025-12-03 15:45:32 +00:00
| `{{SessionId}}` | Current session UUID |
2025-12-13 13:25:49 +00:00
| `{{IsNewSession}}` | `"true"` when a new session was created |
| `{{MediaUrl}}` | Inbound media pseudo-URL (if present) |
| `{{MediaPath}}` | Local media path (if downloaded) |
| `{{MediaType}}` | Media type (image/audio/document/…) |
| `{{Transcript}}` | Audio transcript (when enabled) |
| `{{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) |
2026-01-02 01:11:04 +01:00
| `{{Surface}}` | Surface hint (whatsapp|telegram|discord|imessage|webchat|…) |
2025-12-13 13:25:49 +00:00
## Cron (Gateway scheduler)
Cron is a Gateway-owned scheduler for wakeups and scheduled jobs. See [Cron + wakeups ](./cron.md ) for the full RFC and CLI examples.
2025-12-03 15:45:32 +00:00
2025-12-13 13:25:49 +00:00
```json5
{
cron: {
enabled: true,
maxConcurrentRuns: 2
}
2025-12-03 15:45:32 +00:00
}
```
---
2025-12-17 11:29:12 +01:00
*Next: [Agent Runtime ](./agent.md )* 🦞
2025-12-13 13:25:49 +00:00
<!-- {% endraw %} -->