Home / Docs / Configuration

Configuration

All Zubo configuration lives in a single file: ~/.zubo/config.json. You can edit this file directly with any text editor, or use the zubo config CLI commands for quick changes without opening a file. Changes to the configuration file are picked up on the next restart, while CLI changes via zubo config set take effect immediately if Zubo is running.

Quick Config via CLI

The zubo config command provides a convenient way to read and write configuration values from the terminal:

# Set values
zubo config set activeProvider ollama
zubo config set model llama3.3
zubo config set heartbeatMinutes 60
zubo config set auth.enabled true
zubo config set rateLimit.chatPerMinute 30
zubo config set channels.webchat.port 8080
zubo config set providers.anthropic.model claude-sonnet-4-5-20250929

# Get values
zubo config get                    # Show entire configuration (pretty-printed)
zubo config get activeProvider     # Show a single value
zubo config get channels.telegram  # Show a nested object
zubo config get rateLimit          # Show all rate limit settings

Notes on the CLI:

LLM Providers

Zubo supports a multi-provider architecture. You configure one or more LLM providers under the providers object, set one as activeProvider, and optionally define a failover chain. If the active provider fails (network error, rate limit, authentication error), Zubo automatically tries each failover provider in order until one succeeds.

Supported Providers

Provider Key Default Base URL Notes
anthropic (uses Anthropic SDK directly) Claude models. Supports up to 200k token context. Native streaming. Recommended primary provider.
openai https://api.openai.com/v1 GPT-4o, GPT-4-turbo, and other OpenAI models. Streaming supported.
ollama http://localhost:11434/v1 Run models locally. No API key required. Great as a failover for offline operation.
groq https://api.groq.com/openai/v1 Extremely fast inference. Supports Llama, Mixtral, and other open models.
together https://api.together.xyz/v1 Wide selection of open-source models. Competitive pricing.
openrouter https://openrouter.ai/api/v1 Multi-model gateway. Access Claude, GPT-4, Llama, and more through a single API key.
lmstudio http://localhost:1234/v1 Local model GUI. Download and run models with a graphical interface. No API key required.
google https://generativelanguage.googleapis.com/v1beta Google Gemini models. Supports Gemini Pro, Gemini Flash, and other Google AI models.
deepseek https://api.deepseek.com/v1 DeepSeek models. OpenAI-compatible API with strong reasoning capabilities at competitive pricing.
xai https://api.x.ai/v1 xAI Grok models. OpenAI-compatible API with real-time knowledge.
fireworks https://api.fireworks.ai/inference/v1 Fireworks AI. Fast inference for open-source models with optimized serving.

Provider Configuration Fields

Each provider object under providers accepts the following fields:

Field Type Required Description
apiKey string Yes* API key for the provider. Not required for local providers (Ollama, LM Studio).
baseUrl string No Override the default base URL. Useful for proxies or self-hosted endpoints.
model string No Model name to use. Defaults vary by provider (e.g., claude-sonnet-4-5-20250929 for Anthropic).
streaming boolean No Enable or disable streaming responses. Defaults to true.
contextWindow number No Maximum context window size in tokens. Zubo uses this for context compaction decisions. Defaults to the provider's known maximum.

Multi-Provider Example with Failover

{
  "providers": {
    "anthropic": {
      "apiKey": "sk-ant-api03-...",
      "model": "claude-sonnet-4-5-20250929",
      "streaming": true,
      "contextWindow": 200000
    },
    "openai": {
      "apiKey": "sk-proj-...",
      "model": "gpt-4o",
      "streaming": true
    },
    "ollama": {
      "baseUrl": "http://localhost:11434/v1",
      "model": "llama3.3",
      "streaming": true
    }
  },
  "activeProvider": "anthropic",
  "failover": ["openai", "ollama"]
}

In this configuration, Zubo uses Claude as the primary model. If Anthropic returns an error (rate limit, network issue, etc.), it automatically tries OpenAI next, and finally Ollama as a local fallback. The failover is transparent to the user — they receive a response regardless of which provider handled it.

Smart Routing

Smart routing automatically selects the best provider for each query based on complexity. Simple questions (greetings, quick lookups, short follow-ups) are routed to fast, inexpensive models, while complex queries (multi-step reasoning, code generation, long-form writing) go to your most capable provider. This can significantly reduce costs without noticeable quality loss.

