Obscura Architecture — 8 Crates, Single V8 Isolate, and CDP Dispatch

Deep dive into Obscura's architecture: 8 crate layer design, single V8 Isolate, DOM tree implementation, CDP dispatch, and lifecycle management.

16Yun Engineering TeamJul 5, 20262 min read

From Command to Bytes

When Puppeteer calls page.goto('https://example.com'), what happens inside Obscura? This article traces the full path through 8 crates.

The 8 Crates

obscura-cli       CLI entry: fetch / serve / scrape / mcp
obscura-cdp       CDP WebSocket: routing, 12 domain handlers
obscura-browser   Page type: navigation, lifecycle, context
obscura-js        V8 runtime: deno_core, bootstrap.js, Rust ops
obscura-dom       DOM tree: html5ever parsing, selectors, serialization
obscura-net       HTTP: reqwest, wreq stealth client, cookie jar, tracker blocklist
obscura-mcp       MCP protocol server: 35 tools
obscura           Embeddable library API: Browser / Page / Element / CookieStore
```bash
 
## Request Flow
 
A `Page.navigate` from a CDP client:
 
```bash
CDP Client (Puppeteer)
 WebSocket frame

obscura-cdp/server.rs          accept, route by sessionId


obscura-cdp/dispatch.rs        method router, acquires v8_lock


obscura-cdp/domains/page.rs    Page.navigate handler


obscura-browser/page.rs        navigate_with_wait

       ├──► obscura-net/client.rs       HTTP fetch

       ├──► obscura-dom/tree.rs         parse HTML into DOM tree

       └──► obscura-js/runtime.rs       run inline scripts

                 └──► bootstrap.js + ops.rs   DOM bindings
```bash
 
The dispatcher emits CDP events (`Network.requestWillBeSent`, `Page.frameNavigated`, `Page.lifecycleEvent`) back through the same WebSocket.
 
## Single V8 Isolate
 
All pages share one V8 Isolate. This is Obscura's most important architectural decision.
 
```rust
let _guard = obscura_js::v8_lock::global().lock().await;
page.evaluate(expr).await;
```bash
 
The V8 Isolate is single-threaded by design. All JavaScript operations must acquire this global lock.
 
The dispatcher routes long-running operations through `process_with_interception`, which spawns them onto a tokio `LocalSet` so the dispatcher keeps handling other CDP messages.
 
## DOM Tree
 
Obscura uses `html5ever` for HTML parsing with a custom `tree_sink`.
 
```rust
// obscura-dom/src/tree.rs
struct Node {
    id: NodeId,
    parent: Option<NodeId>,
    children: Vec<NodeId>,
    node_type: NodeType,  // Element / Text / Comment / Document
}
 
struct Tree {
    nodes: Vec<Node>,
    root: NodeId,
}
```bash
 
Key features:
 
- **Cycle guards**: `append_child` and `insert_before` reject inserting an ancestor. A cyclic reparent once made `descendants()` loop forever.
- **Selector engine**: Uses the `selectors` crate (Servo's CSS selector implementation).
- **descendants() cap**: Hard limit on tree traversal depth.
 
## CDP Dispatch
 
Session IDs are `"{targetId}-session"`. The dispatcher routes by `sessionId` to the correct `Page`.
 
`process_with_interception` extracts long-running operations and spawns them into independent tasks, freeing the dispatcher for other messages.
 
## Lifecycle State Machine
 
Managed by `obscura-browser/lifecycle.rs`:
 
```bash
init commit domcontentloaded load networkidle2 networkidle0
```bash
 
## Storage Persistence
 
```bash
<storage-dir>/
├── cookies.json
└── localStorage/
    ├── <origin-1>.json
    └── <origin-2>.json
```bash
 
## Stealth Integration
 
`--stealth` is a global CLI flag. In `scrape`, it is forwarded to workers via `OBSCURA_STEALTH`.
 
## Why 8 Crates
 
- **Single responsibility**: Each crate covers one capability domain
- **Compile caching**: Changing one crate does not rebuild V8
- **Test boundaries**: DOM tests do not need networking, CDP tests do not need V8
- **Optional features**: Stealth only affects `obscura-net`
 
## Summary
 
Obscura's architecture is a series of performance-constrained decisions: a single V8 Isolate avoids thread synchronization overhead, Rust directly calls V8's C API without a bridge layer, and the DOM tree lives in Rust without serialization overhead.

Need an enterprise proxy plan?

We can tailor architecture to your target domains, concurrency, and reliability goals.