Session surfaces
marimohub can run VS Code, OpenCode, or both in an edit sandbox. Each surface shares marimo's workspace, Python environment, credentials, authorization, and session lifetime. A surface does not create another sandbox.
Secondary surfaces are disabled by default. Select an image and enable the matching surface:
# VS Code only
MARIMOHUB_SURFACES=marimo,vscode
MARIMOHUB_COMPUTE_IMAGE=ghcr.io/marimo-team/marimo-sandbox:latest-vscode
# OpenCode only
MARIMOHUB_SURFACES=marimo,opencode
MARIMOHUB_COMPUTE_IMAGE=ghcr.io/marimo-team/marimo-sandbox:latest-opencode
# Both
MARIMOHUB_SURFACES=marimo,vscode,opencode
MARIMOHUB_COMPUTE_IMAGE=ghcr.io/marimo-team/marimo-sandbox:latest-toolsThe toolbar has a Surfaces menu, even when only one surface is enabled. The menu has a start action for each surface. It has a stop action when that surface is running.
By default, a surface opens in the notebook's application tabs. Set its EMBED variable to iframe to open it beside marimo in a split view. The most recently opened split replaces the previous split, while the previous surface remains available as a background tab. Use a surface tab's pop-out control to open its iframe in a separate browser tab.
Set a surface's START variable to eager to start it with each authorized edit session. The default is on-demand. A surface failure does not stop marimo or another surface.
Availability
The compute adapter must expose multiple ports from one sandbox. The local, e2b, cloudflare, and coreweave adapters support this feature. Configuration fails for other adapters. Docker, Podman, Kubernetes, and Modal need create-time port reservation support. W&B runs on the CoreWeave adapter but is not yet wired to reserve surface ports.
Port 2718 belongs to marimo. Each secondary surface must use a unique port. If an image lacks a required binary, only that surface becomes unavailable.
Editing and state
- marimo runs with
--watchwhenever a secondary surface is enabled. A file saved from VS Code or OpenCode then reloads in marimo. - VS Code opens the notebook entry path. OpenCode starts in the workspace without a notebook path.
- VS Code autosaves after one second. Concurrent writes use last-writer-wins.
- Surfaces start and stop independently. Session teardown stops all surfaces, captures eligible workspace changes, and destroys the sandbox.
- Surface configuration, caches, credentials, databases, and UI state stay under
/tmp/.marimohub/surfaces/<session-id>/<surface-id>. They survive a stop and restart in the same sandbox. They are not versioned and do not survive session teardown. - Temporary edit sessions can use surfaces. Their changes are discarded with the sandbox.
API
Surfaces are per session. All routes live under /api/v1/projects/{pid}/notebooks/{nid}/sessions/{sid}/surfaces/{surface}, where surface is vscode or opencode.
| Method | Purpose |
|---|---|
POST | Start the surface, or return it when running. Body: { "open": "<path>" } |
GET | Read the surface state (starting, ready, failed, ...). |
DELETE | Stop the surface. Requires session control. |
open is a workspace-relative file path; only VS Code accepts it.
POST returns 202 with Retry-After: 1 while the surface is starting, and 200 once it is ready. Poll GET until the status leaves starting. A ready surface includes its url; a failed or unavailable surface includes last_error.
Session create (POST .../sessions) accepts a surfaces array of surface ids to start with the session; only edit sessions may list surfaces. Every session response carries can.surfaces, one boolean per surface id, so clients render the start controls from the caller's evaluated grant instead of re-deriving policy.
OpenCode AI providers
When managed AI is enabled, marimohub adds a temporary marimohub provider when OpenCode starts. This OpenAI-compatible provider uses the configured model and a session token. The upstream API key stays on the server.
Project opencode.json files can override the provider or initial model. Users can also add bring-your-own-key providers through /connect. These credentials stay in the temporary surface directory.
The published OpenCode images include the marimo-pair skill and its shell dependencies. OpenCode can use it to work directly with the running marimo kernel.
The managed token expires after MARIMOHUB_AI_TOKEN_TTL_SECONDS, even while OpenCode is open. Restart OpenCode to get a new token. A bring-your-own-key provider uses its own credential.
Security and exposure
Only users who can attach to an edit session can use its surfaces. App sessions and viewer-owned ephemeral sessions cannot use them. VS Code terminals and OpenCode agents can run shell commands and read the notebook credentials.
VS Code supports both exposure modes. In proxy mode, traffic uses a signed /surface-proxy/<token>/vscode/ path. The hub authorizes each HTTP request and WebSocket upgrade.
OpenCode supports only subdomain mode because its client uses root-relative paths. Configuration fails when OpenCode and MARIMOHUB_SANDBOX_EXPOSURE=proxy are both enabled.
In subdomain mode, each surface has a direct, high-entropy URL. The URL is an access capability, so the hub returns it only to authorized editors. Keep the sandbox domain isolated. Do not publish these URLs.
See Configuration for all surface configuration and Security for the trust boundary.