How Cloudflare Computer's WorkspaceRuntime Routes exec Requests to Container, Worker‑Shell, and Worker‑JavaScript Backends

Cloudflare Computer's WorkspaceRuntime routes exec requests through a unified interface that dispatches container and worker‑shell calls via RPC-based CommandExecutor handles while executing worker‑javascript code through native in‑process module handles.

Cloudflare Computer provides a unified execution surface that abstracts diverse compute environments behind a single API. In the cloudflare/computer repository, the WorkspaceRuntime class orchestrates how exec calls reach their destination—whether that's a containerized process, a Worker Durable Object shell, or native JavaScript execution. Understanding this routing mechanism reveals how the platform maintains a consistent developer experience across fundamentally different backend architectures.

Backend Architecture: Command vs. Module Backends

The routing system distinguishes between two fundamental backend types registered in packages/computer/src/workspace.ts.

Command-backends implement the Backend.connect method to return a BackendHandle exposing an RPC-based shell interface. Both the container (computerd FUSE driver) and worker‑shell backends use this pattern. They rely on CommandExecutor to forward exec calls over Cap'n Web RPC channels defined in docs/08_capnweb_interface.md.

Module-backends return a native WorkspaceModuleBackendHandle that runs code in‑process without RPC overhead. The worker‑javascript backend uses this approach, allowing direct JavaScript execution within the Worker environment.

The Execution Flow

When you invoke Workspace.runtime.exec, the system follows a deterministic resolution path:

  1. Backend Resolution – WorkspaceRuntime receives an optional backend identifier. If omitted, it defaults to the first registered backend:
const backend = this.#options.resolveBackendId(requested);
  1. Handle Acquisition – The system branches based on backend type:

    • Command-backends (container, worker‑shell) → Workspace.#shellFor creates a CommandExecutor wrapping the RPC shell stub
    • Module-backends (worker‑javascript) → Workspace.#moduleHandleFor returns a native module handle
  2. Execution Dispatch – The selected handle's exec method receives the source string, working directory, environment variables, and stdin. It returns a ModuleExecutionEnvelope containing an execution ID, event stream (stdout/stderr/exit), and sync object.

  3. Result Wrapping – WorkspaceRuntime.exec in packages/computer/src/runtime/runtime.ts constructs a WorkspaceRuntimeExecHandle that streams events lazily and assembles the final WorkspaceRuntimeResult through its result() method.

  4. Synchronization Bracketing – Every execution is automatically wrapped with Workspace.push and Workspace.pull calls for the selected backend, ensuring the local SQLite store (@cloudflare/dofs) remains synchronized with remote state.

Backend-Specific Routing Logic

Container Backend (computerd)

When routing to a container backend, Workspace.#backendHandleFor resolves the backend ID to a command-backend handle. The associated BackendHandle contains an rpc.shell endpoint that communicates with the computerd binary—the FUSE driver implementation found in packages/computer/src/fuse/driver.ts. The CommandExecutor forwards the exec call over the Cap'n Web RPC contract to the remote process.

Worker‑Shell Backend

Worker‑shell routing follows the identical command-backend path as containers, but the RPC target is a Cloudflare Worker Durable Object running a thin shell implementation. Located in packages/computer/src/backend/worker-shell.ts, this backend exposes the same shell RPC interface as the container driver. The CommandExecutor transparently forwards requests to the Worker's RPC endpoint, executing commands inside the Worker environment.

Worker‑JavaScript Backend

For worker‑javascript execution, the backend ID maps to a module-backend through packages/computer/src/backend/worker-javascript.ts. Because isModuleBackend(backend) returns true, the system invokes Workspace.#moduleHandleFor rather than the shell path. This bypasses RPC entirely—the JavaScript module runs in‑process through the native WorkspaceModuleBackendHandle.exec implementation.

Code Implementation Example

The following example demonstrates registering multiple backends and routing exec requests to each:

import { Workspace } from "@cloudflare/computer";

const ws = new Workspace({
  storage: ctx.storage,
  backends: [
    // Container backend (exec runs inside the `computerd` process)
    { 
      id: "container", 
      type: "container", 
      connect: containerConnect,
      callable: false 
    },
    // Worker‑Shell backend (exec runs inside a Worker Durable Object)
    { 
      id: "shell", 
      type: "worker-shell", 
      connect: shellConnect,
      callable: false 
    },
    // Worker‑JavaScript backend (native JS module execution)
    { 
      id: "js", 
      type: "worker-javascript", 
      connect: jsConnect, 
      callable: true 
    },
  ],
});

// Route to container via RPC
await ws.runtime.exec('echo "Hello from container"', { backend: "container" });

// Route to native JavaScript execution (no RPC)
await ws.runtime.exec('console.log("Hello from JS")', { backend: "js", input: "utf8" });

// Route to Worker shell via RPC
await ws.runtime.exec('ls -la', { backend: "shell" });

Key Source Files

Summary

  • Cloudflare Computer unifies container, worker‑shell, and worker‑javascript execution behind a single Workspace.runtime.exec API.
  • Command-backends (container, worker‑shell) route through CommandExecutor using Cap'n Web RPC to remote processes or Worker Durable Objects.
  • Module-backends (worker‑javascript) execute natively in‑process via WorkspaceModuleBackendHandle, eliminating RPC overhead.
  • Backend resolution occurs through Workspace.#options.resolveBackendId, with handle acquisition branching between #shellFor and #moduleHandleFor.
  • Automatic synchronization via push/pull bracketing keeps the local SQLite store consistent with remote backend state.

Frequently Asked Questions

How does WorkspaceRuntime determine which backend to use for an exec request?

WorkspaceRuntime calls this.#options.resolveBackendId(requested) where requested is the optional backend parameter passed to exec. If no backend is specified, it defaults to the first registered backend in the Workspace configuration. The resolved ID then determines whether the system acquires a command handle via #shellFor or a module handle via #moduleHandleFor.

What is the technical difference between command-backends and module-backends?

Command-backends implement Backend.connect to return a handle exposing an RPC shell interface, requiring serialization of commands across process boundaries via CommandExecutor. Module-backends return WorkspaceModuleBackendHandle objects that implement exec natively, allowing direct in‑process execution without Cap'n Web RPC serialization. This distinction determines whether your code runs remotely (containers, worker‑shells) or locally within the Worker (worker‑javascript).

How does the container backend communicate with the computerd process?

The container backend uses the FUSE driver implementation in packages/computer/src/fuse/driver.ts to establish an RPC connection. When Workspace.#shellFor creates a CommandExecutor for a container backend, it wraps the rpc.shell stub from the BackendHandle. The executor serializes the exec call over the Cap'n Web protocol defined in docs/08_capnweb_interface.md, transmitting it to the remote computerd process managing the container filesystem.

Can I execute JavaScript code without the RPC overhead of container or shell backends?

Yes. By routing your exec request to a worker-javascript backend, the system uses Workspace.#moduleHandleFor to obtain a native WorkspaceModuleBackendHandle. This backend type, implemented in packages/computer/src/backend/worker-javascript.ts, executes JavaScript modules directly within the Worker process. No RPC serialization or remote calls occur, resulting in lower latency execution for JavaScript workloads.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →