Shirabe

Shirabe

Headless browser automation

License: SySL-1.0
GitHub
Checks
Docs
docs.rs

English · 简体中文 · 繁體中文 · 日本語 · 한국어 · Français · Español · Русский · العربية

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

bash
1
2
3
4
5
6
7
8
9
10
# Zero-config: auto-discovers Chrome/Chromium/Edge, or fetches Chrome for Testing.
shirabe debug --port 3001

# Pin a backend, route the browser through a proxy.
SHIRABE_BACKEND=chromium shirabe debug --port 3001 --proxy http://localhost:7890

# Then drive it over HTTP.
curl -X POST http://localhost:3001/navigate \
  -H "Content-Type: application/json" -d '{"url":"https://example.com"}'
curl -X POST http://localhost:3001/screenshot -d '{}'

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:

bash
1
npx @celestia-island/shirabe debug --port 3001

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:

bash
1
npx @celestia-island/shirabe@0.1.0 debug --port 3001

Library

rust
1
2
3
4
5
6
7
8
9
10
11
12
13
use shirabe::{start_debug_server, DebugServerConfig};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let cfg = DebugServerConfig {
        base_url: "about:blank".to_string(),
        dev_port: 0,
        dist_dir: String::new(),
        package_name: String::new(),
        proxy: Some("http://localhost:7890".to_string()),
    };
    start_debug_server(cfg, 3001).await
}

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:

  1. 1
    Backend-specific override — CHROME_PATH / CHROMIUM_PATH / EDGE_PATH.
  2. 2
    Build-time baked path — SHIRABE_BROWSER_PATH, emitted by build.rs when the `auto-fetch` feature downloads Chrome for Testing during the build.
  3. 3
    System binary on $PATH and well-known install locations.
  4. 4
    Runtime fetch (the runtime-fetch feature) — download the pinned build into the shared cache.

Download knobs (build time and runtime alike):

EnvPurpose
SHIRABE_CHROME_VERSIONOverride the pinned Chrome for Testing version.
SHIRABE_CHROME_MIRRORDownload from a mirror instead of storage.googleapis.com.
SHIRABE_CHROME_SHA256Optional hex checksum to verify the download.
SHIRABE_DOWNLOAD_PROXYRoute the download through http:// / https:// / socks5:// proxy.
SHIRABE_DOWNLOAD_TIMEOUT_SECSPer-request timeout (default 600).
SHIRABE_SKIP_BROWSER_FETCHSkip both build-time and runtime downloads.
SHIRABE_BACKENDWhich 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:

toml
1
2
3
4
5
6
7
  [[lib]]
  path = "third_party/libfoo.so"
  optional = true
  target_os = "linux"

  [[lib]]
  path = "third_party/foo.dll"
rust
1
2
3
use shirabe::{BundleReport, render_bundle_report};
let report = BundleReport::build(&backend_exe);
print!("{}", render_bundle_report(&report));

HTTP API

MethodPathDescription
GET/healthServer health
GET/infoBrowser status + selected backend + tab slots
POST/navigateNavigate to a URL
POST/clickClick an element
POST/typeType text
POST/evaluateExecute JavaScript
POST/screenshotCapture a screenshot
POST/wait-for-selectorWait for an element
GET/domQuery the DOM
GET/a11yAccessibility tree
POST/batchBatch operations
POST/browser/closeKill the browser, release its tab slot
POST/browser/restartRestart 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:

EnvDefaultEffect
SHIRABE_MAX_TABS3Machine-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_SECS300A 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_MB150Per-tab JS-heap ceiling; the tab is force-crashed (Page.crash) when exceeded. 0 disables.
SHIRABE_FPS_CAP15requestAnimationFrame throttled to this many callbacks/sec on every page. 0 disables.
SHIRABE_DISABLE_PERF_LOCKSunset1 / true turns the memory ceiling and fps cap off in one go.
SHIRABE_LEASE_FILEtemp dirLocation 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:

bash
1
shirabe mcp

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:

json
1
2
3
4
5
{
  "mcpServers": {
    "shirabe": { "command": "shirabe", "args": ["mcp"] }
  }
}

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

bash
1
2
SHIRABE_SKIP_BROWSER_FETCH=1 cargo clippy --all-targets --all-features -- -D warnings
SHIRABE_SKIP_BROWSER_FETCH=1 cargo test --all-features

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:

EnvDefaultEffect
SHIRABE_VISION_REGISTRY./provider-registry or ../provider-registryExplicit registry directory (must contain models/ + entrypoint/).
SHIRABE_VISION_MODELcheapest chat model with a keyPin a model id; a typo is an error, never a fallback.
SHIRABE_VISION_API_KEYunsetExplicit 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

  1. 1
    The wrapper runs shirabe mcp in a while true loop.
  2. 2
    If the process exits, it restarts within 0.2 seconds.
  3. 3
    To update: kill $(pgrep -f "shirabe mcp" | head -1)