From 6cdc6016d72dcfb7530ac38a8fa92232418ac305 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 5 Oct 2026 11:10:50 +0200 Subject: fix(node): plug an auto-ejected removable root back once its files return A node started with the desktop session runs before the session has mounted its USB drives. The safety net then auto-ejected every removable root and persisted it exactly like an operator's eject, so after each reboot those roots stayed ejected until someone plugged them by hand (seen on a node whose /media drives were mounted a minute after it started). An auto-eject is now stored as such ("auto" in roster.db). At startup and at every reconcile, an auto-ejected root whose path is readable again is checked against a few files the hash cache knows under it, at the same path with the same size and mtime; one found and the root is plugged back and rescanned. An empty mount point or another drive in its place is not recognised and stays ejected. An operator's eject is never undone automatically. Co-Authored-By: Claude Opus 5.5 --- docs/MESHBAY_DESIGN.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'docs/MESHBAY_DESIGN.md') diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 2cfe7a4..9c87372 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1565,7 +1565,8 @@ the whole tree or presents an empty directory to the next scan. Both propagate a though the owner erased their library. So a root has two independent runtime states: -- **`ejected`** — operator-controlled, persisted in `roster.db`. +- **`ejected`** — set by the operator, or by the safety net below; persisted in + `roster.db`, with which of the two set it. - **`available`** — computed as `not ejected and is_live()`. This is what clients and the indexer see. @@ -1586,12 +1587,21 @@ let the following scan read the empty mount point as an erased library. It lives hand-written config must not be rewritten because a USB drive was unplugged. **Auto-eject is the safety net.** If a `removable` root's path disappears, the -availability sweep sets `ejected` as though the operator had clicked it, and -reports it so the daemon persists it. Nothing is deleted: index entries, cached +availability sweep sets `ejected` and reports it so the daemon persists it, marked +as the safety net's. Nothing is deleted: index entries, cached metadata, thumbnails, chat history referencing those files and app directory configurations all survive, the last flagged as temporarily invalid rather than wrong. +**The safety net's eject undoes itself; the operator's never does.** At startup +and at every reconcile, an auto-ejected root whose path is readable again is +checked against what the hash cache knows was under it: a few of those files, +at the same path with the same size and mtime. One found, and the root is +plugged back and rescanned, like a plug. None found, and it stays ejected: an +empty mount point or another drive mounted in its place is exactly what the eject +protects the index from. The case this serves is ordinary: a node started with +the session, before the desktop has mounted its USB drives. + ### 6.3 Indexing The index is **content-addressed**: `GroupIndex` is keyed by blake3, so the same -- cgit v1.2.3