LibreFang Configuration Reference

Complete reference for config.toml, covering every configurable field in the LibreFang Agent OS.


Table of Contents


Overview

LibreFang reads its configuration from a single TOML file:

~/.librefang/config.toml

On Windows, ~ resolves to C:\Users\<username>. If the home directory cannot be determined, the system temp directory is used as a fallback.

Key behaviors:

  • Every struct in the configuration uses #[serde(default)], which means all fields are optional. Omitted fields receive their documented default values.
  • Channel sections ([channels.telegram], [channels.discord], etc.) are Option<T> -- when absent, the channel adapter is disabled. Including the section header (even empty) enables the adapter with defaults.
  • Secrets are never stored in config.toml directly. Instead, fields like api_key_env and bot_token_env hold the name of an environment variable that contains the actual secret. This prevents accidental exposure in version control.
  • Sensitive fields (api_key, shared_secret) are automatically redacted in debug output and logs.

Minimal Configuration

The simplest working configuration only needs an LLM provider API key set as an environment variable. With no config file at all, LibreFang boots with Anthropic as the default provider:

# ~/.librefang/config.toml
# Minimal: just override the model if you want something other than defaults.
# Set ANTHROPIC_API_KEY in your environment.

[default_model]
provider = "anthropic"
model = "claude-sonnet-4-20250514"
api_key_env = "ANTHROPIC_API_KEY"

Or to use a local Ollama instance with no API key:

[default_model]
provider = "ollama"
model = "llama3.2:latest"
base_url = "http://localhost:11434"
api_key_env = ""

Full Example

# ============================================================
# LibreFang Agent OS -- Complete Configuration Reference
# ============================================================

# --- Top-level fields ---
home_dir = "~/.librefang"             # LibreFang home directory
data_dir = "~/.librefang/data"        # SQLite databases and data files
log_level = "info"                   # trace | debug | info | warn | error
api_listen = "127.0.0.1:50051"      # HTTP/WS API bind address
network_enabled = false              # Enable OFP peer-to-peer network
api_key = ""                         # API Bearer token (empty = unauthenticated)
mode = "default"                     # stable | default | dev
language = "en"                      # Locale for CLI/messages
usage_footer = "full"                # off | tokens | cost | full

# --- Default LLM Provider ---
[default_model]
provider = "anthropic"
model = "claude-sonnet-4-20250514"
api_key_env = "ANTHROPIC_API_KEY"
# base_url = "https://api.anthropic.com"  # Optional override

# --- Fallback Providers ---
[[fallback_providers]]
provider = "ollama"
model = "llama3.2:latest"
api_key_env = ""
# base_url = "http://localhost:11434"  # Uses catalog default if omitted

[[fallback_providers]]
provider = "groq"
model = "llama-3.3-70b-versatile"
api_key_env = "GROQ_API_KEY"

# --- Memory ---
[memory]
# sqlite_path = "~/.librefang/data/librefang.db"  # Auto-resolved if omitted
embedding_model = "all-MiniLM-L6-v2"
consolidation_threshold = 10000
decay_rate = 0.1
# embedding_provider = "openai"     # auto-detect if omitted
# embedding_api_key_env = "OPENAI_API_KEY"
consolidation_interval_hours = 24

# --- Network (OFP Wire Protocol) ---
[network]
listen_addresses = ["/ip4/0.0.0.0/tcp/0"]
bootstrap_peers = []
mdns_enabled = true
max_peers = 50
shared_secret = ""                   # Required when network_enabled = true

# --- Web Tools ---
[web]
search_provider = "auto"             # auto | brave | tavily | perplexity | duckduckgo
cache_ttl_minutes = 15

[web.brave]
api_key_env = "BRAVE_API_KEY"
max_results = 5
country = ""
search_lang = ""
freshness = ""

[web.tavily]
api_key_env = "TAVILY_API_KEY"
search_depth = "basic"               # basic | advanced
max_results = 5
include_answer = true

[web.perplexity]
api_key_env = "PERPLEXITY_API_KEY"
model = "sonar"

[web.fetch]
max_chars = 50000
max_response_bytes = 10485760        # 10 MB
timeout_secs = 30
readability = true

# --- MCP Servers ---
[[mcp_servers]]
name = "filesystem"
timeout_secs = 30
env = []
[mcp_servers.transport]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]

[[mcp_servers]]
name = "remote-tools"
timeout_secs = 60
env = ["REMOTE_API_KEY"]
[mcp_servers.transport]
type = "sse"
url = "https://mcp.example.com/events"

# --- A2A Protocol ---
[a2a]
enabled = false
listen_path = "/a2a"

[[a2a.external_agents]]
name = "research-agent"
url = "https://agent.example.com/.well-known/agent.json"

# --- RBAC Users ---
[[users]]
name = "Alice"
role = "owner"                       # owner | admin | user | viewer
api_key_hash = ""
[users.channel_bindings]
telegram = "123456"
discord = "987654321"

[[users]]
name = "Bob"
role = "user"
[users.channel_bindings]
slack = "U0123ABCDEF"

# --- Channel Adapters ---
# (See "Channels" section below for all 40 adapters)

[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = []
# default_agent = "assistant"
poll_interval_secs = 1

[channels.discord]
bot_token_env = "DISCORD_BOT_TOKEN"
allowed_guilds = []
intents = 33280

[channels.slack]
app_token_env = "SLACK_APP_TOKEN"
bot_token_env = "SLACK_BOT_TOKEN"
allowed_channels = []

# --- Budget ---
[budget]
max_hourly_usd = 0.0                # 0.0 = unlimited
max_daily_usd = 10.0
max_monthly_usd = 100.0
alert_threshold = 0.8               # warn at 80% of any limit
default_max_llm_tokens_per_hour = 0  # 0 = per-agent defaults

# --- Vault ---
[vault]
enabled = true
# path = "~/.librefang/vault.enc"   # auto-detected if omitted

# --- Shell Execution Policy ---
[exec_policy]
mode = "allowlist"                   # deny | allowlist | full
safe_bins = ["cat", "sort", "head", "tail", "wc", "date", "echo"]
allowed_commands = []
timeout_secs = 30
max_output_bytes = 102400            # 100 KB
no_output_timeout_secs = 30

# --- Extended Thinking ---
[thinking]
budget_tokens = 10000
stream_thinking = false

# --- Text-to-Speech ---
[tts]
enabled = false
# provider = "openai"               # openai | elevenlabs
max_text_length = 4096
timeout_secs = 30

[tts.openai]
voice = "alloy"                      # alloy | echo | fable | onyx | nova | shimmer
model = "tts-1"                      # tts-1 | tts-1-hd
format = "mp3"                       # mp3 | opus | aac | flac
speed = 1.0                          # 0.25 - 4.0

[tts.elevenlabs]
voice_id = "21m00Tcm4TlvDq8ikWAM"   # Rachel
model_id = "eleven_monolingual_v1"
stability = 0.5
similarity_boost = 0.75

# --- Webhook Triggers ---
[webhook_triggers]
enabled = false
token_env = "LIBREFANG_WEBHOOK_TOKEN"
max_payload_bytes = 65536
rate_limit_per_minute = 30

# --- Approval Policy ---
[approval]
require_approval = ["shell_exec"]    # or false to disable, true for defaults
timeout_secs = 60
auto_approve_autonomous = false
auto_approve = false

# --- Agent Bindings ---
[[bindings]]
agent = "support-bot"
[bindings.match_rule]
channel = "discord"
guild_id = "123456789"

[[bindings]]
agent = "personal"
[bindings.match_rule]
channel = "telegram"
peer_id = "987654"

# --- Broadcast ---
[broadcast]
strategy = "parallel"                # parallel | sequential
[broadcast.routes]
"user-123" = ["agent-a", "agent-b"]

# --- Auto-Reply ---
[auto_reply]
enabled = false
max_concurrent = 3
timeout_secs = 120
suppress_patterns = ["/stop", "/pause"]

# --- Canvas (A2UI) ---
[canvas]
enabled = false
max_html_bytes = 524288              # 512 KB
allowed_tags = []

# --- Docker Sandbox ---
[docker]
enabled = false
image = "python:3.12-slim"
container_prefix = "librefang-sandbox"
workdir = "/workspace"
network = "none"                     # none | bridge | custom
memory_limit = "512m"
cpu_limit = 1.0
timeout_secs = 60
read_only_root = true
pids_limit = 100
mode = "off"                         # off | non_main | all
scope = "session"                    # session | agent | shared
reuse_cool_secs = 300
idle_timeout_secs = 86400            # 24h
max_age_secs = 604800                # 7 days

# --- Device Pairing ---
[pairing]
enabled = false
max_devices = 10
token_expiry_secs = 300
push_provider = "none"               # none | ntfy | gotify

# --- Auth Profiles (key rotation) ---
[auth_profiles]
anthropic = [
  { name = "primary",   api_key_env = "ANTHROPIC_API_KEY",   priority = 0 },
  { name = "secondary", api_key_env = "ANTHROPIC_API_KEY_2", priority = 1 },
]

# --- OAuth ---
[oauth]
# google_client_id = "your-google-client-id"
# github_client_id = "your-github-client-id"
# microsoft_client_id = "your-microsoft-client-id"
# slack_client_id = "your-slack-client-id"

# --- Browser Automation ---
[browser]
headless = true
viewport_width = 1280
viewport_height = 720
timeout_secs = 30
idle_timeout_secs = 300
max_sessions = 5
# chromium_path = "/usr/bin/chromium"  # auto-detected if omitted

# --- Extensions / MCP ---
[extensions]
auto_reconnect = true
reconnect_max_attempts = 10
reconnect_max_backoff_secs = 300
health_check_interval_secs = 60

# --- Config Reload ---
[reload]
mode = "hybrid"                      # off | restart | hot | hybrid
debounce_ms = 500

# --- Provider URL / API Key Overrides ---
[provider_urls]
# ollama = "http://192.168.1.100:11434/v1"
# openai = "https://my-proxy.example.com/v1"

[provider_api_keys]
# nvidia = "NVIDIA_API_KEY"

# --- Config Include ---
include = [
  # "channels.toml",
  # "secrets.toml",
]

# --- Sidecar Channel Adapters ---
[[sidecar_channels]]
name = "my-adapter"
command = "python3"
args = ["adapters/my_adapter.py"]
# channel_type = "custom"
[sidecar_channels.env]
# MY_TOKEN = "xxx"

Section Reference

Top-Level Fields

These fields sit at the root of config.toml (not inside any [section]).

FieldTypeDefaultDescription
home_dirpath~/.librefangLibreFang home directory. Stores config, agents, skills.
data_dirpath~/.librefang/dataDirectory for SQLite databases and persistent data.
log_levelstring"info"Log verbosity. One of: trace, debug, info, warn, error.
api_listenstring"127.0.0.1:50051"Bind address for the HTTP/WebSocket/SSE API server.
network_enabledboolfalseEnable the OFP peer-to-peer network layer.
api_keystring"" (empty)API authentication key. When set, all endpoints except /api/health require Authorization: Bearer <key>. Empty means unauthenticated (local development only).
modestring"default"Kernel operating mode. See below.
languagestring"en"Language/locale code for CLI output and system messages.
usage_footerstring"full"Controls usage info appended to responses. See below.

mode values:

ValueBehavior
stableConservative: no auto-updates, pinned models, frozen skill registry. Uses FallbackDriver.
defaultBalanced: standard operation.
devDeveloper: experimental features enabled.

usage_footer values:

ValueBehavior
offNo usage information shown.
tokensShow token counts only.
costShow estimated cost only.
fullShow both token counts and estimated cost (default).

[default_model]

Configures the primary LLM provider used when agents do not specify their own model.

[default_model]
provider = "anthropic"
model = "claude-sonnet-4-20250514"
api_key_env = "ANTHROPIC_API_KEY"
# base_url = "https://api.anthropic.com"
FieldTypeDefaultDescription
providerstring"anthropic"Provider name. Supported: anthropic, gemini, openai, groq, openrouter, deepseek, together, mistral, fireworks, ollama, vllm, lmstudio, perplexity, cohere, ai21, cerebras, sambanova, huggingface, xai, replicate.
modelstring"claude-sonnet-4-20250514"Model identifier. Aliases like sonnet, haiku, gpt-4o, gemini-flash are resolved by the model catalog.
api_key_envstring"ANTHROPIC_API_KEY"Name of the environment variable holding the API key. The actual key is read from this env var at runtime, never stored in config.
base_urlstring or nullnullOverride the API base URL. Useful for proxies or self-hosted endpoints. When null, the provider's default URL from the model catalog is used.

[memory]

Configures the SQLite-backed memory substrate, including vector embeddings and memory decay.

[memory]
# sqlite_path = "/custom/path/librefang.db"
embedding_model = "all-MiniLM-L6-v2"
consolidation_threshold = 10000
decay_rate = 0.1
embedding_provider = "openai"
embedding_api_key_env = "OPENAI_API_KEY"
consolidation_interval_hours = 24
FieldTypeDefaultDescription
sqlite_pathpath or nullnullExplicit path to the SQLite database file. When null, defaults to {data_dir}/librefang.db.
embedding_modelstring"all-MiniLM-L6-v2"Model name used for generating vector embeddings for semantic memory search.
consolidation_thresholdu6410000Number of stored memories before automatic consolidation is triggered to merge and prune old entries.
decay_ratef320.1Memory confidence decay rate. 0.0 = no decay (memories never fade), 1.0 = aggressive decay. Values between 0.0 and 1.0.
embedding_providerstring or nullnullEmbedding provider (e.g., "openai", "ollama"). null = auto-detect from available keys.
embedding_api_key_envstring or nullnullEnvironment variable name for the embedding API key.
consolidation_interval_hoursu6424How often to run memory consolidation in hours. 0 = disabled.

[network]

Configures the OFP (LibreFang Protocol) peer-to-peer networking layer with HMAC-SHA256 mutual authentication.

[network]
listen_addresses = ["/ip4/0.0.0.0/tcp/0"]
bootstrap_peers = []
mdns_enabled = true
max_peers = 50
shared_secret = "my-cluster-secret"
FieldTypeDefaultDescription
listen_addresseslist of strings["/ip4/0.0.0.0/tcp/0"]libp2p multiaddresses to listen on. Port 0 means auto-assign.
bootstrap_peerslist of strings[]Multiaddresses of bootstrap peers for DHT discovery.
mdns_enabledbooltrueEnable mDNS for automatic local network peer discovery.
max_peersu3250Maximum number of simultaneously connected peers.
shared_secretstring"" (empty)Pre-shared secret for OFP HMAC-SHA256 mutual authentication. Required when network_enabled = true. Both sides must use the same secret. Redacted in logs.

[web]

Configures web search and web fetch capabilities used by agent tools.

[web]
search_provider = "auto"
cache_ttl_minutes = 15
FieldTypeDefaultDescription
search_providerstring"auto"Which search engine to use. See values below.
cache_ttl_minutesu6415Cache duration for search/fetch results in minutes. 0 = caching disabled.

search_provider values:

ValueDescription
autoCascading fallback: tries Tavily, then Brave, then Perplexity, then DuckDuckGo, based on which API keys are available.
braveBrave Search API. Requires BRAVE_API_KEY.
tavilyTavily AI-native search. Requires TAVILY_API_KEY.
perplexityPerplexity AI search. Requires PERPLEXITY_API_KEY.
duckduckgoDuckDuckGo HTML scraping. No API key needed.

[web.brave]

[web.brave]
api_key_env = "BRAVE_API_KEY"
max_results = 5
country = ""
search_lang = ""
freshness = ""
FieldTypeDefaultDescription
api_key_envstring"BRAVE_API_KEY"Environment variable name holding the Brave Search API key.
max_resultsusize5Maximum number of search results to return.
countrystring""Country code for localized results (e.g., "US", "GB"). Empty = no filter.
search_langstring""Language code (e.g., "en", "fr"). Empty = no filter.
freshnessstring""Freshness filter. "pd" = past day, "pw" = past week, "pm" = past month. Empty = no filter.

[web.tavily]

[web.tavily]
api_key_env = "TAVILY_API_KEY"
search_depth = "basic"
max_results = 5
include_answer = true
FieldTypeDefaultDescription
api_key_envstring"TAVILY_API_KEY"Environment variable name holding the Tavily API key.
search_depthstring"basic"Search depth: "basic" for fast results, "advanced" for deeper analysis.
max_resultsusize5Maximum number of search results to return.
include_answerbooltrueWhether to include Tavily's AI-generated answer summary in results.

[web.perplexity]

[web.perplexity]
api_key_env = "PERPLEXITY_API_KEY"
model = "sonar"
FieldTypeDefaultDescription
api_key_envstring"PERPLEXITY_API_KEY"Environment variable name holding the Perplexity API key.
modelstring"sonar"Perplexity model to use for search queries.

[web.fetch]

[web.fetch]
max_chars = 50000
max_response_bytes = 10485760
timeout_secs = 30
readability = true
FieldTypeDefaultDescription
max_charsusize50000Maximum characters returned in fetched content. Content exceeding this is truncated.
max_response_bytesusize10485760 (10 MB)Maximum HTTP response body size in bytes.
timeout_secsu6430HTTP request timeout in seconds.
readabilitybooltrueEnable HTML-to-Markdown readability extraction. When true, fetched HTML is converted to clean Markdown.

[channels]

All 40 channel adapters are configured under [channels.<name>]. Each channel is Option<T> -- omitting the section disables the adapter entirely. Including the section header (even empty) enables it with default values.

Every channel config includes a default_agent field (optional agent name to route messages to) and an overrides sub-table (see Channel Overrides).

[channels.telegram]

[channels.telegram]
bot_token_env = "TELEGRAM_BOT_TOKEN"
allowed_users = []
# default_agent = "assistant"
poll_interval_secs = 1
FieldTypeDefaultDescription
bot_token_envstring"TELEGRAM_BOT_TOKEN"Env var holding the Telegram Bot API token.
allowed_userslist of i64[]Telegram user IDs allowed to interact. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.
poll_interval_secsu641Long-polling interval in seconds.

[channels.discord]

[channels.discord]
bot_token_env = "DISCORD_BOT_TOKEN"
allowed_guilds = []
# default_agent = "assistant"
intents = 33280
FieldTypeDefaultDescription
bot_token_envstring"DISCORD_BOT_TOKEN"Env var holding the Discord bot token.
allowed_guildslist of u64[]Guild (server) IDs allowed. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.
intentsu6433280Gateway intents bitmask. Default = GUILD_MESSAGES | MESSAGE_CONTENT.

[channels.slack]

[channels.slack]
app_token_env = "SLACK_APP_TOKEN"
bot_token_env = "SLACK_BOT_TOKEN"
allowed_channels = []
FieldTypeDefaultDescription
app_token_envstring"SLACK_APP_TOKEN"Env var holding the Slack app-level token (xapp-) for Socket Mode.
bot_token_envstring"SLACK_BOT_TOKEN"Env var holding the Slack bot token (xoxb-) for REST API.
allowed_channelslist of strings[]Channel IDs allowed. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.

[channels.whatsapp]

[channels.whatsapp]
access_token_env = "WHATSAPP_ACCESS_TOKEN"
verify_token_env = "WHATSAPP_VERIFY_TOKEN"
phone_number_id = ""
webhook_port = 8443
allowed_users = []
FieldTypeDefaultDescription
access_token_envstring"WHATSAPP_ACCESS_TOKEN"Env var holding the WhatsApp Cloud API access token.
verify_token_envstring"WHATSAPP_VERIFY_TOKEN"Env var holding the webhook verification token.
phone_number_idstring""WhatsApp Business phone number ID.
webhook_portu168443Port to listen for incoming webhook callbacks.
allowed_userslist of strings[]Phone numbers allowed. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.

[channels.signal]

[channels.signal]
api_url = "http://localhost:8080"
phone_number = ""
allowed_users = []
FieldTypeDefaultDescription
api_urlstring"http://localhost:8080"URL of the signal-cli REST API.
phone_numberstring""Registered phone number for the bot.
allowed_userslist of strings[]Allowed phone numbers. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.

[channels.matrix]

[channels.matrix]
homeserver_url = "https://matrix.org"
user_id = "@librefang:matrix.org"
access_token_env = "MATRIX_ACCESS_TOKEN"
allowed_rooms = []
FieldTypeDefaultDescription
homeserver_urlstring"https://matrix.org"Matrix homeserver URL.
user_idstring""Bot user ID (e.g., "@librefang:matrix.org").
access_token_envstring"MATRIX_ACCESS_TOKEN"Env var holding the Matrix access token.
allowed_roomslist of strings[]Room IDs to listen in. Empty = all joined rooms.
default_agentstring or nullnullAgent name to route messages to.

[channels.email]

[channels.email]
imap_host = "imap.gmail.com"
imap_port = 993
smtp_host = "smtp.gmail.com"
smtp_port = 587
username = "bot@example.com"
password_env = "EMAIL_PASSWORD"
poll_interval_secs = 30
folders = ["INBOX"]
allowed_senders = []
FieldTypeDefaultDescription
imap_hoststring""IMAP server hostname.
imap_portu16993IMAP server port (993 for TLS).
smtp_hoststring""SMTP server hostname.
smtp_portu16587SMTP server port (587 for STARTTLS).
usernamestring""Email address for both IMAP and SMTP.
password_envstring"EMAIL_PASSWORD"Env var holding the email password or app password.
poll_interval_secsu6430IMAP polling interval in seconds.
folderslist of strings["INBOX"]IMAP folders to monitor.
allowed_senderslist of strings[]Only process emails from these senders. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.teams]

