Skip to content

Config file

~/.octo/config.yml holds your default provider(s), model(s), and global settings, so a bare octo works without re-typing flags. Every field is optional — a missing file, or a missing field, falls back to the built-in default. Manage it with octo config rather than hand-editing where possible.

Key Type Description
endpoints list of endpoint Configured providers, each bundling shared connection params and a list of models
default string Composite id <endpoint-id>::<model> used when nothing else selects one; empty falls back to the first endpoint’s first model
lite string Composite id <endpoint-id>::<model> for cheap internal calls (history compaction’s summarize step and session-title generation); empty means no lite model — those calls run on the session’s primary model
vision_helper string Composite id <endpoint-id>::<model> of a vision-capable model that describes images for text-only models. Empty (the default) leaves images refused as before. The model it names must have vision: true
permission_mode string interactive | strict | auto
coauthor bool Append Co-authored-by to git commits the agent writes (default true)
access_key string Shared secret for Web UI / API auth when exposed beyond loopback
compact_auto_pct int Auto-compaction threshold as % of the context window (default 75); the same threshold is checked both between turns and after each tool-call batch
fallback_context_window int Context window in tokens assumed for a model whose name matches no built-in entry (default 128000). Applies only to the fallback — a model the table knows keeps its own window unless its model entry sets context_window. Values below 1000 are rejected as the unit mistake (32 meaning 32k)
reasoning_effort string Global reasoning intensity: low | medium | high | xhigh | max; empty means off
show_reasoning bool Global default for surfacing the reasoning trace to the Web UI
workspace_dir string Default working dir for new web sessions; empty → ~/Octo, or set a literal path to override
tools tools block Per-tool behavior knobs
browser browser block Chrome connection settings
goal { enabled: bool } Gates /goal and the goal tools (default enabled)
trash trash block File recycle-bin behavior (overwrite backups, retention, size cap)
uploads { retention_days: int } Age-out for received attachment files — web uploads, IM-channel images, and IM document/file temp copies (default 30; negative disables)
memory_backend object Optional external semantic memory backend — see Memory backends
language string UI language preference (en | zh); empty means English
notify bool TUI only: send a desktop notification when a turn completes and is waiting for input. Ghostty / iTerm2 / Kitty forward it to the OS; other terminals fall back to the terminal bell (default true)
terminal_title bool TUI only: set the terminal tab/window title to the session name on startup (OSC 2, honoured by Ghostty, iTerm2, Kitty and most modern terminals; default true)
update_check bool Allow the automatic latest-release lookup against GitHub — the version badge and the desktop tray’s daily poll. This is octo’s only outbound request that isn’t a model call; set false and it makes none (default true). An explicit octo upgrade still reaches out

When the agent edits this file, octo validates it right after the write and warns in the turn if it no longer parses or has a semantic problem (a default pointing at a missing endpoint, a duplicate id or half-filled entry) — a broken edit otherwise degrades silently, since octo keeps using the last config that parsed cleanly.

octo tracks a working directory per surface, not one global cwd:

  • TUI / CLI (octo, octo -c) always run tools in the directory you launched them from — workspace_dir and the per-session override below don’t apply. Whatever directory the shell process inherited is what read_file, terminal, and every other tool resolve relative paths against.

  • Web sessions take theirs from the project they belong to. A loose task — one filed under no project — runs in workspace_dir (empty → ~/Octo, created on first use), and that is not something the session can override: a directory is a project’s property. Choosing one on the new-session landing page is what files the session under the project for that directory, creating it if there isn’t one. PATCH /api/sessions/{id}/working_dir answers 409 and says so.

    Which project a session belongs to is decided when it is created and fixed after that — there is no moving a session between projects (PUT /api/sessions/{id}/group answers 409). To work in a project, start the session there: the “+” on the project’s row, or pick its directory on the landing page. Deleting a project is the only thing that takes its sessions out of it, and they become tasks.

    The reason it works this way: a task that could point itself at a repo ran its tools there while its memory stayed in the shared tier, since project memory is scoped by project membership (see memory). You got a session aimed at your project that remembered nothing about it. Attaching the directory to the project — the thing that already scopes memory — removes the split. Fixing membership at creation is the same argument applied to time: the directory, the memory tier, the project notes in the system prompt, and the hooks root all come from the project, and a move would leave a transcript whose first half ran somewhere else.

  • The server’s own launch directory (where octo serve or the desktop app started) is separate from both of the above — it only seeds skill discovery and the project-level memory root, not any session’s tool cwd.

