Configuration
Two files, by intent:
.env— secrets, tokens, channel-enable flags. Never committed.data/config.json— non-secret runtime config (bot name, allow-lists, timezone, budgets, etc.). Generated bybun run setup, edited bybun run pathand friends.
Precedence (lowest → highest):
- Defaults baked into src/lib/config.ts
data/config.jsonprocess.env(MARSCLAW_*overrides)
Env wins because existing users have .env muscle-memory and a binary upgrade must not silently change behaviour. Setup writes both files for you; edit by hand only when you need to.
.env reference
Channel tokens
| Key | Required | Notes |
|---|---|---|
TELEGRAM_BOT_TOKEN | per-channel | From @BotFather |
SLACK_BOT_TOKEN | per-channel | xoxb-… |
SLACK_APP_TOKEN | per-channel | xapp-…, needs connections:write |
MARSCLAW_WHATSAPP | per-channel | Set to 1; QR-scan auth on first run |
Provider auth
| Key | Required | Notes |
|---|---|---|
ANTHROPIC_API_KEY | optional | Only needed if not using Claude CLI's OAuth login |
GEMINI_API_KEY | optional | Use a paid key instead of OAuth (higher quota) |
GOOGLE_OAUTH_CLIENT_ID | optional | For Gmail/Drive/etc. via MCP — desktop OAuth client |
GOOGLE_OAUTH_CLIENT_SECRET | optional | Pairs with GOOGLE_OAUTH_CLIENT_ID |
Voice
| Key | Notes |
|---|---|
MARSCLAW_VOICE | Set to 1 to enable Whisper STT + Kokoro TTS |
WHISPER_URL | Default http://127.0.0.1:9000 |
WHISPER_MODEL | tiny / base / small / medium / large (default base) |
KOKORO_URL | Default http://127.0.0.1:9001 |
KOKORO_VOICE | Default voice (af_heart, af_bella, am_adam, …) |
KOKORO_FORMAT | ogg (voice note) / mp3 / wav |
YouTube transcripts
| Key | Notes |
|---|---|
MARSCLAW_YTDLP_PATH | Absolute path to yt-dlp. Setup pins this; the runtime falls back to common install dirs + a login-shell lookup if unset. See youtube.md |
Behavior overrides
Each shadows the same field in data/config.json.
| Key | Notes |
|---|---|
AGENT_PROVIDER | gemini or claude |
MARSCLAW_BOT_NAME | Name the agent uses for itself |
MARSCLAW_OWNER_NAME | What the agent calls you |
MARSCLAW_OWNER_PHONE | Your WhatsApp number, digits only |
MARSCLAW_TIMEZONE | IANA tz, e.g. Asia/Colombo |
MARSCLAW_LOCATION | Free-text, e.g. Colombo, Sri Lanka |
MARSCLAW_WHATSAPP_ALLOWED_JIDS | Comma-list; empty allows all |
MARSCLAW_TELEGRAM_ALLOWED_CHATS | Comma-list of Telegram chat ids; empty allows all |
MARSCLAW_SLACK_ALLOWED_USERS | Comma-list of Slack user ids; empty allows all |
MARSCLAW_ALLOWED_PATHS | Comma-list; default = cwd |
MARSCLAW_ALLOW_SHELL | 1 to expose the Bash tool to the agent (default 0) — see security.md |
MARSCLAW_ALLOW_WEB | 1 to expose WebFetch/WebSearch (default 0) |
MARSCLAW_ALLOWED_WEB_DOMAINS | Comma-list of hosts WebFetch may hit (e.g. wikipedia.org,*.gov) |
MARSCLAW_ALLOW_MUTATING_TOOLS | 1 to let the agent send mail / write Sheets / etc. (default 0) |
MARSCLAW_AUDIT_LOG | Path to the append-only audit log (default logs/audit.log) |
MARSCLAW_MAX_SESSIONS | LRU cap on Claude SDK sessions |
MARSCLAW_CLAUDE_IDLE_MS | Recycle idle sessions after N ms (default 15 min) |
MARSCLAW_CLAUDE_MAX_SESSION_AGE_MS | Hard cap on session lifetime (default 4 h) |
MARSCLAW_AGENT_TIMEOUT_MS | Per-message timeout (default 300000) |
MARSCLAW_RATE_LIMIT_PER_MINUTE | Inbound per-sender (default 10; 0 disables) |
MARSCLAW_RATE_LIMIT_PER_HOUR | Inbound per-sender (default 60; 0 disables) |
MARSCLAW_DAILY_USD_BUDGET | Anthropic spend cap (0 disables; metered keys only) |
MARSCLAW_TOOL_PERMISSIONS | bypass = disable the canUseTool gate (escape hatch) |
MARSCLAW_WHATSAPP_VERBOSE | Set to 1 for Baileys protocol logs |
LOG_LEVEL | debug / info / warn / error / fatal |
Paths
| Key | Default |
|---|---|
MARSCLAW_DB | data/marsclaw.db |
MARSCLAW_CONFIG | data/config.json |
MARSCLAW_WHATSAPP_AUTH | data/whatsapp-auth |
GEMINI_BIN | gemini |
CLAUDE_BIN | claude |
data/config.json reference
Generated by setup; safe to edit while the bot is stopped. Shape mirrors src/lib/config.ts.
{
"bot_name": "Mars",
"owner_name": "Dimuthu",
"owner_phone": "94701234567",
// One-shot WhatsApp pairing. Setup flips these.
"whatsapp_pair_owner": false,
"whatsapp_pair_code": "",
// Inbound allow-lists. Empty = allow all (with a per-sender warning).
"allowed_jids": ["94701234567@s.whatsapp.net"],
"allowed_telegram_chats": ["123456789"],
"allowed_slack_users": ["U01ABC23DEF"],
// Filesystem sandbox for the agent's Read/Write/Edit/Glob/Grep tools.
// Defaults to [cwd]. Anything outside is denied at the tool-gate layer.
// Sensitive files (.env, data/secrets, data/config.json, ~/.claude.json,
// ~/.gemini, data/whatsapp-auth, data/marsclaw.db) are blocked regardless
// of what's listed here. See docs/security.md.
"allowed_paths": ["/Users/you/marsclaw", "/Users/you/Documents/notes"],
// Capability flags — all off by default. See docs/security.md for the
// egress / threat model implications of each.
"allow_shell": false, // expose the Bash tool? (denylist is not airtight; default off)
"allow_web": false, // expose WebFetch/WebSearch?
"allowed_web_domains": [ // WebFetch host allow-list (bare = apex + subdomains)
"wikipedia.org",
"developer.mozilla.org",
"github.com"
],
"allow_mutating_tools": false, // gmail_send, sheets_write, calendar_create_event, *_raw writes
// Claude SDK session cap & lifetimes.
"max_sessions": 20,
"idle_ms": 900000,
"max_session_age_ms": 14400000,
"timezone": "Asia/Colombo",
"location": "Colombo, Sri Lanka",
"voice_enabled": true,
"agent_provider": "claude",
// Extra Bash patterns to deny, on top of the built-in destructive list.
"extra_bash_denylist": [],
// Inbound rate-limits (per sender). 0 disables.
"rate_limit_per_minute": 10,
"rate_limit_per_hour": 60,
// Anthropic spend cap in USD. 0 disables. Skipped under Claude Pro/Max OAuth.
"daily_usd_budget": 0
}
Allowed paths
The agent's Read, Write, Edit, Glob, Grep, MultiEdit, and NotebookEdit tools are gated against allowed_paths. Default is just the project root.
bun run path list
bun run path add /Users/you/Documents/notes
bun run path remove /Users/you/Documents/notes
bun run path reset # back to [cwd]
The same sandbox applies to MCP send_file — files outside allowed_paths cannot be delivered.
A separate sensitive-path list sits in front of allowed_paths and blocks .env, data/secrets/, data/config.json, data/whatsapp-auth/, data/marsclaw.db, ~/.claude.json, ~/.claude/, and ~/.gemini/ regardless of how allowed_paths is configured. Grep/Glob additionally refuse a search root that straddles any sensitive subtree. See security.md for the full list and rationale.
Escape hatch: MARSCLAW_TOOL_PERMISSIONS=bypass disables the gate entirely. Intended as a one-release migration aid; logs a warning on boot. Do not set this in a running deployment.
Capability flags (security defaults)
Four flags govern what the agent can do beyond reading approved files. All are off by default; turning each one on reopens a specific attack surface that security.md walks through in detail.
| Flag | Default | What it gates |
|---|---|---|
allow_shell | false | The Bash tool. A denylist can't make shell safe; removal is the only sound posture. |
allow_web | false | WebFetch and WebSearch. Web reads are delegated to a researcher subagent in an empty-room context. |
allowed_web_domains | [] | Host allow-list for WebFetch. Bare entry covers apex + subdomains; *.example.com form is equivalent. |
allow_mutating_tools | false | Outbound/mutating MCP tools: gmail_send, sheets_write, calendar_create_event, write-style *_raw calls. |
Bash denylist
extra_bash_denylist adds JavaScript regex source strings to the built-in destructive patterns (rm -rf /, chmod 000, dd of=, fork bombs, mkfs, shred, security find-generic-password, data/secrets). Only relevant when allow_shell is on — when shell is off, Bash isn't available regardless.
Persona & memory files
| File | Purpose |
|---|---|
GEMINI.md | Persona instructions read by the Gemini provider |
CLAUDE.md | Persona instructions read by Claude (also project instructions for claude CLI in this repo) |
skills/*.md | Sub-instructions referenced from a persona via @skills/<name>.md |
MEMORY.md | The agent's own long-term notes. Local-only, gitignored |
MEMORY.template.md | Seed copied to MEMORY.md on first run |
wiki/*.md | Optional longer structured pages the agent can read on demand |
Reset memory: rm MEMORY.md && bun run start.
Multiple Google accounts
Refresh tokens are stored in the macOS Keychain (service marsclaw) or a 0600 fallback file on Linux. Add additional accounts with bun run google login — give each one a short alias (personal, work). The MCP tools accept an account argument to pick which one to use; the default is the alias marked default.