Field Type Default Description
smartRouting.enabled boolean false Enable automatic query routing between providers.
smartRouting.fastProvider string Provider key to use for simple queries (e.g., "groq", "deepseek").
smartRouting.complexProvider string Provider key to use for complex queries. Defaults to activeProvider if not set.
"smartRouting": {
  "enabled": true,
  "fastProvider": "groq",
  "complexProvider": "anthropic"
}

When smart routing is enabled, Zubo classifies each incoming message and routes it accordingly. You can always override the routing for a specific message by prefixing it with the provider name (e.g., @anthropic explain quantum computing).

Channel Configuration

Channels define how users interact with Zubo. Each channel is independently configured under the channels object. All enabled channels share the same memory, personality, tools, and conversation history — a fact learned via Telegram is immediately available in Discord or the web dashboard.

Web Chat

Field Type Default Description
enabled boolean true Enable the built-in web chat dashboard.
port number 3000 HTTP port for the web dashboard and API.
"webchat": {
  "enabled": true,
  "port": 3000
}

Telegram

Field Type Default Description
enabled boolean false Enable the Telegram channel.
botToken string Bot token from @BotFather.
allowedUsers number[] [] Array of numeric Telegram user IDs allowed to interact. Empty array means no restriction.
"telegram": {
  "enabled": true,
  "botToken": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
  "allowedUsers": [12345678, 87654321]
}

Discord

Field Type Default Description
enabled boolean false Enable the Discord channel.
botToken string Bot token from the Discord Developer Portal.
allowedUsers string[] [] Array of Discord user ID strings allowed to interact. Empty array means no restriction.
"discord": {
  "enabled": true,
  "botToken": "MTk4NjIyNDgzNDcxOTI1...",
  "allowedUsers": ["198622483471925248"]
}

Slack

Field Type Default Description
enabled boolean false Enable the Slack channel.
botToken string Bot User OAuth Token (starts with xoxb-).
appToken string App-Level Token (starts with xapp-). Required for Socket Mode.
allowedUsers string[] [] Array of Slack member ID strings allowed to interact. Empty array means no restriction.
"slack": {
  "enabled": true,
  "botToken": "xoxb-1234567890-abcdefghij",
  "appToken": "xapp-1-A0123456789-1234567890123-abcdef",
  "allowedUsers": ["U01ABCDEF"]
}

WhatsApp

Field Type Default Description
enabled boolean false Enable the WhatsApp channel.
authDir string ~/.zubo/whatsapp-auth Directory to store WhatsApp Web authentication data. On first run, a QR code is displayed for pairing.
allowedNumbers string[] [] Array of phone numbers (with country code, e.g., "14155551234") allowed to interact. Empty array means no restriction.
"whatsapp": {
  "enabled": true,
  "authDir": "/Users/you/.zubo/whatsapp-auth",
  "allowedNumbers": ["14155551234", "447700900000"]
}

Signal

Field Type Default Description
enabled boolean false Enable the Signal channel.
phoneNumber string The phone number registered with Signal for the bot (with country code, e.g., "+14155551234").
signalCliPath string signal-cli Path to the signal-cli binary. Defaults to looking on the system PATH.
allowedNumbers string[] [] Array of phone numbers allowed to interact. Empty array means no restriction.
"signal": {
  "enabled": true,
  "phoneNumber": "+14155551234",
  "signalCliPath": "/usr/local/bin/signal-cli",
  "allowedNumbers": ["+447700900000"]
}

Voice Configuration

Zubo supports speech-to-text (STT) for transcribing voice messages received on any channel, and text-to-speech (TTS) for generating spoken audio responses. Voice is configured under the voice object.

Speech-to-Text (STT)

Field Type Default Description
provider string "whisper" STT provider. Currently supported: whisper (OpenAI Whisper API).
apiKey string API key for the STT provider. For Whisper, this is your OpenAI API key.
model string "whisper-1" Model to use for transcription.

Text-to-Speech (TTS)

Field Type Default Description
provider string "openai" TTS provider. Supported: openai, elevenlabs.
apiKey string API key for the TTS provider.
voice string "nova" Voice to use for speech generation. OpenAI voices: alloy, echo, fable, onyx, nova, shimmer. ElevenLabs voices depend on your account.
"voice": {
  "stt": {
    "provider": "whisper",
    "apiKey": "sk-proj-...",
    "model": "whisper-1"
  },
  "tts": {
    "provider": "openai",
    "apiKey": "sk-proj-...",
    "voice": "nova"
  }
}

Agent Settings

These top-level settings control the core agent behavior.

Field Type Default Description
maxTurns number 50 Maximum number of tool-call rounds per conversation turn. When the agent reaches this limit, it stops calling tools and produces a text response with whatever information it has gathered. This prevents runaway tool loops. Reasonable values range from 10 (tightly constrained) to 100 (complex multi-step tasks).
heartbeatMinutes number 30 Interval in minutes (1–1440) between heartbeat cycles. During each heartbeat, Zubo wakes up, checks for pending cron jobs, processes scheduled tasks, evaluates proactive memory triggers, and optionally sends notifications. Set to a low value (1–5) for near-real-time scheduling, or a high value (60–1440) to conserve resources.
{
  "maxTurns": 50,
  "heartbeatMinutes": 30
}

Rate Limiting

Zubo includes built-in per-IP rate limiting to protect against abuse. Rate limits use a sliding window algorithm — each IP address gets an independent counter that resets on a rolling 60-second basis.

Field Type Default Description
rateLimit.chatPerMinute number 60 Maximum chat messages per minute per IP address. Applies to the /api/chat endpoint and WebSocket messages. When exceeded, the client receives a 429 Too Many Requests response.
rateLimit.uploadPerMinute number 10 Maximum file uploads per minute per IP address. Applies to the /api/upload endpoint. Uploads are more resource-intensive (document parsing, chunking, embedding), so the default is lower.
"rateLimit": {
  "chatPerMinute": 60,
  "uploadPerMinute": 10
}

For personal use on localhost, the defaults are generous. If you are exposing Zubo on a public network or sharing it with others, consider lowering these values (e.g., chatPerMinute: 30, uploadPerMinute: 5).

Budget Controls

Zubo tracks token usage and estimated costs for every LLM request. You can set spending limits to prevent surprise bills and view per-model cost breakdowns in the dashboard.

Field Type Default Description
budget.dailyLimit number 0 Maximum daily spend in USD. Set to 0 for no limit. When the limit is reached, Zubo pauses LLM requests and notifies you.
budget.monthlyLimit number 0 Maximum monthly spend in USD. Set to 0 for no limit.
budget.warningThreshold number 0.8 Percentage (0–1) of the budget at which Zubo sends a warning notification. Default is 80%.
"budget": {
  "dailyLimit": 5,
  "monthlyLimit": 50,
  "warningThreshold": 0.8
}

Cost tracking is always active even without spending limits. View your usage breakdown in the dashboard under Analytics → Costs, or ask Zubo directly: “How much have I spent today?”

Authentication

When auth.enabled is true, all HTTP API endpoints and WebSocket connections require a valid API key. This is strongly recommended if you are exposing Zubo's port beyond localhost.

Field Type Default Description
auth.enabled boolean false Enable API key authentication for all HTTP and WebSocket endpoints.

Managing API Keys

API keys can be created and managed via the CLI or the web dashboard:

# Create a new API key
zubo auth create --name "my-app"
# Output: API key created: zb_k1_a1b2c3d4e5f6...

# List all API keys
zubo auth list

# Revoke an API key
zubo auth revoke zb_k1_a1b2c3d4e5f6...

Using API Keys

Include the API key in the Authorization header as a Bearer token:

curl -X POST http://localhost:3000/api/chat \
  -H "Authorization: Bearer zb_k1_a1b2c3d4e5f6..." \
  -H "Content-Type: application/json" \
  -d '{"message": "Hello, Zubo!"}'

For WebSocket connections, pass the key as a query parameter:

ws://localhost:3000/ws?token=zb_k1_a1b2c3d4e5f6...

API keys are stored as hashed values in the SQLite database. The plain-text key is only shown once at creation time and cannot be retrieved later. If you lose a key, revoke it and create a new one.

Sandbox

The sandbox controls how user-installed skills (from ~/.zubo/workspace/skills/) are executed. Built-in tools always run in the main process, but user skills run in isolated subprocesses for safety.

Field Type Default Description
sandbox.enabled boolean true Enable subprocess sandboxing for user-installed skills. When enabled, each skill invocation runs in a separate Bun subprocess with restricted access. Disabling this is not recommended but may be necessary for skills that require direct main-process access.
sandbox.timeoutMs number 30000 Maximum execution time in milliseconds for a single skill invocation. If a skill exceeds this timeout, its subprocess is terminated and an error is returned to the agent. Set higher for skills that perform long-running operations (e.g., large file processing, slow API calls).
"sandbox": {
  "enabled": true,
  "timeoutMs": 30000
}

Full Example

Here is a complete ~/.zubo/config.json showing all configuration sections together. This represents a fully configured instance with Anthropic as the primary provider, Groq for smart routing, Ollama as a local failover, three channels enabled, voice support, budget controls, and production-ready security settings:

{
  "providers": {
    "anthropic": {
      "apiKey": "sk-ant-api03-...",
      "model": "claude-sonnet-4-5-20250929",
      "streaming": true,
      "contextWindow": 200000
    },
    "groq": {
      "apiKey": "gsk_...",
      "model": "llama-3.3-70b-versatile",
      "streaming": true
    },
    "ollama": {
      "baseUrl": "http://localhost:11434/v1",
      "model": "llama3.3",
      "streaming": true
    }
  },
  "activeProvider": "anthropic",
  "failover": ["groq", "ollama"],

  "smartRouting": {
    "enabled": true,
    "fastProvider": "groq",
    "complexProvider": "anthropic"
  },

  "channels": {
    "webchat": {
      "enabled": true,
      "port": 3000
    },
    "telegram": {
      "enabled": true,
      "botToken": "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11",
      "allowedUsers": [12345678]
    },
    "discord": {
      "enabled": true,
      "botToken": "MTk4NjIyNDgzNDcxOTI1...",
      "allowedUsers": []
    },
    "slack": {
      "enabled": false,
      "botToken": "",
      "appToken": "",
      "allowedUsers": []
    },
    "whatsapp": {
      "enabled": false,
      "authDir": "",
      "allowedNumbers": []
    },
    "signal": {
      "enabled": false,
      "phoneNumber": "",
      "signalCliPath": "signal-cli",
      "allowedNumbers": []
    }
  },

  "voice": {
    "stt": {
      "provider": "whisper",
      "apiKey": "sk-proj-...",
      "model": "whisper-1"
    },
    "tts": {
      "provider": "openai",
      "apiKey": "sk-proj-...",
      "voice": "nova"
    }
  },

  "maxTurns": 50,
  "heartbeatMinutes": 30,

  "rateLimit": {
    "chatPerMinute": 60,
    "uploadPerMinute": 10
  },

  "budget": {
    "dailyLimit": 5,
    "monthlyLimit": 50,
    "warningThreshold": 0.8
  },

  "auth": {
    "enabled": false
  },

  "sandbox": {
    "enabled": true,
    "timeoutMs": 30000
  }
}

Tip: You do not need to include every field. Zubo applies sensible defaults for any omitted values. A minimal config only needs activeProvider and the corresponding provider entry with an API key.

Environment

By default, Zubo stores all data and configuration under ~/.zubo. You can override this by setting the ZUBO_HOME environment variable to an absolute path:

# Use a custom home directory
export ZUBO_HOME=/opt/zubo-data
zubo start

# Or inline for a single invocation
ZUBO_HOME=/opt/zubo-data zubo start --daemon

When ZUBO_HOME is set, all paths — config file, database, logs, sessions, models, and workspace — are resolved relative to that directory instead of ~/.zubo. This is useful for running multiple isolated Zubo instances on the same machine, or for placing the data directory on a specific volume or mount point.

Other environment variables recognized by Zubo:

Variable Description
ZUBO_HOME Override the default ~/.zubo data directory.
ZUBO_LOG_LEVEL Set the log level: debug, info, warn, error. Defaults to info.
ZUBO_PORT Override the web dashboard port (takes precedence over config file).
NODE_ENV When set to production, Zubo disables verbose logging and enables additional security hardening.