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:
- Backend Resolution –
WorkspaceRuntimereceives an optionalbackendidentifier. If omitted, it defaults to the first registered backend:
const backend = this.#options.resolveBackendId(requested);
-
Handle Acquisition – The system branches based on backend type:
- Command-backends (container, worker‑shell) →
Workspace.#shellForcreates aCommandExecutorwrapping the RPCshellstub - Module-backends (worker‑javascript) →
Workspace.#moduleHandleForreturns a native module handle
- Command-backends (container, worker‑shell) →
-
Execution Dispatch – The selected handle's
execmethod receives the source string, working directory, environment variables, and stdin. It returns aModuleExecutionEnvelopecontaining an execution ID, event stream (stdout/stderr/exit), and sync object. -
Result Wrapping –
WorkspaceRuntime.execinpackages/computer/src/runtime/runtime.tsconstructs aWorkspaceRuntimeExecHandlethat streams events lazily and assembles the finalWorkspaceRuntimeResultthrough itsresult()method. -
Synchronization Bracketing – Every execution is automatically wrapped with
Workspace.pushandWorkspace.pullcalls 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
packages/computer/src/workspace.ts– CoreWorkspaceclass implementing backend registration, handle caching (#shellFor,#moduleHandleFor), and push/pull synchronization logic.packages/computer/src/runtime/runtime.ts–WorkspaceRuntimeimplementation routingexec,getExec,killExec, anddisposeExecto appropriate backend handles.packages/computer/src/backend/worker-shell.ts– Command-backend implementation for Worker‑Shell providing the RPCshellstub.packages/computer/src/backend/worker-javascript.ts– Module-backend implementation for native JavaScript execution.packages/computer/src/fuse/driver.ts–computerdRPC driver for container backend communication.docs/08_capnweb_interface.md– Cap'n Web RPC contract specification forshell,sync, and related interfaces.
Summary
- Cloudflare Computer unifies container, worker‑shell, and worker‑javascript execution behind a single
Workspace.runtime.execAPI. - Command-backends (container, worker‑shell) route through
CommandExecutorusing 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#shellForand#moduleHandleFor. - Automatic synchronization via
push/pullbracketing 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →