Jerem Flow

Giving Hermes a headless browser (and a VPN) for the web

· 4 mins read · #hermes #self-hosting #docker #browser #vpn #scraping #privacy #ai-agents

An agent that can only read text is half-blind. Giving Hermes a real browser — one it can open, click, and read — turned it from a search tool into something that can actually use the web. 🧭

Why a headless browser at all

Most AI assistants "browse" by fetching a page's text. That works for simple articles, but a growing share of the web doesn't cooperate:

  • JavaScript-heavy sites render their content client-side — the raw HTML is an empty shell.
  • Login walls and interactive pages need a real session, not a text fetch.
  • Dynamic data (dashboards, infinite scroll, live tables) only exists after the page runs.

A headless browser is a full browser with no window. It loads pages exactly like a human's browser would — runs the JavaScript, renders the layout — and exposes a control channel so a program can drive it: open a URL, click a button, read the resulting text. For an agent, that means it can navigate the web the way I would, instead of guessing from a text snapshot.

What I installed (the stack)

The browser runs as a container on my small on-prem server, next to the agent. Three pieces, each with one job:

Layer Choice Role
Browser browserless / Chromium headless Chromium exposing a control API (CDP)
VPN gluetun (WireGuard) routes all browser traffic through a VPN exit
Network an isolated Docker bridge keeps the browser reachable only by the agent
# illustrative — the browser shares the VPN container's network
docker compose -f browser-service.yaml up -d

The key wiring: the browser container uses network_mode: service:vpnproxy, so every request it makes exits through the VPN — never through my home connection. The agent connects to the browser over CDP (the Chrome DevTools Protocol) on a private address, and I point Hermes at it:

hermes config set browser.cdp_url "http://172.20.0.10:3000"

From then on, the agent's browser tool drives this container instead of a throwaway in-process Chromium.

How it's wired up

Hermes ──CDP──▶ Headless Chromium ──▶ gluetun (VPN) ──▶ Web

Hermes driving a headless browser through a VPN

The agent sends a command ("open this page, click that, read the result") over CDP; Chromium does the work; the traffic leaves through the VPN. The browser keeps a persistent profile, so sessions and cookies survive between tasks.

What it unlocks

  • Real extraction on JavaScript-heavy sites that a plain text fetch can't read.
  • Interactive flows — the agent can click, fill, and navigate, not just download.
  • A consistent exit IP for scraping, so sites see one stable identity instead of a rotating one.

The guardrails (the part that matters)

Giving an agent a browser is powerful — so I set boundaries before it became a habit:

  1. VPN by default, always. 🛡️ The browser cannot reach the internet except through the VPN. There is no "direct" path to switch on by accident. My home IP is never exposed to the sites it visits.
  2. Private network only. The browser listens on an isolated Docker bridge that isn't routed outside the host. No port is published to the LAN or the internet — only the agent container can reach it.
  3. A watchdog. A small scheduled check pings the browser every few minutes. If it goes down, I get an alert; if the network link drops, it reconnects itself before I even notice.
  4. Secrets handled carefully. The VPN private key lives in a permissions-restricted file and is never printed into logs or prompts.

The rule I keep in mind: the browser is a tool with a fixed, private, VPN-only identity — not a backdoor into my network, and not a way to leak mine.

The one honest limitation

A stock headless Chromium is detectable — sites can tell it's automated (navigator.webdriver is set, the user agent says HeadlessChrome). For most sites that's irrelevant; for hardened ones (strict bot protection) it's a real constraint. I treat that as a known boundary and reach for it only where it's appropriate — not as a way to bypass a site's terms.

In short

A self-hosted headless Chromium, driven by the agent over CDP, with all traffic forced through a VPN and a watchdog on top. The payoff: an agent that can actually use the modern web, from a private, fixed, VPN-only identity — with the guardrails in place before the convenience.

See also: Hindsight + Hermes — giving my assistant a real long-term memory — another piece of the same self-hosted, privacy-first toolkit.

← Back