Files
obscura/README.md
T
2026-06-08 21:51:39 +02:00

426 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<img src="https://raw.githubusercontent.com/h4ckf0r0day/obscura/main/assets/icon.png" alt="Obscura" width="80" />
</p>
<h2 align="center">Obscura</h2>
<p align="center">
<strong>The open-source headless browser for AI agents and web scraping.</strong><br>
Lightweight, stealthy, and built in Rust.
</p>
---
Obscura is a headless browser engine written in Rust, built for web scraping and AI agent automation. It runs real JavaScript via V8, supports the Chrome DevTools Protocol, and acts as a drop-in replacement for headless Chrome with Puppeteer and Playwright.
### Why Obscura over headless Chrome?
Designed for automation at scale, not desktop browsing.
| Metric | Obscura | Headless Chrome |
|--------------|--------------|------------------|
| Memory | **30 MB** | 200+ MB |
| Binary size | **70 MB** | 300+ MB |
| Anti-detect | **Built-in** | None |
| Page load | **85 ms** | ~500 ms |
| Startup | **Instant** | ~2s |
| Puppeteer | **Yes** | Yes |
| Playwright | **Yes** | Yes |
## 🎉 10,000 stars and what's next
We are working on **Obscura Cloud** the hosted version, with managed infrastructure, residential proxies, and dedicated support. For people who want the engine without operating it themselves.
The open-source engine stays Apache-2.0, fully featured. No feature gating, ever.
**[Get on the waitlist →](https://tally.so/r/gDWzdD)**
## Sponsors
**Obscura** is supported by sponsors who help keep development independent.
Want to sponsor? Email [[email protected]](mailto:[email protected]).
<table>
<tr>
<td width="200" align="center" valign="middle">
<a href="https://www.swiftproxy.net/?ref=obscura" target="_blank">
<img alt="Swiftproxy" src="assets/sponsors/swiftproxy2.png" width="180"/>
</a>
</td>
<td valign="middle">
<a href="https://www.swiftproxy.net/?ref=obscura"><b>Swiftproxy</b></a> — Premium residential proxies built for privacy, automation, and scale.<br><br>
<b>80M+ residential IPs in 190+ countries<br>
Rotating & sticky sessions<br>
Non-expiring traffic<br>
Free trial available<br>
Country, state & city targeting<br>
HTTP, HTTPS & SOCKS5 support<br><br>
🎁 Use code <b>PROXY90</b> for <b>10% off</b>.<br><br></b>
</td>
</tr>
<tr>
<td width="200" align="center" valign="middle">
<a href="https://proxyempire.io/?ref=obscura&utm_source=obscuragithub" target="_blank">
<img alt="ProxyEmpire" src="assets/sponsors/proxyempire.png" width="180"/>
</a>
</td>
<td valign="middle">
🚀 <b>Obscura × ProxyEmpire</b><br>
Using Obscura for AI agents, browser automation, or web scraping? Power it with reliable residential and mobile proxies from <a href="https://proxyempire.io/?ref=obscura&utm_source=obscuragithub"><b>ProxyEmpire</b></a>.<br><br>
<b>🌍 30M+ residential IPs in 170+ countries<br>
📱 4G/5G mobile proxies<br>
🔄 Rotating & sticky sessions<br>
🎯 City, region & ISP targeting<br>
🔐 HTTP, HTTPS & SOCKS5 support<br><br>
🎁 Use code <b>OBSCURA35</b> for a <b>35% recurring discount</b>.<br><br></b>
Better proxies. Fewer blocks. More scalable automation.
</td>
</tr>
<tr>
<td width="200" align="center" valign="middle">
<a href="https://mangoproxy.com/?utm_source=github&utm_medium=partner&utm_campaign=h4ckf0r0day" target="_blank">
<img alt="MangoProxy" src="assets/sponsors/mangoproxy.png" width="180"/>
</a>
</td>
<td valign="middle">
<a href="https://mangoproxy.com/?utm_source=github&utm_medium=partner&utm_campaign=h4ckf0r0day"><b>MangoProxy</b></a> provides residential, ISP, datacenter, and mobile proxies in 200+ countries. Trusted by businesses worldwide for stable connections, fast response times, and scalable proxy infrastructure.<br>
Use Promo code <b>OBSCURA</b> for 8% off Static ISP Proxies.
</td>
</tr>
</table>
## Install
### Download
Grab the latest binary from [Releases](https://github.com/h4ckf0r0day/obscura/releases):
```bash
# Linux x86_64
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz
tar xzf obscura-x86_64-linux.tar.gz
./obscura fetch https://example.com --eval "document.title"
# Linux ARM64 (aarch64)
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-linux.tar.gz
tar xzf obscura-aarch64-linux.tar.gz
# Arch Linux (AUR)
yay -S obscura-browser
# macOS Apple Silicon
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-aarch64-macos.tar.gz
tar xzf obscura-aarch64-macos.tar.gz
# macOS Intel
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-macos.tar.gz
tar xzf obscura-x86_64-macos.tar.gz
# Windows
Download the `.zip` from the releases page and extract it manually.
```
No Chrome, no Node.js, no dependencies. Release archives include both
`obscura` and `obscura-worker`; keep them in the same directory for the
parallel `scrape` command.
Linux release builds target Ubuntu 22.04 so the downloaded binary remains
usable on common LTS servers with glibc 2.35+.
### Docker
```bash
docker run -d --name obscura -p 127.0.0.1:9222:9222 h4ckf0r0day/obscura
```
Image on [Docker Hub](https://hub.docker.com/r/h4ckf0r0day/obscura). Multi-stage build on `distroless/cc`, no shell, no package manager, ~57 MB compressed.
### Build from source
```bash
git clone https://github.com/h4ckf0r0day/obscura.git
cd obscura
cargo build --release
# With stealth mode (anti-detection + tracker blocking)
cargo build --release --features stealth
```
Requires Rust 1.75+ ([rustup.rs](https://rustup.rs)). First build takes ~5 min (V8 compiles from source, cached after).
## Quick Start
### Fetch a page
```bash
# Get the page title
obscura fetch https://example.com --eval "document.title"
# Extract all links
obscura fetch https://example.com --dump links
# Render JavaScript and dump HTML
obscura fetch https://news.ycombinator.com --dump html
# Write dump or eval output to a file
obscura fetch https://example.com --dump text --output page.txt
# Stream the raw response body verbatim (binary-safe; bypasses the JS/DOM layer).
# Use this for images, JSON, JS, CSS, or any non-HTML resource.
obscura fetch https://picsum.photos/200/300 --dump original > photo.jpg
# List every sub-resource URL the page would fetch (NDJSON; one record per asset)
obscura fetch https://example.com --dump assets
# Fetch through an HTTP or SOCKS proxy
obscura --proxy socks5://127.0.0.1:1080 fetch https://example.com --dump text
# Wait for dynamic content
obscura fetch https://example.com --wait-until networkidle0
# Bound navigation time for slow or broken pages
obscura fetch https://example.com --timeout 10
```
### Start the CDP server
```bash
obscura serve --port 9222
# With stealth mode (anti-detection + tracker blocking)
obscura serve --port 9222 --stealth
```
### Scrape in parallel
```bash
obscura scrape url1 url2 url3 ... \
--concurrency 25 \
--eval "document.querySelector('h1').textContent" \
--format json
# Suppress scrape progress on stderr for script-friendly output
obscura scrape https://example.com --quiet --format json
# Scrape workers inherit the global proxy
obscura --proxy http://127.0.0.1:8080 scrape https://example.com https://news.ycombinator.com
```
## Puppeteer / Playwright
### Puppeteer
```bash
npm install puppeteer-core
```
```javascript
import puppeteer from 'puppeteer-core';
const browser = await puppeteer.connect({
browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser',
});
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com');
const stories = await page.evaluate(() =>
Array.from(document.querySelectorAll('.titleline > a'))
.map(a => ({ title: a.textContent, url: a.href }))
);
console.log(stories);
await browser.disconnect();
```
### Playwright
```bash
npm install playwright-core
```
```javascript
import { chromium } from 'playwright-core';
const browser = await chromium.connectOverCDP({
endpointURL: 'ws://127.0.0.1:9222',
});
const page = await browser.newContext().then(ctx => ctx.newPage());
await page.goto('https://en.wikipedia.org/wiki/Web_scraping');
console.log(await page.title());
await browser.close();
```
### Form submission & login
```javascript
await page.goto('https://quotes.toscrape.com/login');
await page.evaluate(() => {
document.querySelector('#username').value = 'admin';
document.querySelector('#password').value = 'admin';
document.querySelector('form').submit();
});
// Obscura handles the POST, follows the 302 redirect, maintains cookies
```
## Benchmarks
Page load:
| Page | Obscura | Chrome |
|------|---------|--------|
| Static HTML | **51 ms** | ~500 ms |
| JS + XHR + fetch | **84 ms** | ~800 ms |
| Dynamic scripts | **78 ms** | ~700 ms |
The full benchmark suite (WPT conformance, obstacle course, real-world corpus, and vs-Chrome speed) lives in a separate repo: https://github.com/h4ckf0r0day/obscura-benchmark
## Stealth Mode
Enable with `--features stealth`.
### Anti-fingerprinting
- Per-session fingerprint randomization (GPU, screen, canvas, audio, battery)
- Realistic `navigator.userAgentData` (Chrome 145, high-entropy values)
- `event.isTrusted = true` for dispatched events
- Hidden internal properties (`Object.keys(window)` safe)
- Native function masking (`Function.prototype.toString()` → `[native code]`)
- `navigator.webdriver = undefined` (matches real Chrome)
### Tracker Blocking
- 3,520 domains blocked
- Blocks analytics, ads, telemetry, and fingerprinting scripts
- Prevents trackers from loading entirely
- Enabled automatically with `--stealth`
## CDP API
Obscura implements the Chrome DevTools Protocol for Puppeteer/Playwright compatibility.
| Domain | Methods |
|--------|---------|
| **Target** | createTarget, closeTarget, attachToTarget, createBrowserContext, disposeBrowserContext |
| **Page** | navigate, getFrameTree, addScriptToEvaluateOnNewDocument, lifecycleEvents |
| **Runtime** | evaluate, callFunctionOn, getProperties, addBinding |
| **DOM** | getDocument, querySelector, querySelectorAll, getOuterHTML, resolveNode |
| **Network** | enable, setCookies, getCookies, setExtraHTTPHeaders, setUserAgentOverride |
| **Fetch** | enable, continueRequest, fulfillRequest, failRequest (live interception) |
| **Storage** | getCookies, setCookies, deleteCookies |
| **Input** | dispatchMouseEvent, dispatchKeyEvent |
| **LP** | getMarkdown (DOM-to-Markdown conversion) |
## CLI Reference
### Tuning V8
Obscura embeds V8 directly. Use `--v8-flags` to pass raw flags through to V8, same syntax as Chromium's `--js-flags` and Node's command-line flags. Most common use is raising the heap cap to fix `JavaScript heap out of memory` on JS-heavy pages:
```bash
obscura --v8-flags "--max-old-space-size=4096" fetch <url>
```
### `obscura serve`
Start a CDP WebSocket server.
| Flag | Default | Description |
|------|---------|-------------|
| `--port` | `9222` | WebSocket port |
| `--proxy` | — | HTTP/SOCKS5 proxy URL |
| `--stealth` | off | Enable anti-detection + tracker blocking |
| `--workers` | `1` | Number of parallel worker processes |
| `--obey-robots` | off | Respect robots.txt |
### `obscura fetch <URL>`
Fetch and render a single page.
| Flag | Default | Description |
|------|---------|-------------|
| `--dump` | `html` | Output: `html`, `text`, `links`, `markdown`, `assets` (NDJSON of every sub-resource URL the page references), or `original` (raw response body) |
| `--eval` | — | JavaScript expression to evaluate |
| `--wait-until` | `load` | Wait: `load`, `domcontentloaded`, `networkidle0` |
| `--timeout` | `30` | Maximum navigation time in seconds |
| `--selector` | — | Wait for CSS selector |
| `--stealth` | off | Anti-detection mode |
| `--output` | — | Write dump or eval output to a file |
| `--quiet` | off | Suppress banner |
| `--proxy` | — | Inherited global HTTP/SOCKS5 proxy URL |
### `obscura scrape <URL...>`
Scrape multiple URLs in parallel with worker processes.
| Flag | Default | Description |
|------|---------|-------------|
| `--concurrency` | `10` | Parallel workers |
| `--eval` | — | JS expression per page |
| `--format` | `json` | Output: `json` or `text` |
| `--quiet` | off | Suppress scrape progress on stderr |
| `--proxy` | — | Inherited global HTTP/SOCKS5 proxy URL for all workers |
## MCP (Model Context Protocol)
Obscura ships an MCP server that exposes browser automation tools to AI agents (Claude Desktop, Cursor, etc.).
### Start
**stdio** (default) — for Claude Desktop and MCP clients that launch a subprocess:
```bash
obscura mcp
```
**HTTP** — for clients that connect over the network:
```bash
obscura mcp --http --port 8080
# endpoint: http://127.0.0.1:8080/mcp
```
Optional flags (both transports):
| Flag | Description |
|------|-------------|
| `--proxy <URL>` | HTTP/SOCKS5 proxy |
| `--user-agent <UA>` | Custom User-Agent string |
| `--stealth` | Enable anti-detection mode |
### Claude Desktop config
```json
{
"mcpServers": {
"obscura": {
"command": "obscura",
"args": ["mcp"]
}
}
}
```
### Tools
| Tool | Description |
|------|-------------|
| `browser_navigate` | Navigate to a URL (`url`, optional `waitUntil`: `load` / `domcontentloaded` / `networkidle0`) |
| `browser_snapshot` | Return the current page URL, title, and body text |
| `browser_click` | Click an element by CSS selector |
| `browser_fill` | Set an input value (triggers `input` + `change` events) |
| `browser_type` | Append text to an input |
| `browser_press_key` | Dispatch a keyboard event (`key`, optional `selector`) |
| `browser_select_option` | Select an `<option>` by value or text |
| `browser_evaluate` | Evaluate a JavaScript expression and return the result |
| `browser_wait_for` | Wait for a CSS selector to appear (`selector`, optional `timeout` in seconds) |
| `browser_network_requests` | List network requests made by the current page |
| `browser_console_messages` | Return console messages logged by the page |
| `browser_close` | Close the page and reset browser state |
## License
Apache 2.0
---