How `vfs_nodes.rev` Relates to the Sync Changes Table for Tombstones in Cloudflare Computer

vfs_nodes.rev tracks the latest revision for live inodes, while vfs_changes.rev records tombstone deletions using the same global monotonic counter, enabling the sync driver to merge live updates with historical deletions into a consistent ordered stream.

In the Cloudflare Computer project's distributed object filesystem (DOFS), revision tracking forms the backbone of reliable synchronization. The VFS maintains a single global revision counter in vfs_meta.rev, and understanding how this counter flows into both live node records and tombstone entries is essential for grasping how the system propagates deletions across distributed clients.

The Global Revision Counter: vfs_meta.rev

Every filesystem mutation—whether mkdir, writeFile, or rm—increments a global, monotonically increasing revision counter. This counter lives in vfs_meta.rev and serves as the single source of chronological truth for the entire VFS.

// packages/dofs/src/rev.ts
export function incrementRev(db: Database): number {
  const row = db.one<{ v: number }>(
    "UPDATE vfs_meta SET v = v + 1 WHERE k = 'rev' RETURNING v"
  );
  return row.v;               // <- new global revision
}

The incrementRev() function in packages/dofs/src/rev.ts atomically bumps this counter and returns the new value. Every subsequent operation uses this returned revision to tag its effects.

How vfs_nodes.rev Tracks Live State

When an inode is created or modified, the current global revision is copied into vfs_nodes.rev for that live record. This column answers: "What is the most recent revision at which this file or directory existed in its current form?"

await db.transactionSync(async (tx) => {
  const rev = incrementRev(tx);            // bumps global rev
  tx.run(
    "INSERT INTO vfs_nodes (inode, rev, ...) VALUES (?, ?, ...)",
    inode, rev
  );
});

The vfs_nodes_by_rev index allows efficient range queries: the sync driver scans vfs_nodes WHERE rev > ? ORDER BY rev to find all live updates since a client's last sync point.

Tombstones in vfs_changes: Preserving Delete History

When a file is deleted, the system faces a problem: simply removing the row from vfs_nodes would cause sync clients to never learn of the deletion. The solution is tombstone entries in vfs_changes.

// packages/dofs/src/sync/changes.ts
await db.transactionSync(async (tx) => {
  const rev = incrementRev(tx);            // same rev is used for the tombstone
  tx.run(
    "INSERT INTO vfs_changes (rev, path, op) VALUES (?, ?, 'delete')",
    rev, "/foo.txt"
  );
  // vfs_nodes entry for /foo.txt is removed elsewhere
});

Key design properties of this approach:

  • vfs_changes.rev uses the same global revision as vfs_nodes.rev, ensuring comparable ordering
  • The tombstone survives even after the vfs_nodes row is purged
  • Multiple operations on the same path can be coalesced by taking MAX(rev)

Merging Live Updates and Tombstones in the Sync Driver

The sync driver in packages/dofs/src/sync/coalesce.ts merges two revision streams to present clients with a complete, ordered change log:

// packages/dofs/src/sync/coalesce.ts
// live updates – scan by rev
db.all("SELECT inode, rev FROM vfs_nodes WHERE rev > ? ORDER BY rev");

// tombstones – scan changes table
db.all(
  "SELECT path, MAX(rev) AS rev FROM vfs_changes WHERE rev > ? AND op = 'delete' GROUP BY path"
);

Because both tables use the same monotonic revision counter, the sync driver can reliably interleave these results. A tombstone's revision will always be greater than or equal to any preceding live mutation for the same inode, preventing race conditions where a client might "resurrect" a deleted file.

Revision Safety Guarantees

The unified revision system provides critical guarantees for distributed synchronization:

  • Ordering: All changes—creations, updates, deletions—are totally ordered by rev
  • Idempotency: Clients can resume sync from any watermark without missing changes
  • Compaction: Old tombstones can eventually be garbage-collected once all clients have synced past them

The sync layer reads the current watermark from vfs_meta.rev via packages/dofs/src/sync/watermarks.ts, then compares against vfs_nodes.rev for live state and vfs_changes.rev for historical deletions.

summary

  • vfs_nodes.rev stores the latest revision for each live inode, enabling efficient incremental sync of existing files
  • vfs_changes.rev stores tombstone deletions using the same global revision counter, ensuring deletions propagate to all clients
  • Both values originate from vfs_meta.rev via incrementRev() in packages/dofs/src/rev.ts
  • The sync driver merges these streams in packages/dofs/src/sync/coalesce.ts to present a unified, ordered change log
  • Tombstone revisions are always ≥ preceding live mutations, preventing synchronization anomalies

frequently asked questions

What happens if two clients delete the same file simultaneously?

Both insert tombstones into vfs_changes with different rev values from sequential calls to incrementRev(). The sync driver coalesces these using MAX(rev), and clients see the deletion at the later revision. Duplicate tombstones are harmless.

Can vfs_nodes.rev ever decrease?

No. The revision counter in vfs_meta.rev only increments, and vfs_nodes.rev is set to copies of this value. Even when an inode is modified, its rev column is updated to a higher value, never lower.

How does the system clean up old tombstones?

Once all connected clients have synced past a tombstone's revision (tracked via watermarks in packages/dofs/src/sync/watermarks.ts), the entry can be safely deleted from vfs_changes. The revision monotonicity ensures no client will request changes from before its acknowledged watermark.

Why not just keep deleted rows in vfs_nodes with a deleted flag?

Separating tombstones into vfs_changes allows the live filesystem queries to remain fast and simple while keeping historical data strictly for synchronization. It also enables compaction strategies that don't require scanning mixed live/deleted tables.

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 →