How to Expose the Unified Workspace API in Cloudflare Computer: A Complete Guide
To expose the unified Workspace API in Cloudflare Computer, instantiate the Workspace class inside a Durable Object, optionally invoke ready() to materialize mounts, and return workspace.stub() to generate a WorkspaceStub that proxies filesystem, runtime, Git, and artifact operations across the Workers-RPC boundary.
Cloudflare Computer bundles a host-side Workspace class that implements a complete filesystem, Git, artifact, and container execution runtime. To make this API available to Workers or other Durable Objects across the network boundary, you wrap the Workspace instance in a serializable stub rather than passing the heavy object directly. This guide walks through the implementation using the actual cloudflare/computer source code.
Understanding the Workspace Architecture
The Workspace class defined in packages/computer/src/workspace.ts provides the full host-side environment including storage backends, mount management, and execution contexts. Because this object contains stateful resources and internal capnweb transport that cannot cross the Workers-RPC boundary directly, Cloudflare Computer exposes it through the WorkspaceStub architecture found in packages/computer/src/stub.ts.
The stub implements the same surface area as the local Workspace by forwarding operations through five specialized sub-stubs:
- Filesystem –
WorkspaceFilesystemStubproxiesreadFile,writeFile,stat,rm, and other filesystem methods toWorkspace.fs - Runtime –
WorkspaceRuntimeStubforwards container execution commands includingexec,getExec,killExec, anddisposeExec - Git –
WorkspaceGitStubexposes only theclimethod as the canonical entry point for Git operations across RPC - Assets – Stub for publishing and asset management APIs
- Artifacts – Stub for artifact retrieval and storage operations
Because the stub maintains identical method signatures to the local Workspace, callers use the same API regardless of whether they access the Workspace in-process or remotely.
Step-by-Step Implementation
Create the Workspace Instance
First, construct the Workspace with storage, backends, and optional mount configurations. The constructor accepts an object defining the Durable Object storage and execution backends.
import { Workspace } from "@cloudflare/computer";
import { CloudflareContainerBackend } from "@cloudflare/computer/backends/container";
export class MyDurableObject extends DurableObject {
#ws = new Workspace({
storage: this.ctx.storage,
backends: [new CloudflareContainerBackend({ id: "container" })],
// Optional: mounts, git configuration, asset handlers
});
// ... methods to expose the stub
}
The Workspace class exports the constructor and configuration interfaces through packages/computer/src/index.ts, which serves as the public entry point for the package.
Materialize Mounts with ready()
Before exposing the Workspace to external callers, optionally invoke await this.#ws.ready() to ensure all configured mounts are indexed and filesystem metadata is loaded. This step prevents latency on the first filesystem operation but is not strictly required for the stub to function.
async getWorkspace() {
await this.#ws.ready(); // Ensures mounts are materialized
return this.#ws.stub(); // Returns the serializable stub
}
Expose the WorkspaceStub
Return workspace.stub() from your Durable Object method to hand a WorkspaceStub across the RPC boundary. The stub extends RpcTarget, making it valid for Workers-RPC serialization.
async getWorkspace(): Promise<WorkspaceStub> {
await this.#ws.ready();
return this.#ws.stub();
}
Clients receiving this stub can interact with the full Workspace API as if they held a local instance, with all calls transparently forwarded to the host Durable Object.
Consuming the API from Workers
On the client side—whether in a Worker, another Durable Object, or any Cloudflare runtime context—the returned stub implements the unified API. You interact with ws.fs, ws.runtime, and ws.git exactly as you would with a local Workspace instance.
async function demo(env: Env) {
// Obtain the stub from the Durable Object
const ws = await env.WORKSPACE_DO.get("instance-id").getWorkspace();
// Filesystem operations proxy to Workspace.fs
await ws.fs.writeFile("/hello.txt", "Cloudflare Computer");
const content = await ws.fs.readFile("/hello.txt", "utf8");
// Runtime execution in the configured container backend
const handle = await ws.runtime.exec("ls -l /workspace");
const { exitCode, stdout } = await handle.result();
// Git operations via the CLI stub (the only cross-RPC entry point)
const gitResult = await ws.git.cli({
args: ["status"],
cwd: "/workspace"
});
}
The WorkspaceFilesystemStub forwards readFile, writeFile, and stat calls directly to the underlying Workspace.fs implementation. Similarly, WorkspaceRuntimeStub handles container lifecycle management, while WorkspaceGitStub restricts Git access to the cli method to maintain security boundaries across the RPC layer.
Advanced RPC Server Composition
For scenarios requiring lower-level control or composition with other RPC contracts (such as ShellRPC or SyncRPC), use the server builders from packages/rpc/src/server.ts instead of the high-level stub.
import { createWorkspaceServer } from "@cloudflare/computer-rpc";
import { createSyncServer } from "@cloudflare/computer-rpc";
import { createShellServer } from "@cloudflare/computer-rpc";
// Inside your Durable Object:
const compositeRpc = createWorkspaceServer(db, runner);
// db = Workspace database instance
// runner = execution driver for container operations
These helpers construct the WorkspaceRPC contract defined in packages/rpc/src/interface.ts, allowing you to combine the Workspace API with additional RPC surfaces like ShellRPC for interactive shell sessions or SyncRPC for file synchronization protocols.
Summary
- Create a
Workspaceinstance in your Durable Object with storage and backend configurations frompackages/computer/src/workspace.ts - Initialize the Workspace with
await workspace.ready()to materialize mounts and cache filesystem metadata before serving requests - Expose the unified API by returning
workspace.stub(), which generates aWorkspaceStubfrompackages/computer/src/stub.tsthat serializes across Workers-RPC - Consume the identical API on the client side through
ws.fs,ws.runtime, andws.git, with all operations proxied to the host Durable Object - Compose advanced RPC servers using
createWorkspaceServerfrompackages/rpc/src/server.tswhen combining Workspace functionality with other RPC contracts likeShellRPC
Frequently Asked Questions
What is the difference between Workspace and WorkspaceStub?
Workspace is the heavy host-side class instantiated in packages/computer/src/workspace.ts that manages filesystem state, container runtimes, and Git operations. WorkspaceStub is a thin RpcTarget wrapper defined in packages/computer/src/stub.ts that forwards method calls across the Workers-RPC boundary. The stub exposes the same API surface but remains lightweight and serializable, whereas the Workspace contains internal transport mechanisms that cannot cross process boundaries.
Why is Git access limited to the cli method across RPC?
The WorkspaceGitStub intentionally exposes only the cli method—accepting args and cwd parameters—rather than individual Git operations like clone or commit. This design consolidates all Git interactions through a single audited entry point in packages/computer/src/stub.ts, reducing the attack surface for command injection while still enabling full Git functionality via argument arrays.
When should I use createWorkspaceServer instead of workspace.stub()?
Use createWorkspaceServer from packages/rpc/src/server.ts when you need to combine the Workspace RPC with other RPC contracts such as ShellRPC or SyncRPC, or when you require custom RPC middleware. The workspace.stub() method provides the standard unified API sufficient for most filesystem and container operations, while the server builders offer granular control over RPC composition defined in packages/rpc/src/interface.ts.
Do I need to call ready() before exposing the stub?
Calling await workspace.ready() is optional but recommended. This method ensures all configured mounts are indexed and backends are initialized before the first client request, preventing cold-start latency on initial filesystem operations. If omitted, the Workspace performs lazy initialization on the first access, which may introduce noticeable delay for complex mount configurations.
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 →