Configuration
Config Loader
Section titled “Config Loader”Configuration is handled by Annihilation.Config (lib/annihilation/config.ex). It loads TOML files, merges them with built-in defaults, and returns a flat map with atom keys.
Loading Config
Section titled “Loading Config”# Auto-detect project root and load configconfig = Annihilation.Config.load()
# Or specify the project root explicitlyconfig = Annihilation.Config.load("/path/to/project")
# Access built-in defaultsdefaults = Annihilation.Config.defaults()Project Root Detection
Section titled “Project Root Detection”Annihilation.Config.detect_project_root/1 walks up the directory tree from the current working directory (or a specified start directory) looking for markers in this order:
- A directory containing
.annihilation/— this is the preferred marker - A directory containing
.git/— fallback for repos without.annihilation/ - If neither is found at
/, falls back toFile.cwd!()
# Auto-detect from cwdroot = Annihilation.Config.detect_project_root()
# Detect from a specific starting pointroot = Annihilation.Config.detect_project_root("/some/nested/dir")Config Locations
Section titled “Config Locations”| Location | Purpose | Precedence |
|---|---|---|
| Built-in defaults | Hardcoded in @defaults | Lowest |
~/.annihilation/config.toml | Global user settings | Middle |
$PROJECT_ROOT/.annihilation/config.toml | Project overrides | Highest |
Project-local settings override global settings, which override built-in defaults. The merge is a shallow Map.merge/2 of the flattened TOML sections.
Built-in Defaults
Section titled “Built-in Defaults”These defaults are defined in Annihilation.Config and are used when no TOML config overrides them:
%{ project_dir: ".annihilation", db_path: "beads.db", search_db_path: "search.db", sessions_dir: "sessions", question_timeout: 120_000, # 2 minutes before drifting auto_create_correction_beads: true, correction_bead_priority: 1, recursion_max_depth: 3, recursion_max_concurrent: 16, recursion_fan_out_concurrency: 4, recursion_child_timeout: 120_000}The loaded config map also includes a :project_root key set to the detected project root path.
Full TOML Reference
Section titled “Full TOML Reference”# --- LLM Providers ---[llm]default_provider = "anthropic" # "anthropic" | "openai"default_model = "claude-sonnet-4-20250514"
[llm.anthropic]api_key_env = "ANTHROPIC_API_KEY" # env var name (not the key itself)max_retries = 5base_delay_ms = 1000
[llm.openai]api_key_env = "OPENAI_API_KEY"max_retries = 5base_delay_ms = 1000
# --- TUI ---[tui]fps = 30theme = "dark"
# --- Security ---[security]shell_allowlist = ["git", "mix", "elixir", "cat", "ls", "grep", "rg", "find"]shell_blocklist = ["rm -rf /", "sudo", "curl | bash"]file_read_outside_project = "ask" # "allow" | "ask" | "deny"
# --- Bursts ---[burst]question_timeout_ms = 120000 # 2 minutes before driftingauto_create_correction_beads = truecorrection_bead_priority = 1
# --- Memory ---[memory]half_life_days = 90harmful_multiplier = 4auto_reflect = true # run reflection after each burst
# --- Session ---[session]compaction_threshold = 0.8 # compact at 80% of context windowkeep_recent_messages = 10
# --- Agent Defaults ---[agent]max_turns = 50max_tokens = 4096TOML Flattening
Section titled “TOML Flattening”The config loader flattens nested TOML sections into a single-level map with atom keys. For example:
[burst]question_timeout_ms = 120000auto_create_correction_beads = truebecomes:
%{question_timeout_ms: 120000, auto_create_correction_beads: true}This means keys across sections must be unique. If two sections define the same key name, the last one processed wins (map merge behavior).
Environment Variables
Section titled “Environment Variables”| Variable | Required | Purpose |
|---|---|---|
ANTHROPIC_API_KEY | Yes (if using Anthropic) | Anthropic API authentication |
OPENAI_API_KEY | Yes (if using OpenAI) | OpenAI API authentication |
API keys are never stored in config files — always use environment variables. The TOML config references them by env var name (e.g., api_key_env = "ANTHROPIC_API_KEY"), not by value.
Implementation Source
Section titled “Implementation Source”The full config module is at lib/annihilation/config.ex:
defmodule Annihilation.Config do @defaults %{ project_dir: ".annihilation", db_path: "beads.db", search_db_path: "search.db", sessions_dir: "sessions", question_timeout: 120_000, auto_create_correction_beads: true, correction_bead_priority: 1, recursion_max_depth: 3, recursion_max_concurrent: 16, recursion_fan_out_concurrency: 4, recursion_child_timeout: 120_000 }
def defaults, do: @defaults
def load(project_root \\ nil) do root = project_root || detect_project_root() global = load_toml(Path.expand("~/.annihilation/config.toml")) project = load_toml(Path.join([root, ".annihilation", "config.toml"]))
@defaults |> Map.merge(global) |> Map.merge(project) |> Map.put(:project_root, root) end
def detect_project_root(start_dir \\ File.cwd!()) do cond do File.dir?(Path.join(start_dir, ".annihilation")) -> start_dir File.dir?(Path.join(start_dir, ".git")) -> start_dir start_dir == "/" -> File.cwd!() true -> detect_project_root(Path.dirname(start_dir)) end endend