Files
clawdbot/docs/configuration.md
2026-01-02 01:19:40 +01:00

765 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
summary: "All configuration options for ~/.clawdis/clawdis.json with examples"
read_when:
- Adding or modifying config fields
---
<!-- {% raw %} -->
# Configuration 🔧
CLAWDIS reads an optional **JSON5** config from `~/.clawdis/clawdis.json` (comments + trailing commas allowed).
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:
- restrict who can trigger the bot (`routing.allowFrom`)
- tune group mention behavior (`routing.groupChat`)
- customize message prefixes (`messages`)
- set the agents workspace (`agent.workspace`)
- tune the embedded agent (`agent`) and session behavior (`session`)
- set the agents identity (`identity`)
## Minimal config (recommended starting point)
```json5
{
agent: { workspace: "~/clawd" },
routing: { allowFrom: ["+15555550123"] }
}
```
## Common options
### `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 havent set them explicitly):
- `messages.responsePrefix` from `identity.emoji`
- `routing.groupChat.mentionPatterns` from `identity.name` (so “@Samantha” works in groups)
```json5
{
identity: { name: "Samantha", theme: "helpful sloth", emoji: "🦥" }
}
```
### `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"
}
}
```
### `logging`
- Default log file: `/tmp/clawdis/clawdis-YYYY-MM-DD.log`
- If you want a stable path, set `logging.file` to `/tmp/clawdis/clawdis.log`.
- Console output can be tuned separately via:
- `logging.consoleLevel` (defaults to `info`, bumps to `debug` when `--verbose`)
- `logging.consoleStyle` (`pretty` | `compact` | `json`)
```json5
{
logging: {
level: "info",
file: "/tmp/clawdis/clawdis.log",
consoleLevel: "info",
consoleStyle: "pretty"
}
}
```
### `routing.allowFrom`
Allowlist of E.164 phone numbers that may trigger auto-replies.
```json5
{
routing: { allowFrom: ["+15555550123", "+447700900123"] }
}
```
### `routing.groupChat`
Group messages default to **require mention** (either metadata mention or regex patterns). Applies to WhatsApp, Telegram, Discord, and iMessage group chats.
```json5
{
routing: {
groupChat: {
mentionPatterns: ["@clawd", "clawdbot", "clawd"],
historyLimit: 50
}
}
}
```
### `routing.queue`
Controls how inbound messages behave when an agent run is already active.
```json5
{
routing: {
queue: {
mode: "interrupt", // global default: queue | interrupt
bySurface: {
whatsapp: "interrupt",
telegram: "interrupt",
discord: "queue",
imessage: "interrupt",
webchat: "queue"
}
}
}
}
```
### `web` (WhatsApp web provider)
WhatsApp runs through the gateways 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)
Clawdis reads `TELEGRAM_BOT_TOKEN` or `telegram.botToken` to start the provider.
Set `telegram.enabled: false` to disable automatic startup.
```json5
{
telegram: {
enabled: true,
botToken: "your-bot-token",
requireMention: true,
allowFrom: ["123456789"],
mediaMaxMb: 5,
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook",
webhookSecret: "secret",
webhookPath: "/telegram-webhook"
}
}
```
### `discord` (bot transport)
Configure the Discord bot by setting the bot token and optional gating:
```json5
{
discord: {
enabled: true,
token: "your-bot-token",
allowFrom: ["discord:1234567890", "*"], // optional DM allowlist (user ids)
guildAllowFrom: {
guilds: ["123456789012345678"], // optional guild allowlist (ids)
users: ["987654321098765432"] // optional user allowlist (ids)
},
requireMention: true, // require @bot mentions in guilds
mediaMaxMb: 8, // clamp inbound media size
historyLimit: 20, // include last N guild messages as context
enableReactions: true // allow agent-triggered reactions
}
}
```
Clawdis reads `DISCORD_BOT_TOKEN` or `discord.token` to start the provider (unless `discord.enabled` is `false`). Use `user:<id>` (DM) or `channel:<id>` (guild channel) when specifying delivery targets for cron/CLI commands.
### `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.
### `agent.workspace`
Sets the **single global workspace directory** used by the agent for file operations.
Default: `~/clawd`.
```json5
{
agent: { workspace: "~/clawd" }
}
```
### `messages`
Controls inbound/outbound prefixes and timestamps.
```json5
{
messages: {
messagePrefix: "[clawdis]",
responsePrefix: "🦞",
timestampPrefix: "Europe/London"
}
}
```
### `talk`
Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID` when unset.
`apiKey` falls back to `ELEVENLABS_API_KEY` (or the gateways shell profile) when unset.
`voiceAliases` lets Talk directives use friendly names (e.g. `"voice":"Clawd"`).
```json5
{
talk: {
voiceId: "elevenlabs_voice_id",
voiceAliases: {
Clawd: "EXAVITQu4vr4xnSDxMaL",
Roger: "CwhRBWXzGAHq8TQ4Fs17"
},
modelId: "eleven_v3",
outputFormat: "mp3_44100_128",
apiKey: "elevenlabs_api_key",
interruptOnSpeech: true
}
}
```
### `agent`
Controls the embedded agent runtime (model/thinking/verbose/timeouts).
`allowedModels` lets `/model` list/filter and enforce a per-session allowlist
(omit to show the full catalog).
`modelAliases` adds short names for `/model` (alias -> provider/model).
```json5
{
agent: {
model: "anthropic/claude-opus-4-5",
allowedModels: [
"anthropic/claude-opus-4-5",
"anthropic/claude-sonnet-4-1"
],
modelAliases: {
Opus: "anthropic/claude-opus-4-5",
Sonnet: "anthropic/claude-sonnet-4-1"
},
thinkingDefault: "low",
verboseDefault: "off",
timeoutSeconds: 600,
mediaMaxMb: 5,
heartbeat: {
every: "30m",
target: "last"
},
maxConcurrent: 3,
bash: {
backgroundMs: 20000,
timeoutSec: 1800,
cleanupMs: 1800000
},
contextTokens: 200000
}
}
```
`agent.model` should be set as `provider/model` (e.g. `anthropic/claude-opus-4-5`).
If `modelAliases` is configured, you may also use the alias key (e.g. `Opus`).
If you omit the provider, CLAWDIS currently assumes `anthropic` as a temporary
deprecation fallback.
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.
`agent.heartbeat` configures periodic heartbeat runs:
- `every`: duration string (`ms`, `s`, `m`, `h`); default unit minutes. Omit or set
`0m` to disable.
- `model`: optional override model for heartbeat runs (`provider/model`).
- `target`: optional delivery channel (`last`, `whatsapp`, `telegram`, `discord`, `imessage`, `none`). Default: `last`.
- `to`: optional recipient override (E.164 for WhatsApp, chat id for Telegram).
- `prompt`: optional override for the heartbeat body (default: `HEARTBEAT`).
`agent.bash` configures background bash defaults:
- `backgroundMs`: time before auto-background (ms, default 20000)
- `timeoutSec`: auto-kill after this runtime (seconds, default 1800)
- `cleanupMs`: how long to keep finished sessions in memory (ms, default 1800000)
`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.
### `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
Select the model via `agent.model` (provider/model).
```json5
{
agent: { model: "custom-proxy/llama-3.1-8b" },
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
}
]
}
}
}
}
```
### Local models (LM Studio) — recommended setup
Best current local setup (what were 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.
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.
### `session`
Controls session scoping, idle expiry, reset triggers, and where the session store is written.
```json5
{
session: {
scope: "per-sender",
idleMinutes: 60,
resetTriggers: ["/new", "/reset"],
store: "~/.clawdis/sessions/sessions.json",
mainKey: "main"
}
}
```
### `skills` (skills config)
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).
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:
- `enabled`: set `false` to disable a skill even if its bundled/installed.
- `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: {
allowBundled: ["brave-search", "gemini"],
load: {
extraDirs: [
"~/Projects/agent-scripts/skills",
"~/Projects/oss/some-skill-pack/skills"
]
},
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 }
}
}
}
```
### `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`
- control URL: `http://127.0.0.1:18791` (CDP uses `18792`)
- CDP URL: `http://127.0.0.1:18792` (control URL + 1)
- profile color: `#FF4500` (lobster-orange)
- Note: the control server is started by the running gateway (Clawdis.app menubar, or `clawdis gateway`).
```json5
{
browser: {
enabled: true,
controlUrl: "http://127.0.0.1:18791",
// cdpUrl: "http://127.0.0.1:18792", // override for remote CDP
color: "#FF4500",
// Advanced:
// headless: false,
// noSandbox: false,
// executablePath: "/usr/bin/chromium",
// attachOnly: false, // set true when tunneling a remote CDP to localhost
}
}
```
### `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)
}
}
```
### `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`
```json5
{
gateway: {
mode: "local", // or "remote"
bind: "loopback",
// controlUi: { enabled: true }
// auth: { mode: "token" | "password" }
// tailscale: { mode: "off" | "serve" | "funnel" }
}
}
```
Notes:
- `clawdis gateway` refuses to start unless `gateway.mode` is set to `local` (or you pass the override flag).
Auth and Tailscale:
- `gateway.auth.mode` sets the handshake requirements (`token` or `password`).
- 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.
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).
- `gateway.remote.password` supplies the password for remote calls (leave unset for no auth).
```json5
{
gateway: {
mode: "remote",
remote: {
url: "ws://gateway.tailnet:18789",
token: "your-token",
password: "your-password"
}
}
}
```
### `hooks` (Gateway webhooks)
Enable a simple HTTP webhook surface on the Gateway HTTP server.
Defaults:
- enabled: `false`
- path: `/hooks`
- maxBodyBytes: `262144` (256 KB)
```json5
{
hooks: {
enabled: true,
token: "shared-secret",
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}}",
},
],
}
}
```
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? }`
- `POST /hooks/<name>` → resolved via `hooks.mappings`
`/hooks/agent` always posts a summary into the main session (and can optionally trigger an immediate heartbeat via `wakeMode: "now"`).
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,
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
}
}
}
```
Note: when `tailscale.mode` is on, Clawdis defaults `serve.path` to `/` so
Tailscale can proxy `/gmail-pubsub` correctly (it strips the set-path prefix).
### `canvasHost` (LAN/tailnet Canvas file server + live reload)
The Gateway serves a directory of HTML/CSS/JS over HTTP so iOS/Android nodes can simply `canvas.navigate` to it.
Default root: `~/clawd/canvas`
Default port: `18793` (chosen to avoid the clawd browser CDP port `18792`)
The server listens on the **bridge bind host** (LAN or Tailnet) so nodes can reach it.
The server:
- serves files under `canvasHost.root`
- injects a tiny live-reload client into served HTML
- watches the directory and broadcasts reloads over a WebSocket endpoint at `/__clawdis/ws`
- auto-creates a starter `index.html` when the directory is empty (so you see something immediately)
- also serves A2UI at `/__clawdis__/a2ui/` and is advertised to nodes as `canvasHostUrl`
(always used by nodes for Canvas/A2UI)
```json5
{
canvasHost: {
root: "~/clawd/canvas",
port: 18793
}
}
```
Disable with:
- config: `canvasHost: { enabled: false }`
- env: `CLAWDIS_SKIP_CANVAS_HOST=1`
### `bridge` (node bridge server)
The Gateway can expose a simple TCP bridge for nodes (iOS/Android), typically on port `18790`.
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/WiFi and Tailscale)
- `tailnet`: bind only to the machines 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 DNSSD)
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 } }
}
```
## Template variables
Template placeholders are expanded in `routing.transcribeAudio.command` (and any future templated command fields).
| Variable | Description |
|----------|-------------|
| `{{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) |
| `{{SessionId}}` | Current session UUID |
| `{{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) |
| `{{Surface}}` | Surface hint (whatsapp|telegram|discord|imessage|webchat|…) |
## 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.
```json5
{
cron: {
enabled: true,
maxConcurrentRuns: 2
}
}
```
---
*Next: [Agent Runtime](./agent.md)* 🦞
<!-- {% endraw %} -->