Building a Local-First Autonomous Browser on a Patched Chromium
A local-first autonomous browser platform built on a pinned Chromium downstream, driving the engine over CDP and refusing to fall back to stock Chrome.
ACID Browser started from a simple stubbornness: if an agent is going to drive a browser, it should drive a real browser I control, not a stock binary borrowed at runtime. The result is a local-first platform where the product body is an ACID-branded Chromium downstream with its own ordered patch stack, and the runtime, harnesses, Gateway, vault, tasks, and scheduler live as external components with their own lifecycles.
Why a downstream instead of automation glue
Most agent-browser setups wrap Playwright or Puppeteer around whatever Chrome happens to be installed. I went the other direction. The repository pins the ACID Chromium downstream and its patch stack, then a self-contained macOS assembler builds an actual arm64 ACID Browser.app and installs it on the audited host. Stock Chrome and Chrome for Testing exist only as an explicit development compatibility mode, selectable through a documented external-chromium backend. They are not ACID Browser, and the distinction is enforced, not just documented.
The bridge talks directly to Chromium over the Chrome DevTools Protocol and exposes semantic DOM and accessibility operations on top of it. Playwright is not a runtime dependency at all. That was a deliberate cut. Every extra layer between the agent's intent and the engine is a place where behavior drifts, so I removed the layer.
Failing closed on the wrong binary
The design decision I am most attached to is that the Runtime defaults to the ACID Browser application and fails closed if its executable is unavailable. It never quietly substitutes an installed Chrome. An agent that silently switches engines is an agent whose evidence you cannot trust later, so the safe behavior is to stop rather than improvise.
Operationally it runs lean. One per-user LaunchAgent supervises three bundled local services: a dashboard on 127.0.0.1:4310, the API and MCP on 127.0.0.1:4311, and the managed bodies beneath ~/Applications. Tailscale Serve publishes a single authenticated combined dashboard, API, and MCP edge to the tailnet, and nothing is publicly funneled. Managed inference goes straight from the Runtime to ACIDaemon, which let me drop the local Gateway port entirely.
What is still open
I am honest about the gates that remain. Public Developer ID and notarized distribution is not done, and one composed, human-driven authenticated-site acceptance is still open. Scoped packaged and live evidence lives under docs/acceptance/ so the state is auditable rather than asserted.
The lesson that keeps proving itself: for autonomous work, control over the substrate matters more than convenience at the wrapper. Owning the Chromium downstream and refusing fallbacks costs build effort up front, and it buys back trust in everything the agent does afterward.
Have something that needs building, or stabilizing?
These notes are the work log. The paid work runs through Moonshine Labs, my product and engineering studio. Tell us what you're building, or grab a call.
Work with us →