Skip to main content

A Rust-backed, CDP-first Python automation library with a Playwright-compatible sync API

Project description

Rustwright — Keep the Playwright API. Drop the driver.

A Rust rewrite of Playwright for Python and Node.js. Keep the API you know while an in-process Rust CDP engine drives Chromium — no Node driver subprocess and no Playwright-specific automation fingerprint. Alpha; Chromium-only.

status: alpha tests license: MIT Python 3.8+ Node.js Chromium only Discord


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 (separate process) ──CDP──► Chromium
rustwright:         your code ────────────────── raw CDP ─────────────────────► Chromium

[!WARNING] Alpha. Chromium-only. The Python package is published on PyPI; the experimental Node.js binding still builds from source. Need Firefox/WebKit or production maturity today? Use playwright-python. Full list: Limitations.

Quickstart

Rustwright is interoperable with Playwright — install it, change one import, and your existing code runs on the Rust engine.

Python

pip install rustwright
python -m rustwright install chromium
- 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()

Prefer not to touch imports at all? Python offers an opt-in shim — rustwright.enable_playwright_compat() — that redirects import playwright... to Rustwright at runtime.

Node.js (experimental)

The Node.js binding is not yet published on npm. Build it from source:

git clone https://github.com/Skyvern-AI/rustwright && cd rustwright/node
npm install
npm run build
- 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();

The build produces a local rustwright package; consume it from another project with npm install /path/to/rustwright/node (or npm link). Only a subset of the API surface is bridged — see Limitations.

515/515 shared parity cases pass against real Playwright (growing suite; full behavioral parity in progress). rustwright.async_api mirrors Playwright's async API (concurrency notes in Limitations).

Why Rustwright?

  • No Node driver subprocess. playwright-python launches 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 Signal hygiene.
  • Trusted input by default. Clicks and typing go through real CDP input events (Input.dispatchMouseEvent), not synthetic element.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.

Remote browsers (Skyvern)

Rustwright removes the local driver process; Skyvern Browser Sessions (from the team behind Rustwright) address the other half — hosting the browser. A session is a persistent cloud browser whose login, cookie, and tab state carry across runs, with configurable timeouts from 5 minutes to 24 hours (60 by default), proxies in 21 countries, and a live view in the Skyvern Cloud UI; sessions bill while open. Creating one returns a browser_address CDP endpoint, and Rustwright connects to it like any remote Chromium.

To get a browser_address: install the Skyvern SDK (pip install skyvern) and copy an API key from app.skyvern.com → Settings. Then:

import asyncio
from rustwright.async_api import async_playwright
from skyvern import Skyvern

async def main():
    session = await Skyvern(api_key="<SKYVERN_API_KEY>").create_browser_session()

    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(session.browser_address)
        page = await browser.new_page()
        await page.goto("https://example.com")

asyncio.run(main())

The sync API connects the same way: p.chromium.connect_over_cdp(browser_address).

Signal hygiene

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 reports playwright_driver: "none".
  • No Runtime.enable on the default path — a normal launch + navigate never enables the CDP Runtime domain, closing the Runtime.enable console-serialization leak behind isAutomatedWithCDP. (Console/page-error/binding opt-ins still enable it lazily — detectable by design.)
  • Headless identity normalized by default — launches with --disable-blink-features=AutomationControlled, rewrites HeadlessChrome/Chrome/ in the UA and client hints, and installs a navigator.webdriver cleanup 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 lazy Runtime.enable for console event/pageerror event/binding opt-ins). The claim is narrow: no Playwright-specific automation fingerprint, plus baseline signal hygiene.

Benchmarks

Rustwright does not headline a speed number yet: launch-facing claims are held to reproducible, isolated CI evidence (Testbox + capped Docker), which is not yet published. One local dev-host diagnostic exists today, where Rustwright won 16/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.

A separate local async-concurrency diagnostic measured ~41 MB for Rustwright versus ~121 MB for playwright-python's Python process plus Node driver — about 66% less client-side memory. That comparison covers the part the library controls; whole-process memory is Chromium-dominated and varied by scenario. It is also diagnostic, not CI-backed launch evidence.

Rustwright vs the 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.
  • 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 JSHandle follow-ups and drag/screenshot/bounding-box.
  • Signal hygiene is partial — 3 of 4 public fingerprint targets clean in local runs (CreepJS still detects headless). No undetectability promise.

Roadmap

  • Publish the Node.js binding to npm
  • CI / Testbox-backed benchmark evidence
  • Native async engine (remove the Python thread-pool bridge)
  • Broaden the Node.js surface (contexts, routing, locators)
  • Close remaining OOPIF gaps
  • Split the core into maintainable modules

Recently shipped:

  • Python package published to PyPI
  • OOPIF auto-attach with flattened CDP sessions
  • 515/515 shared parity suite green against real Playwright
  • Runtime.enable console-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)

Built with 🦀🐉 and a lot of CDP frames · Skyvern-AI/rustwright

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rustwright-0.1.0.tar.gz (2.8 MB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

rustwright-0.1.0-cp38-abi3-win_amd64.whl (3.7 MB view details)

Uploaded CPython 3.8+Windows x86-64

rustwright-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.0 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

rustwright-0.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (3.4 MB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

rustwright-0.1.0-cp38-abi3-macosx_11_0_arm64.whl (3.6 MB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

rustwright-0.1.0-cp38-abi3-macosx_10_12_x86_64.whl (3.8 MB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file rustwright-0.1.0.tar.gz.

File metadata

  • Download URL: rustwright-0.1.0.tar.gz
  • Upload date:
  • Size: 2.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for rustwright-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e2ee9c437d1036373f69b54eca760033f6d72da56bdb0567154985911fc74f23
MD5 3a84620d2af9bab46105192e87d5e45a
BLAKE2b-256 5651723f016530d05bef49c9cf01642d4d349370e9ef305a23589ac6d1b79e78

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0.tar.gz:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rustwright-0.1.0-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: rustwright-0.1.0-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 3.7 MB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for rustwright-0.1.0-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 9f4573b8729f5045b86d2f9b94d98c94670880309c77d69679141d949d670569
MD5 6e3e75a5e7583ee5c2438535d8ff10d7
BLAKE2b-256 c5c50c431efcfeff022ae5a35e40a35a7e8ae1099735668998a7cbcd093c2075

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0-cp38-abi3-win_amd64.whl:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rustwright-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for rustwright-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 be2ab87395d839d15f5e4089858224b5f8109103d3553e6e5874ba2a42979984
MD5 4d631631c55602038c54e6711fac6421
BLAKE2b-256 52d0ac71232f44f2ee6034c46669cd1a5a32b920718e2f53afd26ba64ec2094b

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rustwright-0.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for rustwright-0.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a6e02821bb826cef8ad9ee3d8eb3843c1b9bb5c1752ae85a71d06de26c975c39
MD5 5c2bfe75b1d57ca1668aeb43e5844f7c
BLAKE2b-256 723dad30cdefc41bdfda4f8cbfe1b2ae87733f9b21b5ae8225ccbf84db0cc665

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rustwright-0.1.0-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for rustwright-0.1.0-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 703c32d38c76a9fa8174d546b9bf0f8690e1be29f918ef5154ca8bd9f1ddeadd
MD5 8ca277659b5a3e2009c9f93a711e5d72
BLAKE2b-256 e9ccf886f58cec39c7bfd9a12375d3f7b3260d3078fc46372ec53b5bd05fc4a3

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rustwright-0.1.0-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for rustwright-0.1.0-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 cf77e92662f9f0307d7d7ce5d3a8a259319ea5a7d767f58f120b54e1d1db4e6a
MD5 a3b5cfd5b12d99ce06855801ca2c9e84
BLAKE2b-256 976f7de0628a0a439eb53a25b60c934e80596f1583d2e89330d04b7f8d0da9e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for rustwright-0.1.0-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: release-pypi.yml on Skyvern-AI/rustwright

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page