For each entry select: covered / not applicable — surface does not exist there / NOT covered.
OpenCode 1: covered — the change is in the shared storage code (shared/storage-permissions.ts, features/magic-context/storage-db.ts, shared/storage-file-mutex.ts, hooks/magic-context/lkg-recovery-fence.ts).
OpenCode 2: covered — same shared code.
Pi: covered — Pi opens the database through the same shared storage code.
OMP: covered — same as Pi.
Rust module: NOT covered — the module keeps its own store. Its permission code was not changed or checked.
What changes
Startup permission pass.tightenStorageTreeSync checked every entry under the storage folder with synchronous lstat calls, two times on each first database open in a process.
After one clean full pass, the pass writes a marker file in the storage folder. The marker name holds a policy version (STORAGE_PERMISSIONS_POLICY_VERSION), so a later change to the permission rules can force one more full pass.
When the marker is present and has mode 0600, the pass checks only the storage folder, the database file and its -wal and -shm files, and the models/ and logs/ subtrees. Those two subtrees are written by code that does not set private modes, so they stay recursive.
A wrong mode, an access error, or disabled enforcement removes the marker. The next start then does a full pass again.
Fence folder.
withStorageFileMutex removes its lock directory with rmdir after the locked operation. rmdir fails while another process holds a ticket in the directory, and the error is ignored. Ticket creation already retries when the directory disappears between mkdir and the ticket write.
A fence write uses one fixed temp name, <fence>.json.tmp, under the fence mutex. locked() removes only that name and no longer lists the folder on each operation.
The full pass sweeps empty *.json.locks directories that have no fence file, and old temp files with random names from earlier versions. A temp file is removed only when it is older than 10 minutes (LEGACY_FENCE_TEMP_MIN_AGE_MS), because an older version in another process can still be writing it without the new mutex rules.
fix(storage): keep fresh fence temp files during the full-pass sweep
Tests
storage-permissions-startup.test.ts: with a marker, a fixture with 2,000 fence files gave 2,004 lstat calls before the change (limit 8) and gives 7 after. A real SQLite reopen gave 4,008 before (limit 16) and gives 14 after. The first boot does a full pass and writes a private marker; a wrong mode removes the marker. When the marker check was removed, the startup test failed at 2,004 calls.
storage-permissions-creators.test.ts: every creation path makes files 0600 and directories 0700, including the database, WAL and SHM under umask 0. A creation that finds a wrong mode removes the marker.
lkg-recovery-fence.test.ts, storage-file-mutex-review.test.ts: the .locks directory is gone after an operation and stays while a ticket is held; two operations in sequence on one fence work; locked() does not list the folder; the full pass removes empty orphan .locks directories and keeps the others. An old random-name temp file is removed, a fresh one stays, and unrelated names stay. The existing two-process publication race test passes.
Results on this branch (Bun 1.4.0):
Focused files: 49 pass, 0 fail.
Directory run on the first commit (src/shared, storage database, fence and mutex tests): 1,055 pass, 0 fail.
bun run typecheck, Biome on the changed files, and git diff --check: clean.
Summary by cubic
Skips the full storage permission walk on subsequent starts after one clean pass, cutting startup lstat calls without changing what gets enforced.
After a clean full walk, a 0600 marker file makes later passes check only the storage root, the database and its -wal/-shm files, and the models/ and logs/ subtrees. Any failure, wrong mode, or disabled enforcement removes the marker and forces a full walk again.
The fence mutex removes its empty .locks directory after each operation and uses a fixed temp name instead of listing the folder. The full pass now sweeps empty orphan .locks directories and legacy temp files older than 10 minutes.
Confidence Score: 4/5
The PR appears safe to merge, with a non-blocking gap in marker removal when enforcement changes while a database handle is reused.
Adds a versioned marker so later database opens skip the full storage permission walk.
Database opens skip the full storage walk after one clean pass.
Fence writes reuse one temp file and empty lock folders disappear.
Acknowledged scope: iceteaSA explicitly chose the ten-minute age threshold for deleting legacy temporary files to preserve recent legacy writes.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Fresh database open] --> B{Enforcement enabled?}
B -->|No| C[Remove marker]
B -->|Yes| D{Private marker exists?}
D -->|No| E[Walk full storage tree]
E --> F[Write marker after success]
D -->|Yes| G[Check root, database, models and logs]
G --> H[Remove marker after repair or failure]
I[Cached database open] --> J[Reuse handle without permission pass]