Config Reference¶
Complete field-by-field reference for leo.yaml.
Config lives at ~/.leo/leo.yaml (the Leo home directory).
defaults¶
Settings inherited by all processes, tasks, and templates unless overridden.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | No | Default Claude model (sonnet, opus, haiku). Defaults to sonnet. |
max_turns | int | No | Default maximum agent turns per execution. Defaults to 15. |
permission_mode | string | No | Default permission mode (default, acceptEdits, auto, bypassPermissions, dontAsk, plan). |
bypass_permissions | bool | No | Legacy: pass --dangerously-skip-permissions. Prefer permission_mode. Default false. |
remote_control | bool | No | Enable --remote-control for web/mobile access. Default false. |
allowed_tools | list | No | Default tool whitelist (passed via --allowed-tools). |
disallowed_tools | list | No | Default tool blacklist (passed via --disallowed-tools). |
append_system_prompt | string | No | Extra system prompt appended to all processes/tasks. |
web¶
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
enabled | bool | No | false | Enable the web dashboard. |
port | int | No | 8370 | TCP port for the web UI. |
bind | string | No | 127.0.0.1 | Bind address. Loopback-only by default. |
allowed_hosts | list of strings | No | [] | Extra hostnames/IPs accepted in the Host and Origin headers, in addition to loopback. Required when bind is non-loopback. Entries must not include a port. |
When enabled, the daemon serves a web dashboard with process monitoring, task management, agent dispatch, config editing, and cron preview.
Authentication¶
Both browser and API access require the same token. On first start the daemon mints a random 64-hex-char token and writes it to ~/.leo/state/api.token (mode 0600).
Browser login. Visit the dashboard and you'll be redirected to /login. Paste the token there and a 7-day session cookie is set (HttpOnly, SameSite=Strict). For convenience:
prints a one-click URL (http://<bind>:<port>/login?token=...) — the login page auto-submits if the token is in the query string. The URL contains the token; don't share it.
Click Sign out in the top-right of the dashboard to destroy the session.
API access. /api/* endpoints take the token in an Authorization: Bearer header:
Rotate the token by deleting api.token and restarting the daemon. Existing browser sessions remain valid until they expire (7 days).
Token scope. The bearer token grants access to the full daemon API — including /web/* routes that can restart the service, mutate config, send keys to supervised processes, and write prompt files. Treat it like a root credential. Supervised Claude processes receive this token via LEO_API_TOKEN so the built-in MCP server can call /api/*; if you don't trust a channel plugin with full daemon access, don't install it as a supervised process.
Non-loopback access¶
bind defaults to 127.0.0.1. To expose the web UI on your LAN:
web:
enabled: true
bind: 0.0.0.0
port: 8370
allowed_hosts:
- 192.0.2.10 # the IP your LAN will use to reach this host
- leo.local # or a hostname
allowed_hosts entries are checked against the incoming Host and Origin headers to defend against DNS-rebinding and drive-by cross-origin POSTs. Entries must be bare hostnames or IPs — no port, no scheme. allowed_hosts is required when bind is non-loopback; leo validate will fail otherwise.
The daemon prints a startup warning when bind is non-loopback.
client¶
Remote-host definitions used by the leo agent CLI when leo is invoked as a client of a different machine. Empty on server configs.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
default_host | string | No | — | Host name to use when --host and LEO_HOST are unset. |
hosts | map | No | {} | Named host definitions keyed by short name. |
Each entry under hosts has:
| Field | Type | Required | Description |
|---|---|---|---|
ssh | string | Yes | SSH target passed verbatim (e.g. user@host, or a Host alias from ~/.ssh/config). |
ssh_args | list | No | Extra arguments inserted between the target and the remote command (e.g. ["-p", "2222"]). |
leo_path | string | No | Absolute path to leo on the remote host. Defaults to $HOME/.local/bin/leo (matches install.sh). Override when leo is installed elsewhere or the remote's non-interactive SSH shell doesn't have it on PATH. |
tmux_path | string | No | Path to tmux on the remote host. Used by agent attach and agent logs --follow. Defaults to tmux (relies on PATH). Set to /opt/homebrew/bin/tmux for macOS arm64 homebrew remotes, /usr/local/bin/tmux for macOS intel. |
client:
default_host: prod
hosts:
prod:
ssh: alice@leo.example.com
ssh_args: ["-p", "2222"]
leo_path: /usr/local/bin/leo
tmux_path: /opt/homebrew/bin/tmux
dev:
ssh: alice@devbox.local
Why leo_path exists: SSH runs a non-interactive shell on the remote, which doesn't source .zshrc / .bashrc. If leo lives in ~/.local/bin and PATH is only extended in .zshrc, bare leo won't resolve. The default full path avoids that; set leo_path explicitly when the remote installs leo elsewhere (Homebrew, /usr/local/bin, etc.).
Resolution order for the target host: --host flag → LEO_HOST env → default_host → first entry sorted by key → localhost (only when no hosts are configured). --host localhost is a hard override. See the Remote CLI guide.
Channels¶
Leo does not ship with any built-in messaging channel. Channels are Claude Code plugins the user installs separately (e.g. Telegram, Slack, webhook). In leo.yaml they are referenced by plugin ID strings like plugin:telegram@claude-plugins-official on the channels: field of processes and tasks.
Leo passes the resolved list to the spawned Claude process via the LEO_CHANNELS environment variable. The plugin owns its own credentials, routing, and inbound-message handling.
To install a channel plugin:
Then reference it:
Development Channels¶
For channel plugins that aren't yet published to a registry (or for local plugin development), processes, tasks, and templates accept a parallel dev_channels: field. Leo passes each entry to Claude Code via --dangerously-load-development-channels <id> and exports the list in LEO_DEV_CHANNELS.
processes:
assistant:
channels: [plugin:blackpaw-telegram@blackpaw-plugins]
dev_channels: [plugin:blackpaw-telegram@blackpaw-plugins]
Validation matches channels — each entry must be a valid plugin ID.
Claude Code displays a confirmation prompt before loading development channels. For supervised processes, Leo watches the tmux pane and auto-accepts the prompt so the session starts non-interactively. Silent/nonexistent entries are ignored by Claude Code without warning — verify spellings carefully.
processes¶
Each process is a named entry under the processes map. Processes define long-running Claude sessions supervised by the daemon.
processes:
assistant:
channels: [plugin:telegram@claude-plugins-official]
remote_control: true
enabled: true
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workspace | string | No | ~/.leo/workspace/ | Working directory for this process. |
channels | list | No | -- | Channel plugin IDs (e.g., plugin:telegram@claude-plugins-official). |
dev_channels | list | No | -- | Unpublished channel plugin IDs loaded via --dangerously-load-development-channels. |
model | string | No | defaults.model | Claude model override. |
max_turns | int | No | defaults.max_turns | Max turns override. |
agent | string | No | -- | Run as a specific agent definition. |
permission_mode | string | No | defaults.permission_mode | Permission mode override. |
bypass_permissions | bool | No | defaults.bypass_permissions | Legacy: skip permissions. |
remote_control | bool | No | defaults.remote_control | Enable remote control. |
mcp_config | string | No | <workspace>/config/mcp-servers.json | Path to MCP config file. |
add_dirs | list | No | -- | Additional directories passed via --add-dir. |
env | map | No | -- | Environment variables for the claude process. |
allowed_tools | list | No | defaults.allowed_tools | Tool whitelist. |
disallowed_tools | list | No | defaults.disallowed_tools | Tool blacklist. |
append_system_prompt | string | No | defaults.append_system_prompt | Extra system prompt. |
enabled | bool | No | false | Whether the service should start this process. |
tasks¶
Each task is a named entry under the tasks map. Tasks are invoked by the in-process cron scheduler or manually via leo run <task>.
tasks:
daily-briefing:
schedule: "0 7 * * *"
timezone: America/New_York
prompt_file: prompts/daily-briefing.md
enabled: true
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workspace | string | No | ~/.leo/workspace/ | Working directory. |
schedule | string | Yes | -- | 5-field cron expression. |
timezone | string | No | System default | IANA timezone (e.g., America/New_York). |
prompt_file | string | Yes | -- | Path to prompt file, relative to workspace. |
model | string | No | defaults.model | Claude model override. |
max_turns | int | No | defaults.max_turns | Max turns override. |
timeout | string | No | 30m | Max duration before kill (e.g., 30m, 1h). |
retries | int | No | 0 | Retry attempts on failure. |
channels | list | No | -- | Channel plugin IDs used by notify_on_fail. |
dev_channels | list | No | -- | Unpublished channel plugin IDs loaded via --dangerously-load-development-channels. |
permission_mode | string | No | defaults.permission_mode | Permission mode override. |
allowed_tools | list | No | defaults.allowed_tools | Tool whitelist. |
disallowed_tools | list | No | defaults.disallowed_tools | Tool blacklist. |
append_system_prompt | string | No | defaults.append_system_prompt | Extra system prompt. |
notify_on_fail | bool | No | false | Spawn a short child claude invocation on non-zero exit, instructing it to notify the configured channels. Requires channels: to be set. |
enabled | bool | No | false | Whether the scheduler should run this task. |
silent | bool | No | false | Prepend silent-mode preamble to prompt. |
Silent Mode¶
When silent: true, Leo prepends a preamble instructing the agent to work without narration. The agent should deliver its final message via a configured channel plugin or output NO_REPLY if there's nothing to report.
templates¶
Templates are reusable blueprints for spawning ephemeral agents. Dispatch them via the HTTP API, a channel plugin that exposes agent commands, or the web UI.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
workspace | string | No | ~/.leo/agents/ | Base directory for agent workspaces. Repos are cloned as subdirectories. |
channels | list | No | -- | Channel plugin IDs for spawned agents. |
dev_channels | list | No | -- | Unpublished channel plugin IDs loaded via --dangerously-load-development-channels. |
model | string | No | defaults.model | Claude model. |
max_turns | int | No | defaults.max_turns | Max turns. |
agent | string | No | -- | Agent definition to use. |
remote_control | bool | No | true | Enable remote control (defaults to on for templates). |
mcp_config | string | No | -- | Path to MCP config file. |
add_dirs | list | No | -- | Additional directories. |
env | map | No | -- | Environment variables. |
permission_mode | string | No | defaults.permission_mode | Permission mode. |
allowed_tools | list | No | defaults.allowed_tools | Tool whitelist. |
disallowed_tools | list | No | defaults.disallowed_tools | Tool blacklist. |
append_system_prompt | string | No | defaults.append_system_prompt | Extra system prompt. |
When dispatching with a repo (/agent coding owner/repo via a channel plugin, or leo agent spawn coding --repo owner/repo), Leo clones the repo into <workspace>/<repo> using gh. The agent session is named leo-<template>-<owner>-<repo>.
Override Cascade¶
Process, task, and template settings override defaults: