Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

castellan.toml reference

Layered configuration for governance, shell policy, hooks, and MCP permissions. Schema source: crates/castellan-governance/src/config.rs.

File locations (merge order)

LayerPathPrecedence
User~/.castellan/config.tomllowest
Projectcastellan.toml (walk up from cwd)middle
Local.castellan/local.toml (same repo as project castellan.toml)highest

Later layers override earlier ones. CLI flags and environment variables override all files.

Example

[governance]
session_spend_cap_usd = 12.5
max_risk_score = 0.85
denied_paths = ["/secret", "~/.ssh"]

[shell]
allowed_commands = ["echo", "cargo", "git"]
denied_commands = ["rm", "curl"]
denied_patterns = ["sudo .*"]
max_output_bytes = 65536

[permissions]
default_mode = "ask"
[permissions.tools]
castellan_propose_mutation = "deny"
castellan_deposit_signal = "allow"

[[hooks.hooks]]
event = "PreToolUse"
command = "./scripts/hooks/pre-tool.sh"

Sections

[memory]

KeyTypeDescription
backendjson | sqliteDurable store backend (default: json)
pathstringSQLite database path when backend = "sqlite" (default: .castellan/castellan.db)
mirror_jsonboolAlso write .castellan/episodes/*.json alongside SQL (default: true)
auto_importboolImport existing JSON corpus on first SQLite open (default: true)
checkpoint_interval_ticksintegerPersist mid-episode checkpoints every N scheduler ticks (default: 0 = episode-end only)

See Memory architecture.

[governance]

KeyTypeDescription
session_spend_cap_usdfloatSession spend ceiling
max_risk_scorefloatRisk score threshold
denied_pathsstring[]Paths blocked for tool/file access
[governance.plan_verify]tableOpt-in Guardians-shaped prove-before-execute (default off)

[governance.plan_verify]

KeyTypeDescription
enabledboolRequire verified workflow_plan for high-risk tools (default false)
high_risk_toolsstring[]Tools that need a certificate (default shell, exec)
allowlisted_toolsstring[]Tools permitted inside a workflow plan
taint_edgesarrayOptional { source_ref_prefix, forbidden_sink_tools } constraints
policy_pathstringOptional TOML file overriding allowlists / taint

See Agent hooks — prove-before-execute.

[shell]

KeyTypeDescription
allowed_commandsstring[]Allowlist for shell plugin
denied_commandsstring[]Blocked command names
denied_patternsstring[]Regex patterns to block
max_output_bytesintegerCap captured stdout/stderr

[permissions]

KeyTypeDescription
presetcautious | balanced | permissiveApproval preset expanded into a base policy
default_modeallow | ask | denyDefault MCP tool policy (overrides preset base)
[permissions.tools]mapPer-tool overrides

Permission modes filter tools/list before exposure to MCP clients. castellan doctor reports the active preset.

[coevolution]

Fast-slow co-evolution flywheel. See Evolution.

KeyTypeDescription
fast_remediationboolDeposit pheromone hints from failed episodes after each run (default: true)
slow_evolve_everyintegerRun in-tree castellan evolve every N episodes (0 = off, default)
slow_evolve_generationsintegerGenerations per slow evolve pass (default: 2)

[models]

KeyTypeDescription
providerstringOptional LLM provider: openrouter, openai, ollama, or openai_compatible
base_urlstringOpenAI-compatible API root; defaults by provider (openrouterhttps://openrouter.ai/api/v1)
http_refererstringOptional HTTP-Referer header (OpenRouter attribution; Castellan default when provider = "openrouter")
x_titlestringOptional X-Title header (OpenRouter attribution; default Castellan)
defaultstringFallback model id for all roles (default: mock)
verifystringModel for the verify client
actstringModel for the act client
planstringModel for the plan client

Per-role tiers resolve through one OpenAI-compatible client factory (scheduler never routes through an LLM). Auth is env-only — never put API keys in TOML:

ProviderAPI key env (first wins)
openrouterOPENROUTER_API_KEY, then CASTELLAN_LLM_API_KEY, then OPENAI_API_KEY
openai / openai_compatibleOPENAI_API_KEY or CASTELLAN_LLM_API_KEY
ollamaoptional (CASTELLAN_LLM_API_KEY / OPENAI_API_KEY); local default URL from OLLAMA_URL + /v1
[models]
provider = "openrouter"
default = "openai/gpt-4o-mini"
verify = "anthropic/claude-sonnet-4"
act = "openai/gpt-4o"
# optional overrides:
# base_url = "https://openrouter.ai/api/v1"
# http_referer = "https://github.com/Alphabetsoup16/Flock"
# x_title = "Castellan"

--model / --model-tier on castellan run still override; rebuild CLI with --features llm-http for live HTTP providers.

[[recipes]]

KeyTypeDescription
namestringRecipe identifier for castellan recipes run <name>
descriptionstringShown by castellan recipes list
commandstringShell command executed via sh -c
cwdstringOptional working directory

Recipes inherit [shell] denied_commands / denied_patterns — they are not a policy bypass. Later config layers override recipes by name.

[[recipes]]
name = "gridworld-smoke"
description = "Run gridworld reach-target and write an episode JSON"
command = "castellan run --goal 'reach target' --plugin gridworld --json"

[[hooks.hooks]]

KeyTypeDescription
eventstringHook event name
commandstringShell command to invoke

See Agent hooks.

Environment overrides

VariableMaps to
CASTELLAN_SESSION_SPEND_CAP_USDgovernance.session_spend_cap_usd
CASTELLAN_MAX_RISK_SCOREgovernance.max_risk_score
CASTELLAN_SHELL_ALLOWEDCSV → shell.allowed_commands
CASTELLAN_SHELL_DENIEDCSV → shell.denied_commands
CASTELLAN_DENIED_PATHSCSV → governance.denied_paths
CASTELLAN_PERMISSION_MODEpermissions.default_mode
CASTELLAN_PERMISSION_PROMPTHeadless Prompt resolution: allow | deny (default deny when non-TTY). Legacy shim: FLOCK_PERMISSION_PROMPT
CASTELLAN_MEMORY_BACKENDmemory.backend
CASTELLAN_MEMORY_DBmemory.path
CASTELLAN_MEMORY_MIRROR_JSONmemory.mirror_json

Full list: Environment variables.