Skip to main content
xbot
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage
Edit page

Configuration Reference

All configuration lives in ~/.xbot/config.json. Direct editing is preferred over environment variables.

Quick reference

What you want to doConfig key
Set API keysubscriptions[].api_key
Use DeepSeek/Ollamasubscriptions[].base_url
Enable Feishufeishu.enabled: true
Enable Webweb.enable: true
Docker sandboxsandbox.mode: "docker"
Restrict users*.allow_from: [...]
Max concurrent callsSettings panel (per-user; not in config.json)
Context compressionagent.compression_threshold

Config file location

  • Default: ~/.xbot/config.json
  • Override with the XBOT_HOME environment variable (e.g. XBOT_HOME=/opt/xbot)
  • In Server mode, specify via xbot-cli serve --config /path/to/config.json

Minimal config

Standalone (personal use)

{
  "subscriptions": [
    {
      "name": "default",
      "provider": "openai",
      "api_key": "sk-xxx",
      "model": "gpt-4o"
    }
  ],
  "sandbox": {
    "mode": "none"
  }
}

Server mode + Feishu (team use)

The admin creates subscriptions via /setup in the TUI, then enables the Feishu app:

{
  "feishu": {
    "enabled": true,
    "app_id": "cli_xxx",
    "app_secret": "xxx"
  },
  "web": {
    "enabled": true
  }
}
Warning
Channel config keys: Feishu, QQ, and NapCat use enabled. Web uses enable (note: no d). Both enable and enabled are accepted at runtime for backward compatibility, but match the struct tag for clarity.

LLM subscriptions

xbot manages LLM configuration through a subscription system (not a single global llm key). You can create multiple subscriptions and switch between them per session.

{
  "subscriptions": [
    {
      "name": "default",
      "provider": "openai",
      "api_key": "sk-xxx",
      "base_url": "https://api.openai.com/v1",
      "model": "gpt-4o",
      "max_output_tokens": 0,
      "max_context": 0,
      "thinking_mode": "",
      "active": true,
      "per_model_configs": {}
    }
  ]
}
FieldTypeDefaultDescription
namestring"default"Subscription name (shown in switcher)
providerstring"openai"Provider: openai or anthropic
api_keystring""API key
base_urlstring"https://api.openai.com/v1"API base URL (change for compatible services)
modelstring"gpt-4o"Default model
max_output_tokensint0 (= 32768)Max output tokens
max_contextint0 (= 200000)Max context tokens (0 = use default)
thinking_modestring"" (= auto)Thinking mode: auto / enabled / disabled
activebooltrueWhether this is the active subscription
per_model_configsobject{}Per-model token overrides (see below)

per_model_configs overrides max_output_tokens and max_context per model, taking priority over the subscription-level defaults:

"per_model_configs": {
  "gpt-4o": {"max_output_tokens": 16384, "max_context": 128000},
  "deepseek-chat": {"max_context": 64000}
}

Multiple subscriptions

Switch between them in the TUI via Ctrl+N or /models:

{
  "subscriptions": [
    {
      "name": "GPT-4o",
      "provider": "openai",
      "api_key": "sk-xxx",
      "base_url": "https://api.openai.com/v1",
      "model": "gpt-4o",
      "active": true
    },
    {
      "name": "Claude",
      "provider": "anthropic",
      "api_key": "sk-ant-xxx",
      "model": "claude-sonnet-4-20250514",
      "active": false
    }
  ]
}

Compatible APIs (DeepSeek, Qwen, Ollama, etc.)

{
  "subscriptions": [
    {
      "name": "DeepSeek",
      "provider": "openai",
      "api_key": "your-key",
      "base_url": "https://api.deepseek.com/v1",
      "model": "deepseek-chat"
    }
  ]
}
Note
Server mode: the user_llm_subscriptions table is the single source of truth. The admin creates subscriptions via TUI /setup, then the whole team shares them. The user_settings table must NOT contain subscription fields (provider, model, api_key, etc.).

Model tiers (user-level)

Model tiers are user-level settings, stored in user_settings (Server mode) or the global llm block (CLI mode) — not inside a subscription. Configure via the /settings panel.

FieldDescription
vanguard_modelStrongest reasoning (used by SubAgents)
balance_modelBalanced (used by SubAgents)
swift_modelFast/small (used by SubAgents)

Unconfigured tiers fall back automatically: vanguard → balance → swift.

{
  "llm": {
    "vanguard_model": "claude-opus-4",
    "balance_model": "claude-sonnet-4",
    "swift_model": "claude-haiku-4"
  }
}

Agent configuration

