# How to Integrate Cloudflare Workspace with Durable Objects: Complete Integration and Migration Guide

> Integrate Cloudflare Workspace with Durable Objects using the @cloudflare/computer SDK. Learn migration strategies and leverage fs, runtime, and sync APIs via Capnweb RPC.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: migration-guide
- Published: 2026-08-16

---

**Use the `@cloudflare/computer` SDK to instantiate a `Workspace` inside your Durable Object, which exposes `fs`, `runtime`, and `sync` APIs over Capnweb RPC to a containerized process.**

A **Cloudflare Workspace** provides a virtual filesystem backed by SQLite inside a Durable Object (DO), enabling stateful file operations and command execution that persist across requests. This guide walks through the integration pattern used in the `cloudflare/computer` repository and explains the migration strategies required when upgrading versions.

## Architecture Overview

The Workspace architecture consists of three layers that communicate through a typed RPC protocol.

| Component | Role |
|-----------|------|
| **Durable Object** | Owns SQLite-backed storage via `packages/dofs` and exposes a `Workspace` facade |
| **Capnweb RPC** | Wire format defined in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts) with `SyncRPC` for replication and `ShellRPC` for command execution |
| **computerd** | Container process mounting the workspace via FUSE, executing user code |

The RPC surface splits into two halves as defined in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts):

```ts
export interface WorkspaceRPC {
  sync: SyncRPC;   // push/fetch change entries, object transfer
  shell: ShellRPC; // spawn, query, kill and dispose exec handles
}

```

**Sync flow**: The DO pushes local changes via `push` while the container pulls remote changes through `fetchChanges`. Object payloads stream as `ReadableStream<{hash,bytes}>` to bound memory usage.

**Shell flow**: `exec` returns a streaming handle (`ReadableStream<ExecEvent>`) that survives reconnections through `getExec`, enabling long-running commands to persist across network glitches.

## Binding a Workspace to a Durable Object

Instantiate the `Workspace` class inside your DO constructor or method. The binding name must match your [`wrangler.toml`](https://github.com/cloudflare/computer/blob/main/wrangler.toml) configuration.

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

export class MyDO extends DurableObject {
  workspace = new Workspace({
    binding: "Agent",                     // matches wrangler.toml binding name
    id: self.ctx.id.toString(),           // DO's unique identifier
  });

  async hello() {
    await this.workspace.fs.writeFile("/workspace/hello.txt", "Hello, DO!\n");
    const run = await this.workspace.runtime.exec("cat /workspace/hello.txt", {
      encoding: "utf8",
    });
    return run.stdout; // → "Hello, DO!\n"
  }
}

```

Key implementation details from `packages/computer/src/`:

- The `Workspace` constructor builds `createWorkspaceClient`, which constructs the Capnweb stub before WebSocket upgrade completes—calls queue until the socket is ready
- All paths are absolute and rooted at `/workspace`; the DO enforces bounds checking (out-of-bounds paths return 400)
- See [`examples/worker-shell/README.md`](https://github.com/cloudflare/computer/blob/main/examples/worker-shell/README.md) for a complete runnable demonstration

## File System Operations

The `workspace.fs` API provides standard filesystem methods backed by the DO's SQLite VFS.

```ts
// Create directory structure
await this.workspace.fs.mkdir("/workspace/logs", { recursive: true });

// Write file
await this.workspace.fs.writeFile("/workspace/logs/today.txt", "Log entry\n");

// Read with encoding
const text = await this.workspace.fs.readFile("/workspace/logs/today.txt", "utf8");

// List directory contents
const entries = await this.workspace.fs.readdir("/workspace");

```

## Command Execution

Use `workspace.runtime.exec` to run commands in the container environment.

```ts
const exec = await this.workspace.runtime.exec(
  "bash -c 'echo $PWD && ls /workspace'",
  {
    cwd: "/workspace",
    encoding: "utf8",
  }
);
console.log(exec.stdout);

```

### Handling Exec Restarts After Reconnection

Exec handles are durable. Store the execution ID to resume after DO restarts:

```ts
// Start long-running command
const { id, events } = await this.workspace.runtime.exec("sleep 30 && echo done");

// Persist ID for recovery
await env.KV.put("my-long-run-id", id);

// After restart, re-attach to existing execution
const { events: resumed } = await this.workspace.runtime.getExec({ id });
for await (const ev of resumed) {
  if (ev.name === "stdout") {
    console.log(new TextDecoder().decode(ev.value));
  }
}

```

The resumption capability lives in [`packages/computerd/src/exec/log.ts`](https://github.com/cloudflare/computer/blob/main/packages/computerd/src/exec/log.ts), which maintains a durable SQLite event log.

## Migration Strategies

The `cloudflare/computer` repository provides structured approaches for evolving Workspaces without data loss, documented in [`docs/18_runtime_migration.md`](https://github.com/cloudflare/computer/blob/main/docs/18_runtime_migration.md).

### Compatibility Date Management

Each DO declares a `compatibilityDate` via constructor option or environment variable. The server reports its supported date range during Capnweb handshake—mismatches reject connections, forcing explicit upgrades.

```ts
new Workspace({
  binding: "Agent",
  id: self.ctx.id.toString(),
  compatibilityDate: "2024-08-01", // Pin production dates
});

```

### Schema Upgrades

The VFS schema in [`packages/dofs/src/sync/schema.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/schema.ts) is versioned. Migration logic in [`packages/computer/src/migration.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/migration.ts) runs one-time transformations:

1. Detect current schema version from SQLite metadata
2. Apply sequential migrations to reach target version
3. Guard with migration-status flag in DO storage to ensure idempotency

### RPC Version Bumps

Breaking changes to `WorkspaceRPC` require new stub versions. The client passes its version string during `createWorkspaceClient`; server validation during handshake forces redeployment on mismatch.

### Graceful Reconnection and Data Continuity

Three mechanisms ensure migration safety:

- **Durable exec handles**: Survive server restarts via `getExec` reattachment
- **Automatic stub recreation**: DO re-establishes RPC after migration
- **Content-addressed sync**: Change entries use `sha256(chunk)` hashing; `hasObjects`/`pushObjects` flows in [`packages/dofs/src/sync/changes.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/changes.ts) re-push missing chunks post-migration

### Migration-Safe Sync Push

```ts
// Stream changes from local modifications
await this.workspace.sync.push({
  senderRev: this.currentRev, // >0 for peer DO scenarios
  changes: localChangeStream,
});
// Container automatically handles hasObjects/pushObjects for missing chunks

```

## Testing Migrations

Validate upgrades using the `packages/computerd` test harness:

```bash
npm run test --workspace @cloudflare/computerd

```

This verifies the full push/fetch cycle and streaming correctness after schema changes.

## Summary

- **Integration**: Import `Workspace` from `@cloudflare/computer`, bind to your DO with matching [`wrangler.toml`](https://github.com/cloudflare/computer/blob/main/wrangler.toml) configuration, and use `fs`/`runtime`/`sync` APIs
- **Architecture**: Capnweb RPC over WebSocket or HTTP-batch transports splits into `SyncRPC` for data replication and `ShellRPC` for command execution
- **Path enforcement**: All filesystem operations use `/workspace` root with server-side bounds checking
- **Migration safety**: Pin `compatibilityDate`, implement idempotent schema migrations in [`packages/computer/src/migration.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/migration.ts), and leverage durable exec handles for continuity
- **Data integrity**: Content-addressed change entries enable automatic chunk re-synchronization across versions

## Frequently Asked Questions

### What happens if I don't specify a compatibility date?

The connection may succeed initially but fail unpredictably when the server updates. Without an explicit `compatibilityDate`, your DO cannot detect version mismatches during the Capnweb handshake, leading to potential runtime errors instead of clean rejection with upgrade guidance.

### Can I run multiple Workspaces inside one Durable Object?

Yes. Instantiate multiple `Workspace` instances with different binding names or IDs. Each maintains independent state, RPC connections, and container processes. However, each binding consumes additional resources and WebSocket connections.

### How do I recover from a failed schema migration?

Check the migration-status flag in your DO's storage. If set but incomplete, inspect [`packages/computer/src/migration.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/migration.ts) logs, fix the transformation logic, and redeploy. The idempotent design allows safe retry—migrations skip completed steps based on the stored version marker.

### What triggers a full re-sync versus incremental sync?

`push` sends only `ChangeEntry` records since `senderRev`. If the container reports missing objects via `hasObjects`, the DO streams required chunks through `pushObjects`. A full re-sync occurs only when revision histories diverge irreconcilably, which the sync protocol detects and surfaces as an error requiring manual intervention.