How the FUSE Mount Synchronizes Files Between Durable Objects and the computersd Daemon

The computersd daemon maintains synchronization through a 250ms background sync loop that pulls remote changes from the Durable Object and pushes local modifications back via capn-web RPC.

The Cloudflare computer project enables containerized workloads to interact with Durable Object storage through a POSIX-like filesystem interface. This article explains the FUSE file synchronization mechanism that keeps local filesystem state consistent with the authoritative Durable Object (DO) storage, based on the implementation in the cloudflare/computer repository.

Overview of the Synchronization Architecture

File synchronization between the Durable Object and the computersd daemon operates through three coordinated layers:

  • Virtual File System (VFS) — A SQLite-backed filesystem abstraction in the daemon
  • Sync Driver — The bidirectional replication engine handling change propagation
  • Cap'n Web RPC — The wire protocol for exchanging changes and watermarks

The VFS resides in packages/computerd/src/fuse/vfs.ts, while the sync engine lives in packages/rpc/src/sync-driver.ts. Both sides share the SyncRPC interface defined in the @cloudflare/computer-rpc package.

Creating the VFS with Initial State Pull

When the daemon starts, createNodeVirtualFileSystem() in packages/computerd/src/fuse/vfs.ts (lines 85–100) constructs the virtual filesystem from a SQLiteWorkspaceProvider. If an upstream SyncRPC stub is provided, the function immediately performs an initial pull to populate the local database with the latest DO state before FUSE mounting occurs.

// From packages/computerd/src/fuse/vfs.ts
import { createNodeVirtualFileSystem } from "@cloudflare/computerd";
import { createSyncClient } from "@cloudflare/computer-rpc";

async function mountWithSync(doUrl: string) {
  // Create SyncRPC client pointing at the Durable Object
  const syncRpc = await createSyncClient({ url: doUrl });

  // Build VFS — this performs the initial pull automatically
  const { vfs, stopSync } = await createNodeVirtualFileSystem({ 
    upstream: syncRpc 
  });

  // vfs is now ready for FUSE mounting with current DO state
  // stopSync() terminates the background loop when needed
}

The SQLiteWorkspaceProvider serves as the local storage engine, maintaining file metadata, directory structures, and blob references in SQLite while the sync mechanism keeps this store converged with the DO.

The 250ms Background Sync Loop

After initialization, startSyncLoop() launches a recurring sync process. The implementation in packages/computerd/src/fuse/vfs.ts (lines 123–142) uses setInterval to invoke tick(db, upstream) every 250 milliseconds (SYNC_TICK_MS constant).

Each tick executes three operations:

  1. pullOnce — Fetches and applies remote changes from the Durable Object
  2. pushOnce — Transmits local modifications to the Durable Object
  3. Watermark updates — Persists synchronization cursors to ensure progress

Errors during any tick are logged but do not halt the daemon, providing fault-tolerant operation across transient network failures.

// Conceptual flow of each sync tick
import { tick } from "@cloudflare/computer-rpc/driver";

async function oneSyncTick(db: Database, upstream: SyncRPC) {
  // Pull: receive ChangeEntry objects from DO, apply to local SQLite
  await pullOnce(db, upstream);
  
  // Push: serialize local changes, transmit via capn-web RPC
  await pushOnce(db, upstream);
}

Sync Driver Implementation Details

The core replication logic resides in packages/rpc/src/sync-driver.ts (lines 69–392). This module implements the tick, pullOnce, and pushOnce functions that coordinate state transfer.

Pull Operation

pullOnce invokes SyncRPC.fetchChanges to retrieve ChangeEntry objects since the last recorded watermark. The driver guarantees that changes are only persisted after the advertised snapshot cursor is validated, preventing partial application of remote state.

Push Operation

pushOnce collects local modifications, packages them as ChangeEntry objects with associated blob data, and transmits via SyncRPC.pushChanges. The pushRev cursor advances only upon successful DO acknowledgment, ensuring at-least-once delivery semantics without duplicate application.

Watermark Reconciliation

The SyncRPC.watermarks method enables both sides to query and verify their respective cursors. These watermarks stored in the SQLite database (db) provide the foundation for eventual consistency—even if individual ticks fail, subsequent sync rounds resume from the last confirmed position.

Cap'n Web RPC Protocol

The wire format specification in docs/08_capnweb_interface.md defines the contract between DO and daemon. The @cloudflare/computer-rpc package exports this interface, used by both parties:

Method Purpose
fetchChanges(cursor) Retrieve changes since the provided watermark
pushChanges(entries, cursor) Submit local changes, receive new push revision
watermarks() Exchange current cursor positions for verification

The ChangeEntry type and serialization utilities are defined in packages/dofs/src/sync/changes.ts, providing the structure for file operations (create, modify, delete) and associated blob references that traverse the network.

FUSE Integration and POSIX Semantics

The populated VFS is handed to the FUSE driver (@platformatic/vfs), which exposes standard POSIX operations:

  • readFileSync — Satisfied from local SQLite store
  • writeRangeSync — Buffered locally, synced to DO on next push cycle
  • Directory operations — Metadata served from local workspace provider

Because all modifications flow through the SQLite backing store, the 250ms sync loop captures every local change automatically. Applications using the FUSE mount observe standard filesystem behavior while the underlying synchronization remains transparent.

Summary

  • Initialization: createNodeVirtualFileSystem() pulls current DO state before FUSE mount
  • Active sync: 250ms loop in startSyncLoop() runs tick() with pullOnce/pushOnce
  • Reliability: Watermarks in SQLite enable resumable, at-least-once synchronization
  • Protocol: Cap'n web RPC via SyncRPC interface exchanges ChangeEntry objects
  • Fault tolerance: Tick errors are logged; sync continues from last confirmed watermark

Frequently Asked Questions

How does the sync loop handle network interruptions?

The sync driver in packages/rpc/src/sync-driver.ts treats RPC failures as non-fatal. Errors are logged via the standard logging facility, and the 250ms interval continues executing. Because watermarks are only advanced after successful acknowledgment, the next tick automatically retries from the last confirmed position once connectivity restores.

What happens if both sides modify the same file simultaneously?

The current implementation follows a last-writer-wins policy based on sync timing. The pullOnce/pushOnce sequence in each tick means local changes push only after remote changes are applied. Conflicts are not automatically merged; the most recent change entry by synchronization order prevails. Applications requiring stronger consistency should implement external locking.

Can the sync interval be configured?

The SYNC_TICK_MS constant in packages/computerd/src/fuse/vfs.ts is fixed at 250ms in the current implementation. This balances latency against overhead for typical container workloads. Modifying this value requires source changes and rebuilding the computersd package.

Is the SQLite database shared between multiple daemon instances?

No. Each computersd daemon maintains its own SQLiteWorkspaceProvider instance. The Durable Object serves as the single authoritative source, with each daemon's local database acting as a replica synchronized through the RPC channel. Multiple daemons can mount the same DO, with convergence achieved through the shared watermark protocol.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →