# How to Manually Implement Workspace Push and Pull Operations in Cloudflare Computer

> Learn to manually implement workspace push and pull operations in Cloudflare Computer using Workspace.push() and Workspace.pull() for seamless SQLite synchronization.

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

---

**Manually synchronize local SQLite state with remote backends using the `Workspace.push()` and `Workspace.pull()` methods from the `@cloudflare/computer` package, which serialize changes through a FIFO queue and handle transport errors automatically.**

The `cloudflare/computer` repository provides a distributed computing environment where the `Workspace` class acts as the host-side façade for managing local SQLite storage and remote execution backends. When you need fine-grained control over state synchronization, you can manually trigger **workspace push and pull operations** instead of relying on automatic command-execution brackets. This guide explains the internal mechanics and provides practical implementations using the actual source code from [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts).

## Workspace Class Architecture

The `Workspace` class defined in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) maintains a local SQLite database through a `Database` instance and coordinates with remote backends configured in `WorkspaceOptions.backends`. According to the source implementation, synchronization is explicit: you must invoke `push()` to ship local changes and `pull()` to fetch remote updates.

Before any data transfer occurs, the private `#resolveBackendId(id)` method normalizes the optional backend identifier, defaulting to the first backend in the configuration or `undefined` for filesystem-only workspaces. All operations pass through `#serialize`, which maintains a per-backend FIFO queue (`#mutationTails`) to ensure that concurrent pushes to the same backend execute sequentially while operations against different backends proceed in parallel.

## The Push and Pull Methods

The public API exposes two primary methods for manual synchronization, both accepting an optional backend identifier and returning specific result types.

### Pushing Local Changes

The `push(id?)` method ships every local mutation since the previous push to the specified backend. As implemented in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) (lines 45-70), the method:

- Resolves the backend identifier via `#resolveBackendId` (lines 31-46)
- Acquires a cached handle through `#handleFor(id)`, which lazily connects and reconciles watermarks on first use (lines 99-130)
- Wraps the RPC call with `#runWithInvalidation` to invalidate cached handles on transport errors (lines 84-96)
- Invokes `pushOnce(this.#db, handle.rpc.sync, resolvedId)` from the `@cloudflare/computer-rpc` package (line 61)

The method returns the number of entries shipped as a `number`.

### Pulling Remote Changes

The `pull(id?)` method fetches and applies remote changes to the local SQLite store. Located at lines 71-74 in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts), this method:

- Follows the same serialization and handle acquisition pattern as push
- Calls `pullOnce(this.#db, handle.rpc.sync, resolvedId)` to perform the actual data transfer (line 40)
- Returns an `ApplyResult` object with `{ applied, skipped }` properties indicating how many entries were applied versus skipped

## Implementing Manual Synchronization

When working outside the automatic `CommandExecutor.exec` wrapper—which automatically calls `push()` before and `pull()` after command execution—you can manually control synchronization points.

### Basic Push and Pull Workflow

The following example demonstrates manual synchronization within a Cloudflare Worker or Durable Object:

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

// Construct workspace with R2 backend
const ws = new Workspace({
  storage: ctx.storage,
  backends: [{ id: "r2", type: "r2", callable: true, connect: /* ... */ }],
});

// Write to local SQLite store
await ws.fs.writeFile("/workspace/hello.txt", "Hello, Cloudflare!");

// Push changes to default backend (R2)
const pushed = await ws.push();
console.log(`Pushed ${pushed} entries to R2`);

// Later, fetch remote updates
const result = await ws.pull();
console.log(`Pulled ${result.applied} entries (skipped ${result.skipped.length})`);

```

### Targeting Specific Backends

When configuring multiple backends, explicitly target them by passing the identifier:

```typescript
// Push to specific backend
await ws.push("r2");

// Pull from different backend
await ws.pull("cloudflare-worker");

```

### Handling Retry Logic

For resilient synchronization, use the `SyncRetryScheduler` to handle pending pulls that failed post-command:

```typescript
const status = await ws.retryPendingSync("r2");

switch (status.status) {
  case "complete":
    console.log(`Recovered ${status.applied} entries`);
    break;
  case "pending":
    console.log(`Retry scheduled, attempt ${status.attempt}`);
    break;
  case "exhausted":
    console.error("Max retry attempts reached");
    break;
}

```

The retry logic implementation resides around lines 84-124 in the workspace source file.

## Low-Level RPC Implementation

The actual wire protocol uses Cap'n Proto RPC as defined in [`docs/08_capnweb_interface.md`](https://github.com/cloudflare/computer/blob/main/docs/08_capnweb_interface.md). The helper functions `pushOnce` and `pullOnce` reside in [`packages/computer-rpc/driver.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer-rpc/driver.ts) and implement the low-level synchronization contract. These functions interact with the `BackendHandle` returned by `#handleFor`, which manages the RPC interface defined in [`packages/rpc/src/interface.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/interface.ts).

The `WorkspaceFilesystem` implementation in [`packages/dofs/src/filesystem.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/filesystem.ts) directly mutates the local SQLite store, making those changes available for the next `push()` operation.

## Summary

- The `Workspace` class in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) provides explicit `push()` and `pull()` methods for manual synchronization
- Both methods use private `#serialize` to maintain per-backend FIFO queues and `#runWithInvalidation` for transport-error recovery
- `push()` returns the number of entries shipped, while `pull()` returns an `ApplyResult` with `applied` and `skipped` counts
- Operations default to the first configured backend unless a specific `id` is provided via `#resolveBackendId`
- Low-level RPC calls delegate to `pushOnce` and `pullOnce` in the `@cloudflare/computer-rpc` package using the Cap'n Proto interface defined in [`docs/08_capnweb_interface.md`](https://github.com/cloudflare/computer/blob/main/docs/08_capnweb_interface.md)

## Frequently Asked Questions

### What is the difference between automatic and manual workspace push and pull operations?

Automatic synchronization occurs within `CommandExecutor.exec`, which wraps command execution with `push()` before and `pull()` after. Manual implementation gives you explicit control over when state diverges and converges, essential for batch operations or specific consistency requirements where you cannot rely on implicit synchronization brackets.

### How does the Workspace class handle concurrent push operations?

The `#serialize` method maintains a per-backend FIFO queue (`#mutationTails`) that ensures concurrent pushes to the same backend execute sequentially. Pushes to different backends proceed in parallel, maximizing throughput while maintaining consistency per backend as implemented in lines 110-129 of [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts).

### What happens if a network error occurs during push or pull?

The `#runWithInvalidation` wrapper (lines 84-96) catches transport-level errors and invalidates the cached `BackendHandle`. This ensures the next operation triggers `#handleFor` to establish a fresh connection, automatically recovering from transient network failures without manual intervention.

### Can I use workspace push and pull operations without a remote backend?

Yes. If `#resolveBackendId` returns `undefined` (no backend configured or filesystem-only workspace), the operations handle local state appropriately. The `WorkspaceFilesystem` in [`packages/dofs/src/filesystem.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/filesystem.ts) manages local SQLite mutations regardless of remote synchronization status, making the workspace functional for local-only scenarios.