BrowserPane

BrowserPane is a self-hostable remote browser and workflow execution platform for humans and agents.

Most browser automation products stop at managed browsers, CDP endpoints, or live debug links. BrowserPane treats the live browser session itself as the product surface: a real Chromium session that browser users, supervisors, and automation can all attach to with shared-session policy, owner/viewer controls, and persistent session resources.

The key technical difference is that BrowserPane includes its own host-layer remote browser stack. The Rust bpane-host process runs next to Chromium inside the Linux runtime, captures and classifies the desktop surface, streams tiles, ROI video, audio, cursor, clipboard, files, input, microphone, camera, and resize events through BrowserPane's protocol, and lets the web client render the live session in a regular browser page.

BrowserPane is intended to be integrated into larger automation and workflow systems. Its workflow layer is primarily about browser-run execution, supervision, artifacts, and human intervention around a live browser session, not about replacing a general scheduler or DAG orchestrator.

This means BrowserPane is not only a wrapper around Playwright, CDP, screenshots, or a hosted debug iframe. It owns the live browser transport path from the Linux host to the browser client.

Checkout on youtube: https://www.youtube.com/watch?v=zhj2_B4vLMs

The frozen v1 control-plane API contract now lives in openapi/bpane-control-v1.yaml.

Why BrowserPane

BrowserPane will be worth considering if you need more than "a browser for an agent."

  • BrowserPane owns the remote browser protocol. bpane-host, bpane-gateway, bpane-protocol, and bpane-client form a browser-native live session stack rather than delegating the user experience to a generic remote desktop product.
  • Shared sessions are a first-class feature, not an afterthought. Multiple browser clients can join the same session with collaborative or restricted viewer behavior.
  • Automation attaches to governed sessions instead of bypassing session policy. MCP and other automation flows operate through explicit ownership and session-control APIs.
  • The remoting stack is browser-native. BrowserPane uses WebTransport plus a tile-first render path with optional ROI H.264 instead of relying only on full-frame streaming or vendor-hosted live debug UIs.
  • The session behaves like a real remote workspace. Clipboard, file transfer, audio out, microphone in, camera ingress, resize, and input policy are part of the system design.
  • The platform is self-hostable. Teams can run BrowserPane in their own environment instead of treating browser control as a SaaS-only dependency.

Where It Fits

BrowserPane is a strong fit for:

  • human-in-the-loop browser automation
  • collaborative investigation, support, or review sessions
  • regulated or private deployments that need self-hosted browser access
  • workflow systems that need durable session identity, artifacts, logs, and audit history
  • platforms that need a governed browser execution target inside a larger orchestration stack

Current Status

BrowserPane is still experimental.

Current support and scope:

  • Host runtime: Linux only. Ubuntu 24.04 container is the primary target.
  • Browser runtime: Chromium desktop only. Firefox and Safari are not production targets.
  • Shared sessions: collaborative by default, intended for small curated groups rather than broadcast-scale delivery.
  • Owner/viewer mode: optional exclusive-owner mode is supported in the gateway; restricted viewers are read-only.
  • Camera: disabled by default in the compose stack and requires browser H.264 encode support plus a mapped v4l2loopback device.
  • Control plane: owner-scoped v1 APIs now cover identity/access-review summaries, service principals, identity-to-project mappings, projects, sessions, session templates, egress profiles, automation tasks, session recordings, workflow definitions/runs, file workspaces, credential bindings, and approved extensions.
  • Workflow execution: Git-backed workflow versions run through a gateway-managed workflow-worker; the current executor model is Playwright.
  • Workflow boundary: BrowserPane currently focuses on executing and supervising browser workflows. Broader scheduling, DAG orchestration, and cross-system coordination are expected to sit above BrowserPane rather than inside it.

How The System Is Shaped

At a high level, BrowserPane has five responsibilities:

  1. Run a real browser session in a Linux host environment.
  2. Capture and classify that surface efficiently.
  3. Transport state, input, and media between host and browser.
  4. Render the remote session in a regular web page.
  5. Coordinate durable control-plane resources for identity, projects, sessions, workflows, recordings, files, credentials, extensions, and automation ownership.