[channels.teams]
app_id = ""
app_password_env = "TEAMS_APP_PASSWORD"
webhook_port = 3978
allowed_tenants = []
FieldTypeDefaultDescription
app_idstring""Azure Bot App ID.
app_password_envstring"TEAMS_APP_PASSWORD"Env var holding the Azure Bot Framework app password.
webhook_portu163978Port for the Bot Framework incoming webhook.
allowed_tenantslist of strings[]Azure AD tenant IDs allowed. Empty = allow all.
default_agentstring or nullnullAgent name to route messages to.

[channels.mattermost]

[channels.mattermost]
server_url = "https://mattermost.example.com"
token_env = "MATTERMOST_TOKEN"
allowed_channels = []
FieldTypeDefaultDescription
server_urlstring""Mattermost server URL.
token_envstring"MATTERMOST_TOKEN"Env var holding the Mattermost bot token.
allowed_channelslist of strings[]Channel IDs to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.irc]

[channels.irc]
server = "irc.libera.chat"
port = 6667
nick = "librefang"
# password_env = "IRC_PASSWORD"
channels = ["#librefang"]
use_tls = false
FieldTypeDefaultDescription
serverstring"irc.libera.chat"IRC server hostname.
portu166667IRC server port.
nickstring"librefang"Bot nickname.
password_envstring or nullnullEnv var holding the server password (optional).
channelslist of strings[]IRC channels to join (e.g., ["#librefang", "#general"]).
use_tlsboolfalseUse TLS for the connection.
default_agentstring or nullnullAgent name to route messages to.

[channels.google_chat]

[channels.google_chat]
service_account_env = "GOOGLE_CHAT_SERVICE_ACCOUNT"
space_ids = []
webhook_port = 8444
FieldTypeDefaultDescription
service_account_envstring"GOOGLE_CHAT_SERVICE_ACCOUNT"Env var holding the service account JSON key.
space_idslist of strings[]Google Chat space IDs to listen in.
webhook_portu168444Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.twitch]

[channels.twitch]
oauth_token_env = "TWITCH_OAUTH_TOKEN"
channels = ["mychannel"]
nick = "librefang"
FieldTypeDefaultDescription
oauth_token_envstring"TWITCH_OAUTH_TOKEN"Env var holding the Twitch OAuth token.
channelslist of strings[]Twitch channels to join (without # prefix).
nickstring"librefang"Bot nickname in Twitch chat.
default_agentstring or nullnullAgent name to route messages to.

[channels.rocketchat]

[channels.rocketchat]
server_url = "https://rocketchat.example.com"
token_env = "ROCKETCHAT_TOKEN"
user_id = ""
allowed_channels = []
FieldTypeDefaultDescription
server_urlstring""Rocket.Chat server URL.
token_envstring"ROCKETCHAT_TOKEN"Env var holding the Rocket.Chat auth token.
user_idstring""Bot user ID.
allowed_channelslist of strings[]Channel IDs to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.zulip]

[channels.zulip]
server_url = "https://zulip.example.com"
bot_email = "bot@zulip.example.com"
api_key_env = "ZULIP_API_KEY"
streams = []
FieldTypeDefaultDescription
server_urlstring""Zulip server URL.
bot_emailstring""Bot email address registered in Zulip.
api_key_envstring"ZULIP_API_KEY"Env var holding the Zulip API key.
streamslist of strings[]Stream names to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.xmpp]

[channels.xmpp]
jid = "bot@jabber.org"
password_env = "XMPP_PASSWORD"
server = ""
port = 5222
rooms = []
FieldTypeDefaultDescription
jidstring""XMPP JID (e.g., "bot@jabber.org").
password_envstring"XMPP_PASSWORD"Env var holding the XMPP password.
serverstring""XMPP server hostname. Defaults to the JID domain if empty.
portu165222XMPP server port.
roomslist of strings[]MUC (multi-user chat) rooms to join.
default_agentstring or nullnullAgent name to route messages to.

[channels.line]

[channels.line]
channel_secret_env = "LINE_CHANNEL_SECRET"
access_token_env = "LINE_CHANNEL_ACCESS_TOKEN"
webhook_port = 8450
FieldTypeDefaultDescription
channel_secret_envstring"LINE_CHANNEL_SECRET"Env var holding the LINE channel secret.
access_token_envstring"LINE_CHANNEL_ACCESS_TOKEN"Env var holding the LINE channel access token.
webhook_portu168450Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.viber]

[channels.viber]
auth_token_env = "VIBER_AUTH_TOKEN"
webhook_url = ""
webhook_port = 8451
FieldTypeDefaultDescription
auth_token_envstring"VIBER_AUTH_TOKEN"Env var holding the Viber Bot auth token.
webhook_urlstring""Public URL for the Viber webhook endpoint.
webhook_portu168451Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.messenger]

[channels.messenger]
page_token_env = "MESSENGER_PAGE_TOKEN"
verify_token_env = "MESSENGER_VERIFY_TOKEN"
webhook_port = 8452
FieldTypeDefaultDescription
page_token_envstring"MESSENGER_PAGE_TOKEN"Env var holding the Facebook page access token.
verify_token_envstring"MESSENGER_VERIFY_TOKEN"Env var holding the webhook verify token.
webhook_portu168452Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.reddit]

[channels.reddit]
client_id = ""
client_secret_env = "REDDIT_CLIENT_SECRET"
username = ""
password_env = "REDDIT_PASSWORD"
subreddits = []
FieldTypeDefaultDescription
client_idstring""Reddit app client ID.
client_secret_envstring"REDDIT_CLIENT_SECRET"Env var holding the Reddit client secret.
usernamestring""Reddit bot username.
password_envstring"REDDIT_PASSWORD"Env var holding the Reddit bot password.
subredditslist of strings[]Subreddit names to monitor.
default_agentstring or nullnullAgent name to route messages to.

[channels.mastodon]

[channels.mastodon]
instance_url = "https://mastodon.social"
access_token_env = "MASTODON_ACCESS_TOKEN"
FieldTypeDefaultDescription
instance_urlstring""Mastodon instance URL (e.g., "https://mastodon.social").
access_token_envstring"MASTODON_ACCESS_TOKEN"Env var holding the Mastodon access token.
default_agentstring or nullnullAgent name to route messages to.

[channels.bluesky]

[channels.bluesky]
identifier = "mybot.bsky.social"
app_password_env = "BLUESKY_APP_PASSWORD"
service_url = "https://bsky.social"
FieldTypeDefaultDescription
identifierstring""Bluesky handle or DID.
app_password_envstring"BLUESKY_APP_PASSWORD"Env var holding the Bluesky app password.
service_urlstring"https://bsky.social"PDS (Personal Data Server) URL.
default_agentstring or nullnullAgent name to route messages to.

[channels.feishu]

[channels.feishu]
app_id = ""
app_secret_env = "FEISHU_APP_SECRET"
webhook_port = 8453
FieldTypeDefaultDescription
app_idstring""Feishu/Lark app ID.
app_secret_envstring"FEISHU_APP_SECRET"Env var holding the Feishu app secret.
webhook_portu168453Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.revolt]

[channels.revolt]
bot_token_env = "REVOLT_BOT_TOKEN"
api_url = "https://api.revolt.chat"
FieldTypeDefaultDescription
bot_token_envstring"REVOLT_BOT_TOKEN"Env var holding the Revolt bot token.
api_urlstring"https://api.revolt.chat"Revolt API base URL.
default_agentstring or nullnullAgent name to route messages to.

[channels.nextcloud]

[channels.nextcloud]
server_url = "https://nextcloud.example.com"
token_env = "NEXTCLOUD_TOKEN"
allowed_rooms = []
FieldTypeDefaultDescription
server_urlstring""Nextcloud server URL.
token_envstring"NEXTCLOUD_TOKEN"Env var holding the Nextcloud Talk auth token.
allowed_roomslist of strings[]Room tokens to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.guilded]

[channels.guilded]
bot_token_env = "GUILDED_BOT_TOKEN"
server_ids = []
FieldTypeDefaultDescription
bot_token_envstring"GUILDED_BOT_TOKEN"Env var holding the Guilded bot token.
server_idslist of strings[]Server IDs to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.keybase]

[channels.keybase]
username = ""
paperkey_env = "KEYBASE_PAPERKEY"
allowed_teams = []
FieldTypeDefaultDescription
usernamestring""Keybase username.
paperkey_envstring"KEYBASE_PAPERKEY"Env var holding the Keybase paper key.
allowed_teamslist of strings[]Team names to listen in. Empty = all DMs.
default_agentstring or nullnullAgent name to route messages to.

[channels.threema]

[channels.threema]
threema_id = ""
secret_env = "THREEMA_SECRET"
webhook_port = 8454
FieldTypeDefaultDescription
threema_idstring""Threema Gateway ID.
secret_envstring"THREEMA_SECRET"Env var holding the Threema API secret.
webhook_portu168454Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.nostr]

[channels.nostr]
private_key_env = "NOSTR_PRIVATE_KEY"
relays = ["wss://relay.damus.io"]
FieldTypeDefaultDescription
private_key_envstring"NOSTR_PRIVATE_KEY"Env var holding the Nostr private key (nsec or hex format).
relayslist of strings["wss://relay.damus.io"]Nostr relay WebSocket URLs to connect to.
default_agentstring or nullnullAgent name to route messages to.

[channels.webex]

[channels.webex]
bot_token_env = "WEBEX_BOT_TOKEN"
allowed_rooms = []
FieldTypeDefaultDescription
bot_token_envstring"WEBEX_BOT_TOKEN"Env var holding the Webex bot token.
allowed_roomslist of strings[]Room IDs to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.pumble]

[channels.pumble]
bot_token_env = "PUMBLE_BOT_TOKEN"
webhook_port = 8455
FieldTypeDefaultDescription
bot_token_envstring"PUMBLE_BOT_TOKEN"Env var holding the Pumble bot token.
webhook_portu168455Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.flock]

[channels.flock]
bot_token_env = "FLOCK_BOT_TOKEN"
webhook_port = 8456
FieldTypeDefaultDescription
bot_token_envstring"FLOCK_BOT_TOKEN"Env var holding the Flock bot token.
webhook_portu168456Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.twist]

[channels.twist]
token_env = "TWIST_TOKEN"
workspace_id = ""
allowed_channels = []
FieldTypeDefaultDescription
token_envstring"TWIST_TOKEN"Env var holding the Twist API token.
workspace_idstring""Twist workspace ID.
allowed_channelslist of strings[]Channel IDs to listen in. Empty = all.
default_agentstring or nullnullAgent name to route messages to.

[channels.mumble]

[channels.mumble]
host = "mumble.example.com"
port = 64738
username = "librefang"
password_env = "MUMBLE_PASSWORD"
channel = ""
FieldTypeDefaultDescription
hoststring""Mumble server hostname.
portu1664738Mumble server port.
usernamestring"librefang"Bot username in Mumble.
password_envstring"MUMBLE_PASSWORD"Env var holding the Mumble server password.
channelstring""Mumble channel to join.
default_agentstring or nullnullAgent name to route messages to.

[channels.dingtalk]

[channels.dingtalk]
access_token_env = "DINGTALK_ACCESS_TOKEN"
secret_env = "DINGTALK_SECRET"
webhook_port = 8457
FieldTypeDefaultDescription
access_token_envstring"DINGTALK_ACCESS_TOKEN"Env var holding the DingTalk webhook access token.
secret_envstring"DINGTALK_SECRET"Env var holding the DingTalk signing secret.
webhook_portu168457Port for the incoming webhook.
default_agentstring or nullnullAgent name to route messages to.

[channels.discourse]

[channels.discourse]
base_url = "https://forum.example.com"
api_key_env = "DISCOURSE_API_KEY"
api_username = "system"
categories = []
FieldTypeDefaultDescription
base_urlstring""Discourse forum base URL.
api_key_envstring"DISCOURSE_API_KEY"Env var holding the Discourse API key.
api_usernamestring"system"Discourse API username.
categorieslist of strings[]Category slugs to monitor.
default_agentstring or nullnullAgent name to route messages to.

[channels.gitter]

[channels.gitter]
token_env = "GITTER_TOKEN"
room_id = ""
FieldTypeDefaultDescription
token_envstring"GITTER_TOKEN"Env var holding the Gitter auth token.
room_idstring""Gitter room ID to listen in.
default_agentstring or nullnullAgent name to route messages to.

[channels.ntfy]

[channels.ntfy]
server_url = "https://ntfy.sh"
topic = "my-agent-topic"
token_env = "NTFY_TOKEN"
FieldTypeDefaultDescription
server_urlstring"https://ntfy.sh"ntfy server URL. Can be self-hosted.
topicstring""Topic to subscribe/publish to.
token_envstring"NTFY_TOKEN"Env var holding the auth token. Optional for public topics.
default_agentstring or nullnullAgent name to route messages to.

[channels.gotify]

[channels.gotify]
server_url = "https://gotify.example.com"
app_token_env = "GOTIFY_APP_TOKEN"
client_token_env = "GOTIFY_CLIENT_TOKEN"
FieldTypeDefaultDescription
server_urlstring""Gotify server URL.
app_token_envstring"GOTIFY_APP_TOKEN"Env var holding the Gotify app token (for sending messages).
client_token_envstring"GOTIFY_CLIENT_TOKEN"Env var holding the Gotify client token (for receiving messages via WebSocket).
default_agentstring or nullnullAgent name to route messages to.

[channels.webhook]

[channels.webhook]
secret_env = "WEBHOOK_SECRET"
listen_port = 8460
# callback_url = "https://example.com/webhook"
FieldTypeDefaultDescription
secret_envstring"WEBHOOK_SECRET"Env var holding the HMAC signing secret for verifying incoming webhooks.
listen_portu168460Port to listen for incoming webhook requests.
callback_urlstring or nullnullURL to POST outgoing messages to.
default_agentstring or nullnullAgent name to route messages to.

[channels.linkedin]

[channels.linkedin]
access_token_env = "LINKEDIN_ACCESS_TOKEN"
organization_id = ""
FieldTypeDefaultDescription
access_token_envstring"LINKEDIN_ACCESS_TOKEN"Env var holding the LinkedIn OAuth2 access token.
organization_idstring""LinkedIn organization ID for messaging.
default_agentstring or nullnullAgent name to route messages to.

[[mcp_servers]]

MCP (Model Context Protocol) server connections provide external tool integration. Each entry is a separate [[mcp_servers]] array element.

[[mcp_servers]]
name = "filesystem"
timeout_secs = 30
env = []

[mcp_servers.transport]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/docs"]
[[mcp_servers]]
name = "remote-api"
timeout_secs = 60
env = ["GITHUB_PERSONAL_ACCESS_TOKEN"]

[mcp_servers.transport]
type = "sse"
url = "https://mcp.example.com/sse"
FieldTypeDefaultDescription
namestringrequiredDisplay name for this MCP server. Tools are namespaced as mcp_{name}_{tool}.
timeout_secsu6430Request timeout in seconds.
envlist of strings[]Environment variable names to pass through to the subprocess (stdio transport only).

Transport variants (tagged union on type):

typeFieldsDescription
stdiocommand (string), args (list of strings, default [])Spawn a subprocess, communicate via JSON-RPC over stdin/stdout.
sseurl (string)Connect to an HTTP Server-Sent Events endpoint.

[a2a]

Agent-to-Agent protocol configuration, enabling inter-agent communication across LibreFang instances.

[a2a]
enabled = true
listen_path = "/a2a"

[[a2a.external_agents]]
name = "research-agent"
url = "https://agent.example.com/.well-known/agent.json"

[[a2a.external_agents]]
name = "code-reviewer"
url = "https://reviewer.example.com/.well-known/agent.json"
FieldTypeDefaultDescription
enabledboolfalseWhether A2A protocol is enabled.
listen_pathstring"/a2a"URL path prefix for A2A endpoints.
external_agentslist of objects[]External A2A agents to discover and interact with.

external_agents entries:

FieldTypeDescription
namestringDisplay name for the external agent.
urlstringAgent card endpoint URL (typically /.well-known/agent.json).

[[fallback_providers]]

Fallback provider chain. When the primary LLM provider ([default_model]) fails, these are tried in order.

[[fallback_providers]]
provider = "ollama"
model = "llama3.2:latest"
api_key_env = ""
# base_url = "http://localhost:11434"

[[fallback_providers]]
provider = "groq"
model = "llama-3.3-70b-versatile"
api_key_env = "GROQ_API_KEY"
FieldTypeDefaultDescription
providerstring""Provider name (e.g., "ollama", "groq", "openai").
modelstring""Model identifier for this provider.
api_key_envstring""Env var name for the API key. Empty for local providers (ollama, vllm, lmstudio).
base_urlstring or nullnullBase URL override. Uses catalog default if null.

[[users]]

RBAC multi-user configuration. Users can be assigned roles and bound to channel platform identities.

[[users]]
name = "Alice"
role = "owner"
api_key_hash = "sha256_hash_of_api_key"

[users.channel_bindings]
telegram = "123456"
discord = "987654321"
slack = "U0ABCDEFG"
FieldTypeDefaultDescription
namestringrequiredUser display name.
rolestring"user"User role in the RBAC hierarchy.
channel_bindingsmap of string to string{}Maps channel platform names to platform-specific user IDs, binding this user identity across channels.
api_key_hashstring or nullnullSHA256 hash of the user's personal API key for authenticated API access.

Role hierarchy (highest to lowest privilege):

RoleDescription
ownerFull administrative access. Can manage all agents, users, and configuration.
adminCan manage agents and most settings. Cannot modify owner accounts.
userCan interact with agents. Limited management capabilities.
viewerRead-only access. Can view agent responses but cannot send messages.

Channel Overrides

Every channel adapter supports an [channels.<name>.overrides] sub-table that customizes agent behavior per-channel.

[channels.telegram.overrides]
model = "claude-haiku-4-5-20251001"
system_prompt = "You are a concise Telegram assistant."
dm_policy = "respond"
group_policy = "mention_only"
rate_limit_per_user = 10
threading = true
output_format = "telegram_html"
usage_footer = "tokens"
FieldTypeDefaultDescription
modelstring or nullnullModel override for this channel. Uses the agent's default model when null.
system_promptstring or nullnullSystem prompt override for this channel.
dm_policystring"respond"How the bot handles direct messages. See below.
group_policystring"mention_only"How the bot handles group messages. See below.
rate_limit_per_useru320Maximum messages per user per minute. 0 = unlimited.
threadingboolfalseEnable thread replies (where supported by the platform).
output_formatstring or nullnullOverride output formatting. See below.
usage_footerstring or nullnullOverride usage footer mode for this channel. Values: off, tokens, cost, full.

dm_policy values:

ValueDescription
respondRespond to all direct messages (default).
allowed_onlyOnly respond to DMs from users in the allowed list.
ignoreIgnore all direct messages.

group_policy values:

ValueDescription
allRespond to all messages in group chats.
mention_onlyOnly respond when the bot is @mentioned (default).
commands_onlyOnly respond to slash commands.
ignoreIgnore all group messages.

output_format values:

ValueDescription
markdownStandard Markdown (default).
telegram_htmlTelegram HTML subset (<b>, <i>, <code>, etc.).
slack_mrkdwnSlack mrkdwn format (*bold*, _italic_, `code`).
plain_textNo formatting markup.

[budget]

Global spending budget limits. Set any limit to 0.0 to make it unlimited.

[budget]
max_hourly_usd = 1.0
max_daily_usd = 10.0
max_monthly_usd = 100.0
alert_threshold = 0.8
default_max_llm_tokens_per_hour = 0
FieldTypeDefaultDescription
max_hourly_usdf640.0Maximum total cost in USD per hour. 0.0 = unlimited.
max_daily_usdf640.0Maximum total cost in USD per day. 0.0 = unlimited.
max_monthly_usdf640.0Maximum total cost in USD per month. 0.0 = unlimited.
alert_thresholdf640.8Fraction (0.0--1.0) at which to trigger budget warnings. 0.8 = warn at 80% of any limit.
default_max_llm_tokens_per_houru640Override every agent's per-hour token budget. 0 = keep each agent's own limit.

[vault]

Credential vault for encrypted secret storage.

[vault]
enabled = true
# path = "~/.librefang/vault.enc"
FieldTypeDefaultDescription
enabledbooltrueWhether the vault is enabled. Auto-detected if vault.enc exists.
pathstring or nullnullCustom vault file path. Default: ~/.librefang/vault.enc.

[exec_policy]

Shell/exec security policy controlling what commands agents can execute.

[exec_policy]
mode = "allowlist"
safe_bins = ["cat", "sort", "head", "tail", "wc", "date", "echo", "printf", "pwd"]
allowed_commands = ["git status", "git diff"]
timeout_secs = 30
max_output_bytes = 102400
no_output_timeout_secs = 30
FieldTypeDefaultDescription
modestring"allowlist"Security mode. See values below.
safe_binsstring[]["sleep","true","false","cat","sort","uniq","cut","tr","head","tail","wc","date","echo","printf","basename","dirname","pwd","env"]Stdin-only utilities that bypass the allowlist.
allowed_commandsstring[][]Additional commands allowed when mode = "allowlist".
timeout_secsu6430Maximum execution time in seconds.
max_output_bytesusize102400Maximum stdout/stderr output size (100 KB).
no_output_timeout_secsu6430Kill processes producing no output for this many seconds.

mode values:

ValueAliasesDescription
denynone, disabledBlock all shell execution.
allowlistrestrictedOnly allow commands in safe_bins or allowed_commands (default).
fullallow, all, unrestrictedAllow all commands. Unsafe -- dev only.

[thinking]

Extended thinking configuration for supported models (e.g., Claude with extended thinking).

[thinking]
budget_tokens = 10000
stream_thinking = false
FieldTypeDefaultDescription
budget_tokensu3210000Maximum tokens allocated to the thinking budget.
stream_thinkingboolfalseWhether to stream thinking tokens to the client in real time.

This section is Option<ThinkingConfig> -- when omitted entirely, extended thinking is disabled. Including the section header enables it.


[tts]

Text-to-speech configuration with support for OpenAI and ElevenLabs providers.

[tts]
enabled = true
provider = "openai"
max_text_length = 4096
timeout_secs = 30
FieldTypeDefaultDescription
enabledboolfalseEnable TTS.
providerstring or nullnullDefault TTS provider: "openai" or "elevenlabs".
max_text_lengthusize4096Maximum text length in characters for a single TTS request.
timeout_secsu6430Timeout per TTS API request.

[tts.openai]

[tts.openai]
voice = "alloy"
model = "tts-1"
format = "mp3"
speed = 1.0
FieldTypeDefaultDescription
voicestring"alloy"Voice selection: alloy, echo, fable, onyx, nova, shimmer.
modelstring"tts-1"Model: "tts-1" (fast) or "tts-1-hd" (high quality).
formatstring"mp3"Output audio format: mp3, opus, aac, flac.
speedf321.0Playback speed (0.25 to 4.0).

[tts.elevenlabs]

[tts.elevenlabs]
voice_id = "21m00Tcm4TlvDq8ikWAM"
model_id = "eleven_monolingual_v1"
stability = 0.5
similarity_boost = 0.75
FieldTypeDefaultDescription
voice_idstring"21m00Tcm4TlvDq8ikWAM"ElevenLabs voice ID. Default is "Rachel".
model_idstring"eleven_monolingual_v1"ElevenLabs model ID.
stabilityf320.5Voice stability (0.0--1.0). Lower = more expressive.
similarity_boostf320.75Similarity boost (0.0--1.0). Higher = closer to original voice.

[webhook_triggers]

External webhook endpoints (/hooks/wake, /hooks/agent) for triggering agent actions from external systems.

[webhook_triggers]
enabled = true
token_env = "LIBREFANG_WEBHOOK_TOKEN"
max_payload_bytes = 65536
rate_limit_per_minute = 30
FieldTypeDefaultDescription
enabledboolfalseEnable webhook trigger endpoints.
token_envstring"LIBREFANG_WEBHOOK_TOKEN"Env var holding the bearer token (NOT the token itself). Token must be >= 32 chars.
max_payload_bytesusize65536Maximum request payload size in bytes (64 KB).
rate_limit_per_minuteu3230Max requests per minute per source IP.

This section is Option<WebhookTriggerConfig> -- when omitted, webhook endpoints are disabled.


[approval]

Execution approval policy controlling which tool calls require user confirmation before running.

[approval]
require_approval = ["shell_exec", "file_write"]
timeout_secs = 60
auto_approve_autonomous = false
auto_approve = false
FieldTypeDefaultDescription
require_approvalstring[] or bool["shell_exec"]Tools requiring approval. false = none, true = ["shell_exec"].
timeout_secsu6460Seconds to wait for approval before timing out. Range: 10--300.
auto_approve_autonomousboolfalseAutomatically approve in autonomous mode.
auto_approveboolfalseAlias: if true, clears the require list at boot.

[[bindings]]

Agent bindings route incoming messages from specific channels, accounts, or peers to designated agents. Bindings are evaluated in specificity order (most specific match wins).

[[bindings]]
agent = "support-bot"
[bindings.match_rule]
channel = "discord"
guild_id = "123456789012345678"

[[bindings]]
agent = "personal"
[bindings.match_rule]
channel = "telegram"
peer_id = "987654321"
roles = ["owner"]
FieldTypeDescription
agentstringTarget agent name or ID.
match_rule.channelstring or nullChannel type (e.g., "discord", "telegram", "slack").
match_rule.account_idstring or nullSpecific bot account ID within the channel.
match_rule.peer_idstring or nullPeer/user ID for DM routing.
match_rule.guild_idstring or nullGuild/server ID (Discord/Slack).
match_rule.rolesstring[]Role-based routing (user must have at least one listed role).

Specificity scoring (higher = checked first):

CriterionScore
peer_id set+8
guild_id set+4
roles non-empty+2
account_id set+2
channel set+1

[broadcast]

Broadcast routing sends the same incoming message to multiple agents simultaneously or sequentially.

[broadcast]
strategy = "parallel"

[broadcast.routes]
"user-123" = ["agent-a", "agent-b"]
"user-456" = ["agent-c"]
FieldTypeDefaultDescription
strategystring"parallel"Delivery strategy: "parallel" (all at once) or "sequential" (one at a time).
routesmap{}Map of peer_id to list of agent names that receive the message.

[auto_reply]

Background auto-reply engine that can process messages automatically.

[auto_reply]
enabled = true
max_concurrent = 3
timeout_secs = 120
suppress_patterns = ["/stop", "/pause"]
FieldTypeDefaultDescription
enabledboolfalseEnable the auto-reply engine.
max_concurrentusize3Maximum concurrent auto-reply tasks.
timeout_secsu64120Default timeout per auto-reply in seconds.
suppress_patternsstring[]["/stop", "/pause"]Message patterns that suppress auto-reply.

[canvas]

Canvas (Agent-to-UI) allows agents to render rich HTML content in the dashboard.

[canvas]
enabled = true
max_html_bytes = 524288
allowed_tags = ["div", "span", "p", "h1", "h2", "h3", "img", "a"]
FieldTypeDefaultDescription
enabledboolfalseEnable the canvas tool.
max_html_bytesusize524288Maximum HTML payload size in bytes (512 KB).
allowed_tagsstring[][]Allowed HTML tags. Empty = all safe tags allowed.

[docker]

Docker container sandbox for executing agent commands in isolated environments.

