Every browser is its own context
Each slot has its own cookies, storage and cache on a shared Chromium. A client sees its own pages and nothing else on that Chromium. This is tested against real Chromium, including attempts to reach another slot.
Self-hosted browsers for AI agents and automation
browserbay runs isolated Chromium browsers on infrastructure you control: a VPS, a Raspberry Pi, Kubernetes. Drive them with Playwright, Puppeteer or any agent over the DevTools protocol, and show a live view in your own app so a person can watch and take over.
How it works
Clients for Node, Python and Go. Anything that speaks HTTP and WebSocket works too.
Quick start →import { chromium } from 'playwright'
import { createBrowserBay } from
'@degoke/browserbay/client'
const box = createBrowserBay({
url: 'https://browsers.example.com',
apiKey
})
const slot = await box.acquire({
runId: 'task-42',
maxLifetimeSeconds: 1800
})
try {
const browser = await chromium.connectOverCDP(
slot.cdpUrl,
{ headers: slot.headers }
)
const context = browser.contexts()[0]
const page =
context.pages()[0] ?? (await context.newPage())
await page.goto('https://example.com')
} finally {
await slot.release()
}Illustration. You build the viewer; the box supplies the stream.
Live view
A slot takes more than one connection. Your service drives with Playwright. A person opens a second connection, watches a stream of frames, and clicks and types in the same page. When they step away, your service reconnects and finds the pages as they left them.
// On your server: a second connection to the slot
const ws = new WebSocket(slot.cdpUrl, {
headers: slot.headers
})
// Ask Chromium for frames, and send them on
await send('Page.startScreencast',
{ format: 'jpeg' }, sessionId)
// Turn the person's click into input
await send('Input.dispatchMouseEvent',
{ type: 'mousePressed', x, y,
button: 'left', clickCount: 1 }, sessionId)What you get
Each slot has its own cookies, storage and cache on a shared Chromium. A client sees its own pages and nothing else on that Chromium. This is tested against real Chromium, including attempts to reach another slot.
Run it on a VPS, a container platform or a Raspberry Pi. Nodes connect out to the relay, so a node can sit behind NAT, and no DevTools port is open anywhere.
Start with one container. When you need more browsers, run more nodes, on any machine, and the relay schedules across them.
Three separate secrets. A lease on every browser, so a crashed client cannot hold one forever. A protocol version check between relay and nodes.
Scope
browserbay gives you a browser and stops there. No agent loop, no model calls, no crawler, no screenshot endpoint. That keeps the box small, and it lets one box serve a test suite, a scraper and an agent at the same time.
Deploy
The default image is a complete box: the relay and a Chromium node together. When you want browsers on several machines, run one relay and add nodes as you need them, anywhere. On Kubernetes, a Helm chart runs the relay once and the nodes as their own Deployment, so they scale on their own.
export BROWSERBAY_API_KEY="$(openssl rand -base64 24)"
docker run -d --name browserbay --restart always --init --stop-timeout 130 \
-p 127.0.0.1:8788:8788 -e BROWSERBAY_API_KEY \
ghcr.io/degoke/browserbay