[Cotlet](https://cotlet.dev/index.md) · [Integrations](https://cotlet.dev/integrations.md) · [How it works](https://cotlet.dev/how-it-works.md) · [Use cases](https://cotlet.dev/use-cases.md) · [Security](https://cotlet.dev/security.md) · [For developers](https://cotlet.dev/for-developers.md) · [For automation agencies](https://cotlet.dev/for-agencies.md) · [Blog](https://cotlet.dev/blog.md) · [API docs](https://cotlet.dev/docs.md) · [Help center](https://cotlet.dev/help.md) · [Changelog](https://cotlet.dev/changelog.md) · [Contact](https://cotlet.dev/contact.md) · [Pricing](https://cotlet.dev/pricing.md) · [Sign up](https://cotlet.dev/signup)

# Hermes Agent browser on a VPS: fix CAPTCHAs and lost logins

> How the Hermes Agent browser works, why it hits CAPTCHAs and logouts on a VPS, and which backend fixes what: your Chrome, a cloud browser or a signed-in one.

Source: https://cotlet.dev/blog/hermes-agent-browser

How the Hermes Agent browser works, why it hits CAPTCHAs and logouts on a VPS, and which backend fixes what: your Chrome, a cloud browser or a signed-in one.

Cotlet team · Oct 18, 2026 · 9 min read

Book a slot in my salon app

That app has no API.

On this page

1. [01 How does the Hermes Agent browser work?](#how-does-the-hermes-agent-browser-work)
2. [02 Why does the browser break when Hermes runs on a VPS?](#why-does-the-browser-break-when-hermes-runs-on-a-vps)
3. [03 Which browser should Hermes use?](#which-browser-should-hermes-use)
4. [04 How do you connect Hermes to your own Chrome over SSH?](#how-do-you-connect-hermes-to-your-own-chrome-over-ssh)
5. [05 Worked example: Hermes with a hosted signed-in browser](#worked-example-hermes-with-a-hosted-signed-in-browser)  
  1. [Layer 1: Cotlet as a remote MCP server](#layer-1-cotlet-as-a-remote-mcp-server)
  2. [Layer 2: drive the Cloud Browser over CDP](#layer-2-drive-the-cloud-browser-over-cdp)
  3. [What Cotlet does not do](#what-cotlet-does-not-do)
6. [06 Before you give Hermes a browser with your accounts](#before-you-give-hermes-a-browser-with-your-accounts)
7. [07 Where Cotlet fits](#where-cotlet-fits)

The Hermes Agent browser is a headless Chromium on the machine Hermes runs on, unless you point it somewhere else: a cloud provider such as Browserbase or Browser Use, a Camofox server, or any Chrome you reach over the DevTools protocol with `browser.cdp_url` or `/browser connect`. On a VPS the default fails at logins for a predictable reason. A fresh, signed-out headless browser on a datacenter IP looks like a bot, so sites answer with CAPTCHAs, challenges and blocks. The fix is to give Hermes a browser that is already signed in, on an IP the site has seen before.

This guide covers how the browser tooling works as of October 2026, what each backend fixes, a comparison table, and a worked setup with a hosted signed-in browser.

## How does the Hermes Agent browser work?

Hermes splits the job into a driver and a browser source. Everything below comes from the [Hermes browser docs](https://hermes-agent.nousresearch.com/docs/user-guide/features/browser) and the [hermes-agent repository](https://github.com/NousResearch/hermes-agent), checked on 18 October 2026.

The default driver is Browser Use mode. The model gets one `browser_exec` tool and writes Python that clicks, types and scrapes. Set `browser.backend: "off"` in `~/.hermes/config.yaml` and you get the older built-in tools instead: `browser_navigate`, `browser_snapshot`, `browser_click`, `browser_type` and friends, which work on an accessibility tree with refs like `@e5`.

The browser source is a separate choice:

| Source | How you select it | What it is |
| ------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Local Chromium | default, nothing to set | Hermes’s packaged headless Chromium via agent-browser, a clean throwaway profile |
| Real profile | browser.use\_real\_profile: true | a copy of your desktop browser’s active profile, run headless |
| Browserbase | BROWSERBASE\_API\_KEY and BROWSERBASE\_PROJECT\_ID in \~/.hermes/.env, then hermes tools | cloud browser with stealth, residential proxies and CAPTCHA solving |
| Browser Use cloud | BROWSER\_USE\_API\_KEY, then hermes tools | cloud browser with stealth, proxies, CAPTCHA solving and persistent profiles |
| Firecrawl | FIRECRAWL\_API\_KEY, then hermes setup tools | cloud browser built for scraping |
| Camofox | CAMOFOX\_URL, then hermes tools | self-hosted Firefox fork with fingerprint spoofing |
| Lightpanda | browser.engine: lightpanda | light headless engine, falls back to Chrome for unsupported actions |
| Any Chrome over CDP | browser.cdp\_url, BROWSER\_CDP\_URL or /browser connect | your own Chrome, Brave, Edge or Chromium, or any DevTools endpoint |

`hermes tools` writes the cloud choice to `browser.cloud_provider`. A CDP endpoint beats every cloud provider: when one is set, Hermes attaches to it and launches nothing.

_Hermes Agent docs, Browser Automation page, “Local Chromium-family browser via CDP”, October 2026._

Two details in that section matter on a server. `/browser connect` is a slash command of the interactive CLI, so it does nothing from Telegram, Discord or the web UI. For those, and for cron jobs, put the endpoint in `browser.cdp_url` instead. Once a CDP endpoint is attached, Hermes also exposes `browser_cdp`, a raw DevTools passthrough the other backends do not get.

## Why does the browser break when Hermes runs on a VPS?

Five things go wrong, and they compound.

**The IP is a datacenter IP.** Bot checks score the network an IP belongs to, and hosting ranges score badly. A Hermes user put it plainly in [issue #102731](https://github.com/NousResearch/hermes-agent/issues/102731):

_NousResearch/hermes-agent, issue #102731, “Local Browser Control With Secure Human Login Handoff”, opened 4 September 2026._

**Every session starts signed out.** The default profile is thrown away, so each task logs in again, and each fresh login is exactly the event bot checks watch for. [Issue #130533](https://github.com/NousResearch/hermes-agent/issues/130533) describes it: logins never persist between tasks, recurring chores re-authenticate every time and hit MFA walls.

**Logging in means the VPS holds your credentials.** Hermes does not make you paste passwords into the chat. Its [credential vault](https://hermes-agent.nousresearch.com/docs/user-guide/features/credential-vault) asks once in a masked prompt, encrypts the password on the machine and fills it without the model seeing it. That is well designed. It still means the password sits on the VPS.

**2FA breaks unattended runs.** A cron job has nobody to type a code. The vault’s answer is to store the authenticator key with the login so Hermes generates codes itself. Then one server holds both factors, which turns two-factor into one-factor with extra steps. Weigh that before you do it for an account you care about.

**CAPTCHAs have nobody to solve them.** Headless Hermes stops and says so, as in [issue #12667](https://github.com/NousResearch/hermes-agent/issues/12667), where Google flagged the session and the agent could only report it. Cloud providers ship solvers for this. Whether an agent should solve them is a separate argument, which we make in [can AI solve CAPTCHA, and why we refuse to](https://cotlet.dev/blog/captcha-mfa-ai-agents.md).

## Which browser should Hermes use?

| | Local headless on the VPS | Your desktop Chrome via SSH tunnel | Camofox, persistent | Browserbase or Browser Use cloud | Hosted signed-in browser (Cotlet) |
| ------------------------- | ------------------------- | ---------------------------------- | ------------------------------ | --------------------------------- | ------------------------------------------------- |
| Where it runs | the VPS | your computer | the VPS | provider’s cloud | Cotlet’s EU data centre |
| IP sites see | datacenter | your home or office | datacenter | residential proxy pool | one fixed IP, shown in your console |
| Who signs in | the agent, from the vault | you | you via VNC, or the agent | the agent, or a saved profile | you, in your Cloud Browser |
| Session survives restarts | no | yes, while that Chrome runs | yes, with managed\_persistence | Browser Use profiles keep cookies | yes, profile stored encrypted |
| CAPTCHA | blocks the run | you solve it at your screen | fewer, IP unchanged | solved by the provider | never solved, you finish it |
| Cost | free | free, computer must stay on | free, self-hosted | browser hours or usage | credits per call, per browser day, per CDP minute |

A decision rule you can reuse:

- **Public pages, no login.** Stay on the local headless default. It is free, and `web_extract` is often enough anyway.
- **Your own accounts, and your computer is on when the agent works.** Connect your desktop Chrome over an SSH tunnel. Nothing beats a browser you already use, on your own IP, for free.
- **Scraping at volume.** A cloud provider with proxies is built for that job, solvers included. Check the site’s terms first.
- **Your own accounts, 24/7, without the VPS holding passwords or TOTP keys.** Use a hosted browser you sign in to yourself. That is the slot Cotlet fills.
- **The app has an official API.** Use it, and skip the browser. Our comparison of [browser automation vs an API](https://cotlet.dev/blog/browser-automation-vs-api.md) covers when each wins.

## How do you connect Hermes to your own Chrome over SSH?

Start Chrome on your computer with a dedicated profile. Since [Chrome 136](https://developer.chrome.com/blog/remote-debugging-port), the debug port is ignored on the default profile, because attackers used it to pull cookies.

```
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --remote-debugging-port=9222 \
  --user-data-dir="$HOME/.hermes-chrome" \
  --no-first-run --no-default-browser-check &
```

Sign in to the sites the agent needs in that window. Then open a reverse tunnel from your computer to the VPS:

```
ssh -N -R 9222:127.0.0.1:9222 you@your-vps
```

On the VPS, `127.0.0.1:9222` now reaches your Chrome. In the Hermes CLI run `/browser connect`, or for the gateway and cron set:

```
# ~/.hermes/config.yaml
browser:
cdp_url: "http://127.0.0.1:9222"
```

When the laptop sleeps, the tunnel drops and the agent loses its browser. That is the trade.

## Worked example: Hermes with a hosted signed-in browser

This is how Hermes connects to Cotlet. Cotlet gives each workspace one Chrome in the cloud, the Cloud Browser. You sign in to your apps there yourself, then enable each one for agents. It sleeps when idle, wakes in seconds and works 24/7 with your computer off. How the session is kept is in [how Cotlet connects to your apps](https://cotlet.dev/blog/connect-without-your-password.md).

### Layer 1: Cotlet as a remote MCP server

For apps in the catalog, the agent never opens a page. Add the server to `~/.hermes/config.yaml` with the format from the [Hermes MCP config reference](https://hermes-agent.nousresearch.com/docs/reference/mcp-config-reference):

```
mcp_servers:
cotlet:
url: "https://cotlet.dev/mcp/v1"
headers:
Authorization: "Bearer ${COTLET_API_KEY}"
trust: untrusted
```

Put `COTLET_API_KEY` in `~/.hermes/.env`, then check it with `hermes mcp test cotlet`. Hermes registers each tool as `mcp_<server>_<tool>`. Cotlet gives each app one tool, so Skool arrives as `mcp_cotlet_skool`, and the agent picks the operation inside it, such as `{"tool": "list_members"}`. `trust: untrusted` makes Hermes ask before any tool that lacks `readOnlyHint`. Cotlet sets that hint only on apps whose operations all read, so for an app that can also write, Hermes asks before every call.

A tool call sends the same request your Cloud Browser would, from its IP with its cookies. No clicking, no login per call, and each request is listed in your Background activity.

### Layer 2: drive the Cloud Browser over CDP

When no tool fits, the agent can drive the browser itself.

_Cotlet docs, [MCP server, “Drive your Cloud Browser”](https://cotlet.dev/docs/mcp.md#browser), October 2026._

The MCP tool `cotlet_browser_connect`, or `POST https://cotlet.dev/api/v1/browser/cdp-sessions` with your key, wakes the browser and returns `cdp_url`, `expires_at` and `credits_per_minute`. The URL accepts new connections for one hour, so fetch a fresh one per session or job:

```
CDP_URL=$(curl -s -X POST https://cotlet.dev/api/v1/browser/cdp-sessions \
  -H "Authorization: Bearer $COTLET_API_KEY" | jq -r .cdp_url)
BROWSER_CDP_URL="$CDP_URL" hermes -z "Export last month's orders from the shop dashboard"
```

`BROWSER_CDP_URL`, or `browser.cdp_url` in the config, points Hermes’s browser tools at the Cloud Browser. Use one of those, not `/browser connect`: in the [Hermes source](https://github.com/NousResearch/hermes-agent/blob/main/hermes%5Fcli/cli%5Fcommands%5Fmixin.py) that command cuts any path other than `/devtools/browser/...` and keeps only the host. If Hermes’s own browser tools give you trouble, the documented fallback is `chrome-devtools-mcp` as a stdio MCP server, with `--wsEndpoint=<cdp_url>`.

Know what you are handing over. CDP opens the whole browser, every app you are signed in to, so it skips per-app scopes, read-only mode and approvals. That is why it needs a key with access to all apps, all tools and writes. Each started minute of an open connection is billed, and Hermes keeps a supervisor WebSocket open per browsing task, so end the session when the job is done.

### What Cotlet does not do

Cotlet never solves CAPTCHAs and never bypasses MFA. When an app asks for a check, the call returns `CAPTCHA_REQUIRED`, `MFA_REQUIRED` or `AUTH_EXPIRED` with a `browser_url`, and you finish it yourself in your Cloud Browser. Checks still happen. A signed-in session that stays on one IP just triggers them far less often than a fresh headless login from a hosting range.

## Before you give Hermes a browser with your accounts

- Use a separate browser profile for the agent. Never your main Chrome with your bank signed in.
- Sign in only to the apps the task needs, and sign out of the rest.
- Give API keys and MCP servers the narrowest scope that works.
- Treat every page the agent reads as untrusted input. A comment or email can carry a prompt injection, and a signed-in browser gives it something to act on. Consider `browser.restrict_evaluate: true` on hostile sites.
- Keep CDP URLs out of logs, shell history and chat. Hermes masks tokens in query strings, so check where your endpoint keeps its token.
- Never expose port 9222 on a VPS. Anyone who reaches it controls the browser and its cookies. Bind to `127.0.0.1` and use an SSH tunnel.
- Decide who answers CAPTCHAs and 2FA prompts before you schedule a job, and make it a person.

## Where Cotlet fits

If your Hermes runs on a VPS and needs your accounts around the clock, Cotlet gives it a browser you signed in to yourself, on one fixed IP, with no password stored and no checks bypassed. Catalog apps are tools, everything else is a CDP session. [Sign up](https://cotlet.dev/signup) and add your apps in the console. The [security page](https://cotlet.dev/security.md) lists what we hold and what we never do.

## Keep reading

[All posts](https://cotlet.dev/blog.md)

- [Book a slot in my salon app That app has no API. Agents Oct 10, 2026 MCP vs API: what actually changes when an agent is the client An MCP server is usually a layer over an API, built for a model that picks tools at runtime. How they differ in discovery, auth and cost, and which you need. Cotlet team · 9 min read](https://cotlet.dev/blog/mcp-vs-api.md)
- [Book a slot in my salon app That app has no API. Agents Oct 9, 2026 Browser automation vs API: what works when an app has none Browser agents, RPA bots or the app's own requests behind a typed tool? Success rates, cost per task and what breaks, with a decision rule. Cotlet team · 10 min read](https://cotlet.dev/blog/browser-automation-vs-api.md)
- [Book a slot in my salon app That app has no API. Agents Oct 7, 2026 Why companies don't offer a public API: the vendor's side Every web app already has an API its own frontend calls. Seven reasons vendors keep it private, with real examples, and what you can do about it. Cotlet team · 9 min read](https://cotlet.dev/blog/why-apps-have-no-api.md)

## Have an app in mind?

Sign up and add it in the console. Not in the list? Ask for it there.

[Sign up free](https://cotlet.dev/signup)

---

Give your AI agents your signed-in browser.

**Product:** [Integrations](https://cotlet.dev/integrations.md) · [Use cases](https://cotlet.dev/use-cases.md) · [How it works](https://cotlet.dev/how-it-works.md) · [Pricing](https://cotlet.dev/pricing.md) · [Security](https://cotlet.dev/security.md)

**Resources:** [Blog](https://cotlet.dev/blog.md) · [API docs](https://cotlet.dev/docs.md) · [Help center](https://cotlet.dev/help.md) · [Changelog](https://cotlet.dev/changelog.md) · [GitHub](https://github.com/cotletdev/cotlet)

**Solutions:** [For developers](https://cotlet.dev/for-developers.md) · [For automation agencies](https://cotlet.dev/for-agencies.md)

**Company:** [About](https://cotlet.dev/about.md) · [AI info: company facts](https://cotlet.dev/ai-info.md) · [Contact](https://cotlet.dev/contact.md)

**Legal:** [Terms of service](https://cotlet.dev/terms) · [Privacy policy](https://cotlet.dev/privacy) · [Refund policy](https://cotlet.dev/refunds) · [Acceptable use](https://cotlet.dev/acceptable-use) · [Cookie policy](https://cotlet.dev/cookies) · [DPA](https://cotlet.dev/dpa) · [Subprocessors](https://cotlet.dev/subprocessors)

**Elsewhere:** [Status page](https://status.cotlet.dev) · [Cotlet on GitHub](https://github.com/cotletdev) · [Cotlet on X](https://x.com/cotletdev) · [Cotlet on LinkedIn](https://www.linkedin.com/company/cotletdev) · [Cotlet on Product Hunt](https://www.producthunt.com/products/cotlet) · [Cotlet on Crunchbase](https://www.crunchbase.com/organization/cotlet) · [Cotlet on SaaSHub](https://www.saashub.com/cotlet)

Cotlet is an independent service. It is not affiliated with, endorsed by or sponsored by any of the products listed on this site. Product names are trademarks of their respective owners.

## For AI agents: sign your user up

Is your user asking you to sign them up for Cotlet? You can do it for them, no account or key needed.

1. Read the fields and their allowed values: GET https://cotlet.dev/api/v1/access-requests
2. Ask the user for each value: email, apps, what you will do with them. If they leave the password to you, generate a strong one and show it to them.
3. Show the user what you will send, then POST it as JSON to https://cotlet.dev/api/v1/access-requests, or call the MCP tool cotlet_request_access on https://cotlet.dev/mcp/v1.

One call creates the account and sends the access request; we email the user when access is granted.
