Skip to main content

config.yaml reference

~/.ethos/config.yaml is a flat key: value file. Dotted keys (e.g. retention.messages, providers.0.provider) are how nested structures appear on disk — there is no indentation-based nesting. The parser ignores quotes around values.

Source

The full field set lives in the EthosConfig interface in apps/ethos/src/config.ts. parseConfigYaml reads values; writeConfig writes them. Fields marked @internal are managed by the runtime (e.g. activeContext by ethos set) — do not hand-edit them.

Minimal example

provider: anthropic
model: claude-opus-4-7
apiKey: sk-ant-...
personality: researcher

This is what ethos setup writes for a default Anthropic install. Everything below is optional.

provider

Type: string · Default: anthropic · Required (effectively)

LLM provider id. Resolved at wiring time against the registered provider list. Built-in values: anthropic, openrouter, openai, ollama, gemini. Custom values may resolve through plugins.

provider: anthropic

model

Type: string · Default: claude-opus-4-7 · Required (effectively)

Model id to pass to the provider. Format depends on the provider — Anthropic uses raw model names, OpenRouter uses vendor/model.

model: claude-opus-4-7

apiKey

Type: string · Default: empty · Required

Primary provider API key. For multi-key rotation, leave this set to the most-trusted key and add fallbacks via ethos keys add (which writes ~/.ethos/keys.json). The ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY env vars override this at wiring time when set.

apiKey: sk-ant-...

personality

Type: string · Default: researcher · Required

Id of the default personality. Built-ins: researcher, engineer, reviewer. System: personality-architect, team-architect. User personalities live under ~/.ethos/personalities/<id>/.

personality: engineer

memory

Type: markdown | vector · Default: unset (treated as markdown)

Memory backend. markdown reads and writes ~/.ethos/MEMORY.md and ~/.ethos/USER.md. vector enables the SQLite + embeddings store at ~/.ethos/memory.db.

memory: vector

Notes:

  • Switching backends mid-stream does not migrate data — export from one, then import into the other.
  • Vector mode requires an embeddings-capable provider key.

baseUrl

Type: string · Default: provider default

Override the provider's API endpoint. Required for OpenAI-compatible providers (OpenRouter, Ollama, local proxies).

baseUrl: https://openrouter.ai/api/v1

modelRouting.<personality>

Type: string · Default: falls back to top-level model

Per-personality model override. The key is a personality id; the value is a model string for that personality's provider.

modelRouting.researcher: claude-opus-4-7
modelRouting.engineer: moonshotai/kimi-k2.6

providers.<i>.*

Provider fallback chain. When two or more entries are present, the runtime wraps them in a ChainedProvider with cooldown-based failover. Index 0 is primary; higher indices fall back in order. When only one entry is set, the top-level provider / apiKey / model fields are used.

FieldTypeDescription
providers.<i>.providerstringProvider id for entry <i>.
providers.<i>.apiKeystringAPI key for entry <i>.
providers.<i>.modelstringOptional model override for entry <i>.
providers.<i>.baseUrlstringOptional endpoint override for entry <i>.
providers.0.provider: anthropic
providers.0.apiKey: sk-ant-...
providers.0.model: claude-opus-4-7
providers.1.provider: openrouter
providers.1.apiKey: sk-or-...
providers.1.model: anthropic/claude-opus-4-7

telegram.bots.*

Multi-bot list shape. When set, the gateway creates one TelegramAdapter and one AgentLoop per entry. telegram.bots takes precedence over the legacy telegramToken scalar when both are present.

FieldTypeDefaultDescription
telegram.bots.<i>.tokenstringBot token from BotFather. Required per entry.
telegram.bots.<i>.idstringsha256(token)[:24]Stable, human-readable bot key. Used in session lane names (telegram:<botKey>:<chatId>), log output, and the internal Map<botKey, AgentLoop>. Set once; do not change after the bot goes live.
telegram.bots.<i>.bind.typepersonality | teamRequired. personality routes to a named personality. team routes to the team's coordinator personality and auto-starts the team supervisor.
telegram.bots.<i>.bind.namestringRequired. Personality id (for personality) or team name (for team).
telegram.bots.<i>.bind.allowSlashSwitchbooleanfalseAllow per-chat /personality switching. Disabled by default for identity-bound bots.
telegram.bots.0.token: "123456:ABCdefGhIJklmNopQRstuVwxYZ"
telegram.bots.0.id: researcher-bot
telegram.bots.0.bind.type: personality
telegram.bots.0.bind.name: researcher

telegram.bots.1.token: "654321:XYZabcDeFgHijKlMnOpqRsTuV"
telegram.bots.1.id: coder-bot
telegram.bots.1.bind.type: personality
telegram.bots.1.bind.name: engineer

Notes:

telegramToken

Type: string · Default: unset · Deprecated (use telegram.bots instead)

Legacy scalar shape — wires one bot bound to the default personality. Still supported; creates a single-element entry in the gateway's internal bot list. When both telegramToken and telegram.bots are set, telegram.bots takes precedence and this field is ignored with a deprecation warning.

telegramToken: 123456:ABC-DEF...

slack.apps.*

Multi-app list shape for Slack. Each entry creates one Slack adapter bound to a personality or team. Supersedes the legacy scalar fields slackBotToken, slackAppToken, and slackSigningSecret when set.

FieldTypeDefaultDescription
slack.apps.<i>.botTokenstringxoxb- bot token. Required per entry.
slack.apps.<i>.appTokenstringxapp- app-level token for Socket Mode. Required per entry.
slack.apps.<i>.signingSecretstringSigning secret for inbound webhook verification. Required per entry.
slack.apps.<i>.idstringsha256(botToken)[:24]Stable, human-readable app key. Used in session lane names and log output. Set once; do not change after the app goes live.
slack.apps.<i>.bind.typepersonality | teamRequired. Same semantics as the Telegram equivalent.
slack.apps.<i>.bind.namestringRequired. Personality id or team name.
slack.apps.<i>.bind.allowSlashSwitchbooleanfalseAllow per-channel /personality switching.
slack.apps.0.botToken: "xoxb-..."
slack.apps.0.appToken: "xapp-..."
slack.apps.0.signingSecret: "abc123..."
slack.apps.0.id: eng-slack
slack.apps.0.bind.type: team
slack.apps.0.bind.name: eng

teams.*

Per-team runtime knobs. These apply to teams that the gateway auto-starts via bind.type: team in telegram.bots or slack.apps.

FieldTypeDefaultDescription
teams.<name>.autoStopbooleantrueWhen true, the gateway stops the team supervisor when the gateway shuts down. Set to false to leave the supervisor running across gateway restarts.
teams.eng.autoStop: false

Notes:

  • <name> must match the team manifest filename stem at ~/.ethos/teams/<name>.yaml.
  • With autoStop: false, stop the supervisor manually with ethos team stop <name>.
  • See Connect a Telegram bot to a team for a usage example.

discordToken

Type: string · Default: unset

Bot token for the Discord gateway.

slackBotToken

Type: string · Default: unset

xoxb- bot token for the Slack gateway. Required together with slackAppToken and slackSigningSecret for Slack to bind.

slackBotToken: xoxb-...
slackAppToken: xapp-...
slackSigningSecret: ...

slackAppToken

Type: string · Default: unset

xapp- app-level token for Slack Socket Mode. See slackBotToken for the example.

slackSigningSecret

Type: string · Default: unset

Slack request signing secret. Verifies inbound webhooks when running Slack in HTTP mode.

emailImapHost

Type: string · Default: unset

IMAP server hostname for the email gateway. The email block requires all six of emailImapHost, emailImapPort, emailUser, emailPassword, emailSmtpHost, emailSmtpPort to bind.

emailImapHost: imap.gmail.com
emailImapPort: 993
emailUser: you@example.com
emailPassword: ...
emailSmtpHost: smtp.gmail.com
emailSmtpPort: 587

emailImapPort

Type: integer · Default: unset

IMAP server port. Conventional values: 993 (TLS), 143 (STARTTLS).

emailUser

Type: string · Default: unset

Mailbox username — typically the full email address.

emailPassword

Type: string · Default: unset

Mailbox password. Use an app-specific password where the provider supports it (Gmail, Fastmail).

emailSmtpHost

Type: string · Default: unset

SMTP server hostname for outbound mail.

emailSmtpPort

Type: integer · Default: unset

SMTP server port. Conventional values: 587 (STARTTLS), 465 (TLS).

verbose

Type: boolean · Default: false

Print a per-turn timing summary (LLM time, TTFT, tool wall-clock, tokens, cost) after every chat response.

verbose: true

Notes:

  • Toggle within a session with /verbose — that override is session-local and never written here.

skin

Type: string · Default: engine default

Named skin override. Built-in values: default, mono, paper. Applies across the TUI and the web ConfigProvider. This is the only place a skin is set — a personality is an identity, not a theme, so personalities carry no skin field.

skin: mono

retention.*

Per-category TTLs for the observability store. Values accept duration strings — 30d, 12h, forever. Unset fields fall back to the runtime defaults shown below.

FieldDefaultDescription
retention.messages365dConversation message history.
retention.traces90dTurn traces.
retention.spans90dTool / LLM spans inside traces.
retention.blobs7dLarge response payloads stored out-of-band.
retention.archive730dArchive partitions.
retention.events.error90dError events from errors.jsonl.
retention.events.audit365dAudit events (key rotation, personality writes, approvals).
retention.events.channel365dChannel-adapter events (pairing, dedup).
retention.events.installforeverInstall / migration events. Never deleted by default.
retention.messages: 365d
retention.traces: 90d
retention.events.error: 90d
retention.events.install: forever

personalities.<id>.retention.*

Per-personality retention overrides. Same sub-fields as the top-level retention.* block; values apply only to data tagged with the matching personality id.

personalities.engineer-paired.retention.messages: 730d
personalities.engineer-paired.retention.traces: 180d

Notes:

  • Only the retention sub-block is parsed under personalities.<id>.*. Other top-level keys cannot be overridden per personality from this file — set them in the personality's own config.yaml.

logs.rotation

Type: object · Default: { maxBytes: 10485760, maxFiles: 5, enabled: true }

Controls rotation of ~/.ethos/logs/errors.jsonl. When the file exceeds maxBytes, it is renamed to errors.jsonl.1 and older backups shift up to maxFiles.

KeyTypeDefaultDescription
logs.rotation.maxBytesinteger10485760 (10 MiB)Maximum file size before rotation. Must be a positive integer.
logs.rotation.maxFilesinteger5Maximum number of rotated backup files. Must be a positive integer.
logs.rotation.enabledbooleantrueSet to false to disable rotation entirely.
logs.rotation.maxBytes: 20971520
logs.rotation.maxFiles: 10
logs.rotation.enabled: true

evolver.cron_enabled

Type: boolean · Default: false

When true, registers an in-process cron job that runs ethos evolve run --quiet on the schedule defined by evolver.schedule. The cron job executes inside the chat process — no separate daemon.

evolver.cron_enabled: true

evolver.schedule

Type: string (5-field cron expression) · Default: "0 3 * * *"

Controls when the evolve cron fires. Only meaningful when evolver.cron_enabled is true.

evolver.schedule: 0 3 * * *

Notes:

  • Skill evolution config that is per-personality (e.g. skill_evolution.enabled, skill_evolution.min_tool_calls) lives in the personality config.yaml, not here. The evolver cron keys above control the global schedule; the personality keys control which personalities participate and when.

activeContext

Type: managed · Required: no

Managed by ethos set personality <id> / ethos set team <name>. The runtime writes two dotted keys: activeContext.type (personality | team) and activeContext.name (id or team name). Hand-editing is not supported — values are interpreted only when both keys are present and type is recognised.

File location and permissions

~/.ethos/config.yaml is written by ethos setup and ethos personality set. The companion ~/.ethos/keys.json (chmod 600) holds the rotation pool — manage it through ethos keys, not by hand.

The directory can be relocated with the ETHOS_DIR env var.

See also