Building cdpx: A Driverless CDP Browser Engine for AI Agents
Playwright in Python or Go spent over 150MB on a Node.js subprocess before it touched a single DOM node. For an AI agent driving a browser, that driver is most of the cost. cdpx removes the driver entirely.
cdpx is a single Rust binary that speaks Chrome DevTools Protocol (CDP) over a raw WebSocket, injects a 4KB tree-walker into the page, and compresses interactive state into handles like @e1, @e2. It idles under 25MB RSS. No Node.js, no Python, no external process.
+------------------+ +------------------+ +------------------+
| AI Agent | | cdpx (Rust) | | Chromium |
| stdio JSON-RPC | <----> | ~1.2 MB binary | <----> | WebSocket :9222 |
| or CLI | MCP | tokio runtime | CDP | remote-debug |
+------------------+ +------------------+ +------------------+
|
| injects
v
+------------------+
| dom_walker.min.js|
| 4KB tree walker |
| + occlusion test |
+------------------+
The CDP Layer
cdpx skips driver bootstrap entirely. It connects tokio-tungstenite straight to Chromium's --remote-debugging-port WebSocket and issues CDP JSON-RPC commands. The crate pulls tokio, serde, clap, reqwest, which, url, and tempfile; the lockfile resolves to 148 transitive crates, and the release binary strips to 1.2MB.
The transport is one file: src/cdp.rs (229 lines). It holds an [Target.setAutoAttach] listener and routes raw CDP messages. Because there is no Runtime.enable on by default and no injected __playwright__binding__ global, the page sees no driver footprint that Cloudflare Turnstile or DataDome fingerprint.
Semantic Action-State Projection
Full DOM dumps are token garbage for an agent. cdpx injects dom_walker.min.js (4,001 bytes) which walks the tree, culls viewport-invisible and offscreen nodes, runs an occlusion hit-test, and emits only visible interactive elements. src/sasp.rs (97 lines) assigns monotonic handles and serializes the projection.
The per-turn output stays under 800 tokens. An open on Hacker News produces:
[Page: Hacker News] (url: https://news.ycombinator.com)
------------------------------------------------------------
@e1: link "Hacker News"
@e2: link "new"
@e3: link "past"
@e4: link "comments"
Hydration-Safe Actions
Next.js and Remix pages eat naive clicks: the handler is not attached yet when the driver clicks. cdpx runs actions in two phases:
- Scroll into view and measure the center rect in-page.
- Dispatch a trusted
Input.dispatchMouseEventsequence only after the DOM mutation and React fiber quiet periods settle.
src/action.rs (226 lines) owns this. The guard is not a fixed sleep; it waits for the mutation stream to go quiet, so dead clicks on rehydrating routes stop happening.
MCP over stdio
The same binary is an MCP server. cdpx --mcp speaks stdio JSON-RPC 2.0 and exposes three tools:
browser_navigate(url)- navigates, settles hydration, returns the SASP mapbrowser_act(ref, action, value?)- performsclickortypeon an@eNhandlebrowser_snapshot()- re-emits the token-compressed element map
Any agent host that reads stdio servers - Claude Code, Antigravity, Codex - declares one entry:
{
"mcpServers": {
"cdpx": {
"command": "cdpx",
"args": ["--mcp"]
}
}
}
CLI
For one-off automation the same engine is a CLI:
cdpx open "https://github.com/login"
cdpx type @e2 "octocat"
cdpx click @e4
Flag --headed runs a visible browser; the default is headless.
Build numbers
The whole engine is 1,145 lines of Rust across six source files, plus one embedded 4KB script. Release profile compiles with opt-level = "z", lto = true, codegen-units = 1, panic = "abort", and strip = true to keep the binary at 1.2MB and RSS under 25MB.
cdpx is published on crates.io and the source lives at github.com/AkashPriyadarshii/cdpx.
More Essays
Building Imperium: Offline-First Android Life Ledger in Flutter and Drift
How I built an offline-first Android discipline ledger using Drift SQLite in a background isolate, deterministic Pearson correlation analytics, and zero cloud dependencies.
projectsBuilding Patna: Zero-Bloat Civic Telemetry for a 3,000-Year-Old Imperial Metropolis
How we built a sub-35KB, zero-framework civic portal for Patna running live transit telemetry, weather, client-side RSS pagination, and bilingual i18n with zero build steps.
systemsBuilding c2proof: C to Rust Migration with Verification
A verifier-first c2rust wrapper. Give it a flat C repo, and it outputs a compiling Rust port PR alongside a mathematical proof artifact.