Skip to content

About

Headless browser in pure Rust — Servo-powered, CDP-compatible, designed for AI agents

Resources

Code of conduct

Contributing

Security policy

Stars

33 stars

Watchers

0 watching

Forks

Repository files navigation

OxiBrowser logo

🌐 OxiBrowser

The headless browser built in pure Rust for AI agents.

Not a Chromium fork. Not a C++ wrapper. A browser engine written from scratch in Rust, designed from day one for automation, web scraping, and AI-driven workflows.

CI Crates.io docs.rs GitHub release License: MIT GitHub stars Rust

Report Bug · Request Feature · Read the Docs · Discord


~44 MB
Single static binary
~50 ms
Cold start time
~8 MB
Base memory
697 tests
Full coverage
Rust-first
C toolchain for TLS only
OxiBrowser Headless Chrome
~44 MB binary ~400 MB install
~8 MB RAM base ~200 MB RAM base
~50 ms startup ~800 ms startup
Pure Rust (boa) C++ (V8)
MIT BSD / ToS

✨ Why OxiBrowser?

You're building AI agents that need to browse the web. You don't need a full browser with GPU rendering, audio output, and extension support. You need something fast, small, and programmable.

OxiBrowser is built for exactly that use case:

  • 🤖 AI-Agent First — CLI designed for agents: --json output, describe for schema, skill for prompts, session for multi-step
  • ⚡ Blazing Fast — Cold starts in ~50ms, no Chromium overhead, no Node.js required
  • 🦀 Rust-First — boa_engine (JS, no V8), html5ever (HTML) are pure Rust. TLS uses btls (BoringSSL C binding) for stealth fingerprint emulation. Single static binary.
  • 🔌 CDP Compatible — Puppeteer, Playwright, and any Chrome DevTools Protocol client works out of the box
  • 🛡️ Secure by Default — SSRF protection with CIDR blocking, robots.txt respect, no sandbox escape surface
  • 📦 Tiny Footprint — ~44 MB binary, ~8 MB base memory. Run 100 instances without breaking a sweat

📋 Changelog

See CHANGELOG.md for the full version history and GitHub Releases for release notes.

Recent milestones (v0.17–v0.20): per-frame JS execution contexts (iframe isolation), Shadow DOM (open/closed/declarative/slots), async fetch/XMLHttpRequest/WebSocket, CORS + preflight, cookie PSL/prefixes, multi-tab, PDF export, @font-face webfonts, Blitz/Stylo/Taffy/Parley rendering pipeline, stealth bot-detection, Canvas 2D, custom-element lifecycle, and a 12-domain CDP server.

🚀 Quick Start

Install

cargo install oxibrowser

Fetch a page (human-readable)

$ oxibrowser fetch https://example.com

Example Domain

# Example Domain

