🏠 Home ← Docs Index
Open Beta 📖 Interactive Reader Raw .md llms.txt

GoClaw Configuration Guide

This guide covers everything you need to configure the GoClaw Multi-Agent Orchestration Platform. GoClaw uses a powerful YAML-based configuration system that supports hot reloading, environment variables, and secret interpolation.


Configuration Methods

GoClaw can be configured using multiple methods:

  1. YAML Configuration File: The primary configuration source: config.yaml in the state directory (~/.goclaw/config.yaml), else config.yaml in the working directory.
  2. Environment Variables: Three override the file: PORT (server.port), AI_PROVIDER (ai.provider), and DEBUG=1 or DEBUG=true (debug). GOCLAW_STATE_DIR sets the state directory, which is where config.yaml is read from (default ~/.goclaw); GOCLAW_HOME replaces the home directory that ~/.goclaw is under. GOCLAW_SECRET_KEY sets the key the secrets vault is encrypted with. Other settings are not read from the environment, except through a ${env:VAR} reference in a credential field (see below); the few variables that switch on diagnostics or act as key fallbacks are listed in the CLI reference.
  3. CLI Commands: Direct manipulation of the YAML file via the CLI.
  4. Setup Wizard: Interactive onboarding using goclaw setup. It opens by itself the first time goclaw starts with no config.yaml.

The setup wizard

The wizard offers QuickStart and Advanced. QuickStart asks only what GoClaw cannot guess:

  1. Which AI to use, and its API key. The key is checked with the provider straight away (a request for its list of models, which costs nothing): a refused key is asked for again, and one that could not be checked, with no network for example, is kept. For Ollama, QuickStart lists the models the local Ollama has pulled and asks which one to use.
  2. What the agent is called and, optionally, what it helps with, in one sentence. This becomes the agent's IDENTITY.md. It is asked only while IDENTITY.md is still the file GoClaw seeded; after that, the persona is changed on the Web UI's Instructions page, where you can edit it as text or describe it in words and press Draft.
  3. Optionally, a Discord bot token.

Everything else takes its default: the provider's model and endpoint, the workspace (~/.goclaw/workspace), minimal operating mode, port 8080 on 127.0.0.1. Advanced asks for each of these (the model, a base URL, Vertex AI, the workspace, channels, the gateway) and also checks the key. When you change provider in either flow, the default agent follows it, unless its model names a model of its own.

YAML Features


CLI Configuration Management

You can get, set, append, remove or delete configuration keys directly from the terminal.

Command Description
config set <key=value> Add or update a key-value pair.
config get <key> Retrieve the current value of a key.
config append <key=value> Add a value to a list.
config remove <key=value> Remove a value from a list.
config delete <key> Remove a key entirely, including one GoClaw does not recognise (the start-up log reports those as "unknown setting").

Examples:

# Set AI provider and API key
goclaw config set ai.provider=gemini
goclaw config set ai.gemini.api_key="AIzaSyA..."

# Add a skill to the allowed list
goclaw config append skills.allow=calculator

# View current provider
goclaw config get ai.provider

LLM Providers

ai: says which model provider answers and holds each provider's credentials. Only the block of a provider you use needs a key.

ai:
  provider: "gemini"   # gemini | openai | claude | deepseek | ollama (default: openai)
  gemini:
    api_key: "${secret:GEMINI_API_KEY}"
    model: "gemini-2.5-flash"              # default
    # use_vertex: true                     # Vertex AI instead of an API key, with Google
    # project_id: "my-gcp-project"         # Cloud application default credentials
    # location: "us-central1"              # default
  openai:
    api_key: "${secret:OPENAI_API_KEY}"
    model: "gpt-4o"                        # default
    # base_url: "https://api.openai.com/v1"  # or any OpenAI-compatible endpoint
  anthropic:                               # the block for provider "claude"
    api_key: "${secret:ANTHROPIC_API_KEY}"
    model: "claude-sonnet-5-5"             # default
    # effort: "medium"                     # low, medium, high, xhigh, max; empty is the model's default
    # max_output_tokens: 16000             # default; thinking counts against it
  deepseek:
    api_key: "${secret:DEEPSEEK_API_KEY}"
    model: "deepseek-flash"                # default (reads images); or deepseek-v4-pro
    # base_url: "https://api.deepseek.com" # default
    # reasoning_effort: "high"             # none, low, high, max; empty is DeepSeek's default (high)
    # max_output_tokens: 16000             # default; thinking counts against it
  ollama:
    base_url: "http://localhost:11434"     # default
    model: "llama3"                        # default

Gateway Authentication Token

The gateway does not start without gateway.auth.token; it stops with CRITICAL: Gateway authentication token is missing!. goclaw setup writes a random token when you leave the field blank.

gateway:
  auth:
    token: "${secret:GATEWAY_TOKEN}"   # or "${env:GATEWAY_TOKEN}", or the token itself

Requests to /api/*, the /gateway WebSocket, /v1/* (OpenAI-compatible API), /media/, /uploads/ and /canvas/ must carry it, in any of these forms:

Health probes (/health, /ready), the Web UI's own files, the App API (/api/app/*, which checks its own credentials) and the webhook receivers (/hook/*, /goclaw/hook/*, which check their own secrets) do not take it. When the token is a ${secret:…} reference whose secret is not in the vault, no token is accepted and every request on the gateway's main port fails with a server misconfiguration error, the exempt routes above included (/health, the Web UI's files, the App API, and webhooks that share the port).


Multi-Agent Setup

GoClaw allows you to define multiple "Agents," each with its own isolated identity, model, and workspace.

Defining Agents

In config.yaml, agents are listed under agents.list.

agents:
  default: "main"  # Fallback agent ID
  list:
    - id: "main"
      name: "Personal Assistant"
      model: "gemini-2.0-flash"
    - id: "coding"
      name: "Code Expert"
      model: "claude-sonnet-4-5-20250929"
      skills:
        allow: ["git", "coding"]
      tools:
        shell:
          allow: ["*"]

Routing Bindings

Bindings map incoming messages from different channels to specific agents. The most specific match wins: a peer, then a thread's parent peer, then tenant_id with platform_roles, then platform_roles alone, then tenant_id, then account_id, then the channel alone. A message no binding matches goes to agents.default.

bindings:
  - agent_id: "coding"
    match:
      channel: "discord"
      tenant_id: "123456789012345678" # one Discord server (guild ID)
  - agent_id: "main"
    match:
      channel: "discord" # Fallback for all other Discord messages

What tenant_id and account_id hold depends on the channel. On Discord, tenant_id is the server (guild) ID and account_id is the ID of the user who wrote the message. On Microsoft Teams, tenant_id is the team and account_id is the bot's app ID; on Telegram account_id is the bot's ID, and on Facebook the Page ID. peer: {kind: "direct" | "group", id: "…"} matches one conversation by its ID. See Agent Routing.


MCP Setup (Model Context Protocol)

GoClaw provides native support for MCP, allowing agents to connect to external servers for tools, resources, and prompts.

Configuring MCP Servers

You can define MCP servers in config.yaml using either stdio (command-based) or HTTP/SSE.

mcp:
  servers:
    # Stdio-based server (runs as a subprocess)
    filesystem:
      command: "npx"
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
    
    # Remote server via HTTP/SSE
    remote:
      url: "https://mcp.example.com"
      transport: "sse" # auto | streamable | sse
      headers:
        Authorization: "Bearer ${secret:MCP_TOKEN}"

Using MCP via Tools

When an MCP server is configured, agents gain access to the mcp tool. They can:

  1. connect to a server.
  2. list_tools to see what is available.
  3. call_tool to execute an MCP-hosted tool.

Session Management

Session Reset Rules

Clear sessions automatically, every day at an hour or after a time with no message. reset is the rule for every session. reset_by_type sets a rule per channel; its key is a channel name as it appears in a session key, such as telegram, discord, msteams or facebook. reset_triggers are the chat commands that clear a session on demand.

Automatic resets reach the sessions of every agent in agents.list, each in its own session store.

session:
  reset:
    mode: daily        # daily | idle
    at_hour: 4         # UTC hour, for daily
  reset_by_type:
    msteams:
      mode: idle
      idle_minutes: 60 # cleared after an hour with no message
  reset_triggers: ["/new", "/reset"]

A reset never cuts into a reply. When a session is still answering a message, a daily reset waits for the reply to be saved and then clears the session. An idle reset leaves it alone, since a session that is answering is not idle, and its idle time starts again.

Idle time counts from the last message the running gateway saw: after a restart, a session's idle time starts with its next message.


Tool Safety & Loop Detection

GoClaw watches each session's tool calls for loops. Every call gets a loop score from the session's last history_size calls: the same tool with the same arguments again, a polling tool called over and over, two tools alternating (A, B, A, B), one tool called many times with different arguments, or one call repeated in between others. The thresholds are compared with that score, not with a count of repeats.

tools:
  loop_detection:
    enabled: true
    history_size: 100
    warning_threshold: 10
    critical_threshold: 20
    global_circuit_breaker_threshold: 30
    detectors:                 # all on by default
      generic_repeat: true          # same tool, same arguments
      known_poll_no_progress: true  # a polling tool called again and again
      ping_pong: true               # A, B, A, B
      same_tool_flood: true         # one tool, many different arguments
      identical_repeat_ratio: true  # one call repeated between other calls

Context Pruning

To keep LLM context windows efficient, GoClaw can prune old or large tool results.

context_pruning:
  mode: "cache-ttl" # off | cache-ttl
  soft_trim:
    head_chars: 1000
    tail_chars: 1000

Observability

Configure integration with external observability tools like Grafana or Graylog.

tools:
  observability:
    grafana:
      prod:
        url: "http://grafana.prod:3000"
        token: "${secret:GRAFANA_PROD_TOKEN}"

Gateway Ingress & Webhook Isolation

To protect the internal Web UI and APIs from accidental public exposure via reverse proxies (like Nginx), you can isolate the webhook receiver on a dedicated port.

When gateway.ingress.port is specified, GoClaw creates a completely separate webhook-only HTTP server.

gateway:
  bind: "loopback"
  ingress:
    type: "proxy"
    port: "8081"  # Dedicated listener for webhooks (/hook/*, /goclaw/hook/*, /api/webhook)
    bind: "lan"   # the proxy is on another host; the default, loopback, takes only local connections
    allow_ips: ["10.0.0.0/8"]

This ensures that even if your proxy indiscriminately forwards traffic to 8081, the Web UI remains safe and hidden on server.port (default 8080). Health probes (/health, /ready) will remain accessible on both listeners.

The dedicated listener binds to gateway.ingress.bind, which defaults to loopback (127.0.0.1): set lan (or an IP) when the proxy or the sender is on another host. With a dedicated port, the webhook routes (/hook/*, /goclaw/hook/*) are served only there: a hook URL that points at server.port gets 404. See Public Ingress.

Rate limiting

gateway:
  rate_limit:
    enabled: true     # default
    per_minute: 60    # per client IP (default 60)
    trust_proxy: false # take the client IP from X-Forwarded-For

The budget covers what can be abused without the gateway token: requests with a missing or wrong token (so guessing the token stays limited), webhooks, and the app API. Requests that carry the gateway token and the Web UI's own static files are not counted, so opening the dashboard — dozens of scripts and API calls — does not use it up. Over-budget requests get 429 with Retry-After. Behind a reverse proxy, enable trust_proxy, or every client shares the proxy's IP and budget.


Webhook Routes

webhooks: lists the routes of the webhook receiver at /goclaw/hook/<type>[/<key>]: which agent an outside system's POST reaches, the credential it must carry, and whether it starts a SOP. Routes can also be made on the Webhooks page or through POST /api/webhooks (Gateway Endpoints); those are stored in the database. A route for the exact key comes before a route of its type without a key; between two routes for the same key, the stored one comes first.

webhooks:
  # Graylog alert → the incident-triage SOP, once per alert
  - type: graylog
    key: payments-errors
    secret: "${secret:GRAYLOG_HOOK_PAYMENTS}"
    agent: main
    sop_id: incident-triage

  # GitLab events → a chat turn on agent main
  - type: gitlab
    key: myproject
    secret: "${secret:GITLAB_WEBHOOK_MYPROJECT}"
    agent: main
    events: [push, pipeline, merge_request]

The first route answers https://<gateway>/goclaw/hook/graylog/payments-errors.

Key Meaning
type First path segment: /goclaw/hook/<type>. gitlab, github, graylog and zabbix payloads are read specially; any other name is a generic route. A Graylog notification needs type: graylog: under any other type, Graylog's test notification would run like an alert.
key Second path segment: /goclaw/hook/<type>/<key>. A route without a key takes every key of its type that has no route of its own.
secret The shared secret. GitLab sends it as X-Gitlab-Token; GitHub signs the body with it (X-Hub-Signature-256). Every other type, Graylog included, accepts it as an X-Token header, a ?token= query parameter, or an X-Signature: sha256=<hex> HMAC of the body.
token A second credential, compared only with the ?token= query parameter.
agent The agent ID that gets the event, or "*" for every agent. A chat turn runs on that agent's model. An agent the gateway is not running (not in agents.list, added since the gateway started, or its model failed to start) does not get it: the delivery is answered 409, or, on a "*" route, goes to the other agents.
events Event names to take; empty takes every event. Any other event is answered 200 {"status":"filtered"} and runs nothing.
sop_id Run this SOP for the event instead of a chat turn. See Runs started by a webhook.
batch_mode With sop_id: single (default) runs the SOP once per event, except that events about an object whose run is in progress wait for it and then run once, together (Runs started by a webhook); bulk gathers the route's events and runs it once for them.
batch_window With batch_mode: bulk: how many seconds the route gathers events (default 300). A run takes at most 500 events, oldest first; the rest wait for the next run.

A route with neither secret nor token refuses every delivery with 401. Keep the secret in the vault (goclaw secret set GRAYLOG_HOOK_PAYMENTS <value>) and write a ${secret:…} reference.

The receiver reads webhooks: for each delivery, so a change saved to config.yaml applies to the next delivery without a restart.

A config.yaml route is listed on the Webhooks page, but it cannot be paused, edited or deleted there, and it records no hit count or last error: look for [webhook] lines in the gateway log instead. A route made on the page, or through /api/webhooks, shows both.

An admin can also ask the agent in chat, for example "what did the build-events webhook receive today?". Its webhooks tool lists the routes with their hit count, last hit and last error, and shows a route's recent deliveries: when each came, its event, and the summary of the payload the receiver handed to the agent or SOP. It never shows a route's secret or token, and it is read-only. Only deliveries that reached an agent or a SOP are there; one refused or filtered out shows only in the route's hit count and last error. Anyone else who asks is told that this needs an administrator.

The address the sender calls is the gateway's public address (see Public Ingress). Step-by-step guides: GitLab Webhook Integration and Graylog Alerts That Run a SOP.


State Database Configuration

GoClaw persists sessions, job queues, SOP definitions, scheduled cron states, agent metrics, and auto-learning logs through GORM. The database: block configures the backend driver:

database:
  driver: "sqlite" # "sqlite" (default) | "postgres" | "mysql" | "clickhouse"
  # path: "/var/lib/goclaw/state.sqlite" # SQLite only; leave unset for ~/.goclaw/gc_state.sqlite

Leave path unset unless you mean to move the database. A relative path is taken inside the state directory, so path: "sessions.db" opens a new, empty ~/.goclaw/sessions.db instead of the existing gc_state.sqlite.

Supported Drivers & DSN Examples

Driver Description Connection
sqlite Embedded SQLite (WAL mode, zero setup) path (default ~/.goclaw/gc_state.sqlite)
clickhouse High-throughput analytics & distributed storage dsn: "clickhouse://user:password@localhost:9000/goclaw"
postgres Enterprise relational storage dsn: "host=localhost user=goclaw password=secret dbname=goclaw sslmode=disable"
mysql MySQL / MariaDB relational storage dsn: "goclaw:secret@tcp(localhost:3306)/goclaw?charset=utf8mb4&parseTime=True&loc=Local"

dsn is read only by the postgres, mysql and clickhouse drivers, which require it; sqlite reads only path.