Major changes: - Config-driven adapters: all channels (Slack, Discord, Telegram, WebChat, Webhooks) controlled via config.json with enabled flags and token auto-detection, no CLI flags required - Runtime engine field: runtime.engine selects opencode/claude from config - Interactive install script: 8-phase setup wizard with AI runtime detection/installation, token setup, identity file personalization (personality presets), aetheel CLI command, background service (launchd/systemd) - Live runtime switching: /engine, /model, /provider commands hot-swap the AI runtime from chat without restart, changes persisted to config.json - Usage tracking: per-request cost extraction from Claude Code JSON output, cumulative stats via /usage command - Auto-failover: rate limit detection on both runtimes, automatic switch to other engine on quota errors with user notification - Chat commands work without / prefix (Slack intercepts / in channels), commands: engine, model, provider, config, usage, reload, cron, subagents, status, help - /config set for editing config.json from chat with dotted key notation - Security audit saved to docs/security-audit.md - Full command reference in docs/commands.md - Future changes doc with NanoClaw agent teams analysis - Logo added to README and WebChat UI - README fully rewritten with all features documented
8.4 KiB
Configuration Guide
How Aetheel loads its settings — config file, secrets, and CLI overrides.
Table of Contents
Overview
Aetheel uses a two-file configuration approach:
| File | Location | Purpose |
|---|---|---|
config.json |
~/.aetheel/config.json |
All non-secret settings (model, timeouts, channels, paths) |
.env |
Project root | Secrets only (tokens, passwords, API keys) |
On first run, Aetheel auto-creates ~/.aetheel/config.json with sensible defaults. You only need to edit what you want to change.
Config File
Located at ~/.aetheel/config.json. Created automatically on first run.
Full Default Config
{
"log_level": "INFO",
"runtime": {
"mode": "cli",
"model": null,
"timeout_seconds": 120,
"server_url": "http://localhost:4096",
"format": "json"
},
"claude": {
"model": null,
"timeout_seconds": 120,
"max_turns": 3,
"no_tools": true
},
"discord": {
"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" |
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 |
Top-level
| Key | Type | Default | Description |
|---|---|---|---|
log_level |
string | "INFO" |
"DEBUG", "INFO", "WARNING", "ERROR" |
Secrets
Secrets live in .env in the project root. These are never written to config.json.
Copy the template:
cp .env.example .env
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) |
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 |
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.
python main.py [options]
| Flag | Description |
|---|---|
--model <id> |
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 <level> |
Override log level |
Priority Order
When the same setting is defined in multiple places, the highest priority wins:
CLI arguments > Environment variables (.env) > config.json > 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 | ~/.aetheel/config.json |
No |
| Secrets | <project>/.env |
No (in .gitignore) |
| 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
│ └── <name>/
│ └── SKILL.md
└── daily/ # Session logs
└── YYYY-MM-DD.md
Examples
Minimal Setup (Slack + OpenCode CLI)
.env:
SLACK_BOT_TOKEN=xoxb-your-token
SLACK_APP_TOKEN=xapp-your-token
No config.json changes needed — defaults work.
python main.py
Custom Model + SDK Mode
~/.aetheel/config.json:
{
"runtime": {
"mode": "sdk",
"model": "anthropic/claude-sonnet-4-20250514",
"server_url": "http://localhost:4096"
}
}
Start OpenCode server first, then Aetheel:
opencode serve --port 4096
python main.py
Discord with Listen Channels
~/.aetheel/config.json:
{
"discord": {
"listen_channels": ["1234567890123456"]
}
}
.env:
DISCORD_BOT_TOKEN=your-discord-token
python main.py --discord
Multi-Channel (Slack + Discord + Telegram)
.env:
SLACK_BOT_TOKEN=xoxb-your-token
SLACK_APP_TOKEN=xapp-your-token
DISCORD_BOT_TOKEN=your-discord-token
TELEGRAM_BOT_TOKEN=your-telegram-token
python main.py --discord --telegram
Claude Code Runtime
~/.aetheel/config.json:
{
"claude": {
"model": "claude-sonnet-4-20250514",
"max_turns": 5,
"no_tools": false
}
}
python main.py --claude