# Install, Run, and Pick a Browser

> How the repo turns shell commands into Rust binaries, where state lives, and why the browser choice can be local Chrome, headless Chromium, or Browser Use cloud.

- Repository: browser-use/terminal
- GitHub: https://github.com/browser-use/terminal
- Human wiki: https://grok-wiki.com/public/wiki/browser-use-terminal-686510dbe50c
- Complete Markdown: https://grok-wiki.com/public/wiki/browser-use-terminal-686510dbe50c/llms-full.txt

## Source Files

- `scripts/install/install.sh`
- `python/llm_browser_worker/rust_cli.py`
- `crates/browser-use-cli/src/main.rs`
- `crates/browser-use-tui/src/settings.rs`
- `crates/browser-use-store/src/lib.rs`
- `pyproject.toml`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [scripts/install/install.sh](scripts/install/install.sh)
- [python/llm_browser_worker/rust_cli.py](python/llm_browser_worker/rust_cli.py)
- [crates/browser-use-cli/src/main.rs](crates/browser-use-cli/src/main.rs)
- [crates/browser-use-tui/src/settings.rs](crates/browser-use-tui/src/settings.rs)
- [crates/browser-use-tui/src/runtime.rs](crates/browser-use-tui/src/runtime.rs)
- [crates/browser-use-tui/src/main.rs](crates/browser-use-tui/src/main.rs)
- [crates/browser-use-store/src/lib.rs](crates/browser-use-store/src/lib.rs)
- [crates/browser-use-store/migrations/0001_initial.sql](crates/browser-use-store/migrations/0001_initial.sql)
- [crates/browser-use-store/migrations/0004_app_settings.sql](crates/browser-use-store/migrations/0004_app_settings.sql)
- [crates/browser-use-core/src/lib.rs](crates/browser-use-core/src/lib.rs)
- [crates/browser-use-python-worker/src/lib.rs](crates/browser-use-python-worker/src/lib.rs)
- [python/llm_browser_worker/worker.py](python/llm_browser_worker/worker.py)
- [crates/browser-use-browser/src/lib.rs](crates/browser-use-browser/src/lib.rs)
- [pyproject.toml](pyproject.toml)
- [Cargo.toml](Cargo.toml)
- [README.md](README.md)
</details>

# Install, Run, and Pick a Browser

Browser Use Terminal looks simple from the outside: install it, type `browser`, choose a model, and choose a browser. Under the hood, the repo keeps the user-facing commands thin and pushes real work into Rust binaries, a SQLite-backed state directory, and a browser runtime that can attach to local Chrome, launch managed Chromium, or start Browser Use cloud.

Think of it like a small train station. The shell command is the ticket booth, the Rust binary is the train, the state directory is the station logbook, and the browser choice decides which track the train uses.

Generation note: this page applies the provided bundled Compound Engineering guidance as page-shaping guidance only. Implementation claims below are grounded in repository files, not in a provider-specific workflow.

## The Short Version

The repo exposes three main command names:

| Command | What it does |
|---|---|
| `browser` | Opens the TUI when run with no arguments; forwards arguments to the CLI when arguments are present. |
| `browser-use` | Same hybrid behavior as `browser`. |
| `browser-use-terminal` | Same hybrid behavior when installed, and also the Rust CLI binary name. |
| `but` | Runs the TUI directly. |

The installer downloads a platform-specific release archive, verifies its SHA-256 digest, installs `bin/but` and `bin/browser-use-terminal` into a versioned package directory, and writes small shell wrappers into the visible install directory. The Python package entry points do a similar thing for development: they call `cargo run` when the repo checkout is present.

Sources: [scripts/install/install.sh:724-808](), [scripts/install/install.sh:670-685](), [pyproject.toml:23-30](), [python/llm_browser_worker/rust_cli.py:9-25]()

## Install: Shell Wrappers Around Rust Binaries

The installer starts with a few important defaults:

```sh
REPO="${BUT_RELEASE_REPO:-browser-use/terminal}"
BIN_DIR="${BUT_INSTALL_DIR:-$HOME/.local/bin}"
BUT_HOME_DIR="${BUT_HOME:-$HOME/.browser-use-terminal}"
STANDALONE_ROOT="$BUT_HOME_DIR/packages/standalone"
```

That means visible commands normally go in `~/.local/bin`, while downloaded packages and update metadata live under `~/.browser-use-terminal/packages/standalone`.

Sources: [scripts/install/install.sh:5-14]()

The install script accepts `--release VERSION`, resolves `latest` through GitHub release metadata, chooses a target triple from the current OS and CPU, downloads `browser-use-terminal-$target.tar.gz`, checks the matching `.sha256`, extracts it, and installs it into a versioned release directory.

Sources: [scripts/install/install.sh:46-79](), [scripts/install/install.sh:117-148](), [scripts/install/install.sh:172-183](), [scripts/install/install.sh:692-746](), [scripts/install/install.sh:771-790]()

The release payload is expected to contain executable Rust binaries and a Python worker package:

```text
release dir
├─ bin/
│  ├─ but
│  └─ browser-use-terminal
└─ python/
   └─ llm_browser_worker/worker.py
```

The script treats a release as complete only when both binaries exist and the Python worker file is present.

Sources: [scripts/install/install.sh:481-512]()

## Run: `browser` Decides TUI Or CLI

The installed wrappers are intentionally tiny. For `but`, the wrapper always executes the installed `CURRENT/bin/but`. For `browser`, `browser-use`, and `browser-use-terminal`, the wrapper is hybrid: no arguments opens the TUI, while any arguments are passed to the CLI binary.

```sh
if [ "$#" -eq 0 ]; then
  exec "$CURRENT/bin/but"
fi
exec "$CURRENT/bin/browser-use-terminal" "$@"
```

Sources: [scripts/install/install.sh:522-590](), [scripts/install/install.sh:595-664](), [scripts/install/install.sh:670-685]()

The wrappers also set `BUT_HOME`, `BUT_INSTALL_DIR`, `BUT_RELEASE_REPO`, and `PYTHONPATH`, then run an automatic update check unless `BUT_AUTO_UPDATE` disables it. By default the update interval is `72000` seconds, and `BUT_REQUIRE_LATEST=1` makes a failed update check block startup.

Sources: [scripts/install/install.sh:528-589](), [scripts/install/install.sh:600-664]()

In a source checkout, Python entry points are just development conveniences. `pyproject.toml` maps `browser-use-terminal` to `llm_browser_worker.rust_cli:main` and maps `but` to `llm_browser_worker.rust_cli:tui_main`. Those functions detect the repo root and run the matching Cargo package.

```python
def main() -> None:
    _exec_rust_binary("browser-use-cli", "browser-use-terminal", sys.argv[1:])

def tui_main() -> None:
    _exec_rust_binary("browser-use-tui", "but", sys.argv[1:])
```

Sources: [pyproject.toml:23-30](), [python/llm_browser_worker/rust_cli.py:9-25](), [Cargo.toml:1-11]()

## Where State Lives

There are two related locations:

| Location | Default | Purpose |
|---|---:|---|
| Install/package root | `~/.browser-use-terminal/packages/standalone` | Versioned release packages, `current` symlink, installer lock, update stamp/log. |
| Runtime state dir | `~/.browser-use-terminal` | SQLite state database, artifacts, settings, sessions, events. |

The CLI and TUI both default `--state-dir` to `~/.browser-use-terminal`. The store resolves that path, creates the directory, creates an `artifacts` subdirectory, opens `state.db`, enables WAL mode, and applies migrations.

Sources: [crates/browser-use-cli/src/main.rs:35-43](), [crates/browser-use-tui/src/main.rs:88-99](), [crates/browser-use-store/src/lib.rs:33-50](), [crates/browser-use-store/src/lib.rs:104-145]()

The first migration creates the durable core tables: `sessions`, `events`, `artifacts`, `runs`, and `agent_edges`. A later migration adds `app_settings`, which is where choices such as model, account, browser, and stored API keys are saved.

Sources: [crates/browser-use-store/migrations/0001_initial.sql:1-49](), [crates/browser-use-store/migrations/0004_app_settings.sql:1-5](), [crates/browser-use-store/src/lib.rs:16-25]()

Settings are plain key/value rows. `set_setting` upserts by key, `get_setting` reads one value, and `list_settings` returns all settings ordered by key.

Sources: [crates/browser-use-store/src/lib.rs:653-694]()

## Browser Choice Is Separate From Model Choice

The TUI keeps model/provider and browser as separate settings. That matters for BYOC and BYOK: the browser can be local, managed, or cloud while the agent backend can be Codex, OpenAI, Anthropic, OpenRouter, fake, or none.

Sources: [crates/browser-use-tui/src/settings.rs:4-50](), [crates/browser-use-tui/src/settings.rs:60-79]()

Default settings are provider-neutral at the storage layer: account, model, provider model, browser, agent backend, and setup completion are all separate keys. The default browser is `Local Chrome`.