The default local runtime looks like this:

browser client
  <-> bpane-gateway
  <-> bpane-host
  <-> Chromium + Xorg/Openbox inside a Linux runtime

bpane-host captures the browser desktop surface and emits BrowserPane protocol frames.
bpane-gateway applies session policy and relays WebTransport traffic.
bpane-client renders the live session and sends input/media/file events back.

bpane-gateway also talks to:
  - postgres
  - mcp-bridge
  - workflow-worker
  - recording-worker

Projects And Responsibilities

Project Responsibility
code/apps/bpane-host Linux host agent. Captures the desktop surface, classifies tiles, drives ROI H.264 video, emits audio, injects input, and handles clipboard, file transfer, resize, and camera ingress plumbing.
code/apps/bpane-gateway WebTransport entry point, shared-session coordinator, runtime lifecycle boundary, and owner-scoped control-plane API for identity/access review, projects, sessions, session templates, automation tasks, recordings, workflows, files, credentials, and extensions.
code/shared/bpane-protocol Shared binary wire contract. Defines channels, frame envelopes, typed protocol messages, and incremental frame decoding used by the Rust services and validated against the browser client.
code/web/bpane-client Real browser client. Renders tiles/video, decodes media, captures keyboard/mouse/clipboard input, and manages browser-side audio, camera, and file-transfer flows.
code/integrations/mcp-bridge Automation bridge for MCP/Playwright-style control flows. Exposes compatibility Streamable HTTP on /mcp, session-scoped Streamable HTTP on /sessions/{id}/mcp, compatibility SSE on /sse, session-scoped SSE on /sessions/{id}/sse, and integrates with gateway ownership APIs so automation can attach alongside interactive browser users through delegated session control.
code/integrations/workflow-worker On-demand workflow executor. Downloads pinned workflow source snapshots, attaches with session automation access, runs Playwright workflow entrypoints, resolves credential/workspace inputs, and writes logs, outputs, and produced files back to the gateway.
code/integrations/recording-worker On-demand recording executor. Attaches as a passive recorder client, captures WebM output, and finalizes recording metadata into gateway-managed artifact storage.
deploy/ Local runtime manifests and container images. This is the practical source of truth for how the dev stack is assembled and started.

Rendering Model

BrowserPane is not a simple full-frame video streamer.

  • UI and text travel primarily over the reliable tile path.
  • Media-heavy regions can move to ROI H.264 on the video path.
  • Desktop audio travels separately from visual updates.
  • Input, clipboard, file transfer, microphone, and camera each have dedicated protocol flows.

That split is what lets the system keep static UI sharp while still handling moving video efficiently.

Protocol Model

The shared protocol is a compact binary protocol implemented in bpane-protocol.

  • Reliable typed channels are used for control, input, cursor, clipboard, file transfer, and tiles.
  • Raw media channels are used for video, desktop audio, microphone, and camera payloads.
  • The protocol crate is the source of truth for frame/message definitions; the README stays intentionally high-level.

Local Development

Recommended: Docker Compose

The local session console now defaults to docker_pool mode so Start New Session provisions an isolated browser runtime instead of reusing one shared legacy worker:

Generate a dev certificate once:

./deploy/gen-dev-cert.sh dev/certs

Start the stack:

BPANE_GATEWAY_MAX_ACTIVE_RUNTIMES=2 \
docker compose -f deploy/compose.yml up --build

Then open http://localhost:8080/admin/ in Chromium. The web root redirects to the stable admin console.

The route-backed unified admin redesign is available in parallel at http://localhost:8080/admin-new/. It is under active development for BPANE-00142 and is intended for incremental manual testing while /admin/ remains the stable/default console. The redesigned session preview popup includes a local Metrics drawer that can sample browser transition diagnostics from the BrowserPane client runtime without creating a backend artifact. It reports FPS, transfer rates, tile mix, cache health, scroll fallback health, video datagrams, and render backend, and can copy the current sample as JSON for debugging.

Use these local dev credentials on the login screen:

  • username: demo
  • password: demo-demo

Then:

  1. Click Login
  2. Click Start New Session to create a fresh browser, or select an older session and click Join / Reconnect
  3. Open the same selected session in another signed-in browser window if you want to share it live with another user
  4. Click Delegate MCP if you want the local mcp-bridge to drive that exact session
  5. For external MCP clients, prefer the session-scoped URL shown in the admin MCP panel, for example http://localhost:8931/sessions/{session_id}/mcp

If you explicitly want the older single-runtime compatibility stack, opt into it:

BPANE_GATEWAY_RUNTIME_BACKEND=static_single \
docker compose -f deploy/compose.yml up --build

The compose stack starts:

  • host: Linux host runtime with Xorg dummy, Openbox, Chromium, and bpane-host
  • gateway: WebTransport relay on :4433 and HTTP APIs on :8932
  • postgres: session-control database on :5433
  • vault: local HashiCorp Vault dev server on :8200 for workflow credential bindings
  • keycloak: local OIDC provider on :8091
  • web: local frontend on :8080
  • mcp-bridge: MCP bridge on :8931 (/sessions/{id}/mcp for recommended session-scoped Streamable HTTP, /sessions/{id}/sse for session-scoped legacy SSE, /mcp and /sse for compatibility)
  • recording-worker-image: one-shot build helper for the on-demand recording worker image used by recording.mode=always; the gateway launches short-lived recorder containers when sessions start recording

The local compose file also defines on-demand worker images for workflows and recordings. The gateway launches workflow-worker and recording-worker containers as short-lived jobs; you normally do not start those containers as long-lived services yourself. The gateway mounts the repository at /workspace:ro for local git-backed workflow sources, configures /workspace as a trusted Git safe.directory, and passes --workflow-source-trusted-local-root /workspace so local paths are allowed only from that explicit development root. The recording worker uses the generated local SPKI fingerprint from dev/certs/cert-fingerprint.txt through the gateway's --recording-worker-cert-spki-file setting, so run ./deploy/gen-dev-cert.sh dev/certs before starting compose after certificate rotations. The recording worker forces the SDK render backend to Canvas2D for reliable headless Docker capture. Interactive admin and embedded browser clients keep the default auto backend, so GPU/WebGL rendering remains available for end-user sessions when the browser environment supports it.

The local MCP bridge uses the package-installed @playwright/mcp executable from its own dependencies. It should not download @playwright/mcp@latest on first connect; run npm ci in code/integrations/mcp-bridge or rebuild the image if that local executable is missing.

The gateway supports three runtime backends:

  • static_single: one shared host worker
  • docker_single: one start-on-demand runtime container with idle shutdown
  • docker_pool: multiple start-on-demand runtime containers with explicit max_active_runtimes and max_starting_runtimes

deploy/compose.yml now defaults to docker_pool, but you can still switch backends explicitly when you need a compatibility check:

BPANE_GATEWAY_RUNTIME_BACKEND=docker_pool \
BPANE_GATEWAY_MAX_ACTIVE_RUNTIMES=2 \
docker compose -f deploy/compose.yml up --build

deploy/compose.yml now mounts Docker access into the gateway and forwards a shared host-worker env profile automatically. If your compose project name is not the default deploy, override these defaults too:

  • BPANE_GATEWAY_DOCKER_RUNTIME_IMAGE
  • BPANE_GATEWAY_DOCKER_RUNTIME_NETWORK
  • BPANE_GATEWAY_DOCKER_RUNTIME_SOCKET_VOLUME
  • BPANE_GATEWAY_DOCKER_RUNTIME_SESSION_DATA_VOLUME_PREFIX
  • BPANE_RECORDING_WORKER_NETWORK
  • BPANE_RECORDING_WORKER_OUTPUT_VOLUME
  • BPANE_RECORDING_WORKER_IMAGE

