# Configuration Guide > How Aetheel loads its settings — config file, secrets, and CLI overrides. --- ## Table of Contents 1. [Overview](#overview) 2. [Config File](#config-file) 3. [Secrets (env.vars)](#secrets) 4. [CLI Overrides](#cli-overrides) 5. [Priority Order](#priority-order) 6. [Reference](#reference) 7. [Examples](#examples) --- ## Overview Aetheel uses a single JSON config file for everything — settings and secrets: | File | Location | Purpose | |------|----------|---------| | `config.json` | `~/.aetheel/config.json` | All settings, secrets (via `env.vars` block), and `${VAR}` references | Secrets (tokens, API keys) go in the `env.vars` block inside config.json. They can be referenced elsewhere in the config using `${VAR}` syntax. Process environment variables still override everything. On first run, Aetheel auto-creates `~/.aetheel/config.json` with sensible defaults. --- ## Config File Located at `~/.aetheel/config.json`. Created automatically on first run. ### Full Default Config ```json { "env": { "vars": { "SLACK_BOT_TOKEN": "", "SLACK_APP_TOKEN": "", "TELEGRAM_BOT_TOKEN": "", "DISCORD_BOT_TOKEN": "", "ANTHROPIC_API_KEY": "", "OPENCODE_SERVER_PASSWORD": "" } }, "log_level": "INFO", "runtime": { "mode": "cli", "model": null, "timeout_seconds": 120, "server_url": "http://localhost:4096", "format": "json", "agent": null, "attach": null }, "claude": { "model": null, "timeout_seconds": 120, "max_turns": 3, "no_tools": true }, "slack": { "enabled": true, "bot_token": "${SLACK_BOT_TOKEN}", "app_token": "${SLACK_APP_TOKEN}" }, "telegram": { "enabled": false, "bot_token": "${TELEGRAM_BOT_TOKEN}" }, "discord": { "enabled": false, "bot_token": "${DISCORD_BOT_TOKEN}", "listen_channels": [] }, "memory": { "workspace": "~/.aetheel/workspace", "db_path": "~/.aetheel/memory.db" }, "scheduler": { "db_path": "~/.aetheel/scheduler.db" } } ``` ### Section: `runtime` Controls the OpenCode AI runtime (default). | Key | Type | Default | Description | |-----|------|---------|-------------| | `mode` | string | `"cli"` | `"cli"` (subprocess) or `"sdk"` (opencode serve API) | | `model` | string\|null | `null` | Model ID, e.g. `"anthropic/claude-sonnet-4-20250514"`. Null uses OpenCode's default. | | `timeout_seconds` | int | `120` | Max seconds to wait for a response | | `server_url` | string | `"http://localhost:4096"` | OpenCode server URL (SDK mode only) | | `format` | string | `"json"` | CLI output format: `"json"` (structured) or `"default"` (plain text) | | `workspace` | string\|null | `null` | Working directory for OpenCode. Null uses current directory. | | `provider` | string\|null | `null` | Provider override, e.g. `"anthropic"`, `"openai"` | | `agent` | string\|null | `null` | OpenCode agent name. Use `agents` command to list available agents. | | `attach` | string\|null | `null` | URL of a running `opencode serve` instance to attach to in CLI mode, avoiding MCP cold boot per request. | ### Section: `claude` Controls the Claude Code runtime (used with `--claude` flag). | Key | Type | Default | Description | |-----|------|---------|-------------| | `model` | string\|null | `null` | Model ID, e.g. `"claude-sonnet-4-20250514"` | | `timeout_seconds` | int | `120` | Max seconds to wait for a response | | `max_turns` | int | `3` | Max agentic tool-use turns per request | | `no_tools` | bool | `true` | Disable tools for pure conversation mode | ### Section: `discord` Discord-specific settings (non-secret). | Key | Type | Default | Description | |-----|------|---------|-------------| | `listen_channels` | list[string] | `[]` | Channel IDs where the bot responds to all messages (no @mention needed) | ### Section: `memory` Memory system paths. | Key | Type | Default | Description | |-----|------|---------|-------------| | `workspace` | string | `"~/.aetheel/workspace"` | Directory for identity files (SOUL.md, USER.md, MEMORY.md) and skills | | `db_path` | string | `"~/.aetheel/memory.db"` | SQLite database for embeddings and search index | ### Section: `scheduler` Scheduler storage. | Key | Type | Default | Description | |-----|------|---------|-------------| | `db_path` | string | `"~/.aetheel/scheduler.db"` | SQLite database for persisted scheduled jobs | ### Section: `models` Per-task model routing. Each task type can use a different model, provider, and engine. Omit or set to `null` to use the global `runtime` config. ```json { "models": { "heartbeat": { "engine": "opencode", "model": "ollama/llama3.2", "provider": "ollama" }, "subagent": { "model": "minimax/minimax-m1", "provider": "minimax" }, "default": null } } ``` | Key | Type | Default | Description | |-----|------|---------|-------------| | `heartbeat` | object\|null | `null` | Model override for heartbeat periodic tasks | | `subagent` | object\|null | `null` | Model override for background subagent tasks | | `default` | object\|null | `null` | Fallback model override for all other tasks | Each route object supports: | Key | Type | Default | Description | |-----|------|---------|-------------| | `engine` | string\|null | `null` | `"opencode"` or `"claude"` — null inherits global | | `model` | string\|null | `null` | Model ID (e.g. `"ollama/llama3.2"`) — null inherits global | | `provider` | string\|null | `null` | Provider name (e.g. `"ollama"`, `"minimax"`) — null inherits global | | `timeout_seconds` | int\|null | `null` | Request timeout — null inherits global | ### Top-level | Key | Type | Default | Description | |-----|------|---------|-------------| | `log_level` | string | `"INFO"` | `"DEBUG"`, `"INFO"`, `"WARNING"`, `"ERROR"` | --- ## Secrets Secrets live in the `env.vars` block inside `config.json`. Values defined here are injected into the process environment (if not already set), and can be referenced anywhere in the config using `${VAR}` syntax. ### Example config.json with secrets ```json { "env": { "vars": { "SLACK_BOT_TOKEN": "xoxb-your-bot-token", "SLACK_APP_TOKEN": "xapp-your-app-token", "DISCORD_BOT_TOKEN": "your-discord-token", "ANTHROPIC_API_KEY": "sk-ant-..." } }, "slack": { "enabled": true, "bot_token": "${SLACK_BOT_TOKEN}", "app_token": "${SLACK_APP_TOKEN}" }, "discord": { "enabled": true, "bot_token": "${DISCORD_BOT_TOKEN}" } } ``` ### How `${VAR}` substitution works - `${VAR}` → resolved from process env (including `env.vars`) - `$${VAR}` → literal `${VAR}` (escape sequence) - Missing vars log a warning and keep the literal `${VAR}` string ### Required (at least one adapter) | Variable | Format | Description | |----------|--------|-------------| | `SLACK_BOT_TOKEN` | `xoxb-...` | Slack bot OAuth token | | `SLACK_APP_TOKEN` | `xapp-...` | Slack app-level token (Socket Mode) | ### Optional | Variable | Format | Description | |----------|--------|-------------| | `TELEGRAM_BOT_TOKEN` | string | Telegram bot token from @BotFather | | `DISCORD_BOT_TOKEN` | string | Discord bot token from Developer Portal | | `OPENCODE_SERVER_PASSWORD` | string | Password for `opencode serve` (SDK mode) | | `ANTHROPIC_API_KEY` | `sk-ant-...` | Anthropic API key (Claude Code runtime) | All of these can be set in `env.vars` in config.json, as process environment variables, or both (process env wins). ### Environment Variable Overrides Any config.json setting can also be overridden via environment variables. These take priority over the config file: | Env Variable | Overrides | |-------------|-----------| | `LOG_LEVEL` | `log_level` | | `OPENCODE_MODE` | `runtime.mode` | | `OPENCODE_MODEL` | `runtime.model` | | `OPENCODE_TIMEOUT` | `runtime.timeout_seconds` | | `OPENCODE_SERVER_URL` | `runtime.server_url` | | `OPENCODE_PROVIDER` | `runtime.provider` | | `OPENCODE_WORKSPACE` | `runtime.workspace` | | `OPENCODE_AGENT` | `runtime.agent` | | `OPENCODE_ATTACH` | `runtime.attach` | | `CLAUDE_MODEL` | `claude.model` | | `CLAUDE_TIMEOUT` | `claude.timeout_seconds` | | `CLAUDE_MAX_TURNS` | `claude.max_turns` | | `CLAUDE_NO_TOOLS` | `claude.no_tools` | | `DISCORD_LISTEN_CHANNELS` | `discord.listen_channels` (comma-separated) | | `AETHEEL_WORKSPACE` | `memory.workspace` | | `AETHEEL_MEMORY_DB` | `memory.db_path` | --- ## CLI Overrides CLI arguments have the highest priority and override both config.json and environment variables. ```bash python main.py [options] ``` | Flag | Description | |------|-------------| | `--model ` | Override model for both runtimes | | `--claude` | Use Claude Code runtime instead of OpenCode | | `--cli` | Force CLI mode (OpenCode) | | `--sdk` | Force SDK mode (OpenCode) | | `--telegram` | Enable Telegram adapter | | `--discord` | Enable Discord adapter | | `--test` | Use echo handler (no AI) | | `--log ` | Override log level | --- ## Priority Order When the same setting is defined in multiple places, the highest priority wins: ``` CLI arguments > Process env vars > env.vars block > ${VAR} substitution > config.json values > Defaults ``` For example, if `config.json` sets `runtime.model` to `"anthropic/claude-sonnet-4-20250514"` but you run `python main.py --model openai/gpt-5.1`, the CLI argument wins. --- ## Reference ### File Locations | File | Path | Git-tracked | |------|------|-------------| | Config + Secrets | `~/.aetheel/config.json` | No | | Memory DB | `~/.aetheel/memory.db` | No | | Session DB | `~/.aetheel/sessions.db` | No | | Scheduler DB | `~/.aetheel/scheduler.db` | No | | Identity files | `~/.aetheel/workspace/SOUL.md` etc. | No | | Session logs | `~/.aetheel/workspace/daily/` | No | ### Data Directory Structure ``` ~/.aetheel/ ├── config.json # Main configuration ├── memory.db # Embeddings + search index ├── sessions.db # Persistent session mappings ├── scheduler.db # Scheduled jobs └── workspace/ ├── SOUL.md # Bot personality ├── USER.md # User profile ├── MEMORY.md # Long-term memory ├── skills/ # Skill definitions │ └── / │ └── SKILL.md └── daily/ # Session logs └── YYYY-MM-DD.md ``` --- ## Examples ### Minimal Setup (Slack + OpenCode CLI) `~/.aetheel/config.json`: ```json { "env": { "vars": { "SLACK_BOT_TOKEN": "xoxb-your-token", "SLACK_APP_TOKEN": "xapp-your-token" } }, "slack": { "bot_token": "${SLACK_BOT_TOKEN}", "app_token": "${SLACK_APP_TOKEN}" } } ``` No other changes needed — defaults work. ```bash python main.py ``` ### Custom Model + SDK Mode `~/.aetheel/config.json`: ```json { "runtime": { "mode": "sdk", "model": "anthropic/claude-sonnet-4-20250514", "server_url": "http://localhost:4096" } } ``` Start OpenCode server first, then Aetheel: ```bash opencode serve --port 4096 python main.py ``` ### Discord with Listen Channels `~/.aetheel/config.json`: ```json { "env": { "vars": { "DISCORD_BOT_TOKEN": "your-discord-token" } }, "discord": { "enabled": true, "bot_token": "${DISCORD_BOT_TOKEN}", "listen_channels": ["1234567890123456"] } } ``` ```bash python main.py --discord ``` ### Multi-Channel (Slack + Discord + Telegram) `~/.aetheel/config.json`: ```json { "env": { "vars": { "SLACK_BOT_TOKEN": "xoxb-your-token", "SLACK_APP_TOKEN": "xapp-your-token", "DISCORD_BOT_TOKEN": "your-discord-token", "TELEGRAM_BOT_TOKEN": "your-telegram-token" } }, "slack": { "bot_token": "${SLACK_BOT_TOKEN}", "app_token": "${SLACK_APP_TOKEN}" }, "discord": { "enabled": true, "bot_token": "${DISCORD_BOT_TOKEN}" }, "telegram": { "enabled": true, "bot_token": "${TELEGRAM_BOT_TOKEN}" } } ``` ```bash python main.py --discord --telegram ``` ### Claude Code Runtime `~/.aetheel/config.json`: ```json { "claude": { "model": "claude-sonnet-4-20250514", "max_turns": 5, "no_tools": false } } ``` ```bash python main.py --claude ```