# How File Locks Work Across the `read`, `ls`, `find`, `grep`, `write`, `edit`, and `delete` Tool Sets in Cloudflare Computer

> Discover how Cloudflare Computer file locks protect data. Learn how read operations succeed while write, edit, and delete actions are blocked at the VFS layer.

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

---

**File locks in Cloudflare Computer are implemented as read‑only mount guards at the virtual filesystem (VFS) layer, allowing read operations to proceed while blocking any write, edit, or delete attempts on locked paths.**

All filesystem operations in Cloudflare Computer—whether executed through `read`, `ls`, `find`, `grep`, `write`, `edit`, or `delete`—flow through a SQLite‑backed VFS that enforces logical file locking via the `readOnlyRootFor` guard. This mechanism ensures that when a path is locked for exclusive access, the entire subtree becomes read‑only across both sides of the sync protocol (the Durable Object and the `computerd` daemon).

## The Mount Guard Architecture

File locking in Cloudflare Computer does not rely on OS‑level primitives. Instead, it uses a **logical mount guard** system centralized in [`packages/dofs/src/fs/mount-guard.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/fs/mount-guard.ts).

The `readOnlyRootFor` function queries the VFS metadata to determine whether a given path falls under a blocked mount. If a match exists, the operation receives a read‑only root identifier that triggers appropriate access restrictions.

## How Each Tool Set Operation Handles Locks

### Read, ls, find, grep: Permitted with Read‑Only Access

All **read‑oriented operations** consult `readOnlyRootFor` before proceeding. When a lock is active, these tools receive a read‑only mount root and continue execution without modification capability.

- **`read`** – Retrieves file contents from the blocked path's read‑only view; no write access is granted.
- **`ls`** – Lists directory entries under the read‑only root; metadata like `mtime` and `size` remain accessible.
- **`find`** – Recursively traverses the directory tree using the same read‑only mount point.
- **`grep`** – Searches file contents through the read‑only VFS interface.

```ts
// From mount-guard.ts — returns the blocked root or null if writable
import { readOnlyRootFor } from "@cloudflare/dofs/src/fs/mount-guard.js";

function canRead(db: SQLiteDatabase, path: string): boolean {
  const blockedRoot = readOnlyRootFor(db, path);
  // If blockedRoot exists, reads proceed via the read‑only mount
  // If null, the path is fully writable
  return true; // Reads are always allowed, but writes check blockedRoot
}

```

### Write and Edit: Blocked When Locked

**Write operations** are rejected at the sync driver layer in [`packages/dofs/src/sync/apply.ts`](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/apply.ts). Before applying any batch of changes, the driver checks `readOnlyRootFor` for the target path. A non‑null result triggers an access denial.

The `edit` tool, which opens files for modification, implicitly acquires an exclusive lock. This lock propagates across the sync channel, forcing the remote side into read‑only mode via the same guard mechanism.

```ts
// Conceptual guard check in apply.ts
function applyWriteBatch(db, operations: WriteOp[]) {
  for (const op of operations) {
    const blocked = readOnlyRootFor(db, op.path);
    if (blocked) {
      throw new Error(`Write blocked: ${op.path} is locked under ${blocked}`);
    }
    // Execute UPDATE/INSERT into SQLite VFS tables
  }
}

```

### Delete: Blocked Under Active Locks

**Deletion** follows identical logic to writes. Both `unlink` (file removal) and `rmdir` (directory removal) operations in [`apply.ts`](https://github.com/cloudflare/computer/blob/main/apply.ts) validate the target path against `readOnlyRootFor`. A locked path cannot be deleted until the guard is released.

## Cross‑Side Lock Synchronization

The lock state is **symmetric across both endpoints** of the sync protocol. When a lock is established on one side:

1. The locking side marks the path as blocked in its VFS.
2. A watermark containing the blocked path is transmitted to the remote side.
3. The remote VFS updates its mount guard state, creating a matching read‑only root.
4. Both sides now enforce identical restrictions without race conditions.

This design ensures that a container performing an `edit` via `workspace.runtime.exec` automatically locks out writes from the Durable Object side—and vice versa.

## Lock Lifecycle and Characteristics

| Aspect | Behavior |
|--------|----------|
| **Granularity** | Per‑path subtree; all nested files and directories inherit the lock |
| **Duration** | Transient, tied to the sync transaction or explicit edit session |
| **Storage** | Logical state in SQLite metadata (`blocks` table, mount guard cache) |
| **Release** | Automatic on transaction commit/abort or explicit session close |
| **Conflict resolution** | First‑lock‑wins; subsequent write attempts receive `EACCES`‑style errors |

## Code Example: Safe File Operations with Lock Awareness

```ts
import { readOnlyRootFor } from "@cloudflare/dofs/src/fs/mount-guard.js";

async function guardedWrite(db, path: string, data: Buffer) {
  const blockedRoot = readOnlyRootFor(db, path);
  if (blockedRoot) {
    throw new Error(
      `Cannot write ${path}: locked under read‑only mount ${blockedRoot}`
    );
  }
  
  // Safe to proceed — no active lock
  await db.run(
    `INSERT INTO blocks (path, data, mtime) VALUES (?, ?, ?)`,
    path, data, Date.now()
  );
}

// Read operations skip the guard check for blocking
async function safeRead(db, path: string): Promise<Buffer> {
  // readOnlyRootFor may return a blocked root, but reads continue
  const row = await db.get(`SELECT data FROM blocks WHERE path = ?`, path);
  return row ? row.data : null;
}

```

## Key Implementation Files

| File | Purpose | GitHub Link |
|------|---------|-------------|
| [`mount-guard.ts`](https://github.com/cloudflare/computer/blob/main/mount-guard.ts) | Core `readOnlyRootFor` logic and mount guard state | [source](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/fs/mount-guard.ts) |
| [`apply.ts`](https://github.com/cloudflare/computer/blob/main/apply.ts) | Sync driver that enforces locks before writes/deletes | [source](https://github.com/cloudflare/computer/blob/main/packages/dofs/src/sync/apply.ts) |
| [`shim.ts`](https://github.com/cloudflare/computer/blob/main/shim.ts) | `computerd` shim blocking until VFS state is consistent | [source](https://github.com/cloudflare/computer/blob/main/packages/computerd/src/shim/shim.ts) |
| [`vfs.ts`](https://github.com/cloudflare/computer/blob/main/vfs.ts) | POSIX attribute mapping and lock state propagation | [source](https://github.com/cloudflare/computer/blob/main/packages/computerd/src/fuse/vfs.ts) |

## Summary

- **File locks are logical mount guards**, not OS file locks, implemented in [`mount-guard.ts`](https://github.com/cloudflare/computer/blob/main/mount-guard.ts) via `readOnlyRootFor`.
- **Read tools** (`read`, `ls`, `find`, `grep`) operate through read‑only mounts when locks are active.
- **Write tools** (`write`, `edit`) and **delete** are blocked at the sync driver layer in [`apply.ts`](https://github.com/cloudflare/computer/blob/main/apply.ts) if the target path is locked.
- Locks **synchronize bidirectionally** across the Durable Object and `computerd` daemon through watermark propagation.
- The entire system rides on SQLite‑backed metadata without native filesystem dependencies.

## Frequently Asked Questions

### How does a file lock affect concurrent read operations?

Concurrent reads proceed normally through a read‑only mount view. The `readOnlyRootFor` guard returns the blocked root identifier, which read‑oriented tools use to access file contents and metadata without modification rights. No read operation is ever rejected due to a lock.

### What happens if I attempt to write to a locked file?

The [`apply.ts`](https://github.com/cloudflare/computer/blob/main/apply.ts) sync driver checks `readOnlyRootFor` before executing any write batch. If the target path matches a blocked root, the driver throws an error equivalent to `EACCES` and aborts the transaction. The write never reaches the SQLite `blocks` table.

### How long does a file lock persist?

Locks are transient and bound to the lifecycle of the exclusive operation that created them. An `edit` session holds the lock until explicitly closed or until its transaction commits or aborts. Once released, the mount guard removes the blocked root entry and full write access resumes on both sides of the sync channel.

### Can locks apply to entire directories?

Yes. The mount guard operates on path prefixes. Locking a directory automatically locks all nested files and subdirectories under that path subtree. This behavior ensures consistent isolation for recursive operations like `find` or bulk `delete`.