The default local auth flow is OIDC-based:

  • open http://localhost:8080/admin/
  • click Login
  • authenticate against the local Keycloak realm
  • use the demo account demo / demo-demo
  • return to the admin console and either select an existing session or create a new one, optionally from a session template, reusable browser context, and explicit capability restrictions
  • the admin console joins the selected owner-scoped /api/v1/sessions resource, or creates a new one before opening WebTransport
  • the live session panel and session inspector show the applied template, and the inspector can filter sessions by template, lifecycle state, and runtime state
  • sessions created from the admin console use a 5 minute idle timeout and are stopped automatically if they remain unused or become idle without any browser viewers or MCP owner
  • reconnecting a stopped session now restarts the same session resource instead of creating a new one
  • switching the selected session disconnects the embedded browser from the previous live session before selecting the new one
  • the console UI now shows whether the currently selected session is the exact session delegated to the local MCP bridge
  • in Docker-backed runtime modes, BrowserPane mounts session-specific browser data for the Chromium profile, uploads, and downloads so cookies, cache, downloads, and Chromium session-restore state survive worker restarts without sharing one browser data root across sessions
  • Docker-backed runtime assignments are now persisted in Postgres and recovered on gateway restart, so an existing pool-mode worker can be rebound without launching a duplicate container
  • exact in-memory browser process state is only preserved while the worker is still alive; once idle-stop shuts a worker down, reconnect restores the browser from its persisted profile rather than from a true container checkpoint
  • if you want the local mcp-bridge to follow that same session, click Delegate MCP

The admin console fetches /auth-config.json and performs an Authorization Code + PKCE login. Admin API calls use the resulting OIDC bearer token. Before WebTransport connect, the console mints a short-lived session-scoped connect ticket from the session API and uses that ticket on the transport URL instead of the long-lived bearer token. The legacy development harness remains available at /test-embed.html for smoke tests that still exercise harness-specific hooks.

For Chromium, WebTransport still needs trusted TLS on localhost. The current runtime SPKI fingerprint is served at:

http://localhost:8080/cert-fingerprint
http://localhost:8080/cert-hash

./deploy/gen-dev-cert.sh dev/certs also refreshes dev/certs/cert-fingerprint.txt and dev/certs/cert-hash.txt from the same cert.pem for CLI and WebTransport certificate-hash use. The admin app and browser client request these local certificate metadata endpoints without browser cache reuse so certificate rotations can be picked up after reload.

If a manually launched local Chromium reports Opening handshake failed when joining a session, start it with the local QUIC origin and SPKI trust flags:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --origin-to-force-quic-on=localhost:4433 \
  --ignore-certificate-errors-spki-list="$(cat dev/certs/cert-fingerprint.txt)" \
  http://localhost:8080/admin/

Remote / Self-Hosted Testing

The checked-in compose stack is a local development and regression environment, not a production deployment guide. Remote testing needs HTTPS for the web UI, a browser-trusted WebTransport gateway certificate, aligned OIDC issuer and redirect settings, and private handling for dev-only services such as Postgres, Vault, Keycloak admin surfaces, gateway internals, and the MCP bridge.

See REMOTE_DEPLOYMENT.md for the current remote deployment assumptions and compose override notes.

Session Control Plane

The local stack now includes a frozen v1 session control plane in bpane-gateway.

Canonical contract:

  • openapi/bpane-control-v1.yaml

  • POST /api/v1/sessions

  • GET /api/v1/sessions

  • GET /api/v1/sessions/{id}

  • DELETE /api/v1/sessions/{id}

  • GET /api/v1/identity/me

  • GET /api/v1/identity/access-review

  • POST /api/v1/service-principals

  • GET /api/v1/service-principals

  • GET /api/v1/service-principals/{id}

  • PUT /api/v1/service-principals/{id}

  • POST /api/v1/identity-mappings

  • GET /api/v1/identity-mappings

  • GET /api/v1/identity-mappings/{id}

  • PUT /api/v1/identity-mappings/{id}

  • POST /api/v1/browser-contexts

  • GET /api/v1/browser-contexts

  • GET /api/v1/browser-contexts/{id}

  • POST /api/v1/browser-contexts/{id}/clone

  • GET /api/v1/browser-contexts/{id}/export

  • POST /api/v1/browser-contexts/import

  • DELETE /api/v1/browser-contexts/{id}

  • POST /api/v1/session-templates

  • GET /api/v1/session-templates

  • GET /api/v1/session-templates/{id}

  • PUT /api/v1/session-templates/{id}

  • POST /api/v1/projects

  • GET /api/v1/projects

  • GET /api/v1/projects/{id}

  • PUT /api/v1/projects/{id}

  • GET /api/v1/projects/{id}/usage

  • POST /api/v1/egress-profiles

  • GET /api/v1/egress-profiles

  • GET /api/v1/egress-profiles/{id}

  • PUT /api/v1/egress-profiles/{id}

  • GET /api/v1/egress-profiles/{id}/diagnostics

  • GET /api/v1/sessions/{id}/egress-diagnostics

  • POST /api/v1/sessions/{id}/egress-diagnostics

  • POST /api/v1/sessions/{id}/egress-usage

