01Documentation
Chrome Shared Desktop
Share one headed Chromium-based browser (Chrome Stable) between a human and a coding agent inside a Sandbox0 sandbox. Both use the same tabs and login state, and can inspect applications served on sandbox loopback ports.
The chrome template includes sandbox0-browser: a foreground command
that starts Chromium, its desktop, and a built-in web viewer. Open the viewer
through a Private Preview and attach the agent through local CDP. No additional
template, desktop script, or frontend is required.
The same runtime is also included in coding-agent and used by Sandpi. The
chrome template contains Chrome, the full desktop and Playwright CLI without
installing the coding-agent harnesses. In the dashboard, use Open browser on
the sandbox to start the supervised browser and open its private viewer.
Quickstart#
Use a chrome image containing sandbox0-browser. Existing sandboxes
retain their installed files when a template changes; check availability with
sandbox0-browser --version inside the sandbox. If the command is missing,
use an updated template for a new sandbox or explicitly upgrade the old RootFS.
See Coding Agents.
Install the JavaScript SDK and save the following as browser.mjs:
bashnpm install sandbox0@^0.10.1
javascriptimport { Client } from "sandbox0"; import { setTimeout as sleep } from "node:timers/promises"; const token = process.env.SANDBOX0_API_KEY; const baseUrl = process.env.SANDBOX0_API_URL; if (!token || !baseUrl) { throw new Error("SANDBOX0_API_KEY and SANDBOX0_API_URL are required"); } const client = new Client({ token, baseUrl, }); const sandbox = await client.sandboxes.claim("chrome", { ttl: 3600, hardTtl: 7200, resources: { memory: "2Gi" }, }); try { const session = await sandbox.createSession({ command: ["sandbox0-browser", "--require-version", "2"], cwd: "/workspace", readiness: { type: "output", output: "Browser ready:", timeoutMs: 60000, }, lifecycle: { restart: { policy: "on_failure" }, runtimeRecovery: "restart", }, }, { idempotencyKey: "shared-browser" }); let ready = false; for (let attempt = 0; attempt < 75; attempt++) { const current = await sandbox.getSession(session.id); if (current.phase === "running") { ready = true; break; } if (["failed", "exited", "stopped", "suspended"].includes(current.phase)) { throw new Error(`Browser session ended: ${current.phase}`); } await sleep(1000); } if (!ready) throw new Error("Browser readiness timed out"); const preview = await sandbox.createPreview({ port: 6080, protocol: "http", path: "/", ttlSeconds: 900, }); console.log("Sandbox:", sandbox.id); console.log("Browser session:", session.id); console.log("Preview grant:", preview.id); console.log("Open this one-time link in your browser:", preview.url); } catch (error) { await client.sandboxes.delete(sandbox.id); throw error; }
Set SANDBOX0_API_KEY and SANDBOX0_API_URL for your deployment, then run
node browser.mjs. Open the printed link in your browser to see and control
Chromium. Keep this link private and do not fetch it from your backend first:
it contains a one-time credential that the browser exchanges for a preview
cookie. The SDK/API key stays in your backend or local script.
The supervised session keeps running after this script exits. A Private Preview expires independently; renew it before expiration or create a new grant to open another viewer. See Private Previews for renewal, revocation, and runtime generation behavior.
To stop the browser, call
sandbox.setSessionDesiredState(sessionId, "stopped"). To remove the test
sandbox and its RootFS, call client.sandboxes.delete(sandboxId).
Use An Existing Sandbox#
Run the helper in a supervised session rather than an interactive terminal:
bashs0 sandbox session create "$SANDBOX_ID" \ --restart on_failure --runtime-recovery restart \ --readiness output --ready-output 'Browser ready:' \ --ready-timeout-ms 60000 -- sandbox0-browser --require-version 2
Wait until the session is running, then create a Private Preview for port
6080 as above or with the HTTP API:
bashcurl -X POST "$SANDBOX0_API_URL/api/v1/sandboxes/$SANDBOX_ID/previews" \ -H "Authorization: Bearer $SANDBOX0_API_KEY" \ -H "Content-Type: application/json" \ -d '{"port":6080,"protocol":"http","path":"/","ttl_seconds":900}'
Open data.url in your browser. Creating a preview does not start the browser
process. Run only one shared browser per sandbox; starting a second helper fails
instead of taking over an active profile.
Attach The Agent#
With the browser ready, run these commands inside the same sandbox:
bashplaywright-cli attach --cdp=http://127.0.0.1:9222 playwright-cli tab-list playwright-cli snapshot
Read the installed CLI's version-matched Agent Skill before using automation
commands. attach reuses the existing browser and tabs. A separately launched
browser or isolated context does not share the same interaction. See the
upstream Playwright CLI guide.
The human can sign in through the viewer and let the agent continue in the same
browser. Coordinate before navigating, typing, or closing tabs: sharing a
browser does not serialize human and agent input. When the agent finishes, use
playwright-cli detach to leave Chromium running. Closing the viewer also leaves
the browser running.
Defaults And Configuration#
Runtime version 2 includes wallpaper, a bottom taskbar and browser launcher. Task buttons and the launcher restore minimized windows without duplicating them. Closing the final browser window keeps the desktop and viewer connected; the launcher reopens the same profile, retaining cookies. CDP is restarted without opening a window, so automation can reconnect after Chrome exits. Native maximize/restore and F11 fullscreen are available. Normal windows are fitted to the current work area after restoring or shrinking the desktop, and the taskbar reserves its space when windows are maximized. Small viewers scale a desktop of at least 640×480 while preserving their aspect ratio.
| Setting | Default |
|---|---|
| Viewer HTTP and WebSocket | 127.0.0.1:6080, page /, WebSocket /vnc |
| Readiness endpoint | GET /healthz, available after desktop and CDP readiness |
| Agent CDP | http://127.0.0.1:9222 |
| Desktop VNC | 127.0.0.1:5900 |
| Persistent browser profile | /workspace/.browser/profile |
| Persistent downloads | /workspace/browser-downloads |
| Browser user | sandbox-browser, with Chromium's process sandbox enabled |
| Initial desktop size | 1440x900 |
| Launcher control | 127.0.0.1:9224, local POST /open, browser Origins rejected |
Use sandbox0-browser --help for command options. For example:
bashsandbox0-browser --profile /workspace/my-browser/profile \ --downloads /workspace/my-browser/downloads --size 1920x1080
Profile and download paths must be separate canonical directories under
/workspace, with no symbolic-link ancestors. The helper prepares directory
permissions, serializes startup, and recovers stale Chromium singleton links
only after checking that the profile has no active browser process. Stopping
the helper stops its desktop, window manager, Chromium, and viewer together.
Launch the helper as sandbox root; it starts Chrome and the window manager
as the dedicated browser user.
The viewer is bundled with the image; browser and desktop dependencies are installed at build time. Claiming a sandbox does not automatically start them. The helper has no independent authentication: keep its default loopback bind and use Private Preview, or protect every published route with service auth. CDP and VNC remain on loopback and are not exposed by the viewer.
Preview Local Web Applications#
Open http://127.0.0.1:3000 in the shared Chromium to inspect a development
server in the same sandbox. Here, localhost refers to the sandbox. Your device
connects to the viewer while the remote browser connects to the web app, so you
do not need to publish each development server.
For a direct link rendered in the human's own browser, use a Private Preview of the application's port.
Integrate Into Your Application#
Applications such as Sandpi can use the same helper with an existing noVNC frontend and an authenticated backend proxy. Register a command-backed Sandbox Service that exposes only the VNC bridge to your backend:
yamlservices: - id: shared-browser port: 6080 runtime: type: cmd command: [sandbox0-browser, --require-version, "2", --host, 0.0.0.0] cwd: /workspace ingress: public: true routes: - id: viewer path_prefix: /vnc methods: [GET] auth: mode: header header_name: X-Browser-Proxy header_value_sha256: <sha256-of-backend-route-token> resume: true
The helper uses SANDBOX0_SERVICE_PORT when a service injects it. It opens the
viewer listener only after the desktop and CDP are ready, so the default TCP
service readiness check covers browser startup. Include all services you want
to retain when updating the service list: PUT /services replaces the list.
Your backend authorizes access to the sandbox and relays the frontend's
WebSocket to the service's public_url plus /vnc, using wss and the configured
auth header. Browser clients cannot set that header directly. Keep the route
token and deployment API key in the backend. Enable sandbox auto_resume and
route resume when viewing should wake a paused sandbox.
Sandpi's unified runtime integration invokes this command directly, retaining
its existing profile at /workspace/.sandpi/browser/profile. Upgrade retained
Environment RootFS generations to runtime version 2 before deploying that
integration. Applications must not assume a template promotion changes files
already installed in an existing sandbox.
Upgrade An Existing RootFS#
The candidate build workflow produces browser-runtime.tar.gz and records
its SHA-256 in coding-agent-release.json. These assets belong to private
candidate prereleases; building them does not qualify them for production or
make them publicly downloadable. Obtain an approved exact-image archive from
your platform operator. The archive is checked to be identical across
architectures. Applications can use it to upgrade
only the browser runtime on an existing RootFS, preserving workspace, profile,
and Agent processes.
Stop only the supervised Browser session/service, extract the verified archive
into a separate staging directory, and run
bash /path/to/staged-runtime/upgrade-runtime.sh /path/to/staged-runtime as
sandbox root. The installer refuses occupied Browser ports, installs missing
desktop packages, creates the template's browser user if absent, retains the
previous runtime for recovery, and installs the shared launcher. The archive
includes its WebSocket library, so it does not depend on that library being
installed by a retained coding-agent image. Restart the Browser service and verify
sandbox0-browser --version, the viewer, and your Agent's process continuity.
The helper uses the existing profile when passed with --profile.
Archived snapshots retain their original binaries. Rerun this upgrade after restoring an old snapshot. Do not replace RootFS or use disk-only pause/resume to upgrade a live Environment. If network policy blocks package repositories, provision the required packages separately or arrange an explicit policy change.
Retired Steel Template#
The former public browser template used Steel's session API. New shared desktop
sandboxes use chrome. This is an API change, not a template alias: Steel session,
screenshot, scrape and PDF calls do not become Chrome endpoints. Use Playwright
against the existing Chrome CDP connection instead.
Existing Steel sandboxes keep their own RootFS and are not converted. See Legacy Steel Browser for their integration. The default agent endpoint remains sandbox-local. An agent outside the sandbox can execute Playwright through Sandbox0's execution API; do not publish bare CDP.
Persistence And Recovery#
| State | Recovery behavior |
|---|---|
| Committed profile files, cookies, and local storage | Restored with RootFS; websites may still expire login sessions |
| Downloads | Restored with RootFS |
| Browser and desktop processes | Restarted after filesystem-only recovery by the session/service; supported memory checkpoint/restore can retain execution state |
| Viewer and CDP connections | Reconnect after resume or transport failure |
| Live tabs and in-memory page state | Not guaranteed by RootFS recovery or Chromium session restore |
| Private Preview grants | Recreate after runtime replacement; renew before expiration |
See Pause And Resume for memory checkpoint support and compatibility. Profile persistence does not guarantee continuation of an in-flight browser operation. Reattach the agent when its CDP connection is lost.
RootFS snapshots and forks include profile files, which can contain authenticated sessions. Anyone with workspace access can access those files; grant access to copies accordingly.