This domain is for use in documentation examples...
[Learn more](https://iana.org/domains/example)

Fetch a page (agent mode)

$ oxibrowser fetch https://example.com --json
{"ok":true,"data":{"url":"https://example.com/","title":"Example Domain","status":200,"markdown":"..."},"meta":{"elapsed_ms":152}}

Extract structured data

$ oxibrowser extract https://example.com --links --json
{"ok":true,"data":{"links":["https://iana.org/domains/example"],"title":"Example Domain"}}

Multi-step session (stdin/stdout JSON REPL)

$ oxibrowser session
new
{"ok":true,"data":{"tab_id":"t1"}}
goto t1 https://example.com
{"ok":true,"data":{"status":200,"title":"Example Domain"}}
eval t1 document.title
{"ok":true,"data":{"value":"Example Domain"}}
close t1
{"ok":true,"data":{"closed":"t1"}}
exit
{"ok":true,"data":{"exit":true}}

Start CDP server (Puppeteer/Playwright)

oxibrowser serve --port 9222
import puppeteer from 'puppeteer-core';

const browser = await puppeteer.connect({
    browserWSEndpoint: 'ws://127.0.0.1:9222',
});

const page = await browser.newPage();
await page.goto('https://news.ycombinator.com');
console.log(await page.title());
await browser.close();

📋 CLI Reference

oxibrowser <COMMAND>

COMMANDS:
  fetch      Fetch a URL and return content (markdown default)
  extract    Extract structured data (links, text, elements)
  run        Run a YAML automation script
  session    Interactive stdin/stdout JSON REPL (22 commands)
  serve      Start CDP WebSocket server
  search     Web / GitHub / GitHub-issues search (no browser needed)
  describe   Print CLI schema as JSON (for agents)
  skill      Print agent skill guide
  version    Print version information

fetch — One-shot page fetch

# Human-readable (markdown, default)
oxibrowser fetch https://example.com

# Agent mode
oxibrowser fetch https://example.com --json

# Click then read
oxibrowser fetch https://example.com --click button --wait .result --json

# Quick page summary
oxibrowser fetch https://example.com --summary --json

# Run JS
oxibrowser fetch https://example.com --eval "document.title" --json

# Limit response size
oxibrowser fetch https://example.com --max-bytes 8000 --json

# Select specific fields
oxibrowser fetch https://example.com --fields url,title,status --json

extract — Structured data extraction

# Get all links
oxibrowser extract https://example.com --links --json

# Extract elements by CSS selector
oxibrowser extract https://example.com --selector "a" --all --attrs text,href --json

# Title + full text
oxibrowser extract https://example.com --title --text --json

session — Multi-step automation

oxibrowser session  # Start REPL

# 22 commands:
new, goto, back, forward, reload, click, fill, press, type,
select, check, uncheck, scroll, eval, extract, content,
screenshot, wait, close, close --all, list, help, exit

describe — Agent introspection

# Compact (~200 tokens)
oxibrowser describe --compact

# Full command details
oxibrowser describe fetch
oxibrowser describe session

search — Web / GitHub search (no browser needed)

# Web search (DuckDuckGo)
oxibrowser search "rust async" --engine ddg --max-results 5 --json

# GitHub search
oxibrowser search "memory pool" --source github --json

# GitHub issues for a specific repo
oxibrowser search "panic on shutdown" --source github-issues --repo project-oxi/oxibrowser --json

run — YAML automation

name: example
steps:
  - step_type: goto
    data:
      goto: https://example.com
  - step_type: content
    data:
      format: markdown
oxibrowser run script.yaml

JSON Output Format

All --json responses follow the same schema:

{
  "ok": true,
  "data": { ... },
  "meta": { "elapsed_ms": 152 }
}

On error:

{
  "ok": false,
  "error": "URL scheme must be http or https",
  "error_code": "INVALID_URL"
}

Exit codes: 0=success, 1=runtime, 2=input validation, 3=timeout, 4=network


🏗 Architecture

┌──────────────────────────────────────────────────────┐
│            Puppeteer / Playwright / Rust CDP          │
└────────────────────────┬─────────────────────────────┘
                         │ CDP WebSocket
                         ▼
┌──────────────────────────────────────────────────────┐
│                 CDP Server (12 domains)               │
│  Browser · DOM · Emulation · Fetch · Input            │
│  Log · Network · OXI · Page · Runtime                 │
│  Target · Tracing                                      │
├──────────────────────────────────────────────────────┤
│          Browser → Session → Page → Frame            │
├──────────┬──────────┬──────────────┬─────────────────┤
│  WebAPI  │  Network │  JS Runtime  │  Rendering      │
│  DOM     │  HTTP    │  boa_engine  │  Blitz+Stylo    │
│  Tree    │  Cookies │  ES2024+     │  Taffy+Parley   │
│  Storage │  SSRF    │  per-frame   │  PNG/PDF/font   │
├──────────┴──────────┴──────────────┴─────────────────┤
│  html5ever · encoding_rs · reqwest · boa · blitz-dom  │
└──────────────────────────────────────────────────────┘

Crate Structure

Crate Lines Purpose
oxibrowser 6,103 Binary + CLI (8 subcommands, session REPL, agent features)
oxibrowser-core 35,287 Browser engine: Session, Page, Frame, JS Runtime, Network
oxibrowser-cdp 5,643 CDP WebSocket server with 12 domain handlers
oxibrowser-render 513 Blitz/Stylo/Taffy/Parley rendering pipeline (PNG/PDF/fonts)
Total ~47,500

🌟 Features

Agent-First CLI

Designed for AI agent workflows — no daemon, no socket, single binary:

Feature Description
--json Machine-readable output (opt-in, human by default)
--max-bytes N Truncate response to N bytes
--fields a,b,c Select specific output fields
--summary Quick page metadata (title, links, headings)
describe CLI schema as JSON for agent introspection
skill Agent skill guide for prompt injection
session Stdin/stdout JSON REPL with 22 commands
Exit codes 0=success, 1=runtime, 2=input, 3=timeout, 4=network

JavaScript Runtime (ES2024+)

Powered by boa_engine — pure Rust, no V8 dependency:

Web API Status
document.querySelector / querySelectorAll ✅ Full
document.createElement / createTextNode ✅ Full
element.appendChild / removeChild / insertBefore ✅ Full
element.getAttribute / setAttribute / removeAttribute ✅ Full
element.cloneNode / remove() ✅ Full
element.style (CSSStyleDeclaration) ✅ Property accessor
element.classList (DOMTokenList) ✅ Property accessor
element.textContent / innerHTML ✅ Read/Write
element.addEventListener / dispatchEvent ✅ Full
element.click() ✅ With event handlers
fetch() ✅ Full (channel bridge)
XMLHttpRequest ✅ Full with callbacks
localStorage ✅ Persistent
MutationObserver ✅ observe/disconnect/takeRecords
setTimeout / setInterval ✅ TokioJobQueue
console.log/warn/error ✅ With formatting
URL / URLSearchParams ✅ Full
crypto.getRandomValues ✅ Pseudo-random
TextEncoder / TextDecoder ✅ UTF-8
atob / btoa ✅ Base64
requestAnimationFrame ✅ Polyfill

CDP Protocol (Chrome DevTools Protocol)

12 domain handlers — Puppeteer and Playwright compatible:

Domain Key Methods
Browser getVersion, close
DOM getDocument, describeNode, querySelector, querySelectorAll, getBoxModel, getContentQuads
Emulation setDeviceMetricsOverride, clearDeviceMetricsOverride, setUserAgentOverride
Fetch enable/disable, continueRequest, failRequest, fulfillRequest, getResponseBody
Input dispatchKeyEvent, dispatchMouseEvent, dispatchDragEvent, insertText
Log enable, entryAdded
Network enable/disable, setExtraHTTPHeaders, getResponseBody, JS-fetch/XHR/WS lifecycle events
OXI 🤖 getMarkdown, getPageInfo — AI-native extensions
Page navigate, captureScreenshot, printToPDF, getFrameTree, getTitle
Runtime evaluate, callFunctionOn, enable, consoleAPICalled, exceptionThrown
Target getTargets, createTarget, attachToTarget, setAutoAttach (multi-tab)
Tracing start, end, getCategories (Playwright tracing compatible)

OXI Domain — Built for AI Agents

import websockets, json, asyncio

async def ai_scrape():
    ws = await websockets.connect('ws://localhost:9222/ws')
    
    await ws.send(json.dumps({
        "id": 1, "method": "Page.navigate",
        "params": {"url": "https://news.ycombinator.com"}
    }))
    await asyncio.sleep(2)
    
    # Clean markdown — perfect for LLM ingestion
    await ws.send(json.dumps({"id": 2, "method": "OXI.getMarkdown"}))
    resp = json.loads(await ws.recv())
    print(resp['result']['markdown'])

Network Layer

| HTTP Client | reqwest with cookie persistence, redirect following | | Cookie Jar | Domain-scoped cookies: PSL, __Host-/__Secure- prefixes, expiry, CHIPS partitioning | | CORS | Preflight (OPTIONS), Access-Control-* validation (Fetch §3.2–3.3) | | Auth | Basic + Digest (401-challenge retry) | | Proxy | HTTP / HTTPS / SOCKS proxy via BrowserConfig.proxy | | SSRF Protection | CIDR blocking for private network ranges, scheme-aware | | robots.txt | RFC 9309 compliant parser, --obey-robots flag | | Network Interception | Pause, modify, or block any request via Fetch domain | | WebSocket | Full browser WebSocket API (ws + wss) | | Stealth | Chrome JA4+ fingerprint emulation, bot-detection challenge retry |

Rendering Pipeline

Powered by the Blitz rendering stack — isolated in oxibrowser-render so Stylo/html5ever dependency trees stay out of the core workspace:

  • Blitz-dom — DOM tree representation for layout and paint
  • Stylo (Firefox CSS engine) — CSS cascade, computed styles
  • Taffy — block/inline/flex layout
  • Parley — text shaping with @font-face webfont support
  • PNG screenshots — shadow-DOM-aware rasterization (flattened tree compose)
  • PDF export — Page.printToPDF / Tab::print_to_pdf via printpdf
  • HTML → Markdown — full conversion with heading, link, and list support

🧪 Testing

# Run all tests
cargo test --workspace

# CLI integration tests (fast, no network)
cargo test -p oxibrowser --test cli

# E2E CDP tests
cargo test -p oxibrowser-cdp

# Integration tests (real websites, requires internet)
cargo test --workspace -- --ignored

🔧 Advanced Usage

Rust API

use oxibrowser_core::Browser;
use oxibrowser_core::config::BrowserConfig;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let browser = Browser::new(BrowserConfig::default()).await?;
    let session = browser.new_session().await?;
    
    session.navigate("https://example.com").await?;
    
    let title = session.evaluate("document.title").await?;
    println!("Title: {:?}", title);
    
    Ok(())
)
}

Use as a library

[dependencies]
oxibrowser-core = "0.11"
# Or the CDP server:
oxibrowser-cdp = "0.11"

Request Interception

const client = await page.target().createCDPSession();

await client.send('Fetch.enable', {
    patterns: [{ urlPattern: '*ads*' }]
});

client.on('Fetch.requestPaused', async ({ requestId }) => {
    await client.send('Fetch.failRequest', {
        requestId,
        reason: 'BlockedByClient'
    });
});

🤝 Contributing

See CONTRIBUTING.md for full guidelines.

git clone https://github.com/project-oxi/oxibrowser.git
cd oxibrowser
cargo build
cargo test --workspace
cargo clippy --workspace -- -D warnings

📄 License

OxiBrowser is licensed under the MIT License.

🙏 Acknowledgments

  • boa_engine — Pure Rust JavaScript engine (ES2024+)
  • html5ever — HTML parser from the Servo project
  • reqwest — Ergonomic HTTP client for Rust
  • tokio — Async runtime powering the entire networking stack

⬆ Back to Top

Made with 🦀 in Rust

About

Headless browser in pure Rust — Servo-powered, CDP-compatible, designed for AI agents

Resources

Code of conduct

Contributing

Security policy

Stars

33 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages