Independent AI intelligence Two editions daily · ET
Fervor AI

Analysis · September 1, 2026 · repo

h4ckf0r0day/obscuraChrome DevTools ProtocolPuppeteerPlaywrightagent-infrastructureagent-harnessagent-securitylocal-ai

Obscura Renders the Web Without Chromium, So Your Agent Sees a Different Page Than Your User

A Rust headless browser with its own layout and paint engine, 23,384 stars, and two sentences buried in the README that are the entire reason to read carefully.

Obscura draws web pages without Chromium anywhere in the process. Not headless Chrome with the window stripped off. Its own layout and paint engine, written in Rust, running in 30 MB of memory against the 200-plus that headless Chrome asks for, shipped as a single static binary with no Node and no browser download.

That is a genuinely hard thing to build and the repo has the stars to match: 23,384 of them, 1,733 forks, Apache-2.0, created in April 2026 and pushed to as recently as yesterday. It sits high on the momentum boards this week.

Then, in the middle of the ## Rendering section, under no warning heading at all, sit two sentences:

It remains an evolving independent engine: long-tail CSS, some Web APIs, media playback, compositor effects, and platform font rasterization may differ from Chromium.

For scraping, that sentence is a footnote. For agents, it is the whole article.

The screenshot stops being about the web

Here is the position I will defend for the rest of this piece. The moment your agent's vision comes from a non-Chromium engine, a screenshot is no longer evidence about what a page looks like. It is evidence about what Obscura's paint pipeline did with that page.

Most of the time those are the same thing. Sometimes they are not, and the failure is silent. A layout that reflows because a font rasterized at a different width does not throw. A compositor effect that renders flat does not throw. An element that a visibility check says is hidden because the engine gave it the wrong box model does not throw. The agent just does something else, confidently, and writes a clean summary of it.

The scraping case has a natural error signal: you either got the data or you did not. The agent case does not. It gets a picture, believes it, and acts.

What Obscura actually is

The one-line pitch from the README: "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."

Worth separating two claims that get blurred together. Obscura is not a from-scratch JavaScript engine. It embeds V8, and building from source compiles V8, which is why first builds take around five minutes. What is from scratch is everything after the DOM: layout, paint, screenshots, screencasting, PDF export. "No Chromium required" is true about rendering and not about JavaScript.

It speaks CDP server-side, so puppeteer.connect({ browserWSEndpoint }) and chromium.connectOverCDP() both work against it. That is the reason for the star count. Existing automation code points at a new endpoint and keeps running.

The timeline matters more than the star count. Rendering shipped in v0.2.0 on August 8, 2026. The current release, v0.2.1, landed August 23. So the paint engine that your agent would be seeing the web through is roughly three weeks old.

The release notes are more honest than the README

The README's fidelity caveat is soft. The v0.2.0 release notes have an explicit ## Current limitations heading, and it reads harder:

Rendering is new and is not yet complete Chromium parity. Long-tail CSS, some Web APIs, native media playback, and GPU compositor effects may still differ.

PDF output is currently raster-backed, so text is not selectable. Screencasting captures page activity rather than fixed-rate desktop video.

The raster PDF limitation appears only there. The README says "raster PDF export without starting Chromium" and leaves you to work out what raster implies. If you were planning to have an agent export a page to PDF and then read text back out of it, that is a dead path and the README will not tell you.

Then look at what v0.2.1 fixed two weeks later, under a bullet the maintainer titled "Rendering keeps closing on Chrome": webfont icons and color emoji now render, outset box shadows clip correctly, and textarea gets an intrinsic control box and its own JS interface "so Playwright visibility checks, clicks, and fills work on textareas."

Read that last one again. Two weeks after rendering shipped, Playwright could not reliably see or fill a textarea. I do not say that to dunk on the project. Publishing that changelog is exactly the right behavior and it is more disclosure than most repos at this star count give you. I say it because it is the clearest available measurement of where the engine sits: the gaps being closed right now are basic form controls, not exotic CSS.

The benchmark repo does not measure what you think

There is a companion repo, h4ckf0r0day/obscura-benchmark, and it is substantial: Web Platform Tests conformance, an obstacle course, head-to-head against headless Chrome, a 98-page real-world corpus, a 1500-URL reliability sweep. The headline numbers are strong. WPT subtest pass rate of 83.3% on the Core tier and 85.0% on the Relevant tier. 33 of 33 obstacle stages. Roughly 21x faster and 7x less memory than cold-process headless Chrome across 33 fixtures.

Its README opens with this:

Obscura runs real JavaScript on a V8 runtime and builds a live DOM, but has no rendering, layout, or paint pipeline.