Sources: [crates/browser-use-cli/src/main.rs:1288-1297]()

## The Three Browser Options

| TUI label | Internal mode | What it means | Main requirement |
|---|---|---|---|
| `Local Chrome` | `local` | Attach to the user's already-open browser after remote debugging is enabled. | Local Chrome setup and user approval when needed. |
| `Headless Chromium` | `managed-headless` | Start a Rust-owned managed Chromium with an isolated automation profile. | A launchable Chromium candidate. |
| `Browser Use cloud` | `cloud` | Start and connect to a Browser Use cloud browser, with live view support. | `BROWSER_USE_API_KEY` or a stored cloud key. |

Sources: [crates/browser-use-tui/src/settings.rs:74-83](), [crates/browser-use-tui/src/runtime.rs:75-101](), [crates/browser-use-core/src/lib.rs:177-205]()

### Local Chrome

Local Chrome is the default because it can use the user's real browser state. The core instruction tells the agent to run `browser connect local` before page work. The browser runtime has a matching `connect local` command path.

Sources: [crates/browser-use-core/src/lib.rs:180-184](), [crates/browser-use-browser/src/lib.rs:438-443]()

### Headless Chromium

Headless Chromium maps to `managed-headless`. The core instruction tells the agent to use `browser connect managed --headless`, and the browser runtime routes managed connections through `connect_managed`. When the managed launch is headless, the runtime adds `--headless=new` and uses a managed profile.

Sources: [crates/browser-use-core/src/lib.rs:186-190](), [crates/browser-use-browser/src/lib.rs:444-456](), [crates/browser-use-browser/src/lib.rs:838-858](), [crates/browser-use-browser/src/lib.rs:1688-1702]()

The Python worker also has legacy managed-browser helpers. It reads `LLM_BROWSER_BROWSER_MODE`, normalizes it, and can launch a managed Chrome process for `headless` or `headless-chromium`, but the Rust browser command path is the source-backed path for the TUI's `managed-headless` mode.

Sources: [python/llm_browser_worker/worker.py:229-231](), [python/llm_browser_worker/worker.py:339-345](), [python/llm_browser_worker/worker.py:359-373]()

### Browser Use Cloud

Browser Use cloud maps to `cloud`. The TUI checks for a stored key first, then for the `BROWSER_USE_API_KEY` environment variable. If cloud is selected without a key, the TUI records a failure for a run or prompts the user during setup instead of silently running the wrong browser.

Sources: [crates/browser-use-tui/src/runtime.rs:20-42](), [crates/browser-use-tui/src/runtime.rs:62-73](), [crates/browser-use-tui/src/main.rs:1774-1797](), [crates/browser-use-tui/src/main.rs:2383-2403]()

When a cloud key is saved through the TUI, it is stored at `auth.browser_use_cloud.api_key`. The CLI auth path uses the same setting and switches the saved browser label to `Browser Use cloud`.

Sources: [crates/browser-use-tui/src/settings.rs:74-76](), [crates/browser-use-tui/src/main.rs:1800-1817](), [crates/browser-use-cli/src/main.rs:1307-1317](), [crates/browser-use-cli/src/main.rs:1398-1406]()

The browser runtime itself requires `BROWSER_USE_API_KEY` before calling the Browser Use API. When cloud mode starts, it records the remote browser id and live URL and names the browser `Browser Use cloud`.

Sources: [crates/browser-use-browser/src/lib.rs:944-958](), [crates/browser-use-browser/src/lib.rs:1138-1144](), [crates/browser-use-browser/src/lib.rs:2600-2608]()

## How The Choice Reaches The Worker

The browser choice flows through a few layers:

```text
TUI/CLI setting
  -> AgentRunOptions.browser_mode
  -> core system instruction for the agent
  -> PythonWorker launch env: LLM_BROWSER_BROWSER_MODE
  -> browser helper behavior inside worker.py
```

The core run options contain `browser_mode`, and `with_browser_mode` sets it. Before an agent run, core inserts a system message explaining the selected browser mode and the browser command the agent should use. Core then starts the Python worker with the browser mode and any extra Python environment.

Sources: [crates/browser-use-core/src/lib.rs:121-154](), [crates/browser-use-core/src/lib.rs:177-205](), [crates/browser-use-core/src/lib.rs:729-777]()

The Rust Python worker launcher builds `PYTHONPATH`, tries `uv run` with pinned helper packages first, falls back to `python3`, and exports `LLM_BROWSER_BROWSER_MODE` when a browser mode is present.