[docker]
enabled = true
image = "python:3.12-slim"
container_prefix = "librefang-sandbox"
workdir = "/workspace"
network = "none"
memory_limit = "512m"
cpu_limit = 1.0
timeout_secs = 60
read_only_root = true
pids_limit = 100
mode = "non_main"
scope = "agent"
reuse_cool_secs = 300
idle_timeout_secs = 86400
max_age_secs = 604800
FieldTypeDefaultDescription
enabledboolfalseEnable Docker sandbox.
imagestring"python:3.12-slim"Docker image for the sandbox.
container_prefixstring"librefang-sandbox"Container name prefix.
workdirstring"/workspace"Working directory inside the container.
networkstring"none"Network mode: "none", "bridge", or custom network name.
memory_limitstring"512m"Memory limit (e.g., "256m", "1g").
cpu_limitf641.0CPU limit (e.g., 0.5, 1.0, 2.0).
timeout_secsu6460Maximum execution time per command in seconds.
read_only_rootbooltrueMount root filesystem as read-only.
cap_addstring[][]Additional Linux capabilities to add (default: drop all).
tmpfsstring[]["/tmp:size=64m"]tmpfs mount points.
pids_limitu32100Maximum number of processes.
modestring"off"Sandbox activation: "off", "non_main" (non-main agents only), "all".
scopestring"session"Container lifecycle: "session" (per session), "agent" (reused), "shared" (pool).
reuse_cool_secsu64300Cooldown before reusing a released container.
idle_timeout_secsu6486400Destroy containers after this many seconds of inactivity (24h).
max_age_secsu64604800Forced destruction after this age (7 days).
blocked_mountsstring[][]Host paths blocked from bind mounting.

[pairing]

Device pairing for mobile/desktop companion apps.

[pairing]
enabled = true
max_devices = 10
token_expiry_secs = 300
push_provider = "ntfy"
ntfy_url = "https://ntfy.sh"
ntfy_topic = "librefang-notifications"
FieldTypeDefaultDescription
enabledboolfalseEnable device pairing.
max_devicesusize10Maximum number of paired devices.
token_expiry_secsu64300Pairing token expiry in seconds (5 minutes).
push_providerstring"none"Push notification provider: "none", "ntfy", "gotify".
ntfy_urlstring or nullnullntfy server URL (when push_provider = "ntfy").
ntfy_topicstring or nullnullntfy topic name (when push_provider = "ntfy").

[auth_profiles]

Auth profiles enable key rotation per provider. When one key gets rate-limited or has billing issues, LibreFang automatically falls back to the next profile by priority.

[auth_profiles]
anthropic = [
  { name = "primary",   api_key_env = "ANTHROPIC_API_KEY",   priority = 0 },
  { name = "secondary", api_key_env = "ANTHROPIC_API_KEY_2", priority = 1 },
]
openai = [
  { name = "main", api_key_env = "OPENAI_API_KEY", priority = 0 },
]

Each profile has:

FieldTypeDefaultDescription
namestring--Profile name (e.g., "primary", "secondary").
api_key_envstring--Environment variable holding the API key.
priorityu320Priority ordering. Lower = preferred.

Key resolution order: [provider_api_keys] mapping > [auth_profiles] first-by-priority > convention {PROVIDER_UPPER}_API_KEY.


[oauth]

OAuth client ID overrides for PKCE authentication flows. Used when connecting to third-party services.

[oauth]
google_client_id = "123456.apps.googleusercontent.com"
github_client_id = "Iv1_abcdef123456"
microsoft_client_id = "00000000-0000-0000-0000-000000000000"
slack_client_id = "12345.67890"
FieldTypeDefaultDescription
google_client_idstring or nullnullGoogle OAuth2 client ID for PKCE flow.
github_client_idstring or nullnullGitHub OAuth client ID for PKCE flow.
microsoft_client_idstring or nullnullMicrosoft (Entra ID) OAuth client ID.
slack_client_idstring or nullnullSlack OAuth client ID.

[browser]

Browser automation configuration for headless web browsing.

[browser]
headless = true
viewport_width = 1280
viewport_height = 720
timeout_secs = 30
idle_timeout_secs = 300
max_sessions = 5
chromium_path = "/usr/bin/chromium"
FieldTypeDefaultDescription
headlessbooltrueRun browser in headless mode (no visible window).
viewport_widthu321280Viewport width in pixels.
viewport_heightu32720Viewport height in pixels.
timeout_secsu6430Per-action timeout in seconds.
idle_timeout_secsu64300Auto-close session after this many seconds of inactivity (5 min).
max_sessionsusize5Maximum concurrent browser sessions.
chromium_pathstring or nullnullPath to Chromium/Chrome binary. Auto-detected if omitted.

[extensions]

Extensions and MCP integration resilience configuration.

[extensions]
auto_reconnect = true
reconnect_max_attempts = 10
reconnect_max_backoff_secs = 300
health_check_interval_secs = 60
FieldTypeDefaultDescription
auto_reconnectbooltrueAutomatically reconnect to MCP servers on disconnect.
reconnect_max_attemptsu3210Maximum reconnect attempts before giving up.
reconnect_max_backoff_secsu64300Maximum backoff duration between reconnect attempts (5 min).
health_check_interval_secsu6460Interval between MCP server health checks in seconds.

[reload]

Config file watching and hot-reload behavior.

[reload]
mode = "hybrid"
debounce_ms = 500
FieldTypeDefaultDescription
modestring"hybrid"Reload mode. See values below.
debounce_msu64500Debounce window in milliseconds to avoid rapid reloads.

mode values:

ValueDescription
offNo automatic reloading.
restartFull restart on config change.
hotHot-reload safe sections only (channels, skills, heartbeat).
hybridHot-reload where possible, flag restart-required otherwise (default).

[provider_urls] / [provider_api_keys]

Override the default base URL or API key env var for any provider. Useful for self-hosted instances, proxies, or providers not in the built-in registry.

[provider_urls]
ollama = "http://192.168.1.100:11434/v1"
openai = "https://my-proxy.example.com/v1"

[provider_api_keys]
nvidia = "NVIDIA_API_KEY"
custom_llm = "MY_CUSTOM_LLM_KEY"

[provider_urls] -- Map of provider ID to custom base URL. Overrides the catalog default.

[provider_api_keys] -- Map of provider ID to the environment variable name holding the API key. When not set, the convention {PROVIDER_UPPER}_API_KEY is used (e.g., nvidia resolves to NVIDIA_API_KEY).


Config Include

Split your configuration into multiple files using the include top-level field. Included files are loaded and deep-merged before the root config.

include = [
  "channels.toml",
  "secrets.toml",
]

Rules:

  • Paths are relative to the root config file's directory (~/.librefang/).
  • Absolute paths and .. path components are rejected for security.
  • Included files are merged in order; later values override earlier ones.
  • The root config file's values override all included files.

[[sidecar_channels]]

Sidecar channel adapters allow external processes (written in any language) to act as channel adapters. Communication uses newline-delimited JSON over stdin/stdout.

[[sidecar_channels]]
name = "my-telegram"
command = "python3"
args = ["adapters/telegram_adapter.py"]
channel_type = "custom"

[sidecar_channels.env]
TELEGRAM_BOT_TOKEN = "xxx"
FieldTypeDefaultDescription
namestring--Display name for this adapter.
commandstring--Command to execute (e.g., "python3", "/usr/local/bin/my-adapter").
argsstring[][]Arguments to pass to the command.
envmap{}Extra environment variables to pass to the subprocess.
channel_typestring or nullnullChannel type identifier. Defaults to Custom(name).

Environment Variables

Complete table of all environment variables referenced by the configuration. None of these are read by the config file itself -- they are read at runtime by the kernel and channel adapters.

LLM Provider Keys

VariableUsed ByDescription
ANTHROPIC_API_KEY[default_model]Anthropic API key (Claude models).
GEMINI_API_KEYGemini driverGoogle Gemini API key. Alias: GOOGLE_API_KEY.
OPENAI_API_KEYOpenAI-compat driverOpenAI API key.
GROQ_API_KEYGroq providerGroq API key (fast Llama inference).
DEEPSEEK_API_KEYDeepSeek providerDeepSeek API key.
PERPLEXITY_API_KEYPerplexity provider / web searchPerplexity API key.
OPENROUTER_API_KEYOpenRouter providerOpenRouter API key.
TOGETHER_API_KEYTogether AI providerTogether AI API key.
MISTRAL_API_KEYMistral providerMistral AI API key.
FIREWORKS_API_KEYFireworks providerFireworks AI API key.
COHERE_API_KEYCohere providerCohere API key.
AI21_API_KEYAI21 providerAI21 Labs API key.
CEREBRAS_API_KEYCerebras providerCerebras API key.
SAMBANOVA_API_KEYSambaNova providerSambaNova API key.
HUGGINGFACE_API_KEYHugging Face providerHugging Face Inference API key.
XAI_API_KEYxAI providerxAI (Grok) API key.
REPLICATE_API_KEYReplicate providerReplicate API key.

