Configuration reference

Configuration reference

Bubo reads one runtime config file: config/env.toml under BUBO_ROOT.

Start from the packaged template:

bubo init
# or, from a source checkout:
cp config/env.example.toml config/env.toml

config/env.toml is local operator state. Do not commit real tokens.

String values support environment-variable interpolation:

token = "${GITLAB_TOKEN}"                         # required; fails if unset
url   = "${GITLAB_URL:-https://gitlab.com}"       # fallback when unset

Use $$ for a literal dollar sign.

Runtime requirements

ResourceMinimumRecommendedNotes
CPU2 vCPU4 vCPUGit checkout, JSON parsing, and agent subprocesses.
RAM2 GB4 GBIncrease with review concurrency.
Disk10 GB20 GB+Worktrees, SQLite state, logs, reports, and agent installs.
Networkoutbound HTTPSoutbound HTTPSSCM host and LLM provider access. No inbound port is required for polling.

Required tools:

ToolRequired for
Python 3.14+Bubo runtime.
Node.jsCodex or Claude CLI installs.
gitRepository checkout over tokenized HTTPS.
Codex, Claude, or another CLI agentReview execution.

[scm]

KeyTypeDefaultDescription
providerstring"gitlab"SCM backend. Allowed: "gitlab", "github". BUBO_PROVIDER overrides this for one run.

[gitlab]

Used when [scm].provider = "gitlab".

KeyTypeDefaultDescription
urlstring"https://gitlab.com"GitLab web URL. Use your self-hosted GitLab URL when needed.
api_urlstring"<url>/api/v4"GitLab REST API URL for MRs, diffs, and outcomes.
bot_usernamestringunsetBot username used to separate bot comments from developer replies.
tokenstringunsetGitLab token, api scope. Exported as GITLAB_TOKEN, GITLAB_PERSONAL_ACCESS_TOKEN, and GLAB_TOKEN, and used as the credential for the HTTPS git clone — sent as a per-call auth header, never written to .git/config.

[github]

Used when [scm].provider = "github".

KeyTypeDefaultDescription
api_urlstring"https://api.github.com"GitHub REST API base. Use https://<host>/api/v3 for GitHub Enterprise Server. The clone host is derived from it (api.github.com → github.com; GHES → its own host).
bot_usernamestringunsetBot username used to separate bot comments from developer replies.
tokenstringunsetGitHub token with pull-request read/write access. Exported as GITHUB_TOKEN, GITHUB_PERSONAL_ACCESS_TOKEN, and GH_TOKEN, and used as the credential for the HTTPS git clone — sent as a per-call auth header, never written to .git/config.

[review]

Controls polling, filtering, and review-output policy.

KeyTypeDefaultDescription
dry_runbooleantrueStore planned findings without posting SCM comments.
max_merge_requests_per_pollinteger5Maximum MRs/PRs queued by one poll cycle.
max_findings_per_merge_requestinteger5Maximum findings accepted from one review. Also fills {{MAX_FINDINGS_PER_REVIEW}} in the prompt.
timeout_secondsinteger1800Per-review worker timeout.
min_confidencenumber0.85Minimum finding confidence required to plan or post.
category_min_confidencetable{}Per-canonical-category confidence floors. Only raises the global floor.
calibrate_confidencebooleanfalseDerive category floors from repository outcome history.
calibrate_max_confidencenumber0.97Maximum derived category floor.
allowed_kindsstring array[]Optional allowlist matched against finding severity, category, or type. Empty means no kind filter.
tonestring"terse"Comment voice. Allowed: "terse", "collaborative", "socratic", "formal", "casual".
modestring"collaborate"Surface mode. Allowed: "collaborate", "gate".
suppress_disputed_classesbooleanfalseSuppress finding categories repeatedly disputed on the same repository.
dispute_suppress_thresholdnumber0.5Dispute rate required before a category can be suppressed.
dispute_suppress_min_samplesinteger5Minimum outcome rows required before suppression can apply.
verify_findingsbooleanfalseRe-check surviving findings before planning/posting.
verify_lensesstring array["correctness", "in_diff", "reproduce"]Verification lenses, run in order. Each lens is one verifier call per finding; lenses stop as soon as the remaining ones cannot change the result.
verify_min_votesinteger2Number of verifier votes required for a finding to survive.
verify_confidence_floornumber0.6Minimum verifier confidence for a real vote to count.
verify_max_findingsinteger5Maximum findings verified per change. Findings beyond the cap post unverified.
verify_timeout_secondsinteger300Timeout for each verifier call.
verify_commandstring array[]Verifier command. Empty reuses [agents].reviewer_command.
reconcile_fixed_findingsbooleanfalseOpt in to guarded closure of Bubo-owned cross-head findings.
reconcile_confidence_floornumber0.9FIXED proof minimum; current-checkout path and line evidence are required.
reconcile_timeout_secondsinteger300Per-proof verifier timeout.
reconcile_lease_secondsinteger300Per-finding claim lease for concurrent workers.
reconcile_commandstring array[]Dedicated proof verifier; empty reuses verifier/reviewer.
rereviewstring"full"Policy for new commits after a completed review. Allowed: "full" (review the whole change again), "incremental" (only the commits since the last completed review, plus reconciliation of earlier findings), "off" (only the first commit). See Operate.
settle_secondsinteger0Start a review only once the head has been unchanged this long, measured from when the poller first saw it. 0 starts immediately.

Canonical categories for category_min_confidence: correctness, security, concurrency, resource, error_handling, performance, style, docs, test, design, naming, other.

[governance]

Controls AI-code provenance and advisory governance signals.

KeyTypeDefaultDescription
capture_provenancebooleanfalseCapture per-change AI-provenance bands for audit.
ai_trailer_patternsstring arraybuilt-in patternsRegexes matched against commit-message lines for declared AI assistance.
sensitive_path_globsstring array[]fnmatch globs for sensitive paths, such as payments/** or *.pem.
rigor_modulationbooleanfalseAdd heightened-scrutiny prompt context when a change escalates.
escalate_bandsstring array["likely_ai", "collaborative"]Provenance bands that escalate. Allowed bands: unknown, likely_ai, collaborative.
rigor_require_sensitivebooleantrueRequire a sensitive-path match before escalation.
policy_modestring"off"Advisory policy mode. Allowed: "off", "report-only", "soft".

[poller]

Controls state location and single-MR debug targeting.

KeyTypeDefaultDescription
state_dirstring"var"Runtime state directory. Relative paths resolve under BUBO_ROOT.
interval_secondsinteger900Review-service poll interval.
outcome_sync_interval_secondsinteger3600Review-service outcome-sync interval.
outcome_sync_limitinteger200Maximum findings graded by each service-owned outcome sync.
target_merge_request_iidintegerunsetDebug filter for one GitLab MR IID. Leave unset in production.

[subscription_circuit]

This opt-in circuit breaker pauses reviews after a terminal subscription or quota failure. Review workers only record a signal; the persistent monitor is the sole writer of circuit state.

When enabled, discovery, reviewer invocation, and SCM writes require a fresh monitor heartbeat and a CLOSED circuit. Missing or stale monitor state fails closed.

KeyTypeDefaultPurpose
enabledbooleanfalseEnable subscription pause and recovery monitoring.
monitor_interval_secondsinteger15Monitor heartbeat interval.
heartbeat_ttl_secondsinteger60Maximum age of a usable monitor heartbeat.
probe_interval_secondsinteger300Initial interval before a recovery probe.
probe_backoff_max_secondsinteger3600Maximum recovery-probe backoff.
probe_timeout_secondsinteger30Timeout for one recovery probe.
error_patternsstring arraybuilt-in patternsTerminal subscription/quota error patterns that open the circuit.

[agents]

Controls the review agent command and agent-facing environment.

KeyTypeDefaultDescription
reviewer_commandstring array["codex", "--ask-for-approval", "never", "exec", "--profile", "bubo", "--skip-git-repo-check"]Only codex exec or claude -p is supported. Bubo enforces read-only policy; arbitrary wrappers and other CLIs are rejected. The prompt is appended as the final argument.
llm_modelstring"gpt-6.1-sol"Codex review model. Exported as LLM_MODEL; the review runner passes it to Codex as --model, bubo init templates it into the Codex profile, and the model metric label reads it.
claude_modelstring"claude-opus-5-5"Claude review model, passed as --model when reviewer_command runs claude -p (the Codex llm_model is never sent to Claude). bubo init also templates it into ~/.claude/settings.json; it is the model metric label on that path. Must be a Claude model id.
llm_model_effortstring"medium"Reasoning effort — "low", "medium", or "high". Exported as LLM_MODEL_EFFORT; bubo init templates it into the agent profile. Falls back to the deprecated reasoning_effort key.
llm_api_keystringunsetLLM API key, exported as LLM_API_KEY. By default the review agent authenticates via its own login (written by bubo init), so the key is not injected into the agent environment. The one exception is llm_base_url, which reads the key from the environment at request time.
llm_base_urlstringunsetOptional OpenAI Responses-compatible endpoint for the Codex reviewer (in-house gateway, proxy, local server). Chat Completions-only endpoints and Claude with this setting are unsupported. When set, bubo init points the Codex profile at it via a [model_providers] block, and the agent reads LLM_API_KEY from its environment. Exported as LLM_BASE_URL. Security: this is the one mode that exposes the key to the agent — leave unset unless you need it.
llm_api_key_envstringunsetDeprecated. Named an extra env var to expose the key under; the agent now authenticates via its own login. Still honored when set, but prefer removing it.
dry_runbooleantrueExported as REVIEW_DRY_RUN for the agent. Posting is controlled by [review].dry_run.
codex_profilestring"bubo"Legacy compatibility value. Hardened review commands ignore user profiles and generate their own policy.
codex_sandboxstring"read-only"Read-only isolation is enforced; this setting cannot authorize weaker sandboxing.
post_no_findings_commentbooleantruePost one change-level acknowledgement when a review finds no actionable issues. Honors [review].dry_run.
no_findings_comment_bodystring"Automated review ran — no issues found."Body for the no-findings acknowledgement. Empty disables the acknowledgement.

Before rollout, record the installed version (codex --version or claude --version) and check its help. Codex must expose --ignore-user-config, --ignore-rules, --output-last-message, and --sandbox read-only in codex exec --help. Claude must support --safe-mode, --restricted, --strict-mcp-config, --mcp-config, --tools, and --permission-prompts. Compatibility is feature-based; no universal minimum version is asserted. Unsupported flags fail the review. Do not remove isolation flags to make an older CLI run.

Linux requires working Bubblewrap namespaces; macOS requires sandbox-exec. Validate the deployment host before rollout. A successful outer Bubblewrap test does not establish that Codex’s nested native sandbox works.

Prompt path note: live reviews load the trusted packaged prompts/00-meta.md, with a source-checkout fallback. Set the operator-controlled BUBO_PROMPT_SOURCE to use another trusted prompt file.

[telemetry]

OpenTelemetry measurement and cost settings. See Metrics & OpenTelemetry for the local HTML dashboard and preferences.

KeyTypeDefaultDescription
export_interval_secondsinteger30Metric export interval.
emit_finding_eventsbooleantrueEmit per-finding lifecycle metrics.
emit_outcome_syncbooleantrueEmit outcome-sync metrics.
live_pricingbooleantruePrice Codex runs from LiteLLM’s public price list, fetched at most once a day. Claude runs always record the cost Claude reports.
input_per_1mnumber0.0Input-token price per 1M tokens, for models the live list lacks, or for all models when live_pricing = false.
output_per_1mnumber0.0Output-token price per 1M tokens, used the same way.
cached_input_per_1mnumber0.0Cached-input price per 1M tokens, used the same way.

[analytics]

Shared telemetry preferences. The local HTML dashboard remains available when delivery is disabled.

KeyTypeDefaultDescription
enabledbooleantrueSet false to keep telemetry local.
endpointstringbuilt-inDelivery endpoint. Blank disables delivery.
api_keystringbuilt-inIngestion key. Blank disables delivery.

[analytics].enabled is the only delivery preference. Use false in development and test configuration. Running processes should be restarted after changing it.

[mcp_server]

Controls the bubo-mcp server. See MCP server for client setup.

KeyTypeDefaultDescription
transportstring"stdio"Allowed: "stdio", "http".
hoststring"127.0.0.1"HTTP bind address. Ignored for stdio.
portinteger8765HTTP bind port. Ignored for stdio.
bearer_tokenstringunsetRequired for HTTP transport. Clients send Authorization: Bearer <token>.

[[projects]]

One block per repository.

KeyTypeDefaultDescription
pathstringrequiredRepository path, such as group/repo, group/subgroup/repo, or owner/repo.
enabledbooleantrueInclude or skip the repository during polling.

Direct environment overrides

VariableDescription
BUBO_ROOTInstall root. Defaults to ~/.local/share/bubo.
BUBO_BASE_DIRRuntime state directory. Usually derived from [poller].state_dir.
BUBO_PROVIDEROverrides [scm].provider for one run.
BUBO_PROMPT_SOURCEOverrides the prompt template path.
BUBO_MCP_TRANSPORTOverrides [mcp_server].transport.
BUBO_MCP_HOSTOverrides [mcp_server].host.
BUBO_MCP_PORTOverrides [mcp_server].port.
BUBO_MCP_BEARER_TOKENOverrides [mcp_server].bearer_token.

Runtime files

PathDescription
$BUBO_ROOT/config/env.tomlOperator config.
$BUBO_BASE_DIR/state/reviewer.sqliteReview state, findings, and outcomes.
$BUBO_BASE_DIR/work/Per-change worktrees.
$BUBO_BASE_DIR/work/_mirrors/One bare git mirror per configured project, shared by that project’s worktrees. Safe to delete; it is re-created on the next review. Mirrors of projects removed from [[projects]] are pruned automatically.
$BUBO_BASE_DIR/reports/Agent transcripts.
$BUBO_BASE_DIR/log/JSON-line logs.
$BUBO_BASE_DIR/rendered-prompts/Rendered review rules, inlined into every review prompt.
$BUBO_ROOT/prompts/00-meta.mdDefault prompt template.
$BUBO_ROOT/skills/code-reviewer/Review skill for interactive Codex use; automated reviews do not need it.
~/.codex/config.tomlCodex client config written by bubo init, unless skipped.
~/.claude/settings.jsonClaude settings written by bubo init, unless skipped.

Sample config/env.toml

This sample uses GitLab, Codex, dry-run reviews, no HTTP MCP server, and local SQLite state. Change only the provider block and project paths for GitHub.

[scm]
provider = "gitlab"
 
[gitlab]
url          = "https://gitlab.com"
api_url      = "https://gitlab.com/api/v4"
bot_username = "bubo"
token        = "${GITLAB_TOKEN}"
 
[github]
api_url      = "https://api.github.com"
bot_username = "bubo"
# token      = "${GITHUB_TOKEN}"
 
[review]
dry_run                        = true
max_merge_requests_per_poll    = 5
max_findings_per_merge_request = 5
timeout_seconds                = 1800
min_confidence                 = 0.85
category_min_confidence        = {}
calibrate_confidence           = false
calibrate_max_confidence       = 0.97
allowed_kinds                  = []
tone                           = "terse"
mode                           = "collaborate"
suppress_disputed_classes      = false
dispute_suppress_threshold     = 0.5
dispute_suppress_min_samples   = 5
verify_findings                = false
verify_lenses                  = ["correctness", "in_diff", "reproduce"]
verify_min_votes               = 2
verify_confidence_floor        = 0.6
verify_max_findings            = 5
verify_timeout_seconds         = 300
verify_command                 = []
 
[governance]
capture_provenance    = false
sensitive_path_globs  = []
rigor_modulation      = false
escalate_bands        = ["likely_ai", "collaborative"]
rigor_require_sensitive = true
policy_mode           = "off"
 
[poller]
state_dir        = "var"
interval_seconds = 900
outcome_sync_interval_seconds = 3600
outcome_sync_limit = 200
# target_merge_request_iid = 123
 
[subscription_circuit]
enabled = false
monitor_interval_seconds = 15
heartbeat_ttl_seconds = 60
probe_interval_seconds = 300
probe_backoff_max_seconds = 3600
probe_timeout_seconds = 30
error_patterns = ["insufficient[_ -]?quota", "exceeded your current quota"]
 
[agents]
reviewer_command = [
  "codex",
  "--ask-for-approval",
  "never",
  "exec",
  "--profile",
  "bubo",
  "--skip-git-repo-check",
]
llm_model                  = "gpt-6.1-sol"
claude_model               = "claude-opus-5-5"
llm_model_effort           = "medium"
llm_api_key                = "${LLM_API_KEY}"
# llm_base_url             = "https://llm.internal.example/v1"  # Responses-compatible endpoint
dry_run                    = true
codex_profile              = "bubo"
codex_sandbox              = "read-only"
post_no_findings_comment   = true
no_findings_comment_body   = "Automated review ran — no issues found."
 
[telemetry]
# Consent is [analytics].enabled.
export_interval_seconds = 30
emit_finding_events     = true
emit_outcome_sync       = true
live_pricing            = true
input_per_1m            = 0.0
output_per_1m           = 0.0
cached_input_per_1m     = 0.0
 
[analytics]
# enabled = false   # anonymous usage analytics are on by default; uncomment to opt out
 
[mcp_server]
transport = "stdio"
host      = "127.0.0.1"
port      = 8765
# bearer_token = "${BUBO_MCP_TOKEN}"
 
[[projects]]
path    = "group/repo"
enabled = true
 
[[projects]]
path    = "group/another-repo"
enabled = false
MountainOwlMountainOwl
Bubo · MIT licensed · © 2026