bao-core
A high-performance anti-fingerprint browser runtime in a single Rust stack — SpiderMonkey + Servo + always-on Node.js/Bun APIs + built-in Stealth and CDP.
Package name vs import name: this package is
bao-core; the library name is pinned tobao.Cargo.tomlsaysbao-core, code saysuse bao::….
[]
= "0.1.0"
Usage — three entry points
1. Embed the browser (primary)
use Duration;
use ;
2. CDP automation (Playwright-style)
Start the CDP server from library config (BaoConfig::cdp_port), then
connect in-process or over WebSocket:
use ;
Pump contract — servo-domain CDP commands (Runtime.evaluate,
Page.navigate, …) execute on the runtime thread and need the main
thread to drive it: call runtime.pump_cdp(Duration) in a loop, or use
the run() loop form. Protocol-domain commands (Browser.version,
pages()) need no pumping. An unpumped servo-domain command times out
honestly after 2s — it never returns a fake value.
3. Node/Bun APIs inside the page (dual realm)
page.evaluate_js runs in the Node Realm: DOM and require / fs /
fetch / Bun in one scope.
const h1 = document.?.; // DOM (servo)
const txt = require.; // Node API (Bao)
const res = await ; // Node fetch
Node/Bun host setup without a page: bao::runtime:: (the bun_runtime
surface).
⚠ Same-name trap:
bao::runtime::BaoRuntime(Node/Bun host) ≠ top-levelbao::BaoRuntime(browser coordinator). Browser embedding uses the top-level name; Node/Bun host setup usesbao::runtime::.
Hard constraints & prerequisites
- JSContext is thread-local — DOM ↔ Node interop must stay on the
creating thread; never pass
JSObjectpointers across threads (SIGSEGV). Cross-thread, pass page ids / handles / serialized data. - Full stack always linked — no Cargo features disable browser/CDP/
stealth/Node; behaviour is a runtime choice (
StealthProfile,Permission). - First build compiles SpiderMonkey from source — clang, python3, make required; expect 20–40 min once (cached afterwards).
- Linux media playback needs system GStreamer runtime libraries — the
servo media stack (
bao-servo-media-autoon Linux) loads them at runtime:apt install libgstreamer1.0-0 gstreamer1.0-plugins-base gstreamer1.0-plugins-bad(or your distro's equivalent;-devpackages are a compile-time concern only). - Rust nightly toolchain required — the repo pins
nightly-2026-07-20(seerust-toolchain.toml); rustup consumers runrustup override set nightly-2026-07-20or equivalent. Stable fails with E0554 (#![feature]on a non-nightly compiler). - Disk & build time — the first build compiles SpiderMonkey from
source with symbols downgraded to line-tables (
-gdwarf-4 -g1, shipped in thebao-mozjs-sysbuild since the DWARF downgrade): the SM objects land at ~284 MB (roughly 6× smaller than full DWARF; single translation units shrink ~83%). Expect ~20–40 min for the first build and a target-dir peak in the single-digit GBs, not tens of GB. Requiresbao-core≥ 0.0.3 (its dependency closure locksbao-mozjs-sys140.13.0-6, where the downgrade ships). Consumers who want their own crates leaner can add[profile.dev] debug = "line-tables-only". BAO_<SUFFIX>environment variables are aliased ontoBUN_<SUFFIX>.
Package family
bao-core is the facade; the family includes bao-browser, bao-engine,
bao-stealth, bao-cdp, bao-cdp-client, bun-runtime and the bun_*
base layer, plus maintained forks bao-mozjs(-sys) (+ bao-mozjs-src-*
source satellites), bao-servo-*, bao-stylo, bao-ipc-channel.
License
MPL-2.0 (SpiderMonkey + Servo) · MIT (Bun-derived crates).