
Shirabe
Headless browser automation
Introduction
shirabe is a lightweight, Rust-native browser automation library and debug server. It drives any browser that speaks the Chrome DevTools Protocol — Google Chrome, Chromium, Microsoft Edge — through one hand-rolled CDP engine, and exposes the whole thing over a small HTTP API. It is the browser backbone extracted from the tairitsu packager, hardened to stand on its own.
The guiding idea is the same as ort for ONNX Runtime: you should never have to install a browser by hand. A pinned Chrome for Testing build is fetched into a shared cache at build time (or on first use), located transparently, and driven through CDP. Pin a different backend, ship native libs with your product, route the download through a mirror or proxy — all from environment variables.
Quick Start
CLI
# Zero-config: auto-discovers Chrome/Chromium/Edge, or fetches Chrome for Testing.
# Pin a backend, route the browser through a proxy.
SHIRABE_BACKEND=chromium
# Then drive it over HTTP.
npx (no Rust toolchain required)
Prebuilt binaries are published to npm, so you can run shirabe with a single command — no cargo build, no Chrome install:
The @celestia-island/shirabe root package pulls the right platform subpackage (-linux-x64 / -darwin-arm64 / -win32-x64) automatically via optionalDependencies + a postinstall selector. To pin a version:
Library
use ;
async
Backends & zero-config resolution
Pick a backend with SHIRABE_BACKEND=chrome|chromium|edge|firefox|servo|auto (default auto). The Chromium family (Chrome / Chromium / Edge) is driven in-process through our own CDP engine; Firefox and Servo take a different path — their cores are built by the browser vendors and shipped as dynamic libraries, which shirabe drives through a thin C-binding FFI contract (the foreign-engine feature, see Foreign Engines). Whichever is chosen, shirabe resolves it in this order:
- 1Backend-specific override — CHROME_PATH / CHROMIUM_PATH / EDGE_PATH.
- 2Build-time baked path — SHIRABE_BROWSER_PATH, emitted by build.rs when the `auto-fetch` feature downloads Chrome for Testing during the build.
- 3System binary on $PATH and well-known install locations.
- 4Runtime fetch (the runtime-fetch feature) — download the pinned build into the shared cache.
Download knobs (build time and runtime alike):
| Env | Purpose |
|---|---|
| SHIRABE_CHROME_VERSION | Override the pinned Chrome for Testing version. |
| SHIRABE_CHROME_MIRROR | Download from a mirror instead of storage.googleapis.com. |
| SHIRABE_CHROME_SHA256 | Optional hex checksum to verify the download. |
| SHIRABE_DOWNLOAD_PROXY | Route the download through http:// / https:// / socks5:// proxy. |
| SHIRABE_DOWNLOAD_TIMEOUT_SECS | Per-request timeout (default 600). |
| SHIRABE_SKIP_BROWSER_FETCH | Skip both build-time and runtime downloads. |
| SHIRABE_BACKEND | Which Chromium-family backend to drive. |
Shipping native libraries with your product
A fetched Chrome build (and your own crate) depend on native libraries that a clean container may lack. shirabe gives packagers two tools:
- Declarative bundle — list the .so / .dylib / .dll files to ship via `SHIRABE_BUNDLE_LIBS` (path-sep list) or `SHIRABE_BUNDLE_MANIFEST` (a `bundle.toml` of `[[lib]]` tables). Example manifest:
[[lib]]
path = "third_party/libfoo.so"
optional = true
target_os = "linux"
[[lib]]
path = "third_party/foo.dll"
- Dependency scan — shirabe::collect_runtime_deps(exe) enumerates the shared libraries a binary links against (`ldd` / `otool -L` / a PE import scan), and `shirabe::render_bundle_report(&BundleReport::build(&exe))` prints everything a release script should copy alongside the binary.
use ;
let report = build;
print!;
HTTP API
| Method | Path | Description |
|---|---|---|
| GET | /health | Server health |
| GET | /info | Browser status + selected backend + tab slots |
| POST | /navigate | Navigate to a URL |
| POST | /click | Click an element |
| POST | /type | Type text |
| POST | /evaluate | Execute JavaScript |
| POST | /screenshot | Capture a screenshot |
| POST | /wait-for-selector | Wait for an element |
| GET | /dom | Query the DOM |
| GET | /a11y | Accessibility tree |
| POST | /batch | Batch operations |
| POST | /browser/close | Kill the browser, release its tab slot |
| POST | /browser/restart | Restart the browser ({"override": true} exceeds the tab cap) |
…plus console, network and websocket capture endpoints for full control.
Resource guards
Headless browsers are memory-hungry, so shirabe enforces a set of rigid defaults that keep a swarm of sessions (or one runaway agent) from eating the machine. Every lock is tunable through the SHIRABE_* env namespace — i.e. the MCP server's env configuration — but the defaults are deliberately strict:
| Env | Default | Effect |
|---|---|---|
| SHIRABE_MAX_TABS | 3 | Machine-wide cap on concurrent browsers (a cross-process lease file with heartbeat-based expiry). Exceeding it denies the spawn with a warning that names both remedies: pass override=true (tool/API) or close other browsers first. |
| SHIRABE_IDLE_TIMEOUT_SECS | 300 | A browser with no commands for this long is recycled (killed, slot released). 0 disables. The next tool call lazily restarts it. |
| SHIRABE_TAB_MEM_LIMIT_MB | 150 | Per-tab JS-heap ceiling; the tab is force-crashed (Page.crash) when exceeded. 0 disables. |
| SHIRABE_FPS_CAP | 15 | requestAnimationFrame throttled to this many callbacks/sec on every page. 0 disables. |
| SHIRABE_DISABLE_PERF_LOCKS | unset | 1 / true turns the memory ceiling and fps cap off in one go. |
| SHIRABE_LEASE_FILE | temp dir | Location of the tab-lease heartbeat file (mainly for tests). |
Recycling always prefers a graceful Browser.close on the browser-level devtools socket, so the whole Chromium process tree exits — killing only the main process would orphan its renderer/gpu/network children on Windows.
MCP server
Build shirabe with the mcp feature and run the stdio server — it hosts the headless-browser debug API in-process (no separate shirabe debug daemon to launch) and exposes its operations to AI coding assistants over the Model Context Protocol:
The server advertises fourteen browser tools — browser_navigate, browser_navigate_back, browser_navigate_forward, browser_snapshot, browser_dom, browser_screenshot, browser_click, browser_type, browser_press_key, browser_evaluate, browser_console_messages, browser_resize, browser_close, and browser_analyze_screenshot — plus init, each proxying over loopback to the in-process CDP engine. browser_analyze_screenshot additionally sends the capture to a vision model (see Vision analysis) and returns the review text in one call. One process is both the browser and the MCP server; when it exits, Chrome is killed. The init tool takes resume (keep a healthy browser) and override (exceed the tab cap) flags; browser_close releases the tab slot for other sessions. Wire it into an MCP client:
Set SHIRABE_URL to change the page the browser opens on startup (default about:blank) and SHIRABE_DOWNLOAD_PROXY to route Chrome's traffic through a proxy. The browser backend, mirror, and fetch knobs from the library all still apply.
Development
SHIRABE_SKIP_BROWSER_FETCH=1
SHIRABE_SKIP_BROWSER_FETCH=1
License
SySL-1.0 (Synthetic Source License). See LICENSE or the SySL website.
Vision analysis
browser_analyze_screenshot asks a vision-capable model to review a screenshot (layout structure, rendering issues, CSS suggestions). Models are resolved from a provider-registry checkout — model TOMLs under models/<provider>/<model>.toml, endpoint configs under entrypoint/<provider>/default.toml:
| Env | Default | Effect |
|---|---|---|
| SHIRABE_VISION_REGISTRY | ./provider-registry or ../provider-registry | Explicit registry directory (must contain models/ + entrypoint/). |
| SHIRABE_VISION_MODEL | cheapest chat model with a key | Pin a model id; a typo is an error, never a fallback. |
| SHIRABE_VISION_API_KEY | unset | Explicit deployment-wide key override. Without it only the provider's own env var (e.g. XAI_API_KEY) is used — a generic key is never sent to a different provider. |
Only OpenAI-compatible chat endpoints (protocol = "openai_chat_v1") are supported, and image/video-generation models (max_output_tokens = 0) are excluded from selection.
MCP Server Deployment
For production MCP deployments, use an auto-restart wrapper to keep the server alive across updates without interrupting the client session.
Recommended launcher
#!/bin/bash while true; do /path/to/shirabe mcp sleep 0.2 done
How it works
- 1The wrapper runs shirabe mcp in a while true loop.
- 2If the process exits, it restarts within 0.2 seconds.
- 3To update: kill $(pgrep -f "shirabe mcp" | head -1)