Ryvos uses TOML configuration files with environment variable expansion. The config system supports two layers:
- Global config:
~/.ryvos/config.toml— applies everywhere - Workspace override:
ryvos.tomlin your project root — merges on top of global
Environment variables are expanded with $\{VAR\} syntax anywhere in the config.
Minimal Config
The smallest useful config needs just a model provider:
[model]
provider = "anthropic"
model_id = "claude-sonnet-4-20250514"
api_key = "$\{ANTHROPIC_API_KEY\}"Full Config Reference
[model] — Primary LLM
[model]
provider = "anthropic" # Required. See Providers page for all 19.
model_id = "claude-sonnet-4-20250514" # Required. Model identifier.
api_key = "$\{ANTHROPIC_API_KEY\}" # API key (env var expansion supported).
base_url = "" # Override API endpoint (for proxies, local models).
extra_headers = {} # Additional HTTP headers as key-value pairs.[[fallback_models]] — Fallback Chain
Define backup models that Ryvos tries when the primary fails:
[[fallback_models]]
provider = "openai"
model_id = "gpt-4o"
api_key = "$\{OPENAI_API_KEY\}"
[[fallback_models]]
provider = "ollama"
model_id = "llama3.1:70b"Ryvos tries fallbacks in order. Useful for resilience (e.g., cloud primary with local Ollama fallback).
[agent] — Agent Runtime
[agent]
max_turns = 25 # Max ReAct loop iterations per run.
max_duration_secs = 600 # Hard timeout for a single run (seconds).
workspace = "~/.ryvos" # Default workspace directory.
max_context_tokens = 32000 # Context window budget.
parallel_tools = true # Execute independent tool calls in parallel.
enable_summarization = true # Summarize old messages when context is full.
sandbox = "none" # "none", "docker", or "nsjail".
log = "l2" # Log level: "l1" (summary), "l2" (per-turn), "l3" (per-step).[agent.guardian] — Watchdog Settings
[agent.guardian]
enabled = true
doom_loop_threshold = 3 # Identical consecutive tool calls before intervention.
stall_timeout_secs = 120 # Seconds of no progress before stall alert.
token_budget_soft = 80 # Percentage — emit warning.
token_budget_hard = 95 # Percentage — force stop.[agent.checkpoint] — Session Persistence
[agent.checkpoint]
enabled = true # Save after each turn for crash recovery.[agent.director] — Director Orchestration
[agent.director]
enabled = true # Enable Director for goal-driven runs.
max_evolution_cycles = 3 # Max graph re-planning attempts.[security] — Safety Configuration
Ryvos uses self-learning safety. The security section controls the constitutional AI and audit system:
[security]
auto_approve_up_to = "T1" # Tier auto-approval threshold (T0-T4).
deny_above = "T3" # Tier denial threshold.
approval_timeout_secs = 60 # Seconds before unanswered approval request times out.
dangerous_patterns = [] # Additional regex patterns to flag.:::note The security tier system (T0-T4) provides a baseline. Ryvos's constitutional AI principles and safety memory operate on top of this, learning from experience to make safer decisions over time. :::
Optional User Checkpoints
[security]
pause_before = ["file_delete", "git_push"] # Tools that prompt for confirmation.[channels] — Communication Channels
[channels.telegram]
[channels.telegram]
enabled = true
bot_token = "$\{TELEGRAM_BOT_TOKEN\}"
dm_policy = "allowlist" # "allowlist", "open", or "disabled".
allowed_users = [123456789] # Telegram user IDs (for allowlist policy).[channels.discord]
[channels.discord]
enabled = true
bot_token = "$\{DISCORD_BOT_TOKEN\}"
dm_policy = "allowlist"
allowed_users = ["user_id_here"]
guild_id = "" # Optional: restrict to one Discord server.[channels.slack]
[channels.slack]
enabled = true
bot_token = "$\{SLACK_BOT_TOKEN\}"
app_token = "$\{SLACK_APP_TOKEN\}" # Socket Mode app-level token (xapp-...).
dm_policy = "allowlist"
allowed_users = ["U0123456"][channels.whatsapp]
[channels.whatsapp]
enabled = true
access_token = "$\{WHATSAPP_ACCESS_TOKEN\}"
phone_number_id = "$\{WHATSAPP_PHONE_ID\}"
verify_token = "$\{WHATSAPP_VERIFY_TOKEN\}"
webhook_port = 8443
allowed_numbers = ["+1234567890"] # E.164 format.[gateway] — HTTP/WebSocket API
[gateway]
bind = "127.0.0.1:18789" # Address and port to bind.
[[gateway.api_keys]]
key = "$\{RYVOS_API_KEY\}"
role = "admin" # "viewer", "operator", or "admin".
[[gateway.api_keys]]
key = "$\{RYVOS_READONLY_KEY\}"
role = "viewer"Roles:
| Role | Permissions |
|---|---|
viewer | Read sessions, view metrics |
operator | + Send messages, manage sessions |
admin | + Change config, manage API keys, full access |
[mcp] — Model Context Protocol
[mcp]
[mcp.servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
[mcp.servers.github]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
env = { GITHUB_TOKEN = "$\{GITHUB_TOKEN\}" }
[mcp.servers.remote-tools]
url = "http://localhost:3001/sse" # SSE transport for remote servers.[hooks] — Lifecycle Hooks
[hooks]
on_start = "echo 'Ryvos started'"
on_message = ""
on_tool_call = ""
on_response = ""
on_turn_complete = ""
on_tool_error = ""
on_session_start = ""
on_session_end = ""Hooks are shell commands executed at each lifecycle point. Use them for logging, notifications, or custom integrations.
[heartbeat] — Periodic Health Checks
[heartbeat]
enabled = true
interval_secs = 3600 # Check every hour.
active_hours = "08:00-22:00" # Only run during these hours (local time).
ack_max_chars = 50 # Suppress responses shorter than this (e.g., "all good").
channel = "telegram" # Route alerts to this channel.The heartbeat reads HEARTBEAT.md from your workspace and runs the agent to check on whatever you define there. See Cron & Heartbeat for details.
[cron] — Scheduled Tasks
[[cron.jobs]]
name = "daily-report"
schedule = "0 9 * * *" # Standard 5-field cron expression.
prompt = "Generate a summary of yesterday's git commits and open PRs"
channel = "slack" # Optional: route output to a channel.
[[cron.jobs]]
name = "hourly-check"
schedule = "0 * * * *"
prompt = "Check if the staging server is healthy"[budget] — Cost Controls
[budget]
monthly_limit_cents = 5000 # $50/month hard cap.
warning_threshold_cents = 4000 # Warn at $40.
per_run_limit_cents = 100 # $1 per run max.
[budget.pricing_overrides]
"claude-sonnet-4-20250514" = { input_per_1m = 300, output_per_1m = 1500 } # cents per 1M tokens[embedding] — Semantic Search
[embedding]
provider = "openai" # "openai", "ollama", or "custom".
model = "text-embedding-3-small"
api_key = "$\{OPENAI_API_KEY\}"
base_url = "" # Override for custom embedding endpoints.When configured, memory search uses hybrid BM25 + vector similarity instead of BM25 alone.
[openviking] — Viking Memory Server
[openviking]
enabled = true
url = "http://localhost:1933" # Viking server endpoint.
auto_start = true # Start Viking server with daemon mode.
dual_write = true # Write to both SQLite and Viking.Environment Variable Expansion
Any string value in the config supports $\{VAR\} syntax:
[model]
api_key = "$\{ANTHROPIC_API_KEY\}" # Reads from environment.This works for all fields, not just API keys. Useful for keeping secrets out of config files.
Viewing Resolved Config
Print the fully resolved config (with env vars expanded):
ryvos config:::caution This will print API keys in plaintext. Only use in secure environments. :::
Config Validation
Validate your config without starting Ryvos:
ryvos doctorThe doctor command checks:
- Config file syntax and required fields
- API key validity (test connection to each provider)
- Channel adapter configuration
- MCP server connectivity
- Database integrity
- Security policy consistency
Next Steps
- LLM Providers — Detailed setup for all 18+ providers
- CLI Reference — All commands and flags
- Cron & Heartbeat — Automation configuration