cavi-ai/
GitHub ↗

Configuration

bobby serve loads ./config.toml at startup, overridable with --config / BOBBY_BROWSER_CONFIG. A missing file uses built-in defaults; a malformed file fails startup and names the path.

The committed `config.toml` is the canonical field list and mirrors AppConfig (crates/config). Values below match AppConfig::default() unless you override them.

[server]

FieldDefaultMeaning
host127.0.0.1Bind host (keep loopback unless you control the network)
port7777HTTP listen port (/healthz, /v1/*)

[browser]

FieldDefaultMeaning
profiles_dir./data/profilesPer-profile browser state
headlesstrueNo visible window
max_active8Max concurrent browser workers
upload_roots["./data/uploads"]Allowed roots for file upload
downloads_dir./data/downloadsDownload output directory
artifacts_dir./data/artifactsScreenshots and other artifacts
max_artifact_bytes8388608Max single artifact size
max_screenshot_dimension16384Max screenshot width/height
max_js_result_bytes65536JS eval result bound
max_js_timeout_ms30000Clamp for JS timeout_ms

Engine choice is not a TOML field. Every entry point — bobby serve, the stdio MCP gateway, and bobby doctor — resolves the browser selection through one canonical order:

  1. AUTOMATION_RUNTIME_BROWSER_SELECTION (JSON) — an override; wins when set.
  2. The persisted enrollment at

<config-dir>/bobby-browser/browser-selection.json, written atomically (owner-only, 0600, on Unix) by bobby enroll-firefox-profile.

  1. The built-in default: exact Firefox (fail-closed — with no enrolled

profile, startup fails with an actionable error rather than silently downgrading engines).

A source that is present but malformed is always an error, never skipped. bobby doctor reports which source resolved.

[storage]

FieldDefaultMeaning
journal_path./data/storage/commands.jsonlAppend-only command journal
scheduler_journal_path./data/storage/scheduler-jobs.jsonlAppend-only job scheduler journal
checkpoints_dir./data/storage/checkpointsJournal checkpoints
authority_path./data/storage/authority.jsonAuthority storage

[context]

Durable shared context graph (remembered form structure per site). Only runtimes whose engine selection carries a durable profile identity (Firefox companion enrollment) open the store; Chromium sessions read and write nothing.

FieldDefaultMeaning
dir<config-dir>/bobby-browser/context (filled by bobby serve)Store root; the profile id is appended as a subdirectory. Unset disables promotion
ttl_days90Days a control record is kept without a verified success; swept at store open

[mcp]

Presentation of the MCP tool surface. Nothing here is an enforcement boundary — capability gates remain the only one.

FieldDefaultMeaning
startup_toolsetunset (explore)Phase a connection starts in: full, explore, act, intent, verify. BOBBY_MCP_TOOLSET overrides it

An agent downloads all of tools/list during the handshake, before it can call toolset_select — so a phase chosen after connecting cannot buy back bytes already paid for. Starting narrow can:

Phasetools/list
full~127 KB
explore~76 KB
verify~41 KB
act~69 KB
intent~74 KB

Narrowing changes only what is *advertised*. Hidden tools stay callable, and every phase keeps session/page lifecycle plus toolset_select, so an agent can always widen or clean up. An unparseable BOBBY_MCP_TOOLSET is ignored with a warning; an unparseable startup_toolset in config fails startup.

toml
[mcp]
startup_toolset = "intent"

[http] (outbound)

Controls egress from the runtime (downloads, fetches), not the broker listen socket: allow_loopback, allow_private_network, redirect/body/timeout caps, max_concurrent_requests. Defaults deny private/loopback egress. max_download_bytes defaults to 67108864 (64 MiB) and is projected into the MCP download_url.maxBytes schema at startup, so clients can select a valid bound without probing the policy.

[vision]

Vision can use either a direct HTTP provider or an ACP harness. ACP is the recommended path when Codex, Claude, OpenCode, Hermes, OpenClaw, or another workflow harness already owns the model login: Bobby never receives or stores that provider token.

bash
bobby vision connect --yes --backend acp --provider codex \
  --command codex --arg acp --auth advertised
toml
[vision]
backend = "acp"
profile = "codex"

[vision.acp_profiles.codex]
command = "codex"
args = ["acp"]
auth = "advertised"
FieldDefaultMeaning
prefillfalseLazy batch prefill: one screenshot proposes for every remaining stuck field in a form, cached for the page's generation
corpus_dirunsetWhen set, every vision escalation — executed or rejected — appends one JSONL record to <corpus_dir>/vision-corpus.jsonl: the screenshot, the exact candidate list sent to the model, the proposal, the terminal outcome, and, for verified clicks, the resolved target index. Unset writes nothing
collect_training_datafalseCapture proxy request/proposal pairs for the local training pipeline. Independent of corpus_dir
training_data_dirvision-training-dataDestination for collect_training_data captures

corpus_dir and training_data_dir write page screenshots and candidate text to disk. Point them at a path you control, and treat their contents as page data with the same sensitivity as the pages Bobby visited.

Supported auth paths are advertised, oauth-authorization-code, oauth-device-code, environment, existing-session, and none. Bobby maps each path to an auth-broker strategy and calls the matching harness authenticate method when vision assist runs. Bobby does not read IDE Keychains or OS credential stores. If the harness does not advertise a method id that matches the configured strategy, vision assist fails closed. Multi-step OAuth (AuthRequired challenges) is not fully productized — establish the harness login outside Bobby first. Each vision task gets a new ACP child session, bounded text and image content, a strict JSON result, evidence-digest validation, and an explicit close.

Deny-by-default direct HTTP vision-assist provider. Unset endpoint_url means escalation is unavailable even when the bearer and session opt in.

FieldDefaultMeaning
endpoint_urlunsetBobby → proxy URL — https, or http only on loopback
token_envunsetEnv var name holding the loopback bearer (never store the token here)
timeout_ms15000Per-proposal HTTP timeout
providerunsetActive profile name under [vision.providers]
providers.<name>unsetNamed OpenAI-compatible upstream profiles

Each [vision.providers.<name>] profile:

FieldRequiredMeaning
base_urlrequiredUpstream OpenAI-compatible API base (proxy → provider)
modelrequiredModel id passed to the upstream
api_key_envoptionalEnv var for the upstream API key; omit for local servers (Ollama, LM Studio)

Request / response shapes and confidence floor: Intent commands. Capability + session gates: Capabilities.

Granting vision:assist and creating a session with executionPolicy.visionAssist = true is not enough for functional vision assist — the runtime must also reach a live provider at [vision].endpoint_url. When the URL is unset, escalation is unavailable even with capability and session opt-in. When the URL is set but nothing is listening, bobby doctor warns on vision-endpoint reachability (loopback endpoints suggest bobby serve --vision or manual bobby vision-proxy; external endpoints suggest verifying the remote service).

Code-review-graph answers code structure; bobby vision answers page pixels — do not conflate the two.

Setup (preferred)

  1. Run bobby vision connect (interactive menu or --yes --provider …) to

write endpoint_url, token_env, provider, and the matching [vision.providers.*] table. Add --activate to load/readiness-test it immediately; MLX downloads also require explicit --download-model consent.

  1. Export env vars the connect step printed (BOBBY_VISION_TOKEN, and

api_key_env when the profile requires one).

  1. Start bobby serve --vision — on loopback, bobby auto-spawns

bobby vision-proxy when the port is free.

These are distinct states:

  • configured: provider, model, and endpoint are persisted;
  • cached: the selected local model files exist;
  • readiness-tested: Bobby proved the configured provider can be reached or

loaded during a bounded check;

  • runtime-loaded: a running Bobby command currently owns the managed MLX

worker;

  • externally managed: Ollama or LM Studio owns its own server lifecycle.

Explicit install selection performs the readiness test. bobby doctor --fix can repeat it and normalize Bobby-owned configuration. MLX is started only for the readiness check and then stopped; normal serve, mcp-stdio, and acp-stdio invocations own its runtime lifetime. doctor --fix never starts a persistent daemon. Downloading a missing selected MLX model additionally requires --download-model.

Manual bobby vision-proxy in a separate terminal remains valid when you want full control over the sidecar process.

bash
export BOBBY_VISION_TOKEN=…
export OPENAI_API_KEY=…       # openai profile only
bobby vision connect --yes --provider openai
bobby serve --vision

Preset providers

Providerbase_url (default)model (default)api_key_env
openaihttps://api.openai.com/v1gpt-4o-miniOPENAI_API_KEY
ollamahttp://127.0.0.1:11434/v1llava
lmstudiohttp://127.0.0.1:1234/v1local-model

For LM Studio (or MLX-hosted OpenAI-compatible servers), copy the Server URL the app displays — port 1234 is the common default, not a guarantee. Override with bobby vision connect --base-url … or edit [vision.providers.<name>].base_url after connect.

Custom provider

Any OpenAI-compatible endpoint:

bash
bobby vision connect --yes --provider custom \
  --base-url https://my-host/v1 \
  --model my-vision-model \
  --api-key-env MY_VISION_API_KEY
export MY_VISION_API_KEY=…
export BOBBY_VISION_TOKEN=…
bobby serve --vision

Or hand-edit:

toml
[vision]
endpoint_url = "http://127.0.0.1:9100/vision"
token_env = "BOBBY_VISION_TOKEN"
provider = "myhost"

[vision.providers.myhost]
base_url = "https://my-host/v1"
model = "my-vision-model"
api_key_env = "MY_VISION_API_KEY"   # omit when the upstream needs no key

bobby doctor warns on vision-provider when provider names a missing profile, and on vision-upstream-key when a profile's api_key_env is unset.

[vision] is the single-provider form. [nodes] supersedes it — see below.

[nodes.<name>]

Named, separately addressable nodes. A session picks one by name through executionPolicy.visionNode; nothing is process-wide.

FieldDefaultMeaning
kindrequiredvision — proposes an action from a screenshot. The only kind today; an unknown kind fails config load rather than being ignored.
endpoint_urlrequiredNode URL — https, or http only on loopback
token_envunsetEnv var name holding the node bearer (never store the token here)
timeout_ms15000Per-call HTTP timeout
toml
[nodes.local-vision]
kind = "vision"
endpoint_url = "http://127.0.0.1:8080/propose"

A session that names no node escalates to no node. A session that names a node which is not configured is declined: the runtime never substitutes a different node, and never falls back to a remote default.

Retained page context — what context_ask answers from — is held in-process, not in a node. There is deliberately no kind = "context": an operator could write it and it would reach nothing.

Locality comes from the address, not from a setting. A session bound to a loopback node cannot have its screenshots or page text leave the machine.

When both [vision] and [nodes] are present, [nodes] wins and [vision] is ignored with a startup warning. With only [vision] set, that endpoint is reachable as a node named vision.

[interface]

FieldDefaultMeaning
max_request_bytes1048576Max inbound request body
max_event_batch256Max events per batch read
max_event_retention16384Retained events per principal stream
max_connections64Concurrent interface connections
token_records_path./data/storage/authorities.jsonIssued principal records
max_principals16Max enrolled principals
max_in_flight_per_principal8Fairness / concurrency cap
max_rejection_workers16Concurrent rejection / policy-worker permits (must be > 0)

Bootstrap env (not in config.toml)

Credentials are never stored in config.toml. Resolve via:

  1. AUTOMATION_RUNTIME_BOOTSTRAP_TOKEN /

…_PRINCIPAL / …_CAPABILITIES / …_EXPIRES_AT

  1. Secret file (--bootstrap-env / BOBBY_BROWSER_BOOTSTRAP_ENV or OS config

…/bobby-browser/bootstrap.env from bobby init)

  1. Loopback auto-init on bobby serve

See Authentication, CLI reference, and Run the server.