{
  "agent": {
    "memory_provider": "xbot",
    "work_dir": ".",
    "prompt_file": "prompt.md",
    "max_context_tokens": 200000,
    "enable_auto_compress": true,
    "compression_threshold": 0.9,
    "context_mode": "",
    "max_sub_agent_depth": 5,
    "llm_retry_attempts": 5,
    "llm_retry_delay": "1s",
    "llm_retry_max_delay": "30s",
    "llm_retry_timeout": "120s",
    "mcp_inactivity_timeout": "30m",
    "mcp_cleanup_interval": "5m",
    "session_cache_timeout": "24h",
    "interactive_session_ttl": "30m"
  }
}
FieldTypeDefaultDescription
max_concurrencyint5Concurrency cap. ⚠️ Not a config.json key — a per-user setting stored in the user_settings table; change it from the settings panel (/settings, range 1–100) or the Web LLM console
memory_providerstring"xbot"Memory system: xbot, flat, letta, or none (no memory tools or prompts)
work_dirstring"."Working directory
prompt_filestring"prompt.md"Custom system prompt file
max_context_tokensint200000Max context window (tokens)
model_contextsobject{}Per-model context overrides (model → tokens)
enable_auto_compressbooltrueAuto-compress context when full
compression_thresholdfloat0.9Token ratio that triggers compression
context_modestring""Context management mode
max_sub_agent_depthint5Max SubAgent nesting levels (depth only — nesting the same role is legal, not a cycle)
llm_retry_attemptsint5Total attempts including the initial call; 0 uses 5
llm_retry_delayduration"1s"Initial retry delay
llm_retry_max_delayduration"30s"Max retry delay
llm_retry_timeoutduration"120s"Per-call LLM timeout
mcp_inactivity_timeoutduration"30m"MCP server inactivity timeout
mcp_cleanup_intervalduration"5m"MCP cleanup interval
session_cache_timeoutduration"24h"Session cache timeout
interactive_session_ttlduration"30m"Idle TTL for interactive SubAgent sessions; on expiry the session and its session DB are destroyed. Env: INTERACTIVE_SESSION_TTL. 0/omitted = 30m default

0 or an omitted retry count uses 5 total attempts (including the first call), never unlimited retries. Zero initial/max delay uses 1s/30s. An explicit server --config file uses the same defaults and environment overrides as the default config. Provider error code unsupported_country_region_territory stops immediately; other errors retain bounded exponential backoff.

Note
Duration values are human-readable strings: "30m", "1h30m", "5s". For backward compatibility, legacy nanosecond numbers are also accepted.

Experimental features

{
  "agent": {
    "experimental": {
      "auto_worktree": false
    }
  }
}
FieldDefaultDescription
auto_worktreefalseAuto-create git worktrees when multiple agents share a repo

Sandbox configuration

{
  "sandbox": {
    "mode": "none",
    "idle_timeout": "30m",
    "ws_port": 8080,
    "auth_token": "",
    "public_url": ""
  }
}
FieldTypeDefaultDescription
modestring"none"Sandbox mode: none or docker
remote_modestring""Remote sandbox mode
idle_timeoutduration"30m"Idle timeout (0 = disabled)
ws_portint8080Remote sandbox WebSocket port
auth_tokenstring""Runner auth token
public_urlstring""Public URL the runner connects to

See the Sandbox guide for Docker setup details.

Web search configuration (Tavily)

The WebSearch tool queries the Tavily API. You can set the key at runtime in the Web UI under Settings → Web Search: it takes effect on the next search (no restart) and is shared by every channel (Web / CLI / Feishu). The top-level tavily_api_key in config.json remains the global default used when no runtime value is configured:

{
  "tavily_api_key": "tvly-your-key"
}

Runtime values are stored in the settings database and are never returned in plaintext to clients (the settings API returns ***).

Channel configuration

See the per-channel docs:

Server configuration

{
  "server": {
    "host": "0.0.0.0",
    "port": 8082,
    "read_timeout": "30s",
    "write_timeout": "120s"
  }
}

Storage configuration

All persistent state (sessions, messages, memory, user settings, LLM subscriptions) lives in SQLite by default. A PostgreSQL backend is available:

{
  "storage": {
    "type": "pg",
    "dsn": "postgres://user:password@host:5432/xbot?sslmode=disable"
  }
}
FieldDescription
type"sqlite" (default) or "pg"
dsnPostgreSQL connection string — only used when type is "pg"

Both backends implement the same storage interfaces, so switching backends does not change agent behaviour. The PostgreSQL backend is pure Go (no libpq/cgo). Omit the whole storage block to keep the SQLite default.

CLI configuration (Remote mode)

{
  "cli": {
    "server_url": "ws://127.0.0.1:8082",
    "token": "your-admin-token"
  }
}

Auto-configured during Server-mode installation; usually no manual editing needed.

FieldDescription
server_urlWebSocket URL of the remote agent server
tokenAuth token (matches the server’s admin.token)

Admin configuration

{
  "admin": {
    "token": "random-generated-token",
    "chat_id": ""
  }
}
FieldDescription
tokenAdmin token (auto-generated at install)
chat_idAdmin chat ID (for startup notifications)

Embedding configuration (Letta memory)

Required only when using memory_provider: "letta":

{
  "embedding": {
    "provider": "openai",
    "base_url": "https://api.openai.com/v1",
    "api_key": "",
    "model": "text-embedding-3-small",
    "max_tokens": 2048
  }
}

Plugin configuration

{
  "plugins": {
    "enabled": false,
    "dirs": [],
    "disabled_plugins": [],
    "allow_unverified": false
  }
}
FieldTypeDefaultDescription
enabledboolfalseEnable the plugin system (opt-in)
dirs[]string[]Extra plugin scan directories (default: ~/.xbot/plugins/)
disabled_plugins[]string[]Plugin IDs to skip
allow_unverifiedboolfalseLoad plugins without verified manifests

Log configuration

{
  "log": {
    "level": "info",
    "format": "text"
  }
}

See also