# How Backend Cursors Remain Independent When Multiple Execution Backends Share a Single Workspace in Cloudflare Computer

> Discover how Cloudflare Computer ensures independent backend cursors in shared workspaces by uniquely identifying watermarks and cursor values with backend IDs in SQLite tables.

- Repository: [Cloudflare/computer](https://github.com/cloudflare/computer)
- Tags: internals
- Published: 2026-08-14

---

**Multiple execution backends maintain independent cursors in Cloudflare Computer by keying every watermark and cursor value with a unique backend identifier in shared SQLite tables.**

Cloudflare Computer enables a single `Workspace` instance to coordinate sync operations across heterogeneous execution environments—such as container runtimes and shell processes—without cursor collisions. This article explains the architectural mechanisms in the `@cloudflare/computer` and `@cloudflare/dofs` packages that guarantee isolation, drawing directly from the source implementation.

## The Backend Identifier as Isolation Key

Every execution backend registered with a `Workspace` receives a stable, caller-supplied `id`. This identifier permeates all sync state operations, ensuring distinct cursor tracks.

When constructing a `Workspace`, the `backends` array in `WorkspaceOptions` defines each backend with a unique string identifier:

```ts
const ws = new Workspace({
  storage: ctx.storage,
  backends: [
    { id: "container", ...MyContainerBackend },  // isolated as "container"
    { id: "shell",     ...MyShellBackend },      // isolated as "shell"
  ],
});

```

These identifiers are **not merely cosmetic**—they become part of the primary key in the underlying sync tables.

## Per-Backend Watermark Storage in DOFS

The **DOFS** (Distributed Object File System) sync layer implements all cursor read/write operations in [`packages/dofs/src/sync/watermarks.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/watermarks.ts). Every helper accepts an optional `backend` parameter defaulting to `DEFAULT_BACKEND_ID = "default"`:

- `readWatermark(db, which, backend?)`
- `writeWatermark(db, which, position, backend?)`
- `readFetchCursor(db, backend?)`
- `writeFetchCursor(db, position, backend?)`
- `writeFetchCursorIfAhead(db, position, backend?)`

The SQL schema enforces isolation at the storage level. Both `_vfs_watermark` and `_vfs_fetch_cursor` tables include a `backend` column as part of their composite primary key (`k, backend`). This schema design guarantees that:

- The same logical key (e.g., `fetchRev`) exists as **separate rows** for each backend
- No backend can overwrite another's cursor accidentally
- Query operations return only the rows matching the specified backend parameter

## RPC Driver Forwards Backend Context

The sync driver in [`packages/rpc/src/sync-driver.ts`](https://github.com/cloudflare/computer/blob/main/packages/rpc/src/sync-driver.ts) orchestrates push and pull operations without conflating backend state. Its core methods—`pullOnce`, `pushOnce`, and `reconcileWatermarks`—receive a `backend` identifier from the caller and propagate it unchanged to the watermark helpers:

```ts
// From sync-driver.ts conceptual flow:
async function pullOnce(db, backend, options) {
  const cursor = readFetchCursor(db, backend);  // reads only this backend's cursor
  // ... fetch operations using backend-specific cursor ...
  writeFetchCursor(db, newPosition, backend);   // writes only this backend's cursor
}

```

This **pass-through design** ensures that the RPC layer itself never mixes cursor state between backends. Each `pull("container")` or `push("shell")` invocation advances exactly one cursor track.

## In-Memory Per-Backend State in Workspace

The `Workspace` class in [`packages/computer/src/workspace.ts`](https://github.com/cloudflare/computer/blob/main/packages/computer/src/workspace.ts) mirrors the database isolation with in-memory data structures keyed by backend identifier:

- `#mutationTails: Map<string, MutationTail>` — per-backend mutation queues
- `#handles: Map<string, HandleCache>` — cached filesystem handles
- `#commandHandles: Map<string, CommandHandleCache>` — cached command interfaces
- `#moduleHandles: Map<string, ModuleHandleCache>` — cached module references

When `ws.push("container")` executes, the workspace:

1. Resolves the backend ID to its configuration
2. Accesses only the `#mutationTails` entry for `"container"`
3. Invokes `pushOnce` with `"container"` as the backend parameter
4. Returns results scoped to that backend's applied mutations

Concurrent operations on different backends proceed without blocking because they operate on **disjoint key spaces** in both memory and persistent storage.

## Practical Usage: Isolated Sync Operations

The following patterns demonstrate correct backend-scoped operations:

### Pushing Changes for a Specific Backend

```ts
// Advance only the container backend's cursors
await ws.push("container");

```

### Pulling with Backend-Specific Results

```ts
// Receive and apply only entries for the shell backend
const result = await ws.pull("shell");
console.log(result.applied);  // mutations from "shell" backend only

```

### Debugging Raw Cursor Values

Direct database inspection requires the backend identifier to retrieve the correct row:

```ts
import { readFetchCursor, readWatermark } from "@cloudflare/dofs";

const db = ws.db;

// Distinct rows, distinct values
const containerFetch = readFetchCursor(db, "container");
const shellFetch     = readFetchCursor(db, "shell");

const containerPushRev = readWatermark(db, "pushRev", "container");
const shellPushRev     = readWatermark(db, "pushRev", "shell");

```

Without the backend parameter, all calls would return or modify the `DEFAULT_BACKEND_ID` row—likely corrupting sync state in multi-backend configurations.

## Summary

- **Backend identifiers** supplied at `Workspace` construction become the isolation boundary for all sync operations
- **SQLite primary keys** in `_vfs_watermark` and `_vfs_fetch_cursor` include the backend column, creating separate row namespaces
- **DOFS watermark helpers** accept and propagate the backend parameter through all read/write paths
- **RPC sync driver** forwards caller-provided backend IDs without interpretation, preventing cross-backend cursor leakage
- **Workspace internal maps** maintain per-backend handle caches and mutation queues for memory-level isolation

These layered mechanisms—storage schema, API design, and runtime data structures—collectively guarantee that multiple execution backends share a workspace without interfering in each other's sync progress.

## Frequently Asked Questions

### What happens if two backends use the same identifier?

Cloudflare Computer treats identical `id` values as the **same backend**. Their cursors would collide in the database, with last-write-wins behavior on `_vfs_watermark` and `_vfs_fetch_cursor` rows. Always assign unique, stable identifiers when registering multiple backends.

### Can a workspace dynamically add or remove backends after creation?

The `WorkspaceOptions.backends` array is processed at construction time. The source implementation in [`workspace.ts`](https://github.com/cloudflare/computer/blob/main/workspace.ts) initializes `#mutationTails`, `#handles`, and related structures from this initial list. Dynamic backend reconfiguration would require workspace reconstruction; runtime backend list mutation is not supported in the current implementation.

### Does per-backend isolation affect performance or storage overhead?

Each backend adds **one row per cursor type** (typically 2–3 rows: `fetchRev`, `pushRev`, `fetchCursor`). Storage overhead is negligible—SQLite handles sparse row sets efficiently. Performance remains linear per backend because queries use the composite primary key (`k, backend`) with index-optimized lookups.

### How does backend isolation interact with the sync protocol specification?

The documentation in [`docs/02_sync_protocol.md`](https://github.com/cloudflare/computer/blob/main/docs/02_sync_protocol.md) formalizes cursor semantics and explicitly includes per-backend isolation as a requirement. The implementation in [`watermarks.ts`](https://github.com/cloudflare/computer/blob/main/watermarks.ts) and [`sync-driver.ts`](https://github.com/cloudflare/computer/blob/main/sync-driver.ts) directly satisfies this specification, ensuring protocol-compliant behavior regardless of backend count or type.