Latest full pass is dated July 3, 2026. That is five weeks before the rendering engine existed. None of those numbers validate render fidelity, and the benchmark repo says so about itself. The WPT figures measure DOM and JS conformance. The Full tier at 60.0% is reported "only for transparency" because it includes subtrees the project deliberately did not implement, layout and rendering among them.

Two other things in that repo are worth having before you commit. On the 98-page real-world corpus, Obscura successfully handled 94 of 98 against headless Chrome's 85 of 98, which is a real win. Median latency was 5.2 seconds against Chrome's 2.1. The benchmark concedes that Obscura "is much faster on static content pages and slower on heavy client-rendered SPAs." If your agent's targets are React dashboards, you are trading memory for wall-clock time, not winning both.

Put this into practice

The honest first step is a fidelity diff, not an install. Do it before anything in your stack depends on the answer.

1. Grab the right archive. Rendering is a build variant, not a universal feature. Releases ship four per platform: plain (render, no stealth), -stealth (render plus stealth), -no-render, and -no-render-stealth. Pick wrong and you will spend an hour wondering why screenshots return nothing. Linux builds need glibc 2.35 or newer.

2. Point your existing automation at it. No rewrite. puppeteer.connect({ browserWSEndpoint }) or chromium.connectOverCDP() against the Obscura endpoint, and your current scripts run.

3. Run the diff that matters. Take the twenty pages your agent actually works on, not a generic test suite, and screenshot each through both Obscura and headless Chrome. Compare them by eye first, then diff programmatically if you want a regression gate. You are looking for font metrics, shadow and blur, sticky and fixed elements, anything behind a compositor effect. Twenty pages takes maybe half an hour and it converts an unknown into a known.

4. Set the flags that will bite you. Private IPs are blocked by default as SSRF protection, so anything pointed at localhost or an internal host needs --allow-private-network. As of v0.2.1, file uploads are gated behind --allow-file-access and fail with a clear error without it, which is a breaking change from v0.2.0. Script execution has a 30 second budget via OBSCURA_SCRIPT_DEADLINE_MS and 3 seconds per module. Response bodies over the 2 MiB default cache (OBSCURA_NETWORK_BODY_BUFFER_BYTES) are not retained.

5. Decide about stealth separately. Per-session fingerprint randomization across GPU, screen, canvas, audio, and battery; navigator.webdriver set to undefined; event.isTrusted true on dispatched events; native function masking; 3,520 tracker domains blocked. It is a compile-time feature plus a runtime --stealth flag, and building it needs BoringSSL, CMake, Clang, and libclang. Know your target site's terms before you turn it on.

Honest limitations

Every performance number in this article is the project's own. There is no independent benchmark in either repo, and the README contradicts itself in two places: the headline comparison table says an 85 ms page load while the benchmarks table below it gives 51, 84, and 78 ms across three fixtures, and the stated 70 MB binary sits next to a "~57 MB compressed" Docker image. Small inconsistencies, but they tell you the numbers were gathered at different times and not reconciled.

The strongest stealth claims live only in the v0.1.9 release notes from June, two minor versions back: creepjs reporting 0% stealth detection, one consistent Chrome 145 identity across TLS, JS, and WebGL. Undated against current builds and self-reported.

The README also carries a table asserting that Obscura inspired Cloudflare's first Kitesurf prototype. That is a third-party claim about someone else's engineering history and I did not verify it. Treat it as a marketing line until Cloudflare says the same thing.

And the load-bearing one, stated by the project rather than by me: this is not Chromium parity, and it is not claiming to be. If your agent's job is to see pages the way your user's Chrome renders them, that gap is the risk, and it is the only thing on this page you cannot engineer around with a flag.

What I would watch

Obscura is doing something the agent ecosystem needs. Running ten headless Chrome instances costs you 2 GB of RAM and a lot of patience; the same fleet in Obscura fits in 300 MB. That difference is what makes densely parallel browser agents affordable, and nobody else on the trending boards is attacking it from the engine side.

The open question is which engine becomes the reference. Right now every agent that browses is implicitly measuring the web against Chromium, because Chromium is what it uses. Swap in an independent engine and you have introduced a second definition of what a page looks like, held by nobody, checked by nobody, and improving on someone's weekend schedule.

That may end up fine. Web standards exist precisely so more than one engine can be right. It also may mean a class of agent error that only shows up as "the model made a weird decision on that page" and never as a stack trace.

Run the twenty-page diff. If Obscura draws your pages the way Chrome does, you have just cut your browser fleet's memory by 85% for free. If it does not, you found out on your terms instead of in production. Either answer is worth half an hour, and I would like to know which one you got.

Sources: h4ckf0r0day/obscura, obscura releases, h4ckf0r0day/obscura-benchmark. Repo metadata (stars, forks, license, dates) pulled from the GitHub API and cache-busted shields.io JSON on September 1, 2026.