Web Search Keys

VariableUsed ByDescription
BRAVE_API_KEY[web.brave]Brave Search API key.
TAVILY_API_KEY[web.tavily]Tavily Search API key.
PERPLEXITY_API_KEY[web.perplexity]Perplexity Search API key (shared with LLM provider).

Channel Tokens

VariableChannelDescription
TELEGRAM_BOT_TOKENTelegramBot API token from @BotFather.
DISCORD_BOT_TOKENDiscordDiscord bot token.
SLACK_APP_TOKENSlackSlack app-level token (xapp-) for Socket Mode.
SLACK_BOT_TOKENSlackSlack bot token (xoxb-) for REST API.
WHATSAPP_ACCESS_TOKENWhatsAppWhatsApp Cloud API access token.
WHATSAPP_VERIFY_TOKENWhatsAppWebhook verification token.
MATRIX_ACCESS_TOKENMatrixMatrix homeserver access token.
EMAIL_PASSWORDEmailEmail account password or app password.
TEAMS_APP_PASSWORDTeamsAzure Bot Framework app password.
MATTERMOST_TOKENMattermostMattermost bot token.
TWITCH_OAUTH_TOKENTwitchTwitch OAuth token.
ROCKETCHAT_TOKENRocket.ChatRocket.Chat auth token.
ZULIP_API_KEYZulipZulip bot API key.
XMPP_PASSWORDXMPPXMPP account password.
GOOGLE_CHAT_SERVICE_ACCOUNTGoogle ChatService account JSON key.
LINE_CHANNEL_SECRETLINELINE channel secret.
LINE_CHANNEL_ACCESS_TOKENLINELINE channel access token.
VIBER_AUTH_TOKENViberViber Bot auth token.
MESSENGER_PAGE_TOKENMessengerFacebook page access token.
MESSENGER_VERIFY_TOKENMessengerWebhook verification token.
REDDIT_CLIENT_SECRETRedditReddit app client secret.
REDDIT_PASSWORDRedditReddit bot account password.
MASTODON_ACCESS_TOKENMastodonMastodon access token.
BLUESKY_APP_PASSWORDBlueskyBluesky app password.
FEISHU_APP_SECRETFeishuFeishu/Lark app secret.
REVOLT_BOT_TOKENRevoltRevolt bot token.
NEXTCLOUD_TOKENNextcloudNextcloud Talk auth token.
GUILDED_BOT_TOKENGuildedGuilded bot token.
KEYBASE_PAPERKEYKeybaseKeybase paper key.
THREEMA_SECRETThreemaThreema Gateway API secret.
NOSTR_PRIVATE_KEYNostrNostr private key (nsec or hex).
WEBEX_BOT_TOKENWebexWebex bot token.
PUMBLE_BOT_TOKENPumblePumble bot token.
FLOCK_BOT_TOKENFlockFlock bot token.
TWIST_TOKENTwistTwist API token.
MUMBLE_PASSWORDMumbleMumble server password.
DINGTALK_ACCESS_TOKENDingTalkDingTalk webhook access token.
DINGTALK_SECRETDingTalkDingTalk signing secret.
DISCOURSE_API_KEYDiscourseDiscourse API key.
GITTER_TOKENGitterGitter auth token.
NTFY_TOKENntfyntfy auth token (optional for public topics).
GOTIFY_APP_TOKENGotifyGotify app token (sending).
GOTIFY_CLIENT_TOKENGotifyGotify client token (receiving).
WEBHOOK_SECRETWebhookHMAC signing secret for webhook verification.
LINKEDIN_ACCESS_TOKENLinkedInLinkedIn OAuth2 access token.

Other Service Keys

VariableUsed ByDescription
LIBREFANG_WEBHOOK_TOKEN[webhook_triggers]Bearer token for webhook trigger endpoints. Must be >= 32 chars.
ELEVENLABS_API_KEY[tts.elevenlabs]ElevenLabs TTS API key.
LIBREFANG_HOMECoreOverride the LibreFang home directory (default: ~/.librefang).

Validation

KernelConfig::validate() runs at boot time and returns a list of warnings (non-fatal). The kernel still starts, but logs each warning.

What is validated

For every enabled channel (i.e., its config section is present in the TOML), the validator checks that the corresponding environment variable(s) are set and non-empty:

ChannelEnv vars checked
Telegrambot_token_env
Discordbot_token_env
Slackapp_token_env, bot_token_env (both checked)
WhatsAppaccess_token_env
Matrixaccess_token_env
Emailpassword_env
Teamsapp_password_env
Mattermosttoken_env
Zulipapi_key_env
Twitchoauth_token_env
Rocket.Chattoken_env
Google Chatservice_account_env
XMPPpassword_env
LINEaccess_token_env
Viberauth_token_env
Messengerpage_token_env
Redditclient_secret_env
Mastodonaccess_token_env
Blueskyapp_password_env
Feishuapp_secret_env
Revoltbot_token_env
Nextcloudtoken_env
Guildedbot_token_env
Keybasepaperkey_env
Threemasecret_env
Nostrprivate_key_env
Webexbot_token_env
Pumblebot_token_env
Flockbot_token_env
Twisttoken_env
Mumblepassword_env
DingTalkaccess_token_env
Discourseapi_key_env
Gittertoken_env
ntfytoken_env (only if token_env is non-empty; public topics are OK without auth)
Gotifyapp_token_env
Webhooksecret_env
LinkedInaccess_token_env

For web search providers, the validator checks:

ProviderEnv var checked
braveweb.brave.api_key_env
tavilyweb.tavily.api_key_env
perplexityweb.perplexity.api_key_env
duckduckgo(no check -- no API key needed)
auto(no check -- cascading fallback handles missing keys)

What is NOT validated

  • The api_key_env in [default_model] is not checked by validate(). Missing LLM keys cause errors at runtime when the driver is first used.
  • The shared_secret in [network] is not validated against network_enabled. If networking is enabled with an empty secret, authentication will fail at connection time.
  • MCP server configurations are not validated at config load time. Connection errors surface during the background MCP connect phase.
  • Agent manifests have their own separate validation.

Some subsystems have their own configuration that is not part of config.toml but is worth noting:

Session Compaction (runtime)

Configured internally via CompactionConfig (not currently exposed in config.toml):

FieldDefaultDescription
threshold80Compact when session message count exceeds this.
keep_recent20Number of recent messages preserved verbatim after compaction.
max_summary_tokens1024Maximum tokens for the LLM summary of compacted messages.

WASM Sandbox (runtime)

Configured internally via SandboxConfig (not currently exposed in config.toml):

FieldDefaultDescription
fuel_limit1000000Maximum CPU instruction budget. 0 = unlimited.
max_memory_bytes16777216 (16 MB)Maximum WASM linear memory.
timeout_secsnull (30s fallback)Wall-clock timeout for epoch-based interruption.

Model Routing (per-agent manifest)

Configured in agent manifests via ModelRoutingConfig:

FieldDefaultDescription
simple_model"claude-haiku-4-5-20251001"Model for simple queries.
medium_model"claude-sonnet-4-20250514"Model for medium-complexity queries.
complex_model"claude-sonnet-4-20250514"Model for complex queries.
simple_threshold100Token count below which a query is classified as simple.
complex_threshold500Token count above which a query is classified as complex.

Autonomous Guardrails (per-agent manifest)

Configured in agent manifests via AutonomousConfig:

FieldDefaultDescription
quiet_hoursnullCron expression for quiet hours (agent pauses during this window).
max_iterations50Maximum tool-use iterations per invocation.
max_restarts10Maximum automatic restarts before permanent stop.
heartbeat_interval_secs30Seconds between heartbeat health checks.
heartbeat_channelnullChannel to send heartbeat status to (e.g., "telegram").