browser_oxide 0.1.3

Stealth headless browser engine in Rust: real HTML/CSS/DOM/JS, V8 via deno_core, own BoringSSL TLS/JA4 fingerprint, no Chromium, no CDP — for anti-bot web scraping, archival, and AI agents
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
# BrowserOxide — Headless Stealth Browser with Rust Core

*A from-scratch stealth browser engine in Rust — own BoringSSL TLS/JA4 fingerprint, real JS, no Chromium, no CDP. Python + MCP bindings.*

[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)]()
[![Built with Rust](https://img.shields.io/badge/built%20with-Rust-orange.svg)]()
[![Status: pre-1.0](https://img.shields.io/badge/status-research--grade%20pre--1.0-yellow.svg)]()
[![Anti-bot corpus: routed 118/126](https://img.shields.io/badge/anti--bot%20corpus-routed%20118%2F126-brightgreen.svg)]()
[![Memory: ~15x lighter than Chrome](https://img.shields.io/badge/memory-~15%C3%97%20lighter%20than%20Chrome-brightgreen.svg)]()
[![Bindings: Rust · Python · MCP](https://img.shields.io/badge/bindings-Rust%20%C2%B7%20Python%20%C2%B7%20MCP-informational.svg)]()

**BrowserOxide is a stealth headless browser engine written from scratch in
Rust** for web scraping, archival, and AI agents. It implements a real
HTML/CSS/DOM/JS browser — including its own BoringSSL TLS stack and a *native*
(not injected) browser fingerprint — with **no Chromium and no Chrome DevTools
Protocol (CDP) driver** underneath. You control every surface from the TLS
handshake through WASM to canvas, so the fingerprint is native rather than
injected.

> **TL;DR.** Every other native-fingerprint stealth browser is either Python-only
> (camoufox, a Firefox fork) or drives Chrome over CDP (nodriver, Obscura), where
> the automation is itself detectable. **As of 2026 there is no Rust equivalent to
> camoufox** — BrowserOxide is built to be exactly that. In a same-machine,
> same-IP cleanroom run it routed **118 of 126** commercially-protected sites
> (Cloudflare, Akamai, DataDome, PerimeterX, Kasada) to a real render with **zero
> per-vendor bypass code**. Kasada is the one honest open gap. And because it's a
> single Rust process with no Chromium underneath, it renders pages in **tens of
> MB — roughly 15× lighter than a headless-Chrome process tree** (see
> [Memory]#memory-15-lighter-than-headless-chrome).

**Get started:** [Rust](docs/getting-started-rust.md) · [Python](docs/getting-started-python.md) (`pip install browser-oxide`) · [MCP for AI agents](#mcp-server-for-ai-agents) · [Benchmark](docs/BENCHMARK.md) · [How it compares](#how-it-compares)

> **Status: research-grade, pre-1.0.** API surfaces are not stable. License is MIT OR Apache-2.0.

## Why this exists

Most stealth tooling today wraps a real browser and hides the puppet strings.
CDP-driver stealth plugins patch a handful of JS properties at runtime and
lose to `Function.prototype.toString` checks. Patched-Chromium forks still
inherit Chromium's CDP detection vectors. Patched-Firefox forks ride a
browser engine with low market share, which is itself a fingerprint signal.

BrowserOxide is a different bet: build the engine from the parser up so
the fingerprint properties are *native*, not *injected*. There is no
Chrome process, no CDP client, no WebDriver, no patched-fork inheritance.
Whether that's the *right* bet is empirical — see the numbers section.

## What it is

A complete browser engine for scraping, archival, and AI agent workloads:

- **V8 JavaScript** via `deno_core` 0.403 — full ES2024+, WASM, JIT
- **Arena-allocated DOM** with `NodeId` (Copy, u32) handles, Shadow DOM, iframes
- **Own CSS engine** — Syntax L3 tokenizer, Selectors L4 matcher, cascade,
  `@layer`/`@media`/`@container`, computed styles
- **Real Canvas** — 2D rendering via `tiny-skia`, WebGL stubs, AudioContext
- **Layout** via `taffy` with font metrics for `getBoundingClientRect()`
- **Stealth HTTP** — own TLS stack (`boring2`/BoringSSL), Chrome-matched
  ClientHello, HTTP/1.1 + HTTP/2 (HTTP/3 wired but disabled by default —
  vanilla `quinn-proto` emits randomized transport parameters which is a
  worse fingerprint than not speaking h3)
- **CDP-compatible debugging surface** — drop-in target for Puppeteer/
  Playwright via WebSocket
- **EventSource (SSE)** for streaming endpoints
- **Configurable browser identity** — load profiles from YAML or JSON at
  runtime, or use the built-in `chrome_148_*` / `firefox_135_*` /
  `pixel_9_pro_chrome_148` / `iphone_15_pro_safari_18` presets

## How it compares

The native-fingerprint stealth-browser field, and where BrowserOxide sits:

> **BrowserOxide is the only one built from scratch** — a Rust/Python engine
> with **no Chromium, no Chrome/CDP, and no browser fork** underneath. Every
> other tool is either a fork (camoufox = Firefox), a Chrome driver (nodriver,
> Obscura, undetected-chromedriver), or not a browser at all (curl_cffi).

| Tool | Language | Engine | No CDP/WebDriver | Native fingerprint | Runs JS | TLS impersonation |
|---|---|---|:--:|:--:|:--:|:--:|
| **BrowserOxide** | **Rust/Python** | **from-scratch** ||| ✅ (V8) | ✅ (JA3/JA4) |
| camoufox | Python | Firefox fork || ✅ (patched into Firefox) | ✅ (Gecko) | partial (Firefox NSS) |
| nodriver / Obscura | Python | real Chrome (CDP) | ❌ (uses CDP) | ❌ (injected) | ✅ (Chrome) | ❌ (real Chrome TLS) |
| undetected-chromedriver | Python | real Chrome (WebDriver) | ❌ (WebDriver) | ❌ (injected) | ✅ (Chrome) ||
| curl_cffi | Python | none (HTTP/TLS only) | n/a | n/a || ✅ (JA3/JA4) |

**The takeaway:** camoufox proved native-fingerprint stealth works, but it's a
Python-only Firefox fork. CDP-driven tools (nodriver, Obscura) inherit Chrome's
automation-detection surface. curl_cffi nails the TLS handshake but runs no
JavaScript, so it loses to any JS challenge. **As of 2026 there is no Rust
equivalent to camoufox** — BrowserOxide fills that gap: native fingerprint, no
CDP, real JS, and a Rust core with Python + MCP bindings.

## What it can do (measured, not estimated)

Anti-bot coverage measured against a 126-site corpus of commercially-
protected pages (Cloudflare, Akamai, DataDome, PerimeterX, Kasada,
Shape/F5, etc.), release build. These numbers are from the
vendor-stripped open-source engine — no per-vendor bypass code in the
tree. Same machine, same IP, same hour, same classifier
(`browser_oxide::engine_classify`).

| BrowserOxide profile        | **Pass** (real render, ≥15 KB) | loose `L3` tag |
|------------------------------|--:|--:|
| `chrome_148_macos`           | **114** | 118 |
| `firefox_135_macos`          | **111** | 115 |
| `pixel_9_pro_chrome_148`     | **114** | 118 |
| `iphone_15_pro_safari_18`    | **118** | 121 |
| **best-of-4 routed**         | **118** | 121 |

The headline `Pass` column is the honest gate: the engine's `L3-RENDERED`
tag **and** `≥15 KB` of actual content (`ChallengeVerdict::Pass`, the rule
the engine's own audit harness uses). The loose column counts the
`L3-RENDERED` tag alone — it over-counts by ~4, because the corpus has
10–15 SPA-bootstrap sites that ship a 2–13 KB shell to *any* HTTP client
and get tagged `L3-RENDERED` by the *absence* of a challenge marker even
though no real render happened (e.g. `duolingo` returns a 13.5 KB shell —
**not** a pass). "Routed" = the caller picks the best profile per domain,
which most real scraping pipelines do naturally. Full breakdown and the
scoring caveats (the strict gate also has a few false *negatives*) are in
[`docs/BENCHMARK.md`](docs/BENCHMARK.md).

(Profile labels reflect the actual emitted User-Agent. All presets ship a
current Chrome 148 / Firefox 135 / Safari 18 identity.)

**The hard residual** is seven sites that returned no real content on any
profile: three Kasada pages (`canadagoose.com`, `hyatt.com`,
`realtor.com` — no OSS tool publicly passes Kasada from scratch),
`etsy.com` (DataDome interactive Device-Check, human-gated, out of
scope), `duolingo.com` (CSR SPA that only reaches its shell),
`wildberries.ru` (WBAAS), and `homedepot.com` (Akamai sec-cpt — flaky;
renders on a good risk-roll). `adidas.com` is *not* in this set — it
passes via routing (chrome + iphone render the 1.5 MB storefront; firefox
+ pixel stay at the interstitial). `adidas`/`homedepot` are both flaky
Akamai, so the exact residual shifts ±1–2 sites run to run.

> **The engine carries the number, not bypass code.** A/B measurements
> with per-vendor challenge solvers enabled vs fully removed from the
> tree show no difference in routed pass rate. Every site that renders,
> renders on the from-scratch TLS + fingerprint + V8 engine alone. The
> open-source engine ships no solver implementations (see "Challenge
> solving" below).

### Things to know before believing the numbers

- **Anti-bot responses are noisy.** Single sweep runs vary by ±5 sites
  from WAF lottery alone (measured per-site 3× re-tests: amazon variants
  have ~1-in-3 pass rate per fetch). The routed `118` is the central
  tendency of one cleanroom run, not a guaranteed per-run result.
- **Kasada is the OSS-wide gap.** No open-source tool publicly passes
  Kasada from scratch.

### Memory: ~15× lighter than headless Chrome

BrowserOxide is a **single Rust process** — no Chromium, no CDP, no renderer
subprocesses — so it renders a page in **tens of MB** while a headless Chrome
instance spins up a multi-process tree that runs into the **gigabytes**. Peak
resident set size (RSS), **5-run median**, same box, same pages — BO
single-process `VmHWM` vs Chrome's *full* process tree (browser + renderers +
GPU + utility, summed):

| Page | BrowserOxide | headless Chrome 147 | |
|---|--:|--:|--:|
| example.com (528 B) | **49 MB** | 1,028 MB | **~21× lighter** |
| wikipedia (~230 KB) | **63 MB** | 1,111 MB | **~18× lighter** |
| x.com (SPA) | **87 MB** | 1,302 MB | **~15× lighter** |
| nytimes.com | **133 MB** | 1,743 MB | **~13× lighter** |

Across the full 126-site corpus, BO's median per-page RSS is ~79 MB. This is
the practical edge for fleets: where one Chrome wants ~1–2 GB/page, you can run
**10–20+ BrowserOxide pages in the same footprint**.

### Per-page performance

Per-page wall-clock, 5-run median, same box, single IP, warm binary cache:

| Path | example.com (528 B) | hacker news (~35 KB) | wikipedia (~230 KB) |
|---|--:|--:|--:|
| cold (`Page::navigate`) | 244 ms | 444 ms | 849 ms |
| **warm pool (`PagePool::navigate`)** | **141 ms** | **333 ms** | **724 ms** |

A warm `PagePool` amortizes V8 isolate + snapshot setup across
navigations (the `PagePool::navigate(url)` API).

Full per-profile + per-site breakdown: [`docs/BENCHMARK.md`](docs/BENCHMARK.md).

## FAQ

### Is there a Rust equivalent to camoufox?
BrowserOxide is built to be exactly that. camoufox is a native-fingerprint
stealth browser, but it's a Python-only Firefox fork. As of 2026 there is no other
from-scratch stealth browser engine in Rust — BrowserOxide provides a native
fingerprint, no CDP, real JavaScript execution, and a Rust core with Python and
MCP bindings.

### Is BrowserOxide a Chromium or Firefox fork?
No. The HTML parser, CSS engine, DOM, layout, and TLS stack are written from
scratch in Rust; JavaScript runs on V8 via `deno_core`. There is no Chrome
process and no CDP driver, so it doesn't inherit Chromium's automation-detection
vectors (`navigator.webdriver`, `cdc_*` variables, CDP WebSocket fingerprints).

### How is it different from nodriver, Obscura, or undetected-chromedriver?
Those drive a real Chrome over CDP or WebDriver. BrowserOxide is its own engine
— no automation protocol underneath — so the fingerprint is native rather than
patched onto an automated Chrome that vendors can detect.

### How is it different from curl_cffi?
curl_cffi impersonates a browser's TLS handshake but doesn't run JavaScript, so it
fails any site needing JS or a real DOM. BrowserOxide ships a full V8 runtime,
real DOM/CSS/layout/canvas **and** a Chrome-matched TLS fingerprint.

### Does it pass Cloudflare, Akamai, and DataDome?
In a measured 126-site cleanroom run it routed 118/126 commercially-protected
sites to a real render, including most Cloudflare, Akamai, AWS WAF, and DataDome
pages — with no per-vendor bypass code. DataDome's interactive Device-Check
(human-gated, e.g. etsy.com) is not cleared.

### Does it pass Kasada?
No. Kasada (e.g. canadagoose.com, hyatt.com, realtor.com) is the standing open
gap; no open-source tool publicly passes Kasada from scratch, and BrowserOxide
ships no Kasada solver.

### Can I use it from Python?
Yes — `pip install browser-oxide`, then `Browser` / `Page` / `Profile` / `Verdict`
(the GIL is released during navigation). There's also an MCP server so AI agents
can drive the engine. See [docs/getting-started-python.md](docs/getting-started-python.md).

### Does it work with Puppeteer or Playwright?
Yes, via a CDP-compatible WebSocket server (`CdpServer::start_navigable`) as a
drop-in target — but nothing drives a real browser underneath. There is no
Selenium/WebDriver support.

### Is it production-ready?
It's research-grade and pre-1.0; APIs are not stable and the crates aren't on
crates.io yet. Anti-bot pass rates are point-in-time and vary ±5 sites per run
from WAF noise. MIT OR Apache-2.0.

## Challenge solving

The engine exposes a `ChallengeSolver` trait + a
`Page::navigate_with_solvers(url, profile, n, solvers)` entry point so
embedders can plug in per-vendor challenge handling (Akamai BMP
sensor_data, Kasada PoW, DataDome interstitial round-trip, Cloudflare
orchestrator). **The open-source engine ships no solver
implementations** — `Page::navigate` registers an empty set, so a
challenged page resolves to `ChallengeVerdict::ChallengeIncomplete`
rather than being auto-cleared. This is deliberate (see `SCOPE.md`):
site-specific bypass code is out of scope here, and — per the A/B
measurement above — it isn't what produces the corpus pass rate
anyway.

## Architecture

```
HTML → DOM (+ Shadow DOM) → CSS → Layout → JS (V8 + WASM) ← Stealth profile
  ↑          ↑                       ↓
  │       iframes              Canvas 2D (tiny-skia)
  │                                  ↓
  └────── HTTP/1+2 (stealth TLS) ────┘
```

15 crates, MIT OR Apache-2.0. No GPL/LGPL/AGPL. The only MPL-2.0 in
the default tree is `cooked-waker`, pulled in transitively via
`deno_core` → `v8` and linked unmodified; MPL-2.0 is file-scope
copyleft so this does not infect downstream code. An optional
`blocker` Cargo feature (off by default) adds Brave's MPL-2.0
`adblock` crate. Both are explicit per-crate exceptions in
`deny.toml`. Full crate inventory in `docs/ARCHITECTURE.md`.

| Crate | Description |
|---|---|
| `css_parser` | CSS Syntax Level 3 tokenizer + parser (with nesting) |
| `css_selectors` | Selectors Level 4 parser + matcher |
| `css_values` | CSS property value parsing + computed values |
| `css_cascade` | Cascade, `@layer`, `@media`, `@container`, inheritance |
| `dom` | Arena DOM + Shadow DOM + iframe contexts |
| `html_parser` | `html5ever` integration → DOM |
| `js_runtime` | V8 (`deno_core`) + DOM bindings + WASM + Web APIs |
| `canvas` | Canvas 2D (`tiny-skia`) + WebGL stubs + AudioContext |
| `layout` | Box model via `taffy` (`getBoundingClientRect`) |
| `net` | HTTP/1+2+3 + stealth TLS (`boring2`/BoringSSL) + WebSocket + SSE |
| `event_loop` | Timers, microtasks, Promises, rAF |
| `workers` | Web Workers + Service Workers (separate V8 isolates) |
| `stealth` | Fingerprint profiles (100+ properties), navigator spoofing |
| `protocol` | CDP server (Puppeteer/Playwright drop-in) |
| `browser` | Top-level `Browser`/`Page` API + `ChallengeSolver` trait |

## Quick start

The engine is `!Send` (V8 isolates are per-thread), so run it on a current-thread
runtime + `LocalSet`:

```rust
use browser::{ChallengeVerdict, Page};

fn main() {
    let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    tokio::task::LocalSet::new().block_on(&rt, async {
        let profile = stealth::presets::chrome_148_macos();   // built-in identity
        let mut page = Page::navigate("https://example.com", profile, 5).await.unwrap();

        println!("{}", page.title());
        println!("{}", page.evaluate("document.querySelectorAll('a').length").unwrap());

        // challenge pages return HTTP 200 — trust the verdict, not the status:
        if page.challenge_verdict() == ChallengeVerdict::Pass {
            println!("real content rendered");
        }
    });
}
```

Runnable: `cargo run --release -p browser_oxide --example getting_started -- https://example.com`.
Full walkthrough: [docs/getting-started-rust.md](docs/getting-started-rust.md).

### Configurable browser identity

The browser identity (UA string, Chrome version, screen, locale, TLS
impersonation label, etc.) is a `StealthProfile`. Load one from disk:

```rust
use stealth::StealthProfile;

let profile = StealthProfile::load_from_file("profiles/chrome_148_macos.yaml")?;
profile.validate()?;
let mut page = Page::navigate("https://example.com", profile, 5).await?;
```

YAML and JSON are both supported; format is picked by extension. See
`crates/browser_oxide/profiles/chrome_148_macos.yaml` for the full field
schema. The struct definition (`StealthProfile` in `crates/browser_oxide/src/stealth/
profile.rs`) is the source of truth — every field is documented there.

### Python

```python
from browser_oxide import Browser, Profile, Verdict

with Browser(profile=Profile.chrome()) as b:
    page = b.navigate("https://example.com")
    print(page.title, len(page.html), page.verdict)
    if page.verdict == Verdict.PASS:
        print(page.evaluate("navigator.userAgent"))
```

`pip install browser-oxide` (or `maturin develop` from source). Full guide:
[docs/getting-started-python.md](docs/getting-started-python.md).

### MCP server (for AI agents)

A Model Context Protocol server (`browser-oxide-mcp`) lets an AI agent drive the
stealth engine — tools: `fetch_page`, `evaluate`, and `check_protection` (*"is
this URL behind Akamai/DataDome/Kasada, and did a real render get through?"*).

```json
{ "mcpServers": { "browser-oxide": { "command": "browser-oxide-mcp" } } }
```

### CDP server (Puppeteer/Playwright drop-in)

```rust
use protocol::CdpServer;

let server = CdpServer::start_navigable(9222)?;
// Connect with Puppeteer to ws://127.0.0.1:9222
```

## Build and test

```bash
cargo test --workspace -- --test-threads=1    # V8 isolates are per-thread
cargo clippy --workspace -- -D warnings
cargo fmt --all -- --check

# Live anti-bot sweep (needs internet, --release for fair timing)
cargo test --release -p browser_oxide --test holistic_sweep -- --ignored --test-threads=1 --nocapture

# Sweep your own URL list with rich per-site metrics (timing, tag, body len):
cargo run --release -p browser_oxide --example sweep_metrics -- <profile> <corpus.json> <out.json>
```

`<corpus.json>` is a JSON array of `{ "cat", "name", "url" }` entries.
Runtime tuning (navigation budgets, the `BROWSER_OXIDE_SECCPT_BUDGET_MS`
sec-cpt budget, stealth, snapshot, debug) is documented in
[docs/CONFIGURATION.md](docs/CONFIGURATION.md).

The browser-comparison harness (`crates/browser_oxide/tests/browser_comparison
.rs`) compares against locally-installed Chrome / Lightpanda when those
binaries are present; it is `#[ignore]` by default.

## Documentation

**Using it**

| Guide | Description |
|---|---|
| [Getting started (Rust)]docs/getting-started-rust.md | Install, navigate, read the page, verdicts, pooling |
| [Getting started (Python)]docs/getting-started-python.md | `pip install browser-oxide`; the `Browser`/`Page`/`Profile` API |
| [Profiles]docs/guides/PROFILES.md | Choosing & customizing browser identities; routing |
| [Challenges]docs/guides/CHALLENGES.md | Verdict semantics + the `ChallengeSolver` extension point |
| [Stealth FAQ]docs/guides/STEALTH_FAQ.md | What's native vs. not — the honest boundary |
| [Debugging]docs/guides/DEBUGGING.md | Thin renders, fetch logs, JS errors, the probes |
| [Configuration]docs/CONFIGURATION.md | Environment variables — navigation budgets, stealth, snapshot, debug, sweep harness |
| [CDP server]docs/guides/CDP.md | Puppeteer/Playwright drop-in |
| [Benchmark]docs/BENCHMARK.md | Measured anti-bot pass rates |
| [Memory]docs/MEMORY.md | Footprint vs headless Chrome (~15× lighter) + reproduction steps |

**Engine internals**

| Doc | Description |
|---|---|
| [Architecture]docs/ARCHITECTURE.md | Workspace layout, dependency graph, external deps |
| [Stealth profiles]docs/STEALTH.md | Custom browser identity — fields, loading from YAML/JSON, consistency rules |
| [Networking]docs/NETWORKING.md | HTTP/1+2+3 + stealth TLS + WebSocket + SSE |
| [CDP Protocol]docs/PROTOCOL.md | Puppeteer/Playwright drop-in surface |
| [CSS Parser]docs/CSS_PARSER.md / [Selectors]docs/CSS_SELECTORS.md / [Values]docs/CSS_VALUES.md / [Cascade]docs/CSS_CASCADE.md | The CSS engine |
| [DOM]docs/DOM.md | Arena DOM + Shadow DOM + iframes + Web APIs |
| [JS Runtime]docs/JS_RUNTIME.md | V8 + `deno_core` + WASM + API surface |
| [Canvas]docs/CANVAS.md | Canvas 2D + WebGL stubs + AudioContext |
| [Layout]docs/LAYOUT.md | Box model via `taffy` |
| [Event Loop]docs/EVENT_LOOP.md | Timers, microtasks, rAF, Promises |
| [Workers]docs/WORKERS.md | Dedicated/Shared/Service Workers |

## Use, scope, what this is not for

This is engine-side research. The intended use is automated browsing for
archival, accessibility, AI agents, security research, and CTF-style
challenges where you have legitimate authorization to access the target
site. The repository ships an engine, not a "circumvent paywalls" recipe
list; site-specific recipes and reverse-engineering notes are kept in a
private companion repository.

If you build a product on top of this, respect the target site's terms,
robots policy, and rate limits. The maintainer is not responsible for
downstream misuse.

## License

Licensed under either of

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE)
- MIT license ([LICENSE-MIT]LICENSE-MIT)

at your option. Unless explicitly stated otherwise, any contribution
intentionally submitted for inclusion shall be dual-licensed as above,
without any additional terms or conditions.

## Citation

```bibtex
@software{browseroxide,
  title  = {BrowserOxide: A From-Scratch Stealth Headless Browser Engine in Rust},
  author = {Yury Fedoseev},
  year   = {2026},
  url    = {https://github.com/yfedoseev/browser_oxide}
}
```

---

**Rust** + **Python** + **MCP** | MIT OR Apache-2.0 | from-scratch stealth headless browser engine — no Chromium, no CDP, no fork | routed 118/126 on a 126-site anti-bot corpus