Skip to content

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.

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.

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.

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.

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:.

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 {}.

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.

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.

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.

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.

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.

model: proxy:gpt-5.5
fallback_models:
- openai:gpt-5.5
- relay:claude-sonnet-5
providers:
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.

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.

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.

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.

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
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.

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.

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.

The following ~/.bub/config.yml lists every currently writable setting. Every key except model can be removed to use its default.

~/.bub/config.yml
model: openrouter:openrouter/free
command_prefix: ","
fallback_models:
- openai:gpt-5.5
api_key:
openrouter: sk-or-...
api_base:
openrouter: https://openrouter.ai/api/v1
providers:
proxy:
type: openai
api_base: https://llm-proxy.example.com/v1
api_key: sk-proxy-...
max_steps: 100
max_tokens: 16384
model_timeout_seconds: 120
client_args:
timeout: 60
max_retries: 2
completion_args:
reasoning_effort: medium
verbose: 0
enabled_channels: all
debounce_seconds: 1.0
max_wait_seconds: 10.0
active_time_window: 60.0
stream_output: false
spill:
threshold: 4096
telegram:
token: "123456:abcdef..."
allow_users: "123456789,your_username"
allow_chats: "-1001234567890"
proxy: socks5://127.0.0.1:7890

Settings 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 —