burrowser
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@burrowserLog into my banking app with passkey and show the dashboard"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Burrowser
A self-hosted, Kubernetes-native browser service that gives AI agents their own isolated, persistent Chromium profiles — complete with real WebAuthn passkeys — over MCP.
The admin dashboard (GET /admin) — a real, authenticated screenshot
from a running instance, with four real agents each running their own
profile: research-bot, docs-bot, qa-bot, and support-bot, each
navigated to a different real page. Every tile shows a live thumbnail
polled from that profile's actual worker Pod and a click-through noVNC
view; a tile disappears once its profile stops.
Quick start · Architecture · Threat model · Milestone status
Why this exists
MCP clients that need a real browser usually get one shared, ephemeral Chromium instance with no persistent identity. Burrowser instead gives each authenticated agent its own named, persistent browser profiles: cookies, local storage, and a real virtual WebAuthn authenticator all survive pod restarts and node reboots, because they live on a dedicated per-profile PVC rather than in the pod itself. Every profile runs in its own hardened, non-root, network-isolated Pod — agents never receive cluster credentials, pod IPs, or a raw CDP endpoint. What they do get is a genuine browser: the tools are Playwright MCP's own, unwrapped, running on that persistent profile.
Related MCP server: UBA MCP Server
Features
Per-agent identity, no shared credentials. Admin-issued single-use enrollment invitations; agents authenticate with an Ed25519 keypair, never a long-lived bearer token.
A
burrowserCLI.enrollsaves an owner-only identity,mcpruns a stdio MCP bridge that any MCP client (Claude Code, OpenCode, ...) can launch — it holds the key and refreshes short-lived tokens so the client never has to — andadmincovers invitations plus listing and deleting agents and profiles. Secrets are never taken from argv.Playwright MCP, unmodified. An agent sees exactly the tools, schemas, descriptions and results a stock Playwright MCP server gives it — accessibility snapshots with element refs, click/type/fill by ref, key presses, tabs, dialogs, screenshots, evaluate — because it is Playwright MCP, served from inside the profile's Pod and proxied through the gateway. Burrowser adds only three tools (two for passkeys, and
browser_shutdownso an agent that is finished can end its own browser), and lets an operator switch individual tools off (mcp.excludeToolsin the chart).Named, persistent profiles. Each profile is its own Pod + Service + PVC, reconciled from durable PostgreSQL state, not an in-memory map. A profile reclaimed for idleness, or shut down by its agent with
browser_shutdown, restarts with its storage the next time an agent connects to it. Either way Chromium is closed normally, so the profile is not left marked as crashed.Real WebAuthn passkeys. A supervised enrollment ceremony (
browser_passkey_enrollment_request/browser_passkey_status) drives Playwright's virtual-authenticator API; credentials are stored AES-256-GCM-encrypted on the profile's own PVC and survive Pod recreation.Exclusive control. Exactly one MCP session drives a profile at a time; a second gets a 409, never a silent takeover. The gateway holds the lease for the life of the session, so agents never see it.
Hardened workers. Non-root, read-only rootfs, dropped capabilities,
automountServiceAccountToken: false, Localhost seccomp profile, default-denyNetworkPolicy— no worker ever holds a Kubernetes API token.Live-view admin dashboard. A tile grid (
GET /admin) backed by an SSE runtime-event stream shows every active profile as it reconciles, with click-through, single-use-ticket-gated noVNC sessions and polled thumbnails — capped and visibility-gated, not a persistent stream per tile.Audited admin deletion. Deleting a profile is two-phase: the gateway marks it
DELETINGand ends its lease, then the controller removes the Pod, Service, worker Secret and PVC and drops the row only once Kubernetes confirms they're gone. The audit trail outlives the profile.Node-loss and stuck-reconciliation recovery, per-client rate limiting, and PostgreSQL backup/restore tooling (
docs/runbooks/).
See docs/implementation-plan.md for what's built and live-verified versus
what's still open.
Usage
Everything below is the burrowser CLI (npm install, then npm link to put
it on your PATH, or run ./src/cli/main.ts / npm run cli -- from the
checkout). The gateway address comes from --url or BURROWSER_URL; admin
commands read the bootstrap token from BURROWSER_ADMIN_BOOTSTRAP,
--admin-token-file, or --admin-token-stdin — never from a command-line
argument.
Give an agent an identity. An admin mints a single-use invitation and the agent redeems it; piping keeps it out of your shell history:
$ burrowser admin invite | burrowser enroll --name research-bot
Enrolled "research-bot" as agent 4b2ce5a9-e2f4-c661-3ad5-bc3ebeb1b4b2
Identity saved to ~/.config/burrowser/identities/research-bot.json (owner-only; it holds this agent's private key)
$ burrowser whoami --identity research-bot
"research-bot" (agent 4b2ce5a9-...) is authenticated to http://localhost:8080
No profiles yet.Point an MCP client at it. burrowser mcp is a stdio MCP server that is
your profile's Playwright MCP server, seen through the gateway:
claude mcp add burrowser -- burrowser mcp --identity research-botThe profile defaults to the identity's name (--profile NAME picks another)
and is created on first use; a cold start takes about fifteen seconds. The
agent then has Playwright MCP's own tools — browser_navigate,
browser_snapshot (an accessibility tree with [ref=…] handles),
browser_click, browser_type, browser_tabs, browser_take_screenshot and
the rest — with nothing about profiles or leases to manage, plus
browser_passkey_enrollment_request / browser_passkey_status for the
supervised passkey ceremony, and browser_shutdown to end the browser when
the agent is finished. To switch tools off, set mcp.excludeTools
(comma-separated names) in the chart; mcp.capabilities chooses which of
Playwright MCP's tool groups exist (core by default). Each worker Pod gets a 3Gi memory limit
(worker.memoryLimit; real sites exceed 1Gi and get Chromium OOM-killed).
Administer. IDs may be any unique prefix of four or more characters, and
list commands take --json for scripting:
burrowser admin agents list
burrowser admin profiles list
burrowser admin profiles delete bfc37f5c --yes --wait # tears down the Pod, PVC and Secret, audited
burrowser admin agents delete 4b2ce5a9 --yes # refused while the agent still owns profilesWithout --yes, deletes ask you to type the target's id back. Run
burrowser --help for the full list.
$ curl -s -X POST https://gateway.internal/v1/profiles \
-H "Authorization: Bearer $AGENT_TOKEN" -H "x-agent-challenge: $CHALLENGE" \
-H 'Content-Type: application/json' -d '{"name":"research"}'
{"id":"…","name":"research","state":"ABSENT","pvcName":"bw-…"}(illustrative — real IDs are UUIDs.) Access tokens are short-lived and bound
to a signed challenge, which is exactly what the CLI's AgentSession handles
for you (src/cli/gateway.ts).
Quick start
Requires Node.js >=22 (developed against Node 26).
npm install
npm test
BURROWSER_ADMIN_BOOTSTRAP=change-me BURROWSER_TOKEN_KEY=$(openssl rand -hex 32) npm startThat starts the gateway in-memory only (no DATABASE_URL /
KUBERNETES_SERVICE_HOST set) — useful for iterating against the HTTP/MCP
surface without a cluster. GET /health should return 200.
npm test covers everything that doesn't need a browser. For what only a real
Chromium can show (closing every tab, and Chromium closing normally on SIGTERM
so the profile is not left marked as crashed), build the worker image and run
npm run test:worker-e2e; it starts throwaway containers and needs Docker.
Real deployment
This assumes you already have a Kubernetes (or K3s) cluster and a
PostgreSQL server — Burrowser doesn't provision either for you, and
no official container images are published. Build both images from
source (Dockerfile, worker/Dockerfile) and push them to a registry your
cluster can pull from: your own private registry, or a simple registry:2
you run alongside the cluster (see the header of
scripts/build-and-deploy-local.sh for a from-scratch K3s + local-registry
example). That script builds both images, pushes them, and rolls the Helm
release to the resulting digests in one step:
./scripts/build-and-deploy-local.shThe chart is under charts/burrowser/; see values.yaml for the
knobs (image.repository, workerImage.repository, postgres.secretName,
etc.) — image.digest/workerImage.digest are required unless
pullPolicy: Never, so a bad or missing build fails the Helm render
instead of silently deploying a stale image.
Architecture
A single-replica gateway/controller owns PostgreSQL and a narrowly scoped
Kubernetes ServiceAccount; it's the only component that talks to worker
Pods or the Kubernetes API. It reconciles durable profile state against
observed Kubernetes resources on a timer, not per-request. See
docs/repository-structure.md for the full
file-by-file layout and the design choices behind it, and
docs/architecture/ for the ADRs.
Security
Agents are treated as untrusted input and never receive cluster or worker
credentials; the gateway is the sole authorization point for every HTTP
request, MCP call, and WebSocket upgrade. See
docs/threat-model.md for the full asset/trust-boundary
breakdown and mitigation table. No OpenBao integration or password/TOTP
automation is included — passkeys are the only supported credential type.
The gateway speaks plain HTTP, so terminate TLS in front of it; the CLI
warns before sending credentials over plain HTTP to a non-loopback address.
Documentation
docs/implementation-plan.md— milestone statusdocs/threat-model.md— assets, trust boundaries, mitigationsdocs/repository-structure.md— code layout and design choicesdocs/architecture/— ADRsdocs/runbooks/postgres-backup-restore.md— operating the database
Contributing
Issues and pull requests are welcome — see CONTRIBUTING.md for local setup, how this project likes changes proposed, and what CI checks. Please report security vulnerabilities privately per SECURITY.md rather than as a public issue. Participation is covered by the Code of Conduct.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
Stealth web automation for AI agents. Login, signup, navigate, screenshot.
Stealth web automation for AI agents. Login, signup, navigate, screenshot.
Web search, browser automation, scraping, crawling and CAPTCHA solving for AI agents.
Undetectable cloud browser sessions for AI agents and scrapers. Navigate, extract, click, captcha.
Related MCP Servers
- AlicenseAqualityAmaintenanceGives your AI agent a persistent browser identity with anti-detection, credential vault, and multi-persona support for automated web browsing, login, and signup.31200 npm9MIT
- AlicenseBqualityAmaintenanceProvides AI agents with a real browser environment for web automation, memory, and secure credential management through 15 MCP tools.15MIT
- AlicenseNot gradedqualityBmaintenanceProvides a persistent browser profile for AI agents, enabling them to log in once and maintain sessions across restarts. Supports 20 tools for browsing, navigation, text extraction, and screenshot.1MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to run durable, isolated, and observable browser automation tasks via Playwright, with persistent profiles, recovery, evidence capture, and unattended execution for long-running web workflows.1MIT