Each item in endpoints: bundles the connection params shared by a set of models. The two-level shape lets the same model name live under more than one endpoint — say claude-sonnet-5 via both the official Anthropic endpoint and a relay — which the old flat list couldn’t express.

Key Type Description
id string Unique identifier and composite-id prefix; matches ^[a-zA-Z0-9_-]+$, no ::. The wizard names it after the provider (anthropic, openai, custom); rename it freely
name string Optional display name shown in the UI; may differ from id
provider string anthropic | openai | custom
base_url string Endpoint URL; empty uses the vendor default. Required for the custom vendor
api_key string Plaintext fallback used only if the provider’s env var is empty (mode 0600). Optional for the custom vendor — local servers such as Ollama take none, and no auth header is sent
protocol string Wire format for the custom vendor only: anthropic | openai
rpm int Optional: cap on provider calls started against this endpoint per rolling 60-second window. 0/absent = unlimited
max_concurrency int Optional: cap on provider calls in flight against this endpoint at once. 0/absent = unlimited
models list of model The models offered through this endpoint

rpm and max_concurrency exist for endpoints that publish a quota — free tiers typically allow something like 8 requests a minute and one at a time, and answer HTTP 429 past that. Both gates cover every caller sharing the endpoint (the conversation itself, sub-agents, workflow steps, background title generation, the vision helper), which is what makes the cap hold when several run at once; a request over the limit waits for room rather than failing. Set them from the provider’s published numbers. Neither has a UI — hand-edit config.yml; editing the endpoint in the Web UI leaves them untouched.

Two details worth knowing before you tighten them. Retries inside one call don’t spend a second rpm permit, so a call that keeps failing can put up to four requests on the wire against one permit — set rpm slightly under the published quota if the endpoint counts rejected requests. And max_concurrency gates background work too: session titles come from a throwaway call with a five-second budget fired when your message arrives, so at max_concurrency: 1 it queues behind the turn itself and the session falls back to a snippet of your first message. Cap rpm alone to keep model-written titles.

Each item in an endpoint’s models: list is independently selectable. --model <name> selects it — and with it the endpoint’s connection params — while default / lite reference it by composite id <endpoint-id>::<model>. Connection settings (base_url, api_key, protocol) and reasoning (reasoning_effort, show_reasoning) live on the endpoint and top level respectively, not here.

Key Type Description
model string The model id sent to the API (e.g. claude-sonnet-4-6) — also the name --model and the HTTP API use
context_window int Optional context window in tokens for this endpoint’s model deployment. Overrides the built-in model table; values from 1 through 999 are rejected as likely unit mistakes
vision bool Whether tools may hand this model images; a model-level capability, auto-detected for known models

Context-window precedence is: the selected model entry’s context_window, then the built-in model table, then fallback_context_window, then the built-in 128k default. Because the value belongs to an endpoint model entry, two endpoints may expose the same model id with different limits; selecting them by composite id keeps those limits separate.

endpoints:
- id: anthropic
provider: anthropic
models:
- model: claude-sonnet-5
context_window: 32000
vision: true
- id: deepseek
provider: custom
protocol: anthropic
base_url: https://api.deepseek.com/anthropic
models:
- model: deepseek-chat
default: anthropic::claude-sonnet-5
lite: anthropic::claude-sonnet-5
vision_helper: "" # e.g. anthropic::claude-sonnet-5 — lets text-only models "see" images
reasoning_effort: "" # low | medium | high | xhigh | max; empty = off
permission_mode: interactive
coauthor: true
tools:
tool_search:
enabled: auto # auto (default) | on | off
threshold_pct: 10
disabled_skills: []

disabled_skills hides listed skills from the model and the UI without deleting them from disk. See Connect MCP servers for what tool_search does.

browser:
attach_running: true # reuse your logged-in Chrome instead of a throwaway profile
connect_port: 9222 # attach via --remote-debugging-port instead
user_data_dir: ""
exec_path: ""
download_dir: ""

See Automate with browser control.

trash:
overwrite_backup: true # stage a copy before write_file/edit_file overwrites (git-clean files skipped)
retention_days: 14 # age out entries at startup; negative disables
max_size_mb: 10240 # evict oldest past this cap; negative disables

See Sandbox the agent for how the recycle bin fits into the safety model, and octo trash list|restore|rm|empty for recovery from the CLI.

Next: many of these settings have a matching CLI flag for a per-run override — see the CLI reference.