Summary
PeerStorage.scanOfflineChanges() walks the filesystem and uploads new or modified files, but it never tombstones CouchDB documents whose corresponding files are missing on disk. If a file is deleted while the bridge is down, the CouchDB document persists forever — the deletion is never propagated.
Current behavior
- Offline scan: walks disk, uploads new/changed files. Deletions are invisible (the walk only sees what exists).
- Live watch: catches deletions via
dispatchDeleted(), but only for events that happen while the watcher is running.
Proposed fix
After the offline scan walk completes, iterate all CouchDB documents and tombstone those whose paths are absent on disk. This requires:
- A promise-based gate on
PeerStorage so the Hub knows when the offline walk finished (waitOfflineScanComplete()).
- A
reconcileDiskDeletions() method on Hub that iterates CouchDB docs, checks disk existence via Deno.stat(), and tombstones missing ones via DirectFileManipulator.delete().
- Integration into
Hub.start() so it runs automatically after an offline scan (no-op when scanOfflineChanges is false).
The reconcile pass respects excludePrefixes (if that config option is added — see related feature request) and logs a summary of scanned/deleted/failed paths.
Design notes
- The reconcile runs only when
scanOfflineChanges is true, so steady-state operation is unaffected.
- It uses
DirectFileManipulator directly (not PeerCouchDB) to avoid the isRepeating dedup gate, which is unnecessary for a one-shot reconcile.
- The
PeerStorage.start() method is changed from await p.start() to void p.start() in the Hub (Deno watchFs never returns; chokidar returns after setup), with the reconcile gated on the new completion promise.
Impact
Without this, any file deleted while the bridge is down leaves a zombie document in CouchDB. On restart, the document is never cleaned up, and other clients never learn of the deletion.
Summary
PeerStorage.scanOfflineChanges()walks the filesystem and uploads new or modified files, but it never tombstones CouchDB documents whose corresponding files are missing on disk. If a file is deleted while the bridge is down, the CouchDB document persists forever — the deletion is never propagated.Current behavior
dispatchDeleted(), but only for events that happen while the watcher is running.Proposed fix
After the offline scan walk completes, iterate all CouchDB documents and tombstone those whose paths are absent on disk. This requires:
PeerStorageso theHubknows when the offline walk finished (waitOfflineScanComplete()).reconcileDiskDeletions()method onHubthat iterates CouchDB docs, checks disk existence viaDeno.stat(), and tombstones missing ones viaDirectFileManipulator.delete().Hub.start()so it runs automatically after an offline scan (no-op whenscanOfflineChangesis false).The reconcile pass respects
excludePrefixes(if that config option is added — see related feature request) and logs a summary of scanned/deleted/failed paths.Design notes
scanOfflineChangesis true, so steady-state operation is unaffected.DirectFileManipulatordirectly (notPeerCouchDB) to avoid theisRepeatingdedup gate, which is unnecessary for a one-shot reconcile.PeerStorage.start()method is changed fromawait p.start()tovoid p.start()in the Hub (Deno watchFs never returns; chokidar returns after setup), with the reconcile gated on the new completion promise.Impact
Without this, any file deleted while the bridge is down leaves a zombie document in CouchDB. On restart, the document is never cleaned up, and other clients never learn of the deletion.