All clawq configuration lives in a single JSON file at ~/.clawq/config.json (or $CLAWQ_HOME/config.json if the CLAWQ_HOME environment variable is set). This page covers every configuration section, the interactive wizard, programmatic access via the CLI, and environment variables.

Configuration Methods

Interactive Wizard

The recommended way to configure clawq for the first time:

clawq onboard       # full onboarding wizard
clawq config wizard # re-run the wizard any time

The wizard walks through provider, model, security, channels, gateway, and memory settings. When run in a TTY, it launches a full interactive TUI. When piped or redirected, it falls back to generating a starter template.

Channel Setup Wizards

For configuring individual channels, use the dedicated setup wizards:

clawq setup discord     # Discord bot token, guilds, users, intents
clawq setup telegram    # Telegram bot token, allowed users
clawq setup slack       # Slack bot token, app token, signing secret
clawq setup github      # GitHub PAT, repos, webhook paths
clawq setup teams       # MS Teams app credentials
clawq setup tunnel      # Tunnel provider configuration
clawq setup summarizer  # Autosummarizer settings

CLI Set/Get/Show

Manage individual values by dot-path:

# Set a value
clawq config set providers.openrouter.api_key "sk-..."
clawq config set agent_defaults.primary_model "openrouter:gpt-5.4"
clawq config set channels.telegram.accounts.main.bot_token "123:ABC..."

# Read a value
clawq config get providers.openrouter.default_model

# Show full config (secrets redacted)
clawq config show

# Show a specific section
clawq config show security
clawq config show channels

In chat channels, use /config menu to interactively browse config sections, or /config show <section> directly.

Manual Editing

$EDITOR ~/.clawq/config.json

After editing, validate with:

clawq doctor

Live Reload

When the daemon is running (clawq agent), config changes are picked up automatically — the daemon polls the file every 10 seconds and reloads when it detects a modification. Changes to provider settings, agent defaults, memory limits, heartbeat timing, and security flags take effect without a restart.

You can also force an immediate reload by sending SIGHUP:

kill -HUP $(cat ~/.clawq/daemon_state.json | jq .pid)

Structural changes that affect startup (enabling/disabling channels, changing gateway port/host) still require a daemon restart.

Environment Variables

VariablePurpose
CLAWQ_HOMEOverride the clawq data directory (default: ~/.clawq). All config, database, logs, and state files are stored here. Propagated through daemon restarts.
CLAWQ_MASTER_KEYPassphrase for encrypting/decrypting API keys at rest (AES-256-GCM via PBKDF2)
CLAWQ_UPDATE_BINARY_URLURL for fetching binary updates when clawq update is used. Supports direct binary download or GitHub release URLs.
CLAWQ_TUNNEL_COMMANDFull command to invoke for custom tunnel provider (used when tunnel.provider is "custom")
CLAWQ_TUNNEL_URL_REGEXRegex with a capture group used to extract the public URL from the custom tunnel command’s output
CLAWQ_TUNNEL_URLStatic URL override for Cloudflare quick tunnels when URL extraction is not needed
CLAWQ_DEBUG_HTTPSet to 1 to enable HTTP debug logging (saves all requests/responses to HAR files). Same effect as log.debug_http: true in config.
CLAWQ_RUNNER_TOKENBearer token for remote runner authentication against the gateway’s /mcp and /runner/ask endpoints. Auto-set for background tasks.
CLAWQ_MCP_URLMCP-over-HTTP endpoint URL (e.g. http://127.0.0.1:8080/mcp). Auto-set for background tasks.
CLAWQ_RUNNER_ASK_URLSimple REST endpoint URL for non-MCP runners to ask questions (e.g. http://127.0.0.1:8080/runner/ask). Auto-set for background tasks.

When security.encrypt_secrets is true and CLAWQ_MASTER_KEY is set, API keys are automatically decrypted at runtime. Encrypted values are stored with a $ENC: prefix.

Configuration Sections

providers

An object keyed by provider name. The active provider is selected via agent_defaults.primary_model using the provider:model format.

{
  "providers": {
    "openrouter": {
      "api_key": "sk-or-v1-...",
      "base_url": "https://openrouter.ai/api/v1",
      "default_model": "openai/gpt-4o"
    },
    "openai": {
      "api_key": "sk-...",
      "base_url": "https://api.openai.com/v1",
      "default_model": "gpt-4o"
    }
  }
}
FieldRequiredDescription
api_keyYesAPI key (or $ENC:... encrypted value)
base_urlNoAPI base URL (defaults to OpenAI)
kindNoProvider kind override (for example openai-codex)
default_modelNoDefault model for this provider. Required for quota-based failover: when another provider is deprioritized, only providers with a default_model are eligible alternatives
thinking_budget_tokensNoProvider reasoning budget when supported
oai_thinking_styleNoOAI-compatible reasoning tag style; defaults to none

default_provider

:::caution[Deprecated] default_provider is deprecated as of B425. The provider is already embedded in agent_defaults.primary_model using the provider:model format (e.g. "openrouter:gpt-5.4").

Migration: remove default_provider from your config.json and ensure agent_defaults.primary_model is set to a canonical provider:model string. clawq will warn at startup if this field is still present. :::

{
  "default_provider": "openrouter"
}

This selects which key from providers is used when a request does not choose a provider explicitly. This field is no longer needed when agent_defaults.primary_model uses the provider:model format.

channels

Channel configurations for Telegram, Discord, Slack, GitHub, and other runtime integrations.

{
  "channels": {
    "telegram": {
      "accounts": {
        "main": {
          "bot_token": "7123456789:AAF1k...",
          "allow_from": ["*"],
          "totp": {
            "enabled": true,
            "secret": "BASE32SECRET",
            "session_ttl_hours": 24
          }
        }
      }
    },
    "discord": {
      "enabled": true,
      "bot_token": "MTIz...",
      "application_id": "123456789"
    },
    "slack": {
      "enabled": true,
      "bot_token": "xoxb-...",
      "app_token": "xapp-...",
      "signing_secret": "abc123..."
    },
    "github": {
      "auth": {
        "type": "pat",
        "token": "ghp_..."
      },
      "repos": [
        {
          "name": "acme/backend",
          "webhook_secret": "super-secret-value",
          "webhook_path": "/github/webhook/backend",
          "allow_users": ["octocat"],
          "react_to": ["issue_comment", "workflow_job"],
          "include_pr_files": true
        }
      ]
    }
  }
}

Telegram fields:

Telegram supports multiple named accounts under channels.telegram.accounts.

FieldDescription
accounts.<name>.bot_tokenBot token from @BotFather
accounts.<name>.allow_fromArray of allowed chat IDs, or ["*"] for all users
accounts.<name>.totp.enabledRequire TOTP pairing for non-allowlisted users
accounts.<name>.totp.secretShared TOTP secret used by clawq otp-show
accounts.<name>.totp.session_ttl_hoursHow long a successful pairing stays valid

Discord fields:

FieldDescription
enabledEnable/disable this channel
bot_tokenBot token from Discord Developer Portal
application_idApplication ID from Discord Developer Portal

Slack fields:

FieldDescription
enabledEnable/disable this channel
bot_tokenBot token (xoxb-...)
app_tokenApp-level token (xapp-...) for Socket Mode
signing_secretSigning secret for Events API HMAC verification

GitHub fields:

FieldDescription
auth.typeAuthentication type, currently pat
auth.tokenGitHub Personal Access Token used for API replies and PR file fetches
repos[].nameRequired canonical owner/repo identity for that webhook path
repos[].webhook_secretShared secret used for GitHub HMAC verification
repos[].webhook_pathInbound webhook path; must be unique across configured repos
repos[].agent_nameOptional named agent override
repos[].allow_usersGitHub usernames allowed for user-generated or arbitrary-content events
repos[].react_toEvent allowlist for that repo path; empty means accept all delivered events
repos[].include_pr_filesFetch changed file lists for supported PR/comment flows

GitHub hook files live under ~/.clawq/workspace/gh-hooks/, and verified payload snapshots are written to ~/.clawq/workspace/tmp/github-deliveries/. See the Channels page for a hook file example and event trust model details.

Channel-specific default model:

Each channel can have its own default model that overrides the global primary_model. This is useful when different channels should use different models (e.g. a cheaper model for high-traffic Telegram chats, a stronger model for Discord).

clawq channel set-model discord anthropic:claude-opus-4-6
clawq channel show-model discord
clawq channel clear-model discord

The model resolution hierarchy (highest to lowest priority):

  1. Per-session model override (clawq session model SESSION set MODEL)
  2. Channel-specific default model (clawq channel set-model NAME MODEL)
  3. Global agent_defaults.primary_model

All 17 channel types are supported: telegram, discord, slack, github, teams, matrix, mattermost, dingtalk, imessage, signal, irc, email, whatsapp, nostr, lark, line, onebot.

security

{
  "security": {
    "workspace_only": true,
    "tools_enabled": true,
    "audit_enabled": true,
    "encrypt_secrets": false,
    "rate_limit_rpm": 60,
    "audit_retention_days": 90
  }
}
FieldDefaultDescription
workspace_onlytrueRestrict file operations to workspace directory
tools_enabledtrueEnable agent tool invocations
audit_enabledtrueEnable audit logging of tool invocations and security events
encrypt_secretsfalseEncrypt API keys at rest using CLAWQ_MASTER_KEY
rate_limit_rpm60Requests per minute rate limit
audit_retention_days90Days to retain audit log entries
allowed_cwd_patterns["$CLAWQ_WORKSPACE/**", "$USER_HOME/src/projects-clawq/**", "/clawq/path/to/somewhere/else/**"]Glob patterns for directories agents may change_working_dir into. Supports $CLAWQ_WORKSPACE and $USER_HOME pseudo-variables.

gateway

{
  "gateway": {
    "host": "127.0.0.1",
    "port": 13451,
    "require_pairing": true,
    "auth_token": null,
    "max_pair_attempts": 5,
    "pair_lockout_seconds": 300
  }
}
FieldDefaultDescription
host127.0.0.1Gateway bind address
port13451Gateway port
require_pairingtrueRequire browser/API clients to pair before posting chat requests
auth_tokenunsetStatic bearer token or x-api-key alternative for scripted access
max_pair_attempts5Failed OTP attempts allowed before temporary lockout
pair_lockout_seconds300Lockout duration after too many failed pair attempts

The gateway root / serves the embedded web UI. Supporting endpoints include /chat/stream for SSE chat streaming, /commands for slash-command metadata, /ui-version for reload prompts, and /pair for browser pairing.

memory

{
  "memory": {
    "backend": "sqlite",
    "search_enabled": true,
    "embedding_provider": "openai",
    "embedding_model": "text-embedding-3-small",
    "vector_weight": 50,
    "keyword_weight": 50,
    "ttl_days": 365
  }
}
FieldDefaultDescription
backendsqliteMemory backend type
search_enabledfalseEnable hybrid vector + FTS search
embedding_providerProvider for generating embeddings
embedding_modelModel for generating embeddings
vector_weight50Weight for vector similarity results (must sum to 100 with keyword_weight)
keyword_weight50Weight for keyword FTS results
ttl_days365Days before memory entries expire

resilience

{
  "resilience": {
    "timeout_s": 120.0,
    "max_retries": 2,
    "base_delay_s": 1.0,
    "fallback_provider": "groq"
  }
}
FieldDefaultDescription
timeout_s120.0Timeout for LLM API calls in seconds
max_retries2Maximum retry attempts with exponential backoff
base_delay_s1.0Base delay between retries
fallback_providerProvider name to use if primary fails

mcp

{
  "mcp": {
    "enabled": true,
    "exposed_tools": ["file_read", "file_write", "shell_exec"]
  }
}
FieldDefaultDescription
enabledtrueEnable the MCP server (JSON-RPC via stdio)
exposed_toolsallArray of tool names to expose (omit for all)
runner_relay_enabledtrueEnable HTTP endpoints (/mcp, /runner/ask, /runner/token) for remote runner question relay
runner_token_ttl_hours24TTL in hours for runner bearer tokens
runner_question_timeout_s300Timeout in seconds for relayed questions before giving up

mcp_servers (MCP Client)

Connect clawq to external MCP servers to discover and invoke their tools. Configuration is read from a separate file at ~/.clawq/mcp_servers.json (a JSON array of server entries):

[
  {
    "name": "my-server",
    "command": "node",
    "args": ["/path/to/server.js"],
    "env": {
      "MY_VAR": "value"
    }
  },
  {
    "name": "remote-server",
    "url": "http://localhost:3000/mcp",
    "headers": {
      "Authorization": "Bearer mytoken"
    }
  }
]

stdio-based server fields:

FieldDescription
nameUnique identifier for the server
commandExecutable to spawn
argsArray of arguments passed to the command
envObject of extra environment variables injected into the process

HTTP-based server fields:

FieldDescription
nameUnique identifier for the server
urlHTTP or HTTPS URL of the MCP endpoint
headersObject of extra request headers (e.g. Authorization)

Tools discovered from external MCP servers are automatically registered and available to the agent during turns.

tunnel

{
  "tunnel": {
    "provider": "cloudflare",
    "enabled": true,
    "url": "",
    "managed": false,
    "tunnel_name": "",
    "config_dir": ""
  }
}
FieldDefaultDescription
provider"cloudflare"Tunnel provider: cloudflare, tailscale, ngrok, or custom
enabledfalseEnable the tunnel
url""Static URL override (skips dynamic URL extraction)
managedfalseUse a named/managed Cloudflare tunnel (requires tunnel_name)
tunnel_name""Name of the Cloudflare named tunnel (used when managed is true)
config_dir""Path to cloudflared config directory (contains config.yml)

Quick tunnel (ad-hoc): Set enabled: true with default settings. clawq spawns cloudflared tunnel --url http://localhost:PORT and extracts the assigned trycloudflare.com URL automatically.

Named/managed tunnel: Set managed: true and tunnel_name to a pre-existing Cloudflare named tunnel. You must first create the tunnel and DNS routes externally via cloudflared tunnel create and cloudflared tunnel route dns. Set url to the public hostname so clawq knows the URL. Optionally set config_dir if config.yml is not in the default location.

Other providers: Set provider to tailscale, ngrok, or custom. For custom tunnels, set CLAWQ_TUNNEL_COMMAND and optionally CLAWQ_TUNNEL_URL_REGEX environment variables.

interactive

{
  "interactive": {
    "enable_question_notes": true
  }
}

Controls behavior of the ask_user_question tool, which lets the agent ask clarifying questions mid-turn via interactive channels.

FieldDefaultDescription
enable_question_notestrueAfter structured answers (select, confirm, rating, number, date), prompt the user with “Add notes? (reply or ‘skip’)”. Set to false to skip the notes prompt

Live reconfiguration: The daemon watches for tunnel config changes on SIGHUP and via file-change polling (every 10 seconds). When tunnel settings change, the daemon automatically stops the old tunnel and starts a new one — no daemon restart required. Use clawq tunnel apply to trigger reconciliation explicitly, or clawq tunnel restart to force a stop-and-restart even without config changes.

Model Format

The canonical format for model references is provider:model (colon separator). Examples: openai:gpt-5.4, anthropic:claude-sonnet-4-6, groq:llama-4-scout-17b-16e.

Legacy format deprecated

The legacy provider/model (slash) format is still accepted but deprecated and will be removed in a future release.

Deprecation warnings appear:

  • On config load when primary_model uses the legacy format
  • In clawq status output
  • When using config set agent_defaults.primary_model
  • In /model set (prints a format hint)
  • models set-default and /model set-default auto-normalize to canonical format

Note: providers.*.default_model is the model identifier as understood by that provider’s API (e.g. openai/gpt-4o on OpenRouter) — this is not subject to the format deprecation.

model_context_limits

{
  "model_context_limits": {
    "openai-codex:gpt-5.4": 272000
  }
}

Optional top-level object mapping model names to manual token limits used for context budgeting and history compaction. Configured entries take precedence over built-in defaults.

heartbeat

{
  "heartbeat": {
    "enabled": true,
    "interval_seconds": 250,
    "quiet_start": 23,
    "quiet_end": 8
  }
}
FieldDefaultDescription
enabledtrueGlobal master switch for heartbeat delivery
interval_seconds250Seconds between heartbeat ticks
quiet_start23Hour (0-23) when quiet period begins
quiet_end8Hour (0-23) when quiet period ends

Per-session opt-in required

Sessions must opt in individually via /heartbeat on or clawq session heartbeat SESSION on. Only Telegram, Slack, Discord, and Teams sessions are valid heartbeat targets.

The daemon reads HEARTBEAT.md from the workspace root on each tick.

error_watcher

{
  "error_watcher": {
    "ec_enabled": false,
    "scan_interval_s": 30.0,
    "cooldown_s": 300.0,
    "max_errors_per_batch": 10,
    "auto_fix_enabled": false
  }
}
FieldDefaultDescription
ec_enabledfalse (true for -dev builds)Enable the error correction watcher
scan_interval_s30.0Seconds between daemon log scans
primary_models["anthropic:claude-opus-4-6", "openai-codex:gpt-5.4"]Models for diagnosis
fallback_models["zai_coding:glm-5", "kimi_coding:kimi-for-code"]Fallback models
cooldown_s300.0Cooldown between EC runs
max_errors_per_batch10Max errors per scan batch
ignore_patterns[]Regex patterns to ignore in logs
auto_fix_enabledfalseAutomatically apply fixes
ec_commit_tag"[INTERNAL_EC]"Git commit tag for EC fixes

Auto-fix is off by default

Set auto_fix_enabled: true only after verifying the watcher behaves correctly in your environment. EC fixes are tagged with ec_commit_tag for easy identification and rollback.

notify

{
  "notify": {
    "notify_channel": "telegram",
    "notify_target": "123456789"
  }
}
FieldDescription
notify_channelChannel type for outbound notifications (e.g. telegram)
notify_targetTarget identifier (e.g. chat ID) for proactive sends
{
  "web_search": {
    "search_provider": "brave",
    "search_api_key": "BSA...",
    "num_results": 5
  }
}
FieldDescription
search_provider"brave" or "ddg" (DuckDuckGo)
search_api_keyAPI key for the search provider
num_resultsNumber of results to return
search_base_urlOptional endpoint override (e.g. for SearXNG)

zai_mcp

{
  "zai_mcp": {
    "enabled": true,
    "websearch_enabled": true,
    "webfetch_enabled": true
  }
}
FieldDefaultDescription
enabledtrue (when section present)Enable Z.ai MCP-powered web tools
api_keyauto-detectedBearer token; auto-detected from providers.zai or providers.zai_coding if absent
websearch_enabledtrueRegister zai_websearch tool
webfetch_enabledtrueRegister zai_webfetch tool

voice

{
  "voice": {
    "stt_enabled": true,
    "tts_enabled": false,
    "stt_provider": "openai",
    "tts_provider": "openai",
    "tts_model": "tts-1",
    "tts_voice": "alloy",
    "audio_dir": ""
  }
}
FieldDescription
stt_enabledEnable speech-to-text
tts_enabledEnable text-to-speech
stt_providerSTT provider name
tts_providerTTS provider name
tts_modelTTS model identifier
tts_voiceVoice selection
audio_dirDirectory for audio file storage

When an stt provider is configured (separate from the voice section), voice messages and audio attachments sent to any channel are automatically transcribed with progress messages (“Transcribing…” → “Transcribing… Done!”). Validation limits are hardcoded: max 25 MB file size, max 1 hour duration, and music files (.mp3, .m4a, .flac, .aac, .wma) are skipped unless the filename contains “voice”, “recording”, or “audio_message”.

web_channel

{
  "web_channel": {
    "enabled": true,
    "path_prefix": "/web",
    "token_ttl_hours": 24
  }
}
FieldDefaultDescription
enabledEnable the web channel routes
path_prefix"/web"URL path prefix for web channel endpoints
totp_secretOptional TOTP secret for web channel auth
token_ttl_hours24Token validity period in hours

telemetry

{
  "telemetry": {
    "enabled": false,
    "endpoint": "",
    "service_name": "clawq"
  }
}
FieldDescription
enabledEnable telemetry reporting
endpointTelemetry collection endpoint URL
service_nameService name for telemetry tags

agent_bindings

An array of routing rules that dispatch incoming messages to named agent configurations by matching against a user or channel identifier.

{
  "agent_bindings": [
    {
      "pattern": "user:123456789",
      "agent_name": "coder",
      "priority": 10
    },
    {
      "pattern": "channel:987654321",
      "agent_name": "ops",
      "priority": 5
    },
    {
      "pattern": "default",
      "agent_name": "default",
      "priority": 0
    }
  ]
}
FieldRequiredDescription
patternYesMatch target. Use user:<id> to match a sender, channel:<id> to match a channel, guild:<id> to match a Discord server, or default to match all unmatched messages
agent_nameYesNamed agent configuration to route to
priorityNoInteger priority; higher values are evaluated first. Defaults to 0

Bindings are evaluated in priority order (highest first). The first matching pattern wins. The default pattern serves as a catch-all when no other binding matches.

log

{
  "log": {
    "max_size_mb": 10,
    "max_files": 5,
    "debug_http": false
  }
}
FieldDefaultDescription
max_size_mb10Maximum daemon log file size before rotation
max_files5Number of historical log files to retain
debug_httpfalseSave all HTTP requests/responses to HAR files in ~/.clawq/log-http-debug/. Also activatable via CLAWQ_DEBUG_HTTP=1 env var

observer

{
  "observer": {
    "enabled": true,
    "check_every_n_messages": 5
  }
}
FieldDefaultDescription
enabledtrueEnable turn observer for stuck/looping detection
model"groq:openai/gpt-oss-120b"Model for observer analysis
check_every_n_messages5Check frequency
round1_window8Recent messages to analyze in round 1
round2_window30Extended window for round 2 analysis
thinking_token_threshold5000Thinking token threshold
consecutive_errors_threshold3Error count before intervention
repeat_call_threshold2Repeated tool call threshold

summarizer

{
  "summarizer": {
    "enabled": true,
    "threshold_chars": 1500
  }
}
FieldDefaultDescription
enabledtrueEnable autosummarizer for long tool output
model"groq:openai/gpt-oss-120b"Model for summarization
escalation_modelOptional stronger model for escalation
threshold_chars1500Character threshold to trigger summarization
p1_max_chars200000Max chars for phase 1
p2_max_chars12000Max chars for phase 2
context_window_messages4Messages of context to include
excluded_tools["tool_search", ...]Tools exempt from summarization
max_age_days30Days before summaries expire

Workspace Files

The workspace directory (~/.clawq/workspace/ by default, or $CLAWQ_HOME/workspace/) contains Markdown files that are read at the start of each agent turn and included in the system prompt context.

FilePurpose
EGO.mdAgent identity and core principles. Takes highest precedence when instructions conflict.
AGENTS.mdOperating protocol and routing instructions. Read on every turn.
SOUL.mdExtended personality and behavioral guidelines.
TOOLS.mdAgent-specific tool notes and usage guidance.
IDENTITY.mdOptional supplementary identity context.
USER.mdUser profile, preferences, and working style.
HEARTBEAT.mdInstructions read and executed on each heartbeat tick.
MEMORY.mdCurated long-term memory. Loaded only in direct, private sessions.

These files are created with sensible defaults by clawq workspace init. Edit them to customize agent behavior without changing config.json. The workspace_only security setting restricts file tool operations to this directory.

Full Example

{
  "providers": {
    "openrouter": {
      "api_key": "sk-or-v1-...",
      "base_url": "https://openrouter.ai/api/v1",
      "default_model": "openai/gpt-4o"
    }
  },
  "channels": {
    "telegram": {
      "accounts": {
        "main": {
          "bot_token": "7123456789:AAF1k...",
          "allow_from": ["*"]
        }
      }
    }
  },
  "security": {
    "workspace_only": true,
    "tools_enabled": true,
    "audit_enabled": true,
    "encrypt_secrets": false
  },
  "gateway": {
    "host": "127.0.0.1",
    "port": 13451,
    "require_pairing": true
  },
  "memory": {
    "backend": "sqlite",
    "search_enabled": false
  },
  "resilience": {
    "timeout_s": 120.0,
    "max_retries": 2
  },
  "tunnel": {
    "provider": "cloudflare",
    "enabled": false
  }
}

Secret Encryption

To encrypt API keys at rest:

  1. Set security.encrypt_secrets to true
  2. Set the CLAWQ_MASTER_KEY environment variable with a strong passphrase
  3. Run clawq auth encrypt to encrypt all plaintext API keys
  4. Keys are stored as $ENC:... values and automatically decrypted at runtime