Skip to content

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.

Bub resolves a setting value in this order, highest priority first:

  1. CLI flag (e.g. --workspace, --project, --enable-channel).
  2. Environment variable (BUB_*).
  3. .env values loaded during CLI startup or by settings classes that declare env_file=".env".
  4. ~/.bub/config.yml entry, loaded by bub.configure.load.
  5. Field default declared on the Settings subclass.

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.

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

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.

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.

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.

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.

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.

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.

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.

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.