Settings
These settings belong to the Bub application. bub.core.Agent does not read YAML or BUB_*; it receives constructor arguments only. Plugins can register their own sections, with fields defined by each plugin’s documentation. For setup steps, see Configuration.
Precedence
Section titled “Precedence”Settings classes resolve fields in this order, from highest to lowest priority:
| Order | Source | Description |
|---|---|---|
| 1 | Environment variables | BUB_*; read when a settings instance is created |
| 2 | .env in the current directory |
find_dotenv(usecwd=True); does not overwrite existing environment variables |
| 3 | YAML file | ~/.bub/config.yml by default, loaded by configure.load |
| 4 | Field defaults | Defaults declared in code |
--workspace, --project, and --enable-channel are command arguments, not layers in this common settings table. They respectively change the framework workspace, plugin project path, and channel selection for the invocation.
Settings instances are cached within the process. After the first ensure_config(), changing os.environ usually does not affect instances that have already been created; start a new process when configuration must change.
Config file
Section titled “Config file”| Path | Source |
|---|---|
~/.bub/config.yml |
DEFAULT_CONFIG_FILE in src/bub/framework.py |
BubFramework(config_file=Path(...)) |
Constructor argument, resolved to an absolute path |
BUB_HOME changes only bub.home (history, Tapes, the plugin project, code mode files, and similar paths); it does not move the default ~/.bub/config.yml. Pass config_file explicitly to use a different configuration file.
Runtime paths
Section titled “Runtime paths”| Variable | Default | Description |
|---|---|---|
BUB_HOME |
~/.bub |
bub.home; setting it in .env also works |
BUB_PROJECT |
<BUB_HOME>/bub-project |
Project directory for bub install / uninstall / update |
Typer reads BUB_PROJECT. The default project directory is created automatically when missing; an explicitly selected alternative directory should already exist.
Environment variable index
Section titled “Environment variable index”Every YAML key has a corresponding environment variable, which takes precedence. This table is ordered by prefix; names beginning with BUB_ can be set directly in .env.
| Variable | Default | YAML path | Details |
|---|---|---|---|
BUB_HOME |
~/.bub |
— | Runtime paths |
BUB_PROJECT |
<BUB_HOME>/bub-project |
— | Runtime paths |
BUB_MODEL |
openrouter:openrouter/free |
model |
AgentSettings |
BUB_COMMAND_PREFIX |
, |
command_prefix |
Command prefix |
BUB_FALLBACK_MODELS |
null |
fallback_models |
Models and fallback |
BUB_API_KEY |
unset | api_key |
Credential resolution |
BUB_API_BASE |
unset | api_base |
Credential resolution |
BUB_<PROVIDER>_API_KEY |
unset | — | Credential resolution |
BUB_<PROVIDER>_API_BASE |
unset | — | Credential resolution |
BUB_PROVIDERS |
{} |
providers |
Custom providers |
BUB_MAX_STEPS |
sys.maxsize |
max_steps |
AgentSettings |
BUB_MAX_TOKENS |
16384 |
max_tokens |
AgentSettings |
BUB_SUBAGENT_MAX_CONCURRENCY |
4 |
subagent_max_concurrency |
AgentSettings |
BUB_SUBAGENT_TIMEOUT_SECONDS |
null |
subagent_timeout_seconds |
AgentSettings |
BUB_MODEL_TIMEOUT_SECONDS |
null |
model_timeout_seconds |
AgentSettings |
BUB_CLIENT_ARGS |
{} |
client_args |
client_args |
BUB_COMPLETION_ARGS |
{} |
completion_args |
completion_args |
BUB_VERBOSE |
0 |
verbose |
AgentSettings |
BUB_ENABLED_CHANNELS |
all |
enabled_channels |
Channel selection |
BUB_DEBOUNCE_SECONDS |
1.0 |
debounce_seconds |
ChannelSettings |
BUB_MAX_WAIT_SECONDS |
10.0 |
max_wait_seconds |
ChannelSettings |
BUB_ACTIVE_TIME_WINDOW |
60.0 |
active_time_window |
ChannelSettings |
BUB_STREAM_OUTPUT |
false |
stream_output |
ChannelSettings |
BUB_SPILL_THRESHOLD |
4096 |
spill.threshold |
SpillSettings |
BUB_TELEGRAM_TOKEN |
"" |
telegram.token |
TelegramSettings |
BUB_TELEGRAM_ALLOW_USERS |
unset | telegram.allow_users |
TelegramSettings |
BUB_TELEGRAM_ALLOW_CHATS |
unset | telegram.allow_chats |
TelegramSettings |
BUB_TELEGRAM_PROXY |
unset | telegram.proxy |
TelegramSettings |
CODEX_HOME |
~/.codex |
— | Third-party login |
Flat keys such as BUB_MODEL, BUB_COMMAND_PREFIX, and BUB_ENABLED_CHANNELS go at the YAML root; keys under spill: and telegram: go in nested sections. BUB_TELEGRAM_* maps to fields of the same name under telegram:, and BUB_SPILL_* maps to fields under spill:.
AgentSettings
Section titled “AgentSettings”src/bub/builtin/settings.py
@config()class AgentSettings(Settings): model_config = SettingsConfigDict( env_prefix="BUB_", env_parse_none_str="null", extra="ignore", ) model: str = "openrouter:openrouter/free" command_prefix: str = "," fallback_models: list[str] | None = None api_key: str | dict[str, str] | None = None api_base: str | dict[str, str] | None = None providers: dict[str, CustomProvider] = {} max_steps: int = sys.maxsize max_tokens: int = 16384 subagent_max_concurrency: int = 4 subagent_timeout_seconds: int | None = None model_timeout_seconds: int | None = None client_args: dict[str, Any] = {} completion_args: dict[str, Any] = {} verbose: int = 0| Environment variable | Default | YAML | Description |
|---|---|---|---|
BUB_MODEL |
openrouter:openrouter/free |
model |
Must be provider:model |
BUB_COMMAND_PREFIX |
, |
command_prefix |
Non-empty with no whitespace; may contain multiple characters |
BUB_FALLBACK_MODELS |
null |
fallback_models |
JSON array; used only for the configured default model |
BUB_API_KEY |
unset | api_key |
String, or a mapping from providers to keys |
BUB_API_BASE |
unset | api_base |
String, or a mapping from providers to addresses |
BUB_<PROVIDER>_API_KEY |
unset | — | For example, BUB_OPENAI_API_KEY |
BUB_<PROVIDER>_API_BASE |
unset | — | For example, BUB_OPENROUTER_API_BASE |
BUB_PROVIDERS |
{} |
providers |
JSON object defining custom providers |
BUB_MAX_STEPS |
sys.maxsize |
max_steps |
Maximum model Steps per Run; must be a positive integer |
BUB_MAX_TOKENS |
16384 |
max_tokens |
Default output limit; omitted for Codex calls |
BUB_SUBAGENT_MAX_CONCURRENCY |
4 |
subagent_max_concurrency |
Maximum subagents running at once in the process; later ones wait in a queue |
BUB_SUBAGENT_TIMEOUT_SECONDS |
null |
subagent_timeout_seconds |
Default time limit of one subagent run; null means no limit |
BUB_MODEL_TIMEOUT_SECONDS |
null |
model_timeout_seconds |
Seconds for one model call, including streaming |
BUB_CLIENT_ARGS |
{} |
client_args |
JSON object passed to the model client |
BUB_COMPLETION_ARGS |
{} |
completion_args |
JSON object passed to each generation |
BUB_VERBOSE |
0 |
verbose |
0 is INFO, 1 is DEBUG, and 2 is TRACE |
A null value for client_args or completion_args is treated as {}.
Command prefix
Section titled “Command prefix”| Setting | Effect |
|---|---|
, |
,help and ,tape.info are commands |
! |
!help is a command, while ,help is ordinary text |
/ |
In Telegram, enter a command with /bub /help |
CLI quit, exit, thinking, completion, and shell mode share this prefix. Telegram also handles its own slash commands; these are not the same parser.
Credential resolution
Section titled “Credential resolution”Model calls use the first non-empty value in this order:
| Order | Source |
|---|---|
| 1 | providers.<name>.api_key / .api_base |
| 2 | Settings fields api_key / api_base: the provider’s key in the mapping, or the field value when it is a string |
| 3 | <PROVIDER>_API_KEY / _API_BASE without the BUB_ prefix, such as OPENAI_API_KEY |
BUB_<PROVIDER>_API_KEY / _API_BASE is not read directly in step 3 above. At startup, Bub scans ^BUB_(.+)_(API_KEY|API_BASE)$ and writes matches into the settings field mappings, with lower priority than BUB_API_KEY / BUB_API_BASE in the environment and YAML. Names matching registered Republic providers use their canonical names; others remain lowercase. The client also receives env_prefix=BUB_<PROVIDER> so Republic can read again if the value supplied by Bub is empty.
If YAML or BUB_API_KEY / BUB_API_BASE contains a single string, it applies to every provider and prevents mapping and step-3 fallback. Use a mapping or BUB_<PROVIDER>_… to configure providers separately.
Models and fallback
Section titled “Models and fallback”Common prefixes for BUB_MODEL:
| Prefix | Purpose |
|---|---|
openai: |
OpenAI or an OpenAI-compatible endpoint |
codex: |
ChatGPT plan login |
openrouter: |
OpenRouter |
anthropic: / google: / azure-openai: |
Native protocols for each provider |
ollama: / magpie: |
Local services |
See Republic providers for the complete list.
fallback_models applies only when the current model identifier matches the configured model; it does not apply when model= is passed explicitly or the session switches to another model. Fallback attempts to open each request in sequence and stops retrying once stream consumption has begun.
client_args
Section titled “client_args”| Key | Behavior |
|---|---|
headers |
Merged with the default User-Agent: bub/<version>; explicit values win |
api_format |
Usually set to chat for an OpenAI-compatible Chat Completions endpoint |
env_prefix |
Defaults to BUB_<PROVIDER>, allowing the client to read provider-specific environment variables |
timeout / max_retries |
Passed to the underlying client; the overall call timeout still comes from model_timeout_seconds |
api_key / api_base |
Supplied by Bub and not read from client_args |
An injected HTTP client must be an httpx2.AsyncClient and remains caller-owned.
completion_args
Section titled “completion_args”| Key | Rule |
|---|---|
max_tokens |
Uses BUB_MAX_TOKENS when not specified for a call; removed for Codex |
reasoning_effort |
Session or per-Turn values override this setting |
tools |
Supplied by the Agent for the current Step |
extra_body |
Passed to the provider unchanged; Anthropic adds ephemeral cache control by default |
Call-level options override generation options of the same name in completion_args before provider encoding. completion_args.max_tokens is superseded by BUB_MAX_TOKENS or call-level max_tokens; do not treat it as an independent limit. Providers may rewrite some fields. If a parameter must apply consistently, prefer an explicit Bub setting over relying on extra_body.
Custom providers
Section titled “Custom providers”model: proxy:gpt-5.5fallback_models: - openai:gpt-5.5 - relay:claude-sonnet-5providers: proxy: type: openai api_base: https://llm-proxy.example.com/v1 api_key: sk-proxy-... relay: type: anthropic api_base: https://relay.example.com api_key: sk-relay-...| Field | Required | Description |
|---|---|---|
type |
Yes | Republic provider to emulate; configuration validation fails if it is not registered |
api_base |
No | Falls back to the general api_base or BUB_<NAME>_API_BASE when omitted |
api_key |
No | Falls back to the general api_key or BUB_<NAME>_API_KEY when omitted |
proxy:gpt-5.5 and openai:gpt-5.5 use the same protocol but can use different addresses and keys.
ChannelSettings
Section titled “ChannelSettings”src/bub/channels/manager.py
@config()class ChannelSettings(Settings): model_config = SettingsConfigDict( env_prefix="BUB_", extra="ignore", env_file=".env", ) enabled_channels: str = "all" debounce_seconds: float = 1.0 max_wait_seconds: float = 10.0 active_time_window: float = 60.0 stream_output: bool = False| Environment variable | Default | YAML | Description |
|---|---|---|---|
BUB_ENABLED_CHANNELS |
all |
enabled_channels |
Comma-separated names, all, or !name |
BUB_DEBOUNCE_SECONDS |
1.0 |
debounce_seconds |
Batch wait reset after each active message |
BUB_MAX_WAIT_SECONDS |
10.0 |
max_wait_seconds |
Used only while waiting for inactive follow-up messages; not a total batch cap |
BUB_ACTIVE_TIME_WINDOW |
60.0 |
active_time_window |
Seconds after the last active message during which inactive messages may join |
BUB_STREAM_OUTPUT |
false |
stream_output |
Whether the gateway pushes streaming events to channels; bub chat forces true |
The name BUB_MAX_WAIT_SECONDS can be misleading: it is used only when an inactive follow-up message starts a waiting batch. Active-message batches are still timed by BUB_DEBOUNCE_SECONDS; continued active messages do not force the batch to end after 10 seconds. Only channels with needs_debounce=True, such as Telegram, use this buffering.
Channel selection
Section titled “Channel selection”enabled_channels |
Result |
|---|---|
Contains all |
Every enabled non-Interface channel, minus !name exclusions |
| Explicit ordinary channels | Listed channels that are enabled and not excluded |
| List contains a non-Lifecycle channel | Also attaches every enabled Lifecycle channel |
| Lifecycle channels only | Starts only those Lifecycle channels |
Only !name |
No included channels; startup fails |
bub gateway --enable-channel overrides the environment selection and can be repeated. An Interface must be selected explicitly, such as cli or acp-server. See CLI › Channel selection for more examples.
TelegramSettings
Section titled “TelegramSettings”src/bub/channels/telegram.py
@config(name="telegram")class TelegramSettings(Settings): model_config = SettingsConfigDict( env_prefix="BUB_TELEGRAM_", extra="ignore", env_file=".env", ) token: str = "" allow_users: str | None = None allow_chats: str | None = None proxy: str | None = None| Environment variable | Default | YAML | Description |
|---|---|---|---|
BUB_TELEGRAM_TOKEN |
"" |
token |
The channel is enabled only when non-empty |
BUB_TELEGRAM_ALLOW_USERS |
unset | allow_users |
Comma-separated user IDs or usernames; empty means unrestricted |
BUB_TELEGRAM_ALLOW_CHATS |
unset | allow_chats |
Comma-separated chat IDs; empty means unrestricted |
BUB_TELEGRAM_PROXY |
unset | proxy |
http://user:<password>@host:port or socks5://host:port |
| Match | Ordinary message | /start |
|---|---|---|
Chat ID is not in allow_chats |
Silently ignored | Replies with fixed rejection text |
Neither user ID nor username is in allow_users |
Replies Access denied. |
Does not check the user |
| Both are configured | Both must match | Checks only the chat |
See Gateway › Telegram for deployment and group-chat behavior.
SpillSettings
Section titled “SpillSettings”src/bub/builtin/spill.py
@config(name="spill")class SpillSettings(Settings): model_config = SettingsConfigDict( env_prefix="BUB_SPILL_", extra="ignore", env_file=".env", ) threshold: int = 4096| Environment variable | Default | YAML | Description |
|---|---|---|---|
BUB_SPILL_THRESHOLD |
4096 |
threshold |
Estimated token limit at four characters per token; 0 disables new spills |
Rendered results of successful tool calls above the threshold are written to the spill sidecar, and the model receives a preview and read handle. Negative values violate ge=0 and fail configuration validation. Preview length, chunk size, and similar values are implementation constants, not settings.
| Situation | Behavior |
|---|---|
Current tool set has no spill.read |
Does not create a new spill; returns the result unchanged |
Result is already spill.read output |
Does not spill it again |
Threshold is 0 |
Stops creating new spills; existing sidecar results remain readable |
Third-party login variables
Section titled “Third-party login variables”| Variable | Default | Read by | Description |
|---|---|---|---|
CODEX_HOME |
~/.codex |
bub login codex |
Directory containing auth.json; can be overridden by --codex-home |
These variables belong to third-party CLIs and are not AgentSettings fields.
Session settings
Section titled “Session settings”The following are neither environment variables nor YAML fields. Commands write them to the current session Tape, and they are restored for the next Turn and after a restart:
| Key | Switch command (default prefix ,) |
Purpose |
|---|---|---|
model |
,model openai:gpt-5.5 |
Overrides the session’s default model; configured fallback_models does not apply after switching to another model |
reasoning_effort |
,reasoning_effort high |
Overrides completion_args.reasoning_effort |
code_mode |
,code_mode enable=true |
Enables or disables code mode for this session |
The actual prefix follows command_prefix. These values take effect from the next Turn and persist in the session Tape.
Plugin settings
Section titled “Plugin settings”from pydantic_settings import SettingsConfigDict
from bub import Settings, config
@config(name="orders")class OrderSettings(Settings): model_config = SettingsConfigDict( env_prefix="BUB_ORDERS_", extra="ignore", ) timeout: int = 30| Item | Description |
|---|---|
@config(name="x") |
Registers under CONFIG_MAP["x"]; a section can have multiple classes |
| YAML | Reads from the top-level x: section |
@config() |
Registers at the root and reads top-level fields |
| Environment variables | Determined by the subclass’s env_prefix |
| Validation | configure.validate() instantiates every registered class with its YAML section data |
| Reading | ensure_config(cls) returns the process-cached instance |
| Function | Purpose |
|---|---|
configure.load(path) |
Reads YAML and replaces the current configuration data |
configure.get_config_data() |
Returns a deep copy; excludes defaults and environment overrides |
configure.merge(base, *updates) |
Recursively merges, with later values taking precedence |
configure.save(path, data) |
Validates and writes to disk, creating parent directories |
configure.get_value(path, default) |
Reads a registered setting by dotted path, then falls back to the root |
Plugin settings usually support YAML, environment variables, and .env. If plugin documentation lists only one variable, do not assume it also has a synonymous BUB_ name. See Storage and Observability for common plugin variables. Tracing variables such as OTEL_* and LOGFIRE_TOKEN are also documented on the observability page and do not belong to the Settings classes on this page.
Complete example
Section titled “Complete example”The following ~/.bub/config.yml lists every currently writable setting. Every key except model can be removed to use its default.
model: openrouter:openrouter/freecommand_prefix: ","fallback_models: - openai:gpt-5.5api_key: openrouter: sk-or-...api_base: openrouter: https://openrouter.ai/api/v1providers: proxy: type: openai api_base: https://llm-proxy.example.com/v1 api_key: sk-proxy-...max_steps: 100max_tokens: 16384model_timeout_seconds: 120client_args: timeout: 60 max_retries: 2completion_args: reasoning_effort: mediumverbose: 0
enabled_channels: alldebounce_seconds: 1.0max_wait_seconds: 10.0active_time_window: 60.0stream_output: false
spill: threshold: 4096
telegram: token: "123456:abcdef..." allow_users: "123456789,your_username" allow_chats: "-1001234567890" proxy: socks5://127.0.0.1:7890Settings that do not belong in this file:
| Setting | Location | Environment variable |
|---|---|---|
bub.home |
Runtime path; not written to YAML | BUB_HOME |
| Plugin project directory | Command argument --project |
BUB_PROJECT |
| Codex login directory | Third-party CLI | CODEX_HOME |
Session-level model / reasoning_effort / code_mode |
Written to the session Tape and switched by commands | — |