Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
A Rust rewrite of Playwright, a popular browser automation library. Rustwright is interoperable with Playwright but runs on an in-process Rust CDP engine — 2.55× faster and 70% less memory (no Node driver), with no Playwright automation fingerprint. Alpha; Chromium-only.
What is Rustwright?
Rustwright is a browser automation library for Python and Node.js that keeps the Playwright API you already know but drives Chromium from a native Rust engine speaking raw Chrome DevTools Protocol — no driver subprocess in the path.
playwright-python: your code ──pipe──► Node driver ──CDP──► Chromium
rustwright: your code ────────── raw CDP ──────────► Chromium
Quickstart
Rustwright is interoperable with Playwright — install it, change one import, and your existing code runs on the Rust engine.
Python
- from playwright.sync_api import sync_playwright
+ from rustwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
page.goto("https://example.com")
print(page.title())
browser.close()
Node.js (experimental)
Install from npm:
The Node binding drives an existing Chromium/Chrome — point Rustwright at it with RUSTWRIGHT_CHROMIUM, CHROME, or CHROMIUM. Prefer to build from source? git clone the repo and run npm install && npm run build in node/.
- import { chromium } from 'playwright';
+ import { chromium } from 'rustwright';
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
console.log(await page.title());
await browser.close();
Only a subset of the API surface is bridged — see Limitations.
Why Rustwright?
- No Node driver subprocess.
playwright-pythonlaunches and pipes to a bundled Node driver. Rustwright's engine is native — the browser-control code runs in-process. - Raw CDP, in Rust. A from-scratch async CDP client — not a wrapper around another automation library.
- No Playwright automation fingerprint. The driver never loads, so its signatures never appear. See Automation detection.
- Trusted input by default. Clicks and typing go through real CDP input events (
Input.dispatchMouseEvent), not syntheticelement.click()DOM calls. Untrusted DOM shortcuts are opt-in only. - Cross-origin iframes (OOPIF). Auto-attaches out-of-process iframe targets with flattened CDP sessions and routes
frame_locator()across origins. - One engine, two languages. The same Rust core backs the Python and Node bindings.
How it works
One Rust core — an async CDP client built on Tokio (WebSocket, with opt-in Unix-pipe transport) — talks to Chromium directly, and thin PyO3 (Python) and napi-rs (Node) bindings expose it in-process. The two-line diagram above is the entire architecture.
Already have a Chromium/Chrome binary? Point Rustwright at it with RUSTWRIGHT_CHROMIUM, CHROME, or CHROMIUM.
Browser automation for AI agents
Two agent-facing surfaces drive Chromium from a compact accessibility snapshot
with stable element refs (e1, e2, …) instead of raw HTML or screenshots:
take a snapshot, then act on an element by its ref. Refs are session-scoped and
never reused; resolution is best-effort for cooperative pages (not a security
boundary), and snapshots reflect page values with password fields masked.
CLI — rustwright-agent
Ships with the rustwright package (pure Python, no extra runtime dependency).
Named sessions keep one browser alive across commands:
Full verbs, flags, session model, and threat model: docs/agent-interfaces.md.
MCP server — rustwright-mcp
A Model Context Protocol server (a separate,
opt-in package under mcp/) exposing browser tools — browser_navigate,
browser_snapshot, browser_click, browser_type, … — over stdio, so
MCP-compatible agents (Claude Code, Claude Desktop, and others) can browse with
Rustwright. Register it with Claude Code, no clone required:
…or point any MCP client at it:
Key environment variables: RUSTWRIGHT_MCP_HEADLESS (0 runs headed),
RUSTWRIGHT_MCP_CHANNEL (Chromium channel, e.g. chrome), and
RUSTWRIGHT_MCP_ALLOW_EVAL (1 exposes the browser_evaluate tool, off by
default). Full setup, tool list, and configuration: mcp/README.md.
Remote browsers (Skyvern)
Rustwright drives browsers — but you still need somewhere to run them. Skyvern (the team behind Rustwright) offers hosted Browser Sessions as a paid service that funds this project.
Features:
- Persistent cloud browsers — logins, cookies, and tab state carry across runs
- Configurable timeouts — 5 minutes to 24 hours (60 min default)
- Proxies in 21 countries
- Live view — watch and interact with the session in the Skyvern Cloud UI
Each session returns a browser_address CDP endpoint that Rustwright connects to like any remote Chromium (sessions bill while open).
Get started:
- Make an account at app.skyvern.com
- Grab your API key from Settings
pip install skyvern
= await
= await
= await
await
Remote sessions are Python-only for now — Rustwright's Node binding doesn't support
connect_over_cdpyet (it's on the Roadmap).
Automation detection
Because Rustwright never loads Playwright's Node driver, it never emits the automation signatures that ship with it:
- No Playwright driver signatures — no
__playwright__binding__/ utility-world globals, no driver bootstrap. The backend reportsplaywright_driver: "none". - No
Runtime.enableon the default path — a normal launch + navigate never enables the CDP Runtime domain, closing theRuntime.enableconsole-serialization leak behindisAutomatedWithCDP. (Console/page-error/binding opt-ins still enable it lazily — detectable by design.) - Headless identity normalized by default — launches with
--disable-blink-features=AutomationControlled, rewritesHeadlessChrome/→Chrome/in the UA and client hints, and installs anavigator.webdrivercleanup init script.
Local fingerprint runs — default Playwright failed webdriver/headless checks that Rustwright passed; these are local diagnostics, not a guarantee:
| Probe | Result |
|---|---|
| SannySoft | ✅ Clean |
| BrowserScan | ✅ Clean |
| DeviceAndBrowserInfo | ✅ Clean (after the Runtime-domain cleanup) |
| CreepJS | ⚠️ Detects headless |
[!IMPORTANT] Rustwright is not "undetectable." It is not a CAPTCHA or Cloudflare bypass, and it is not fully CDP-invisible — it still uses CDP primitives (
Target.setAutoAttach, init scripts, and lazyRuntime.enablefor console event/pageerror event/binding opt-ins). The claim is narrow: no Playwright-specific automation fingerprint, plus baseline signal hygiene.
Benchmarks
The headline numbers are local diagnostics, not yet capped-CI evidence. On speed, one dev-host run (warm browser, 5 iterations) won 16 of 17 case means:
| Run | Cases | Rustwright | playwright-python | Speedup |
|---|---|---|---|---|
| Local dev host (warm browser, 5 iterations) | 17 | 5,256 ms | 13,418 ms | 2.55× |
Treat it as a diagnostic, not a launch claim — it is not capped-Docker/CI evidence. Methodology: BENCHMARK.md.
On memory, a form-fill diagnostic recorded the client library's footprint at 133.5 MiB for playwright-python (Python + Node driver) versus 40.6 MiB for Rustwright (no driver) — about 70% less; a separate async-concurrency diagnostic measured ~66% less on the same client-stack basis. Both cover the part the library controls — Chromium-dominated whole-process memory is roughly equal — and both are demo-grade diagnostics, not capped-CI evidence.
Alternatives
| Rustwright | playwright-python | Puppeteer | Patchright | |
|---|---|---|---|---|
| API | Playwright-shaped (Py + Node) | Official Python Playwright | JS/TS Puppeteer | Playwright drop-in fork |
| Engine / transport | Rust core, raw CDP | Python → Node driver | Node over CDP | Patched PW driver |
| In-process engine (no driver subprocess) | ✅ | ❌ bundled Node driver | ✅ Node is the runtime | ❌ Playwright-style driver |
| Browsers | Chromium only | Chromium, Firefox, WebKit | Chrome, Firefox | Chromium-based |
| Default input | Trusted CDP events | Browser-level | Browser / CDP | Playwright + stealth |
| Cross-origin iframes | OOPIF (alpha) | Mature | Frame APIs | Inherits Playwright |
| Playwright fingerprint | No | Yes | n/a | Patched |
| Maturity | 🟠 Alpha | 🟢 Mature | 🟢 Mature | 🟡 Focused fork |
Rustwright's lane: a Rust CDP engine under the Playwright API, for Chromium.
Limitations
See LIMITATIONS.md for detail.
- Alpha — API shape covered; full behavioral parity not yet proven.
- API coverage — ~96% of Playwright's Python sync API (515 of 536 methods; 411 exercised by the shared parity registry); the async API provides 488 of 536. Full report:
docs/PARITY.md. - Chromium only — Firefox and WebKit error explicitly.
- Node bindings are early — a subset of the surface is bridged (
launch,newPage,goto,click,fill,title,textContent,evaluate,screenshot,close); contexts, routing, tracing, and locators are Python-only for now. - Async concurrency (Python) — the async API wraps the sync engine via threads; recommended for ≈≤25 concurrent workflows/process, not high fan-out.
- OOPIF — residual gaps in non-main-frame
JSHandlefollow-ups and drag/screenshot/bounding-box. - Automation detection is partial — 3 of 4 public fingerprint targets clean in local runs (CreepJS still detects headless). No undetectability promise.
Roadmap
- Kotlin binding — idiomatic Kotlin wrapper (Kotlin/JVM can already consume the Java FFM binding)
- Grow the new language bindings beyond the alpha subset (contexts, routing, locators)
- Rustwright MCP server — expose browser automation as tools for MCP-compatible AI agents (mcp/)
- CI / Testbox-backed benchmark evidence
- Broaden the Node.js surface (contexts, routing, locators)
- Close remaining OOPIF gaps
Recently shipped:
- Language bindings (alpha) — Go, Java, C#/.NET, Ruby, and PHP over a shared C ABI, plus a native Rust API (
bindings/CONTRACT.md); cross-language equivalence gated in CI - Python package published to PyPI
- Node.js binding published to npm
- Native async engine over the Tokio CDP core
- OOPIF auto-attach with flattened CDP sessions
- 515/515 shared parity suite green against real Playwright
-
Runtime.enableconsole-serialization leak closed on the default path
Firefox and WebKit are not planned — Rustwright is deliberately Chromium-only.
Contributing
Rustwright is Rust + Python + Node. cargo builds the engine; maturin develop --release installs the Python package; cd node && npm run build builds the Node addon; the Python suite exercises the engine against real Chromium. Full Docker gate: 1,046 tests pass (6 skipped), plus 515/515 shared parity cases run against real Playwright; CI (test.yml) runs a fast representative subset on every PR.
See CONTRIBUTING.md for build details and the code-layout reality.
Project status
Rustwright is an early alpha from Skyvern, developed in the open. If the architecture resonates, give it a ⭐.
Questions, ideas, or want to help? Join the Skyvern community on Discord.
License
MIT © 2026 Ikonomos Inc (dba Skyvern)