Skip to content

Reference: config.yaml ​

Every top-level block, what it's for, and what happens when it's absent. Copy config/config.example.yaml to config/config.yaml to start — every block below except organization, providers and defaults is optional and off by default. Which blocks (and which environment variables) turn a capability on, what appears when one does, and what it costs to run is the matrix in Turn features on and off.

BlockPurposeWhen absent
organizationThe GitHub organization (or user) this installation serves — the account its GitHub App is installed on. Names the shared memory scope (org:<organization>) and the About block every model run carriesrequired — nothing in the code assumes an organization
providersModel providers (anthropic, openai-compatible + a baseUrl) and which env var holds each one's keyrequired — nothing to route to
defaultsagent used with no other signal; models/efforts per agent (efforts.general also controls the operator turn on models.general); verbosity — how much the bot says about its own doing, quiet (default) | verbose | debug (routing-and-config item 28); an optional boundary capping every run's budget, identity and machine classrequired — the built-in agent floor is the last resort, not a real default; no boundary caps nothing
channels / usersStatic per-scope defaults, keyed by platform-namespaced id (slack:C…, slack:U…): agent, model/models, effort/efforts, verbosity, harness (which harness drives each preset's runs requested in that scope — pi or opencode, over the top-level harness block; a user's word moves that person's runs alone — see Put a preset on OpenCode), instructions, mcpServers; a scope's boundary intersects with the others' (it can only tighten) — see Restrict who can do whatthat scope has no static defaults; runtime config set still applies
grantsWhat each actor holds — actions (commands, agent:run:<name>, dispatch), channels whose runs it may read, repo memory it owns (repos) and independent code access (codeRepos, omitted inherits repos) — keyed by platform-namespaced id, or <ns>:* for everyone authenticated on a surface (access:* is the org)nobody is an admin; Slack users hold the open chat commands and every unrestricted agent, browser sign-ins every read and unrestricted agents, both human baselines open code repos, credentials nothing — see reference: authorization
restrictAgents and repos closed to everyone not granted themnothing restricted — human baselines admit unrestricted agents and code repos; credentials and jobs still need explicit grants — see reference: authorization
executionWhere tool calls actually run: local (bot host), e2b / cloudflare (per-thread sandbox), plus an optional resident block for always-warm per-repo environmentslocal — fine for dev, not for untrusted users reaching coding
workspaceDirWhere local-execution workspaces live on disk./workspaces
memoryMemory that outlives a run: read/write to a durable store, per-scope budget; model/effort pick the reflection call's model and its effort level (card-applied; defaults: the run's own model, the model's own effort)off — model input is byte-identical to memory disabled
selfImprovementThe friction → GitHub-issue pipeline over run history: target repo, label, thresholdsfriction propose refuses (no target repo); friction report still works over runHistory
schedulesWhere the /runs Scheduled panel reads cron firing history fromthe panel lists schedules with no firing history
shipagent:ship pipeline caps: maxRounds, and maxMinutes — the ship preset's declared budget, which a scope's boundary or a budget: directive clips per runsane built-in defaults (3 rounds, 240 min)
spawnThe fan-out cap a spawning run meets: maxChildren live children per run (at least 1) — an agent:conductor spawn past it is refused until a child finishes3
routingThe one door's operator mode: on runs its typed decision, shadow records the same turn without executing it, off is the rollback lever. The retired readers' classifier has no model, effort, auto or answer keyon
intakeThe thread-reply gate's defaults layer (record 0058; routing-and-config item 27): threadReplies is how an unmentioned reply in a thread the bot is part of is treated — mention (only a mention is answered), classify (one cheap model turn decides whether the bot was addressed; the code's default) or always (today's answer-every-reply path, byte for byte) — also settable per channel and per user (intake.threadReplies on their scopes); model is the verdict's <provider>/<model> (default defaults.models.general); effort is that model's effort level, card-applied, default the model's own. A model whose operator card declares it can answer neither a forced tool call nor the text contract is refused at load when the default mode is classify. Nothing calls the gate yet — the Slack wiring lands in a later releasethe default mode is classify once the gate is wired; until then behavior is unchanged
harnessWhich harness each preset's runs are driven by, deployment-wide: a mapping of preset to pi or opencode, the names the harness objects declare — see harness.md item 8 and Put a preset on OpenCode. This block is the defaults layer of the per-scope harness setting: a channel's or a user's harness (static under channels/users, or config set channel|me --harness.<preset> …) overrides it for the runs in that scope, user over channel over here; defaults.harness is refused pointing at this block. A run keeps the harness it started on, so changing a word moves the next run, never one in flight; any other word fails the load naming the twoevery preset on pi — nothing defaults to OpenCode
piWhat the harness writes into pi's per-run settings for every run on pi: compaction.reserveTokens and compaction.keepRecentTokens, pi's own thresholds in tokens — a reserve near the model's window makes a short run compact, which a receipt or a test of the compaction path wants — see harness-pi.md item 4pi's own defaults (16384 reserved, 20000 kept); the settings file is byte-identical to before the block existed
opencodeWhat the harness writes into OpenCode's per-run configuration for every run on OpenCode: compaction.buffer and compaction.keepTokens, OpenCode's own thresholds in tokens — see harness.md item 8OpenCode's own defaults; the configuration file names no compaction
costs/costs dashboard: Cloudflare account + token, optional Anthropic admin key, named groups of Workers/containers/DOs to price; snapshot.everyHours (default 24, 1–168) is how often the billing sources are read into the snapshot the page serves; snapshot.alertChannel (optional, slack:C…) is told after three failed takes in a row and when one lands again; prices (optional) maps <provider>/<model> → { input, output, cacheRead, cacheWrite } in USD per million tokens, over the built-in Anthropic list — the table that prices a run's tokens on the costs page and on its own page (costs.md item 4b)/costs refuses to start — nothing to report on
delivery/delivery page and delivery report: the repositories the page serves, the identities the indicators judge by (the review agent's login, agent logins, agent co-author names) and how often each repository's snapshot of GitHub's facts is refreshed (snapshot.everyMinutes, default 60); needs a GitHub credential; the snapshot lives on the state Worker any *.worker block names/delivery answers 503 naming the block; delivery report --repo owner/name still works
dashboardDashboard authentication: auth: access (the Cloudflare Access JWT, ACCESS_TEAM_DOMAIN + ACCESS_AUD), token (Authorization: Bearer from the env var token.env names, default DASHBOARD_TOKEN, resolving to the one actor token.actor — access:<name>) or none (loopback callers on a localhost deployment only; a public PUBLIC_BASE_URL refuses to start) — see dashboard routesaccess when both ACCESS_* are set, else none — a deployed installation is unchanged; a localhost one without Access now admits its loopback callers
slack.catchUpReconnect catch-up window after a deploy/drainon, 30-minute window
slack.relayAppsThe Slack apps (by bot_id, B…) whose Sent by Claude in <#C…> on behalf of <@U…> relay footer names the person a run is for and billed to — see slack-channel.md item 13; any other app's post is the app's own request, footer or notno footer is honoured — every app-posted request is the app's own (slack:bot:B…)
runtimeOverridesWhere chat-set overrides (config set, config instructions) persistdata/overrides.json on host disk — ephemeral on Cloudflare Containers
runHistoryDurable run records: retention window, byte/count caps, which store backs itoff — runs are live-only, evicted ~60s after finish
tracingSpan log verbosity: log: roots (one JSON line per request) or slow (plus every span of 1 s or more)roots

Two blocks that matter most for "does a restart lose anything" ​

runtimeOverrides.worker and runHistory.worker (and memory.worker, schedules.worker) all point at the same state Worker (deploy/cloudflare-memory/) with the same bearer. Set them, and a bot restart loses nothing durable: overrides, memory, and run history (the friction ledger reads it) all live on Durable Objects, not the container's disk. Skip them (the default on a fresh clone), and all three fall back to a host-disk file or in-memory store — fine for local dev, silently ephemeral on a platform with no persistent disk. See explanation: Worker topology.

Full annotated example ​

config/config.example.yaml in the repo root is the living reference — every key, every default, every caveat, as a comment next to the setting it documents. This page is the map; that file is the terrain.

Optional account verification ​

dashboard.identityLink supplies slackClientId, slackTeamId and slackClientSecretEnv to the configured account-verification handler. All three must be nonblank; the secret remains in the named environment variable. The browser host registers the handler. Account changes require human Cloudflare Access authentication, the configured organization and directory; an explicit token or none strategy is refused. Register /settings/identities/callback with the Slack app before use. Missing prerequisites remain unavailable, and configuration alone does not prove a live account operation.