The crates.io package ships the engine and nothing else
Playwright's API on a Rust CDP engine, Chromium browser automation for Python & Node, no driver subprocess. (Alpha).
At a glance
- What is it?
- A Playwright-compatible automation library that keeps the familiar API but drives Chromium over raw CDP from a native Rust engine, with no Node driver in the path. The engineering details worth reading are the packaging, the measurement rig in the Dockerfile, and the parts of the surface that are still Alpha.
- Who is it for?
- Rustwright is worth a try if you want Playwright's shape without a Node process in the middle, particularly for local agent work where the compact snapshots and the absence of a driver both matter, and the Python path is the one to use since the Node binding is marked experimental. Three things to check before you commit.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- Skyvern-AI/rustwright954 ★ · +126
| Repository | 2026-08-02 | 2026-11-13 |
|---|---|---|
| Skyvern-AI/rustwright | 828 | 954 |
126 Stars in the last 103 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The architecture diagram is the whole architecture
The comparison between this and Playwright fits in two lines:
playwright-python: your code ──pipe──► Node driver ──CDP──► Chromium
rustwright: your code ────────── raw CDP ──────────► ChromiumThe removed box in the middle is the point of the project. Playwright's Python package launches a bundled Node driver and pipes to it, while here the browser control code runs in process. That is also the source of the two headline numbers in the header, 2.55 times faster and 70 percent less memory, since there is no second runtime to start, feed and keep resident.
Underneath, one Rust core speaks the protocol directly: an async CDP client built on Tokio over WebSocket, with an opt-in Unix pipe transport. Thin bindings expose it to Python through PyO3 and to Node through napi-rs. The README is explicit that the CDP client is written from scratch rather than wrapping another automation library.
The two bindings are not equally finished. Python is the documented path, and the Node one is labelled experimental, which matches how they obtain a browser: Python downloads its own with `python -m rustwright install chromium`, while the Node binding expects a Chromium or Chrome already on the machine and is pointed at it through RUSTWRIGHT_CHROMIUM, CHROME or CHROMIUM.
The published crate is a subset of the repository
The Cargo manifest carries a comment explaining what gets published:
> The crates.io package carries only the engine sources, not the bindings, > tests, or workflows of the whole repository.
The include list that follows is the evidence, three globs for the Rust sources under src/, the README and the LICENSE. Two crates are on the registry, rustwright and rustwright-core, and the workspace has four members, the Node binding, a C API crate, a rust-native directory and an agent crate.
Two version details sit oddly next to each other. The Python package and the core crate are both 0.3.0, and there are no GitHub releases at all, so the numbers come from the package registries only. And the Python package declares requires-python of 3.9 up to but not including 3.16, while the PyO3 dependency is configured for abi3-py38, an ABI level one version lower than the floor the package advertises.
There is also a feature that the manifest forbids from shipping. test-support pulls in rustls and the webpki roots for native connection probes and local certificate injection, and its comment says default and release wheel builds must not enable it.
Installing the package quietly registers a pytest plugin
The Python distribution declares two entry points beyond the console script. There is a `rustwright` command, and there is a pytest11 entry point pointing at a plugin module inside the package.
That second one has a consequence worth stating plainly: installing rustwright into an environment where you run pytest activates its plugin, whether or not you are testing browser code. The package is a test automation library, so that is defensible, but it is the kind of side effect that surprises someone who installed it as a dependency of an application.
The other metadata is conventional. The build backend is maturin with the pyo3 extension-module feature, the Python sources live in the python/ directory, the extension module is named rustwright._rustwright, and there is a dev extra with pytest. There is also a compare extra that pulls playwright 1.50 or newer, which exists so the benchmark can run the library it is comparing against.
The pytest configuration points at a tests directory and adds a short summary flag, so the plugin and the suite meet in the same place.
The Dockerfile is a measurement rig rather than a runtime image
The container file at the root is not packaging a service. It is building an environment in which the benchmarks can run, and the arguments say so.
The base image defaults to python:3.13-slim-bookworm and is itself an argument, so it can be swapped. Two more arguments do the interesting work: a reference Playwright version pinned at 1.59.0, and a flag to install Puppeteer, off by default. Then four path variables point at three separate browser caches, one for rustwright, one for Playwright, one for a Playwright reference tree, and a fifth for a Puppeteer package directory under a benchmark node_modules.
Three browser installations in one image is the clearest sign this is a harness. The reference version argument exists so the comparison always runs against the same Playwright build, and the Puppeteer flag is off because most benchmark runs do not need it.
The toolchain side installs build-essential, ffmpeg, git, pkg-config, unzip and xz, then Rust with the minimal rustup profile. It also sets PIP_BREAK_SYSTEM_PACKAGES, which is the flag that lets pip write into the system environment of a slim image.
The two headline numbers come from two different places
The header states 2.55 times faster and 70 percent less memory, and each links somewhere different. The speed figure points at the benchmark section of the README, and the memory figure points into BENCHMARK.md at a section titled client memory form fill diagnostic.
So the memory number is anchored to one workflow, filling a form, and the header presents it as a general property. That is a narrower claim than the sentence around it, and it is the kind of distinction that matters when you are deciding whether a given page load will fit in a container.
The other header claim is a design consequence rather than a measurement. No Playwright automation fingerprint, because the driver never loads and therefore its signatures never appear. The same section of the README backs trusted input with a specific mechanism: clicks and typing go through real CDP input events, Input.dispatchMouseEvent, rather than synthetic DOM calls such as element.click().
Cross-origin iframes are handled too, with out-of-process iframe targets auto-attaching, flattened CDP sessions, and frame_locator() routed across origins.
Element refs are session scoped and not a security boundary
The agent-facing surface is a compact accessibility snapshot with element references, e1, e2 and so on, rather than raw HTML or screenshots. Three properties are stated and they matter in different ways.
Refs are session scoped, and they are never reused, so a reference in one snapshot does not silently point at a different element in the next. They are also described as best effort rather than as a security boundary, which is the honest framing for something derived from a page you do not control. And snapshots include page values while masking password fields.
The CLI is the smallest way to see it:
rustwright-cli open https://example.com
rustwright-cli snapshot # compact page tree with @eN refs
rustwright-cli click @e1
rustwright-cli closeA comment inside that block is a reminder that this is a shell interface for an agent loop: the snapshot returns a tree with @eN references, and a click takes one of them. The same limitation applies to the MCP server, which is described as the canonical Rustwright MCP server and exposes browser_* tools over stdio with compact snapshots and inline PNG screenshots.
Seven language directories, two documented installations
The repository root has directories for python, node, go, java, php, ruby, csharp, capi, rust-native, bindings, agent, cli, mcp, benchmarks, tools, docs, examples and tests. That is seven language directories and four binding layers.
The badges advertise fewer: PyPI, npm, two crates, RubyGems, NuGet and Maven Central. The README documents installation for three things: Python with pip, Node with npm, and the native CLI with a curl pipe. So a Go, Java, PHP, C# or Ruby user has a directory in the tree and possibly a registry listing, and no install command in the README.
The badge block even explains its own restraint, in an HTML comment. The MCP npm package publishes on a separate mcp-npm-v tag family rather than the main version tag, and the comment says not to add its badge until that tag has shipped. It has not: the MCP install documented here is from source, `cargo install --git` against this repository, with the npm package described as on the way.
Since there are no GitHub releases, that tag discipline is also why nothing in the repository tells you when 0.3.0 was published.
Editorial conclusion
Rustwright is worth a try if you want Playwright's shape without a Node process in the middle, particularly for local agent work where the compact snapshots and the absence of a driver both matter, and the Python path is the one to use since the Node binding is marked experimental. Three things to check before you commit. Read LIMITATIONS.md, because the README says only a subset of the API surface is bridged and a rewrite is not a drop-in for code that leans on the rest. Treat the two headline numbers as what they are, a speed figure from the benchmark section and a memory figure from one form-fill diagnostic, and measure your own pages rather than the demo. And decide how the library enters your environment, because the package registers a pytest plugin on install, which will be active in any environment where your tests run.
Frequently asked questions
What is Rustwright?
A browser automation library for Python and Node that keeps the Playwright API but drives Chromium from a native Rust engine speaking raw CDP, with no driver subprocess. You change one import, from playwright.sync_api to rustwright.sync_api, and the same code runs against the Rust engine. The project describes itself as Alpha and Chromium only.
How do I install Rustwright for Python?
pip install rustwright, then python -m rustwright install chromium to fetch a browser. The package requires Python 3.9 or newer and below 3.16, the distribution is built with maturin, and the underlying crate states a Rust floor of 1.85. CPython 3.15 is verified against a release candidate and CI tracks the 3.15 development branch until general availability.
Can Rustwright use a Chrome or Chromium I already have?
Yes, through the RUSTWRIGHT_CHROMIUM variable, or the shorter CHROME and CHROMIUM names. The Node binding requires an existing browser this way, and the MCP server accepts the same variable in its environment. Otherwise the downloaded browser from python -m rustwright install chromium is found automatically.
Is Rustwright a drop-in replacement for Playwright?
It is interoperable rather than complete. The README says only a subset of the API surface is bridged and points to LIMITATIONS.md for the details, and the project is labelled Alpha with Chromium as the only supported browser. A compare extra that installs Playwright 1.50 or newer exists so the benchmarks can run the comparison.
How do I give an agent a browser through Rustwright?
Use rustwright-mcp, a native Rust MCP server that exposes browser_* tools over stdio with compact accessibility snapshots and inline PNG screenshots, installed from source with cargo install --git against the repository. With Claude Code it registers as claude mcp add rustwright -- rustwright-mcp. Element references are session scoped and never reused, and the documentation states they are best effort rather than a security boundary.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/skyvern-ai-rustwright)Star history chart for your README
[](https://hysenlabs.com/projects/skyvern-ai-rustwright)Was this useful?