Settings
This page lists every BUB_* environment variable read by Bub, the YAML key under ~/.bub/config.yml that maps to it, and the pydantic-settings class that defines it. For deployment recipes see Operate › Configure.
Precedence
Section titled “Precedence”Bub resolves a setting value in this order, highest priority first:
- CLI flag (e.g.
--workspace,--project,--enable-channel). - Environment variable (
BUB_*). .envvalues loaded during CLI startup or by settings classes that declareenv_file=".env".~/.bub/config.ymlentry, loaded bybub.configure.load.- Field default declared on the
Settingssubclass.
Verified via Settings.settings_customise_sources in src/bub/configure.py, which returns (env_settings, dotenv_settings, init_settings, file_secret_settings) — env beats .env, and both beat the dict produced from YAML when ensure_config(...) calls model_validate.
Config file location
Section titled “Config file location”| Path | Source |
|---|---|
~/.bub/config.yml |
DEFAULT_CONFIG_FILE in src/bub/framework.py. |
Override via BubFramework(config_file=...) |
Constructor argument. |
~/.bub/ is also the default value of bub.home, controlled by the BUB_HOME environment variable (src/bub/__init__.py). BUB_HOME affects bub.home consumers such as history, tapes, and the managed plugin project; the default config file path remains ~/.bub/config.yml unless an embedding application passes BubFramework(config_file=...).
Framework — runtime paths
Section titled “Framework — runtime paths”| Env var | Default | YAML key | Read by | Description |
|---|---|---|---|---|
BUB_HOME |
~/.bub |
— | bub.home (bub/__init__.py) |
Root directory for history, tape store, and the managed plugin project. Does not move the default config file. |
BUB_PROJECT |
BUB_HOME/bub-project, or ~/.bub/bub-project when BUB_HOME is unset |
— | --project option in bub install / uninstall / update |
Plugin project directory; created on first install via uv init. |
Agent — AgentSettings
Section titled “Agent — AgentSettings”Defined in src/bub/builtin/settings.py:
class AgentSettings(Settings):
model_config = SettingsConfigDict(env_prefix="BUB_", env_parse_none_str="null", extra="ignore")
model: str = DEFAULT_MODEL # "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] = Field(default_factory=dict)
max_steps: int = Field(default=sys.maxsize, gt=0)
max_tokens: int = DEFAULT_MAX_TOKENS # 16384
model_timeout_seconds: int | None = None
client_args: dict[str, Any] = Field(default_factory=dict)
completion_args: dict[str, Any] = Field(default_factory=dict)
verbose: int = Field(default=0, ge=0, le=2)
Loaded under the YAML root section.
| Env var | Default | YAML key | Description |
|---|---|---|---|
BUB_MODEL |
openrouter:openrouter/free |
model |
Default model identifier (provider:model_name). |
BUB_COMMAND_PREFIX |
, |
command_prefix |
Command prefix for the builtin agent and channels. Must be non-empty and contain no whitespace; multi-character prefixes are supported. |
BUB_FALLBACK_MODELS |
null |
fallback_models |
Optional list of fallback model identifiers. |
BUB_API_KEY |
unset | api_key |
Default API key. May also be a JSON object mapping provider → key. |
BUB_API_BASE |
unset | api_base |
Default API base URL or per-provider mapping. |
BUB_<PROVIDER>_API_KEY |
unset | — | Provider-scoped API key, e.g. BUB_OPENAI_API_KEY. |
BUB_<PROVIDER>_API_BASE |
unset | — | Provider-scoped API base URL, e.g. BUB_OPENROUTER_API_BASE. |
BUB_PROVIDERS |
{} |
providers |
Named endpoints, each speaking one Republic provider’s API (type) with its own api_base and api_key. See Custom providers. |
BUB_MAX_STEPS |
unlimited | max_steps |
Maximum agent loop iterations per turn. Must be a positive integer when set. |
BUB_MAX_TOKENS |
16384 |
max_tokens |
Maximum tokens per model call; omitted for Codex. |
BUB_MODEL_TIMEOUT_SECONDS |
null |
model_timeout_seconds |
Per-call timeout in seconds. |
BUB_CLIENT_ARGS |
{} |
client_args |
Extra kwargs passed to the underlying model client (JSON / dict). |
BUB_COMPLETION_ARGS |
{} |
completion_args |
Extra kwargs passed to each completion call, e.g. {"reasoning_effort":"high"}. Bub supplies the tools and token limit; see option precedence below. |
BUB_VERBOSE |
0 |
verbose |
Logging verbosity level (0–2). |
For example, command_prefix: "!" enables !help in builtin channels; ,help becomes ordinary text.
CLI controls (quit, exit, thinking), completion, and shell mode use the same prefix.
Telegram keeps its existing native slash-command filtering; with / as the prefix, use /bub /help.
Provider-specific defaults are gathered at startup by scanning os.environ for ^BUB_(.+)_(API_KEY|API_BASE)$ and resolving the captured environment prefix to the Republic provider name (for example, AZURE_OPENAI → azure-openai).
Bub uses Republic for model requests. Use codex:<model> for ChatGPT plan access and openai:<model> for OpenAI API access. A stored Codex login never changes the openai: route. Provider identifiers follow Republic, including google: and azure-openai:.
client_args accepts Republic provider options such as headers, api_format, timeout, and max_retries. Bub supplies the resolved connection settings (api_key, api_base); other options pass through unchanged. Protocol defaults follow Republic. Set api_format: chat explicitly for a Chat Completions endpoint. An injected HTTP client must be an httpx2.AsyncClient and stays caller-owned.
completion_args accepts Republic chat options. Bub supplies tools and the runtime token limit; a session’s reasoning_effort overrides the configured chat option. The runtime token limit is omitted for Codex because that endpoint does not support it. Republic handles wire-format conversion and Anthropic requests enable prompt caching.
Provider-specific fields go in extra_body, which is accepted in both client_args and completion_args. Republic deep-merges provider defaults with request extras, with request values taking precedence, then merges them over the encoded request body. These explicit wire fields can override chat options, including reasoning and token limits; Bub passes them through unchanged.
Provider names and available protocols follow Republic’s provider directory. Custom provider implementations must be registered with Republic.
Custom providers
Section titled “Custom providers”The model prefix normally names a Republic provider, and api_key / api_base hold one value per provider. Use providers when an endpoint speaks an API Republic already supports but needs a name of its own: a company proxy, a relay or local gateway, a self-hosted server, or a second account with the same vendor. Each name becomes a model prefix, and type is the Republic provider whose API it speaks:
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-...
Here proxy:gpt-5.5 and openai:gpt-5.5 both use the OpenAI API but go to different endpoints with different keys; openai: keeps its own api_key / api_base. A field left out of an entry falls back to api_key / api_base and BUB_<NAME>_API_KEY / BUB_<NAME>_API_BASE. An unknown type is rejected when settings load.
Spill sidecar — SpillSettings
Section titled “Spill sidecar — SpillSettings”Defined in src/bub/builtin/spill.py and registered by the builtin sidecar plugin:
@config(name="spill")
class SpillSettings(Settings):
model_config = SettingsConfigDict(env_prefix="BUB_SPILL_", extra="ignore", env_file=".env")
threshold: int = Field(default=4096, ge=0)
Loaded under the YAML spill: section.
New results are spilled only when spill.read is available in the current model tool set. Otherwise, the full result is returned.
| Env var | Default | YAML key (spill.*) |
Description |
|---|---|---|---|
BUB_SPILL_THRESHOLD |
4096 |
threshold |
Estimated tokens (4 chars each) above which rendered tool results, including failures, are stored in the spill sidecar. Set to 0 to stop creating new spills while keeping the sidecar mounted for existing handles and lifecycle operations. |
Channels — ChannelSettings
Section titled “Channels — ChannelSettings”Defined in src/bub/channels/manager.py:
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
Loaded under the YAML root section.
| Env var | Default | YAML key | Description |
|---|---|---|---|
BUB_ENABLED_CHANNELS |
all |
enabled_channels |
Comma-separated channel names, all, or exclusions prefixed with !. The default runtime set includes every enabled non-Interface channel, and explicit lists that contain a non-Lifecycle channel also attach enabled Lifecycle runtimes unless excluded. Overridden per-invocation by bub gateway --enable-channel. |
BUB_DEBOUNCE_SECONDS |
1.0 |
debounce_seconds |
Minimum gap between two messages from the same channel when the channel sets needs_debounce=True. |
BUB_MAX_WAIT_SECONDS |
10.0 |
max_wait_seconds |
Hard cap for the debounce wait. |
BUB_ACTIVE_TIME_WINDOW |
60.0 |
active_time_window |
Window in seconds during which a session stays “active” for buffered handling. |
BUB_STREAM_OUTPUT |
false |
stream_output |
Stream model output to channels in real time. bub chat forces True; bub gateway honors the setting. |
Telegram — TelegramSettings
Section titled “Telegram — TelegramSettings”Defined in 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
Loaded under the YAML telegram: section.
| Env var | Default | YAML key (telegram.*) |
Description |
|---|---|---|---|
BUB_TELEGRAM_TOKEN |
"" |
token |
Telegram bot token. Required to enable the channel. |
BUB_TELEGRAM_ALLOW_USERS |
unset | allow_users |
Comma-separated allowlist of Telegram user IDs. Empty means no restriction. |
BUB_TELEGRAM_ALLOW_CHATS |
unset | allow_chats |
Comma-separated allowlist of Telegram chat IDs. Empty means no restriction. |
BUB_TELEGRAM_PROXY |
unset | proxy |
Proxy URL for the Telegram API, e.g. http://user:pass@host:port or socks5://host:port. |
See Operate › Channels › Telegram for deployment notes.
Login — third-party env vars
Section titled “Login — third-party env vars”bub login codex reads one non-BUB_* env var:
| Env var | Default | Read by | Description |
|---|---|---|---|
CODEX_HOME |
~/.codex |
bub login codex (src/bub/builtin/auth.py) |
Directory to store Codex OAuth auth.json. Overridden by --codex-home. |
Plugin-specific settings
Section titled “Plugin-specific settings”Plugins can register their own Settings subclass via the @config(name="...") decorator (see Build › Plugins). The decorator records the class under CONFIG_MAP[name], which configure.validate then validates and ensure_config reads. The YAML key matches the registered name; env vars follow whatever env_prefix the subclass declares.