cocoon sandbox

Browser sandboxes

The browser flavor boots with headless Chromium (Chrome for Testing) already running and its CDP endpoint on guest loopback 9222. An agent claims it and drives it with any CDP client — Playwright connectOverCDP, Puppeteer, Stagehand — through the existing port relay. Two things no hosted browser service offers: checkpoint/branch of a live browser, and CDP on the no-network lane over vsock.

sb, err := client.New(ctx, "ghcr.io/cocoonstack/sandbox/browser:24.04",
    sandbox.WithNetwork(sandbox.NetEgress), sandbox.WithSize(sandbox.Large))

The claim returns when silkd answers (the usual tiers); Chromium finishes starting a beat later — poll /json/version until it answers:

pc, err := sb.DialPort(ctx, 9222)
// GET /json/version with Host: localhost → {"Browser":"HeadlessChrome/150…"}

Claim shape

CDP access

Chromium M113+ binds the DevTools port to loopback only — here that is a feature: silkd’s forwarder dials guest loopback, so CDP is reachable only through an authorized claim and never sits on a guest NIC.

Checkpoint / branch a live browser

sb.Checkpoint + ck.New fork a warmed browser — in-memory tabs, cookies, localStorage — in checkpoint-restore time; the branch answers /json/version without relaunching Chrome. Hosted browser services cold-start a browser per session; here a warmed profile is a template you branch from.

What works, what differs

The hardware acceptance is e2e/cmd/browsersmoke: claim → /json/version over the relay → open a target via PUT /json/new → checkpoint/branch re-verification.