These endpoints are bearer-protected, owner-scoped, and stored in Postgres. The full contract is in the OpenAPI file; the route lists below call out the operator-facing surfaces that are most relevant for local development.

Session templates store reusable defaults for session creation, including owner mode, project id, viewport, idle timeout, labels, integration context, network identity, and recording policy. Creating a session with a UUID template_id merges those defaults before the session is persisted; explicit caller fields win over template defaults. The admin create-session configurator follows the same rule: selecting a template leaves owner mode and idle timeout unset unless the operator chooses an explicit override, and the API payload preview shows the exact fields that will be sent. GET /api/v1/sessions accepts catalog filters such as template_id, state, runtime_state, label.<key>, integration.<key>, limit, and offset. Project resources let operators group sessions and workflow runs under an owner-scoped tenant, case, customer, or environment boundary. A project carries labels, lifecycle state, optional quotas such as max_active_sessions, max_active_workflow_runs, max_retained_storage_bytes, max_session_creations, max_session_creations_per_window with session_creation_window_sec, max_runtime_usage_ms, and max_egress_total_bytes, policy bindings for allowed session templates, egress profiles, approved extensions, and reusable browser contexts, a usage_budget_enforcement mode, browser upload/download, session-file binding, and manual-recording policy switches, and sanitized usage counters. Creating a session or workflow run with project_id records the admission decision and enforces archived-project checks plus template, egress, extension, reusable-context, and file-workspace allow-lists before runtime launch. Project policy can also reject browser upload/download transfers, session-file binding creation, and ad-hoc manual recording starts for project sessions. Session resources expose capabilities.file_transfer=false when the project disables either browser upload or browser download transfer, and explicit session create requests can narrow browser input, clipboard, audio, microphone, camera, file transfer, and resize capabilities for that session. Credential bindings and egress profiles can be owner-scoped or assigned to a project; project-scoped sessions may use owner-scoped egress profiles or profiles from the same project, and may only use project-bound proxy credential bindings from that same project. Owner-scoped bindings remain available to the owner. Project-scoped sessions that exceed max_active_sessions are persisted as visible queued session resources with active_session_quota_exceeded admission metadata, queue position, queue age, current dispatch blocker, and an explicit queued-session cancel operation. Queued sessions are promoted when capacity opens; workflow runs also inherit the project from their bound session when the request omits project_id. Project usage includes active and queued session counts, total session creations, live-plus-finalized browser runtime milliseconds, sanitized egress receive/transmit byte counters, and retained storage. Retained storage currently counts workflow produced files, completed recording artifacts, uploaded/downloaded session files, and files retained in project-owned file workspaces; workflow outputs stored in a workspace owned by the same project are counted once through the workspace. The gateway rejects new retained artifacts that would exceed the project storage quota. Egress byte counters are intentionally metadata-only until an attached proxy or secure web gateway reports authoritative traffic totals. Session-creation, runtime, and egress byte budgets emit alerts at 80% and 100% of the configured budget. Projects can opt into usage_budget_enforcement=block_session_creation to reject new project sessions after max_session_creations is reached, the rolling max_session_creations_per_window budget is exhausted, or max_runtime_usage_ms has already been consumed. Existing sessions are not stopped by runtime-budget admission. Egress budgets remain advisory until proxy ingestion is authoritative. Session and workflow-run resources include the project summary and admission reason so the admin live view, inspectors, CLI, and API clients all show whether work was admitted under project quota, queued by project capacity, rejected by project policy, or left owner-scoped. Identity resources expose a sanitized access-review foundation: GET /api/v1/identity/me returns the current bearer principal as user, service_principal, or legacy_dev_token, while GET /api/v1/identity/access-review returns registered service-principal metadata, explicit identity-to-project mappings, unmapped principal signals, project usage, owner-scoped resource counts, and currently delegated automation principals. Owners can register external OIDC clients through /api/v1/service-principals, map external users, groups, claims, or registered service principals to BrowserPane projects through /api/v1/identity-mappings, use active/disabled state for lifecycle review, and block new automation delegation to disabled registered clients while keeping existing session metadata inspectable for cleanup. Access review evaluates group and claim mappings from a bounded safe OIDC claim set (groups, roles, tenant, tenant_id, organization, organization_id, org_id, department, realm_access.roles, and resource_access.<client>.roles) without returning raw bearer-token payloads. The admin Operations Overlay has a matching Identity tab for access-review visibility, service-principal create/edit/disable/re-enable operations, identity-mapping create/edit/disable/re-enable operations, and project-name rendering where visible. The operator CLI exposes the same payload through identity me, identity access-review, service-principal, and identity-mapping commands. Network identity metadata lets callers declare locale, language preferences, timezone, geolocation, browser identity, user-agent override, and an egress_profile_id on either a session template or an explicit session create payload. Egress profiles are owner-scoped resources with safe proxy metadata, bypass rules, optional credential-binding references for proxy auth, custom CA references, state, labels, and sanitized effective status; session resources, /status, and /egress-diagnostics include the inherited network identity, effective egress summary, and sanitized diagnostics without embedding proxy credentials or raw CA material. Diagnostics distinguish configuration-only evidence, runtime launch metadata, and the latest active profile or browser probe. Egress-profile probes perform a real proxy request and, when a proxy credential binding is configured, resolve the binding only for that probe so operators can see whether proxy authentication succeeds or is rejected. When a session consumes an egress profile with proxy authentication, the gateway also checks that any project-scoped egress profile and credential binding belong to the same project as the session. The active browser probe runs only against an already-ready session runtime and stores sanitized public-IP, TLS issuer, and failure summary fields; diagnostics do not store requested URLs, headers, proxy credentials, CA material, or decrypted traffic. Egress observers can report sanitized per-session receive/transmit byte deltas through /api/v1/sessions/{id}/egress-usage; project usage rolls those counters up for budget alerts while detailed URL, status, timing, credential, payload, and decrypted traffic logs remain in the configured proxy or secure web gateway. Egress-side communication tracking belongs at the configured proxy or secure web gateway. BrowserPane emits safe correlation metadata instead: docker-backed runtime containers carry browserpane.session_id and egress-profile labels, and the gateway logs a sanitized runtime startup event that joins the session, runtime container, and egress profile. A runnable local Squid access-log example and sanitized usage reporter are available in deploy/examples/egress-observer. Egress profiles default to traffic_observation.mode=metadata_only. Full HTTPS inspection must be explicit with mode=tls_intercept, and the API requires a proxy, custom CA reference, and sensitive_log_sink_ref so operators do not enable decrypted traffic logging without an approved SIEM/log-storage target. Docker-backed runtimes materialize file:// or absolute-path custom CA bundle references into the session data volume and install them into Chromium's NSS trust store before launch; non-file CA providers remain a provider-integration follow-up. If proxy.credential_binding_id is set, the gateway resolves the owner-visible credential binding through the configured secret provider at runtime launch and writes only a session-local proxy-auth file; project-bound bindings are limited to sessions in the same project. Credentials are not embedded in proxy URLs, API responses, CLI output, Docker labels, or normal logs. Browser context resources let callers name owner-scoped or project-owned Chromium profile contexts and bind new sessions with browser_context.mode=reusable plus a context_id. Project-owned contexts can only be reused by sessions in the same project; owner-scoped contexts remain available to owner-scoped and project-scoped sessions. Docker-backed runtimes materialize reusable contexts as a context-scoped Chromium profile volume mounted at the normal profile path, while uploads, downloads, and session-file mounts remain tied to the concrete session. Only one active runtime writer may use a reusable context at a time; additional sessions with the same context can be created but runtime access is rejected until the active writer stops or releases its runtime. Browser context resources include a usage summary with the current visible session reference count and the active runtime writer session id, when one exists. Docker-backed runtimes also include approximate profile storage bytes when Docker volume-size inspection is available. Contexts can carry an optional max_profile_storage_bytes limit; once the inspected profile size exceeds that limit, the API reports usage.profile_storage_limit_exceeded=true and rejects new reusable sessions from that context until the operator deletes or replaces the context. Contexts can also carry an optional retention_sec window; the API returns retention_expires_at from the last-used timestamp, or creation time if the context has never been used. The gateway scans for expired ready contexts on startup and then every --browser-context-retention-cleanup-interval-secs seconds, unless that interval is set to 0; docker-backed cleanup removes the context profile volume and skips active runtime writers for a later pass. API clients and the admin UI use these fields to make the same lifecycle and cleanup decisions. Inactive reusable contexts can be cloned with POST /api/v1/browser-contexts/{id}/clone; docker-backed runtimes copy the source Chromium profile volume into a new context-scoped profile volume when the source volume exists, while static runtimes treat clone as metadata-only. Inactive reusable contexts can also be exported with GET /api/v1/browser-contexts/{id}/export; the response is a zip archive with manifest.json and, for docker-backed contexts with profile data, profile.tar.gz. BrowserPane export archives can be imported as new reusable contexts with POST /api/v1/browser-contexts/import using application/zip plus x-bpane-browser-context-name; callers can set x-bpane-browser-context-project-id to import directly into a project. Omitted metadata defaults to the archive manifest, and imports never overwrite an existing context. Deleting a reusable context refuses active runtime writers and, for docker-backed runtimes, removes the context-scoped Chromium profile volume when no active writer exists. The admin create-session configurator can create reusable context catalog entries, select a ready context for a new session, preview the resulting browser_context payload, and show the bound context in live session rows and the session inspector detail view. The admin operations overlay and /admin/browser-contexts route also expose a reusable-context catalog with session references, guarded clone/export/import/delete, and copyable API examples.

