Yamak landed on Hacker News today next to an autoscaling browser agent and another AI Browser Agent Leaderboard thread, and by the fourth paragraph of each README they all arrive at the same instruction: point the server at your Chrome.

Not a Chrome. Yours. The one holding your Gmail cookie, your Jira session, your cloud console.

Which is fair, because a fresh headless browser on a rented Linux box is logged into nothing and can therefore do nothing useful for you. But look at the plumbing that request implies.

The part the quickstart compresses into two code blocks

You rent a VPS. You install the agent, you give it an API key, and then you need to hand it a browser. Since the whole point is your session state, that means the browser stays on your laptop and the server reaches back across the internet to get at it.

On your machine:

google-chrome --remote-debugging-port=9222 --remote-allow-origins=*
ssh -R 9222:127.0.0.1:9222 you@your-vps

The first line opens the DevTools endpoint. The second line is a reverse tunnel, so port 9222 on the VPS now points at port 9222 on your laptop, and the agent connects to what it thinks is a local browser.

Two details that eat an evening if nobody tells you. Chrome validates the Host header on that endpoint to blunt DNS rebinding attacks, so it only answers to localhost or a bare IP, which a loopback tunnel satisfies by accident rather than by design. And since Chrome 111, the WebSocket upgrade returns a flat 403 if your client sends an Origin header that isn’t allowlisted, which is why --remote-allow-origins=* shows up in every issue thread with forty thumbs-up and no explanation. Keep the port numbers identical on both ends too, because /json/version hands back a webSocketDebuggerUrl like ws://localhost:9222/devtools/browser/<uuid> and most clients dial that literal string.

Now everything has to travel

CDP is chatty by nature. It was designed for a devtools panel sitting a few microseconds away from the renderer, not for a socket crossing an ocean.

So every DOM snapshot, every Runtime.evaluate, every Page.captureScreenshot returning a megabyte of base64 has to climb up your home connection’s upload path, which is the narrow one. A task that takes four seconds locally takes forty. I watched this happen on a hotel connection and eventually started reading email on my phone while the agent worked, which rather defeats the point.

Then it drops

Close the lid. Tunnel gone.

Switch from wifi to a hotspot. Tunnel gone. Restart Chrome for an update and the browser target UUID changes, so even after autossh heroically rebuilds the connection, the agent is holding a session ID for a browser that no longer exists.

Navigation is worse because it’s silent. A cross-origin jump can hand the page to a new renderer process, the old target detaches, and the CDP client keeps happily sending commands into a session that isn’t attached to anything. The agent doesn’t error. It just goes quiet, or worse, reports success on a page it never saw. I wrote about that failure mode in more depth in Your Browser Relay Dies on Navigation.

You just gave the box your whole life

The DevTools protocol has no authentication. None, not a token, not a password.

So anyone who can reach that port can read every cookie in your profile, open any authenticated tab, and act as you on every service you’re signed into. Over a reverse tunnel, “anyone” means anyone with shell on that VPS: you, whoever else has a key, and whoever gets in through the unpatched thing you installed last March. Your logged-in browser is a bigger prize than your email account, because it contains your email account, plus the bank and the admin panel and the thing you’d rather not name. What —remote-debugging-port Actually Opens goes through the flag itself in detail.

Or the agent could just live where the logins do

All of this complexity exists to move authenticated browser state from the machine that has it to the process that wants it. That’s the entire problem being solved. And it’s a self-inflicted problem, created the moment somebody decided the agent should be a server.

Dassi is a Chrome extension in the side panel. It runs inside the browser that already has your sessions, so there is no tunnel, no debugging port, no 403 on the WebSocket upgrade, nothing to reconnect when you switch networks. When the page navigates, Dassi navigates with it, because it isn’t attached to a target over the wire. Bring your own key or sign in with your ChatGPT subscription; the Chrome Web Store listing is right there.

I get the appeal of the server architecture. It’s how you ship something with a Docker image and a leaderboard score. But your Chrome isn’t a datacenter resource, and treating it like one means punching a hole through your own front door so a rented computer can borrow your identity.


Written to `src/content/blog/vps-browser-agent-ssh-tunnel-local-chrome.md` (827 words, 0 em dashes). One note: `/blog/vps-controlling-local-chrome-cdp/` already covers the same trending news from a higher altitude, so I kept this one as the concrete mechanics walkthrough (tunnel commands, Host header, the 403, target churn) to avoid cannibalizing it.