Skip to content
Guide Reference Download app

Workspace runtime API

@claxedo/workspace-runtime is the per-workspace host service — one process per workspace. It owns the routes below plus the PTY and process managers, the LSP/VCS surface, and the relay-host tunnel to workspace-relay.

RouteSourceAuth
GET /api/wr/healthsrc/server.tsnone; minimal liveness and exposure boundary metadata only
GET /api/wr/capabilitiessrc/server.tsexposure-dependent runtime auth
POST /api/wr/configsrc/routes/config.tsworkspace-runtime management auth
GET /api/wr/harness-config-optionssrc/workspace/runtime.tsexposure-dependent runtime auth
GET /api/wr/events, GET /api/wr/runtime-eventssrc/routes/runtime-events.ts, src/routes/events.tsexposure-dependent runtime auth
* /api/wr/file/*, GET /api/wr/find/filesrc/routes/file.tsexposure-dependent runtime auth
* /api/wr/diff/*, * /api/wr/git/*src/routes/diff.ts, src/routes/git-source.tsexposure-dependent runtime auth
* /api/wr/pty/*src/routes/pty.tsexposure-dependent runtime auth
* /api/wr/process/*src/routes/process.tsexposure-dependent runtime auth
* /api/wr/hook/*src/routes/agent-hook.tsexposure-dependent runtime auth
* /api/wr/session-env/*session-env routes (mounted by the host)exposure-dependent runtime auth
* /session/*SessionRoutes (mounted by the host in src/workspace/runtime.tsnot part of mountWorkspaceCore, which mounts only PTY, hooks, events, process, file, and diff/git routes)implicit (host-level)
* /mcp/*MCP routesimplicit
* /lsp, * /vcs, * /global/eventcompatibility routes mounted by hostimplicit

workspace-runtime has two event systems with different jobs.

SurfaceTransportEvent familyContract
GET /global/eventSSEOpenCode-compatible CompatEnvelope valuesPrimary client stream for session/message/permission/question lifecycle. In OpenCode compatibility mode this may proxy the upstream OpenCode /global/event; otherwise it fans out the host RuntimeEventHub global stream and emits server.connected plus heartbeat frames.
GET /api/wr/runtime-eventsSSERuntimeEventEnvelope values wrapping raw AgentRuntimeEvent payloadsPrimary runtime-event stream for host-mounted core clients that need adapter-native runtime events. Mounted by mountWorkspaceCore().
GET /api/wr/eventsSSEWorkspaceRuntimeEvent values from workspaceRuntimeBusNeutral runtime path for the compatibility/internal process-global stream.
GET /eventSSEWorkspaceRuntimeEvent values from workspaceRuntimeBusCompatibility/internal process-global stream for PTY lifecycle, PTY stream summaries, process status/config events, agent lifecycle, session lifecycle, and heartbeats. Not the primary session/message event stream.
GET /api/wr/pty/:ptyID/connectWebSocketPTY bytes plus cursor metadataSupported PTY data stream. PTY lifecycle summaries also appear on /event, but terminal bytes are delivered over this WebSocket.
GET /api/wr/process/logsHTTP snapshotText log tailProcess output is poll/snapshot based through PTY log snapshots. There is no separate supported process-output event stream. Process status summaries appear on /event.

RuntimeEventHub is the primary hub for session/runtime events. Session routes publish OpenCode-compatible events to its global channel and bridge only terminal lifecycle states into workspaceRuntimeBus as agent.lifecycle compatibility events (mapping busy session.status to Busy, permission/question asks to UserActionRequired, session.idle to Idle, and session.error to Error).

workspace-runtime supports five deployment shapes. They share the same per-workspace route surface but not the same trust boundary.

ShapeHow it is createdListen/auth expectation
Local loopback / trusted localstartServer(port, { exposure: loopbackWorkspaceRuntimeExposure() }) with the default host or WORKSPACE_RUNTIME_HOST=127.0.0.1 / localhostMay run without host-level auth because the socket is loopback-only. For local development and app-owned desktop flows.
Private VM runtimestartServer(port, { exposure: privateNetworkWorkspaceRuntimeExposure(...), target }) inside an operator-controlled VM, usually with WORKSPACE_RUNTIME_HOST=0.0.0.0 or a private interfaceMust configure relay-host auth or a private-network exposure with both a host guard and runtime auth. The dev-unsafe opt-out is only for self-managed deployments where surrounding controls are intentionally the auth boundary.
Relay-attached runtimestartServer() with workspaceRelayRuntimeOptionsFromEnv() / relayHostAuthFromEnv() and hostTunnelFromEnv()Verifies Relay Host Tokens (RHTs) from workspace-relay, requires the relay transit marker on relay-issued tokens, and may maintain an outbound host tunnel to the relay.
Embedded Hono appcreateWorkspaceRuntimeApp() mounted inside another trusted processThe embedding process owns the outer network/auth boundary. Pass relayHostAuth, configToken, and a WorkspaceTarget explicitly when exposing runtime routes outside loopback.
Low-level host objectcreateWorkspaceHost() / mountWorkspaceCore() used without startServer()No socket is created. The caller owns routing, auth, lifecycle, and disposal; use only behind an existing trusted API surface.

Runtime CORS follows the exposure declaration. The kit default allows only loopback origins (http://localhost:*, http://127.0.0.1:*) on loopback exposure — it contains no product domains. Hosts inject product origins (a hosted app origin, for example) via the corsOrigin option on WorkspaceRuntimeServerOptions. Relay, private-network, and embedded exposure do not emit runtime CORS headers by default — the relay, trusted ingress, or embedding server owns origin policy.

Local development needs none of these — a loopback runtime boots with zero environment variables. These knobs exist for the relay-attached production shape. A relay-attached runtime has two independent pieces: inbound RHT verification (relayHostAuthFromEnv()) and outbound host-tunnel attachment (hostTunnelFromEnv()).

Env varPurpose
WORKSPACE_RUNTIME_RELAY_JWKS_URLPreferred RHT verification source. Verifies relay-minted RHTs and supports signing-key rotation.
WORKSPACE_RUNTIME_RELAY_HOST_VERIFY_PEMStatic PEM fallback for RHT verification when JWKS discovery is unavailable.
WORKSPACE_RUNTIME_WORKSPACE_IDWorkspace id the runtime hosts; RHT claims and x-workspace-id must match it.
WORKSPACE_RUNTIME_HOST_IDHost id expected in the RHT. Defaults to workspaceId() when omitted.
WORKSPACE_RUNTIME_TRUSTED_DIRECT_TOKENDirect host token accepted by the relay-host middleware without an RHT. Whole-host authority — keep it off untrusted clients.

Relay-issued RHT requests must include x-forwarded-by: workspace-relay. workspace-relay sets that marker after stripping client-supplied x-forwarded-* headers. Direct trusted-token calls bypass the marker.

Env varPurpose
WORKSPACE_RUNTIME_RELAY_URLRelay base URL. If unset, the runtime does not open a host tunnel.
WORKSPACE_RUNTIME_RELAY_TUNNEL_TOKENBearer token for the runtime-to-relay tunnel connection.
WORKSPACE_RUNTIME_RELAY_WORKSPACE_IDSComma-separated workspace ids advertised by this host tunnel. Defaults to the current workspace id.
WORKSPACE_RUNTIME_LOCAL_BASE_URLLocal runtime URL forwarded by the tunnel. Defaults to http://127.0.0.1:<port>.

Additional tuning knobs (tunnel ping cadence, a full Authorization-header override for the tunnel token, drain timeout, body-size limits, PTY backpressure watermarks) exist with sensible defaults — see the package source if you need them.

startServer() requires an explicit exposure declaration and defaults to 127.0.0.1. A runtime may listen on a non-loopback host (0.0.0.0, ::, or a LAN address) only when relay-host auth is configured, a private-network exposure supplies both host guard and runtime auth, or the operator explicitly sets WORKSPACE_RUNTIME_ALLOW_UNAUTHENTICATED_NON_LOOPBACK=1.

Session, session-env, file, PTY, process, OpenCode-compat, and diff/VCS routes are pinned to WORKSPACE_RUNTIME_DIRECTORY or the WorkspaceTarget passed to createWorkspaceRuntimeApp(). Callers may omit directory (use the pinned workspace), pass that exact directory, or pass the synthetic workspace:<workspaceId> target. Any other directory is rejected. Absolute paths, .. escapes, null bytes, and symlinks resolving outside the workspace are rejected before any read, write, stream, or subprocess start.


For install instructions, embedding patterns, and architecture, see the workspace-runtime package page.