The admin console also uses a bearer-protected realtime WebSocket for owner-scoped snapshot updates:

  • GET /api/v1/admin/events

The same frozen API surface also includes session-scoped runtime routes:

  • POST /api/v1/sessions/{id}/access-tokens
  • POST /api/v1/sessions/{id}/automation-access
  • GET /api/v1/sessions/{id}/status
  • POST /api/v1/sessions/{id}/stop
  • POST /api/v1/sessions/{id}/release
  • POST /api/v1/sessions/{id}/kill
  • POST /api/v1/sessions/{id}/connections/{connection_id}/disconnect
  • POST /api/v1/sessions/{id}/connections/disconnect-all
  • POST /api/v1/sessions/{id}/mcp-owner
  • DELETE /api/v1/sessions/{id}/mcp-owner
  • POST /api/v1/sessions/{id}/automation-owner
  • DELETE /api/v1/sessions/{id}/automation-owner

Session-scoped file binding routes let owners attach existing workspace files to a session-level mount contract and let owners or automation read those bound resources through the API:

  • POST /api/v1/sessions/{id}/file-bindings
  • GET /api/v1/sessions/{id}/file-bindings
  • GET /api/v1/sessions/{id}/file-bindings/{binding_id}
  • GET /api/v1/sessions/{id}/file-bindings/{binding_id}/content
  • DELETE /api/v1/sessions/{id}/file-bindings/{binding_id}
  • GET /api/v1/sessions/{id}/files
  • GET /api/v1/sessions/{id}/files/{file_id}
  • GET /api/v1/sessions/{id}/files/{file_id}/content

Bindings snapshot workspace-file metadata, enforce relative mount paths, reject duplicate active mount paths per session, and allow session automation access to read/list bound file resources. The file-inspection/download APIs are available today; browser-container mount materialization is a separate runtime concern. For project sessions, allow_session_file_bindings=false rejects new session-file bindings. allow_browser_uploads=false and allow_browser_downloads=false reject live browser transfer sources from the corresponding direction and surface the session as file-transfer restricted.

Session resources and status responses now expose a richer lifecycle model:

  • persisted state
  • derived runtime_state
  • derived runtime_resume_mode
  • derived presence_state
  • connection_counts by role
  • live connections descriptors on the status route
  • stop_eligibility with blocker details
  • idle timing metadata
  • runtime_released_at and stopped_at timestamps
  • side-effect-free status snapshots, including for stopped sessions

Lifecycle control semantics are now explicit:

  • DELETE /api/v1/sessions/{id} follows safe-stop semantics
  • POST /api/v1/sessions/{id}/stop stops only when no blockers remain
  • POST /api/v1/sessions/{id}/release releases the live runtime while preserving the session resource and profile
  • POST /api/v1/sessions/{id}/kill force-terminates live attachments and releases the runtime
  • connection-level disconnect routes remove live attachments without stopping the session runtime

The local dev flow uses those routes to bridge browser-owned and automation-owned control:

  • the admin console resolves or creates an owner-scoped session before connect
  • it then mints a short-lived session_connect_ticket from POST /api/v1/sessions/{id}/access-tokens
  • the gateway routes the WebTransport connect through that explicit session id instead of one global token path
  • Delegate MCP assigns that session to the local bpane-mcp-bridge service principal
  • the console then calls the authenticated gateway proxy at /api/v1/mcp-bridge/control-session; the gateway validates the owner/session and forwards the request to mcp-bridge with its internal control bearer token so the bridge adopts that same session for later ownership/status calls
  • external MCP clients can avoid the mutable bridge control target by connecting directly to :8931/sessions/{session_id}/mcp after that session is delegated
  • direct :8931/control-session remains a bridge-local compatibility control target. In local compose it is bearer-protected and intended for gateway proxying/internal smokes, not browser frontend calls.
  • the local mcp-bridge now resolves the managed session's runtime CDP endpoint from the session resource, so delegated control also works in docker_pool mode

MCP delegation terminology:

  • gateway delegation: the owner grants the bpane-mcp-bridge service principal access to a session through POST /api/v1/sessions/{id}/automation-owner
  • bridge-adopted session: the compatibility /control-session pointer targets that session
  • MCP-owned session: the bridge has claimed session-scoped /mcp-owner while at least one MCP client is active
  • connected MCP client: one streamable HTTP or SSE MCP transport is bound to a bridge target; with /sessions/{id}/mcp that target is immutable for the connection lifetime

Supported local operator CLI:

  • Repo-level wrapper: ./scripts/bpane <command>
  • Package entrypoint: cd code/web/bpane-client && npm run bpane:cli -- <command>
  • Installable package binary name: bpane
  • Configuration precedence: command flags, environment variables, selected profile, then local defaults
  • Local profile path: ~/.config/bpane/config.json, override with BPANE_CONFIG or --config
  • Profile selection: BPANE_PROFILE or --profile
  • Gateway URL source: BPANE_BASE_URL, BPANE_API_URL, --base-url, or --api-url
  • Bearer token source: BPANE_ACCESS_TOKEN, --access-token, or --token
  • Profile files are written with 0600 permissions; access tokens are only persisted when profile init is run with --save-token
  • Successful responses and CLI errors are emitted as structured JSON; unknown options fail as usage errors instead of being ignored

Minimal local operator setup:

export BPANE_ACCESS_TOKEN=<owner bearer token>
./scripts/bpane profile init local \
  --base-url http://localhost:8080 \
  --mcp-control-url http://localhost:8080/api/v1/mcp-bridge/control-session \
  --mcp-endpoint-base-url http://localhost:8931 \
  --set-default

Common session operations:

./scripts/bpane session list
./scripts/bpane session list --state stopped --label suite=smoke --limit 5
./scripts/bpane session list --template-id <template-id> --label team=support
./scripts/bpane session create --label purpose=manual-test
./scripts/bpane session create --project-id <project-id> --label purpose=tenant-test
./scripts/bpane session create --browser-context-id <context-id> --label purpose=context-test
./scripts/bpane session create \
  --locale de-DE \
  --language de-DE \
  --language en-US \
  --timezone Europe/Berlin \
  --egress-profile-