{"id":50774761,"url":"https://github.com/geoffdutton/obscura-audited","last_synced_at":"2026-06-11T22:30:28.000Z","repository":{"id":353495903,"uuid":"1219569076","full_name":"geoffdutton/obscura-audited","owner":"geoffdutton","description":"The headless browser for AI agents and web scraping","archived":false,"fork":false,"pushed_at":"2026-04-24T07:22:47.000Z","size":232,"stargazers_count":0,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-24T07:24:29.265Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/geoffdutton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":"AUDIT.md","citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-24T02:21:04.000Z","updated_at":"2026-04-24T07:22:43.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/geoffdutton/obscura-audited","commit_stats":null,"previous_names":["geoffdutton/obscura"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/geoffdutton/obscura-audited","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoffdutton%2Fobscura-audited","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoffdutton%2Fobscura-audited/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoffdutton%2Fobscura-audited/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoffdutton%2Fobscura-audited/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geoffdutton","download_url":"https://codeload.github.com/geoffdutton/obscura-audited/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoffdutton%2Fobscura-audited/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34221150,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-11T02:00:06.485Z","response_time":57,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-11T22:30:27.338Z","updated_at":"2026-06-11T22:30:27.993Z","avatar_url":"https://github.com/geoffdutton.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/geoffdutton/obscura-audited/main/assets/icon.png\" alt=\"Obscura\" width=\"80\" /\u003e\n\u003c/p\u003e\n\n\u003ch2 align=\"center\"\u003eObscura\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cstrong\u003eThe open-source headless browser for AI agents and web scraping.\u003c/strong\u003e\u003cbr\u003e\n  Lightweight, stealthy, and built in Rust.\n\u003c/p\u003e\n\n---\n\n*Note:* Forked from [h4ckf0r0day/obscura](https://github.com/h4ckf0r0day/obscura/commit/b00a1f20e74fd0099509cbafd7dc33ced82dbcbe)\n\nObscura 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.\n\n### Why Obscura over headless Chrome?\n\nDesigned for automation at scale, not desktop browsing.\n\n| Metric       | Obscura      | Headless Chrome |\n|--------------|--------------|------------------|\n| Memory       | **30 MB**    | 200+ MB          |\n| Binary size  | **70 MB**    | 300+ MB          |\n| Anti-detect  | **Built-in** | None          |\n| Page load    | **85 ms**    | ~500 ms          |\n| Startup      | **Instant**  | ~2s              |\n| Puppeteer    | **Yes**      | Yes              |\n| Playwright   | **Yes**      | Yes              |\n\n## Install\n\n### Build from source\n\n```bash\ngit clone https://github.com/h4ckf0r0day/obscura.git\ncd obscura\ncargo build --release\n\n# With stealth mode (anti-detection + tracker blocking) — Linux only\ncargo build --release --features stealth\n```\n\nRequires Rust 1.75+ ([rustup.rs](https://rustup.rs)). First build takes ~5 min (V8 compiles from source, cached after).\n\n#### Stealth build on macOS / Windows\n\n`--features stealth` depends on `wreq` → `boring-sys2`, whose symbol-prefixing is broken on macOS/Windows. Use a Linux container:\n\n```bash\ndocker run --rm \\\n  -v \"$PWD\":/src -w /src \\\n  -v obscura-cargo-registry:/usr/local/cargo/registry \\\n  -v obscura-linux-target:/src/target \\\n  rust:1.95 cargo build --release --features stealth\n```\n\nThe same wrapper runs the bot-detection check (`scripts/verify-stealth.sh`). The named volumes cache the cargo registry and the Linux target dir across runs.\n\n## Quick Start\n\n### Fetch a page\n\n```bash\n# Get the page title\nobscura fetch https://example.com --eval \"document.title\"\n\n# Extract all links\nobscura fetch https://example.com --dump links\n\n# Render JavaScript and dump HTML\nobscura fetch https://news.ycombinator.com --dump html\n\n# Wait for dynamic content\nobscura fetch https://example.com --wait-until networkidle0\n```\n\n### Start the CDP server\n\n```bash\nobscura serve --port 9222\n\n# With stealth mode (anti-detection + tracker blocking)\nobscura serve --port 9222 --stealth\n```\n\n### Scrape in parallel\n\n```bash\nobscura scrape url1 url2 url3 ... \\\n  --concurrency 25 \\\n  --eval \"document.querySelector('h1').textContent\" \\\n  --format json\n```\n\n## Puppeteer / Playwright\n\n### Puppeteer\n\n```bash\nnpm install puppeteer-core\n```\n\n```javascript\nimport puppeteer from 'puppeteer-core';\n\nconst browser = await puppeteer.connect({\n  browserWSEndpoint: 'ws://127.0.0.1:9222/devtools/browser',\n});\n\nconst page = await browser.newPage();\nawait page.goto('https://news.ycombinator.com');\n\nconst stories = await page.evaluate(() =\u003e\n  Array.from(document.querySelectorAll('.titleline \u003e a'))\n    .map(a =\u003e ({ title: a.textContent, url: a.href }))\n);\nconsole.log(stories);\n\nawait browser.disconnect();\n```\n\n### Playwright\n\n```bash\nnpm install playwright-core\n```\n\n```javascript\nimport { chromium } from 'playwright-core';\n\nconst browser = await chromium.connectOverCDP({\n  endpointURL: 'ws://127.0.0.1:9222',\n});\n\nconst page = await browser.newContext().then(ctx =\u003e ctx.newPage());\nawait page.goto('https://en.wikipedia.org/wiki/Web_scraping');\nconsole.log(await page.title());\n\nawait browser.close();\n```\n\n### Form submission \u0026 login\n\n```javascript\nawait page.goto('https://quotes.toscrape.com/login');\nawait page.evaluate(() =\u003e {\n  document.querySelector('#username').value = 'admin';\n  document.querySelector('#password').value = 'admin';\n  document.querySelector('form').submit();\n});\n// Obscura handles the POST, follows the 302 redirect, maintains cookies\n```\n\n## Benchmarks\n\nPage load:\n\n| Page | Obscura | Chrome |\n|------|---------|--------|\n| Static HTML | **51 ms** | ~500 ms |\n| JS + XHR + fetch | **84 ms** | ~800 ms |\n| Dynamic scripts | **78 ms** | ~700 ms |\n\n## Stealth Mode\n\nEnable with `--features stealth`.\n\n### Anti-fingerprinting\n- Per-session fingerprint randomization (GPU, screen, canvas, audio, battery)\n- Realistic `navigator.userAgentData` (Chrome 145, high-entropy values)\n- `event.isTrusted = true` for dispatched events\n- Hidden internal properties (`Object.keys(window)` safe)\n- Native function masking (`Function.prototype.toString()` → `[native code]`)\n- `navigator.webdriver = undefined` (matches real Chrome)\n\n### Tracker Blocking\n- 3,520 domains blocked\n- Blocks analytics, ads, telemetry, and fingerprinting scripts\n- Prevents trackers from loading entirely\n- Enabled automatically with `--stealth`\n\n## CDP API\n\nObscura implements the Chrome DevTools Protocol for Puppeteer/Playwright compatibility.\n\n| Domain | Methods |\n|--------|---------|\n| **Target** | createTarget, closeTarget, attachToTarget, createBrowserContext, disposeBrowserContext |\n| **Page** | navigate, getFrameTree, addScriptToEvaluateOnNewDocument, lifecycleEvents |\n| **Runtime** | evaluate, callFunctionOn, getProperties, addBinding |\n| **DOM** | getDocument, querySelector, querySelectorAll, getOuterHTML, resolveNode |\n| **Network** | enable, setCookies, getCookies, setExtraHTTPHeaders, setUserAgentOverride |\n| **Fetch** | enable, continueRequest, fulfillRequest, failRequest (live interception) |\n| **Storage** | getCookies, setCookies, deleteCookies |\n| **Input** | dispatchMouseEvent, dispatchKeyEvent |\n| **LP** | getMarkdown (DOM-to-Markdown conversion) |\n## CLI Reference\n\n### `obscura serve`\n\nStart a CDP WebSocket server.\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--port` | `9222` | WebSocket port |\n| `--proxy` | — | HTTP/SOCKS5 proxy URL |\n| `--stealth` | off | Enable anti-detection + tracker blocking |\n| `--workers` | `1` | Number of parallel worker processes |\n| `--obey-robots` | off | Respect robots.txt |\n\n### `obscura fetch \u003cURL\u003e`\n\nFetch and render a single page.\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--dump` | `html` | Output: `html`, `text`, or `links` |\n| `--eval` | — | JavaScript expression to evaluate |\n| `--wait-until` | `load` | Wait: `load`, `domcontentloaded`, `networkidle0` |\n| `--selector` | — | Wait for CSS selector |\n| `--stealth` | off | Anti-detection mode |\n| `--quiet` | off | Suppress banner |\n\n### `obscura scrape \u003cURL...\u003e`\n\nScrape multiple URLs in parallel with worker processes.\n\n| Flag | Default | Description |\n|------|---------|-------------|\n| `--concurrency` | `10` | Parallel workers |\n| `--eval` | — | JS expression per page |\n| `--format` | `json` | Output: `json` or `text` |\n\n## License\n\nApache 2.0\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeoffdutton%2Fobscura-audited","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeoffdutton%2Fobscura-audited","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeoffdutton%2Fobscura-audited/lists"}