Sources: [crates/browser-use-python-worker/src/lib.rs:84-126](), [crates/browser-use-python-worker/src/lib.rs:128-163](), [crates/browser-use-python-worker/src/lib.rs:426-443]()

## CLI Browser Preference Commands

The CLI also supports browser preference state. `browser preference use <local|cloud|managed-headless>` normalizes the mode, stores both an internal mode and display label, and reports the next step as `browser connect`.

Sources: [crates/browser-use-core/src/lib.rs:3017-3035](), [crates/browser-use-core/src/lib.rs:3233-3261]()

A plain `browser connect` can then resolve through the saved preference:

| Saved mode | Resolved command |
|---|---|
| `local` | `browser connect local` |
| `managed-headless` | `browser connect managed --headless` |
| `managed-headed` | `browser connect managed --headed` |
| `cloud` | `browser remote start` |

Sources: [crates/browser-use-core/src/lib.rs:3132-3150](), [crates/browser-use-core/src/lib.rs:3152-3188]()

## Architecture Map

```mermaid
flowchart TB
  subgraph Shell["Shell commands"]
    Browser["browser / browser-use"]
    But["but"]
  end

  subgraph Install["Installed package root"]
    Current["packages/standalone/current"]
    Bin["bin/but + bin/browser-use-terminal"]
    Py["python/llm_browser_worker"]
  end

  subgraph Rust["Rust binaries"]
    TUI["browser-use-tui"]
    CLI["browser-use-cli"]
    Core["browser-use-core"]
    Store["browser-use-store"]
    BrowserRuntime["browser-use-browser"]
    WorkerLauncher["browser-use-python-worker"]
  end

  subgraph State["Runtime state dir"]
    DB["state.db"]
    Artifacts["artifacts/"]
    Settings["app_settings"]
  end

  subgraph BrowserOptions["Browser options"]
    Local["Local Chrome"]
    Headless["Headless Chromium"]
    Cloud["Browser Use cloud"]
  end

  Browser -->|no args| TUI
  Browser -->|args| CLI
  But --> TUI
  TUI --> Core
  CLI --> Core
  Core --> Store
  Store --> DB
  Store --> Artifacts
  Store --> Settings
  Core --> WorkerLauncher
  Core --> BrowserRuntime
  BrowserRuntime --> Local
  BrowserRuntime --> Headless
  BrowserRuntime --> Cloud
  Current --> Bin
  Current --> Py
```

Sources: [scripts/install/install.sh:595-676](), [Cargo.toml:1-11](), [crates/browser-use-store/src/lib.rs:104-145](), [crates/browser-use-tui/src/runtime.rs:75-101](), [crates/browser-use-browser/src/lib.rs:438-456]()

## Practical Rules For Newcomers

Use `browser` when you want the guided terminal UI. Use `browser auth status`, `browser config show`, or other arguments when you want CLI behavior. The README exposes the same user-facing split: launch with `browser`, then use `/auth`, `/model`, `/browser`, and `/update` inside the TUI.

Sources: [README.md:65-88](), [scripts/install/install.sh:440-461]()

Choose `Local Chrome` when the task needs your real logged-in browser. Choose `Headless Chromium` when you want a cleaner Rust-owned automation browser. Choose `Browser Use cloud` when the browser should be remote, and make sure the cloud key is available through stored settings or `BROWSER_USE_API_KEY`.

Sources: [README.md:20-27](), [crates/browser-use-tui/src/settings.rs:74-83](), [crates/browser-use-tui/src/runtime.rs:20-42]()

Keep model and browser decisions separate. The code supports multiple model backends and multiple browser modes as independent settings, which keeps the architecture BYOC/BYOK friendly instead of tying the terminal to one model provider or one browser provider.

Sources: [crates/browser-use-tui/src/settings.rs:4-79](), [crates/browser-use-cli/src/main.rs:1288-1297]()

## Summary

Browser Use Terminal turns shell commands into Rust binaries through small wrappers and Python entry-point shims, keeps durable state in `~/.browser-use-terminal/state.db` plus an `artifacts` directory, and routes browser work through an explicit browser mode. Local Chrome, Headless Chromium, and Browser Use cloud are not separate apps; they are saved settings that become run options, agent instructions, worker environment, and browser-runtime commands.

Sources: [scripts/install/install.sh:670-808](), [crates/browser-use-store/src/lib.rs:104-145](), [crates/browser-use-core/src/lib.rs:177-205](), [crates/browser-use-tui/src/runtime.rs:75-101]()
