How to Instantiate and Use the Workspace Class in a Cloudflare Durable Object
The Workspace class acts as the primary host-side wrapper that enables Durable Objects to interact with local SQLite storage, remote computerd daemons, and backend services through a coordinated runtime API.
In the cloudflare/computer repository, the Workspace class serves as the central façade for managing filesystem operations, command execution, and state synchronization within Cloudflare's edge computing environment. Whether you are building a remote development environment or a persistent file-backed service, understanding how to properly instantiate and configure this class inside a Durable Object is essential for leveraging the full capabilities of the computer package.
Understanding the Workspace Architecture
The Workspace class coordinates multiple internal components to provide a unified interface for Durable Objects.
Workspace ([packages/computer/src/workspace.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts)) owns a Database backed by ctx.storage and a WorkspaceFilesystem instance. It manages synchronization with remote backends and provides access to execution runtimes.
WorkspaceFilesystem ([packages/dofs/src/fs/filesystem.ts](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/fs/filesystem.ts)) implements a thin wrapper around the @cloudflare/dofs virtual filesystem, enabling direct SQLite-backed reads and writes to the local store.
WorkspaceRuntime ([packages/computer/src/runtime/runtime.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/runtime/runtime.ts)) exposes the exec, getExec, killExec, and disposeExec methods used to run commands on remote backends.
WorkspaceStub ([packages/computer/src/stub.ts](https://github.com/cloudflare/computer/blob/main/packages/computer/src/stub.ts)) provides an RPC-compatible shim that can cross Workers-RPC boundaries, allowing other Workers to interact with your Durable Object's Workspace instance.
Instantiating the Workspace Class in a Durable Object
To create a Workspace instance inside a Durable Object, you must pass a WorkspaceOptions object to the constructor during initialization.
Required Constructor Options
The constructor requires at minimum a storage field implementing DurableObjectStorageLike, which is typically the Durable Object's ctx.storage:
export class MyDurableObject extends DurableObject {
#workspace: Workspace;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.#workspace = new Workspace({
storage: ctx.storage,
});
}
}
Optional Configuration Fields
For production use, you will typically provide additional configuration:
- backends: An array of backend descriptors (e.g., command backends communicating with
computerd) - sessionId: An optional identifier propagating to mount factories, assets, and artifacts
- mounts: Optional mount registry for read-only or special-purpose VFS mounts
- git, assets, artifacts: Optional factories/clients lazily created on first use
this.#workspace = new Workspace({
storage: ctx.storage,
backends: [env.COMPUTER_BACKEND],
sessionId: ctx.id.toString(),
});
Core Workspace Operations
Once instantiated, the Workspace provides methods for initialization, command execution, and data synchronization.
Initializing with ready()
Call ready() to materialize mounts and optionally pre-warm backend connections before handling requests:
async fetch(request: Request) {
// Initialize all lazy-loaded resources
await this.#workspace.ready({ all: true });
return new Response("Workspace initialized");
}
Executing Commands via the Runtime
The WorkspaceRuntime accessible via workspace.runtime provides the exec() method for running commands. This method automatically synchronizes state—pushing local changes before execution and pulling remote results afterward:
async runScript() {
const result = await this.#workspace.runtime.exec({
source: "node -e \"console.log('executing on computerd')\"",
timeoutMs: 5_000,
});
// Process the stream of WorkspaceRuntimeEvent objects
for await (const event of result.events) {
console.log(event);
}
}
Manual Synchronization with push() and pull()
For scenarios requiring explicit control over synchronization, use push() and pull():
async syncWorkspace() {
// Push local SQLite changes to remote backend
const pushed = await this.#workspace.push();
console.log(`Synchronized ${pushed} entries to remote`);
// Pull remote changes into local SQLite store
const { applied, skipped } = await this.#workspace.pull();
console.log(`Applied ${applied} remote entries, skipped ${skipped.length}`);
}
Cross-Boundary RPC with WorkspaceStub
To allow other Workers to interact with your Workspace, return a WorkspaceStub from your Durable Object's methods. The stub forwards RPC calls across the Workers boundary while maintaining the Workspace's state:
async fetch(request: Request) {
const stub = this.#workspace.stub();
return new Response(JSON.stringify({ workspaceStub: stub }));
}
The stub exposes methods like push(), pull(), and runtime.exec(), enabling distributed architectures where client Workers orchestrate operations on Durable Object-hosted Workspaces.
Complete Durable Object Implementation Example
Here is a complete implementation demonstrating construction, command execution, and RPC exposure:
import { DurableObject } from "cloudflare:workers";
import { Workspace } from "@cloudflare/computer";
export class ComputerDO extends DurableObject {
#ws: Workspace;
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
this.#ws = new Workspace({
storage: ctx.storage,
backends: [env.COMPUTER_BACKEND],
sessionId: ctx.id.toString(),
});
}
async fetch(request: Request) {
await this.#ws.ready({ all: true });
// Return RPC stub for cross-worker access
if (new URL(request.url).pathname === "/stub") {
return Response.json({ stub: this.#ws.stub() });
}
// Execute command directly within the DO
const result = await this.#ws.runtime.exec({
source: "uname -a",
timeoutMs: 10_000,
});
return new Response(JSON.stringify({ result }));
}
async sync() {
await this.#ws.push();
await this.#ws.pull();
return { status: "synchronized" };
}
}
Summary
- The
Workspaceclass incloudflare/computerwrapsctx.storageto provide SQLite-backed filesystem operations and remote command execution for Durable Objects. - Instantiate
Workspaceby passingctx.storagefrom the Durable Object constructor, optionally configuring backends, session IDs, and mount registries. - Use
ready()to initialize lazy-loaded resources,runtime.exec()to run commands with automatic sync, andpush()/pull()for manual state synchronization. - Return
workspace.stub()from Durable Object methods to enable RPC access from other Workers. - All filesystem operations hit the local SQLite store immediately; remote synchronization occurs explicitly via the sync methods or automatically during command execution.
Frequently Asked Questions
What storage backend does the Workspace class use?
The Workspace class uses the Durable Object's ctx.storage as its backing store, wrapping it in a WorkspaceFilesystem that provides SQLite-backed virtual filesystem operations. According to the source code in packages/computer/src/workspace.ts, the constructor requires a storage field implementing DurableObjectStorageLike, which is typically the storage object provided to the Durable Object's constructor.
How does command execution handle state synchronization?
When you call workspace.runtime.exec(), the implementation automatically calls push() before executing the command to ensure the remote backend has the latest local state, then calls pull() after execution to retrieve any changes made by the command. This behavior is defined in the WorkspaceRuntime class within packages/computer/src/runtime/runtime.ts, ensuring consistency between the local SQLite store and remote computerd instances.
Can I use the Workspace class outside of Durable Objects?
Yes, the Workspace class works in any Cloudflare Worker context provided you supply a valid DurableObjectStorageLike implementation. While designed for Durable Objects using ctx.storage, you can instantiate it with a test stub or custom storage implementation for use in standard Workers or testing environments, as demonstrated in the unit tests located in packages/computer/src/workspace.test.ts.
What is the purpose of the WorkspaceStub?
WorkspaceStub serves as an RPC-compatible proxy that can cross the Workers-RPC boundary, allowing client Workers to invoke Workspace methods on a Durable Object-hosted instance. When you call workspace.stub(), you receive a serializable object (defined in packages/computer/src/stub.ts) that forwards push(), pull(), and runtime.exec() calls back to the original Workspace instance, enabling distributed architectures.
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 →