From 14911aae21ec4975f7a805b91e3c4867cb41974b Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 12 Oct 2026 00:17:32 +0200 Subject: feat: let the operator pause and resume a root's indexing (MNP 6.2) A whole-root walk now checks its root before every file: removed or ejected, it stops instead of reading the drive to the end; paused, it finishes from the hash cache alone. A paused root is not watched, not swept by reconcile and survives a restart (roster), its indexed files stay served. Resume runs the reconcile sweep over the root, so entries and their enrichment are kept. Signed root_pause / root_resume over MNP, loopback routes, CLI `root pause|resume`, a pause button on the indexing dock and pause/resume in the roots table of Group settings. Co-Authored-By: Claude Opus 5.5 --- docs/MESHBAY_DESIGN.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) (limited to 'docs/MESHBAY_DESIGN.md') diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 9c63c52..d0b5475 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -16,7 +16,7 @@ > them — it names the invariant that holds today, not the incident that produced > it. §13 is the register of those labels. > -> Wire versions at the time of writing: **MNP 6.1** (oldest peer accepted 4.0), +> Wire versions at the time of writing: **MNP 6.2** (oldest peer accepted 4.0), > **MHP 0.1**, packages **0.19.0**. The normative source for the wire format is > `MESHBAY_NODE_PROTOCOL.md`; this document states the design the protocol > serves, not its byte layout. @@ -1231,7 +1231,7 @@ from anything in the response. | `invite_link_create`, `invite_cancel` | the operator | | group key init and rotation | **local admin API or CLI only**; the node generates the key itself | | root add, root `writable`/`removable`, hosting a group, transfer limits | **local admin API or CLI only** (MNP 6.0) | -| root remove/eject/plug, `apps_enabled`, app directories | operator-signed | +| root remove/eject/plug/pause/resume, `apps_enabled`, app directories | operator-signed | | ~~`gek_bundle_store`~~ | **the message does not exist.** No member ever hands the node key material | `gek_bundle_store` was deleted rather than gated. The operator's X25519 public key @@ -1641,8 +1641,9 @@ A signed op proves that the operator's key signed, not that they meant it: in a browser that key is driven by code the hub serves (T3), and in the desktop application by a renderer that parses content from nodes. Either could otherwise have shared any folder on the machine, writable, from anywhere. The operator still -sees every root and its flags from any browser; removing, ejecting and plugging stay -signed ops, because they narrow what is shared or restore what already was. +sees every root and its flags from any browser; removing, ejecting, plugging, pausing +and resuming stay signed ops, because they narrow what is shared or restore what +already was. > **There is one answer to "may this member write", and it is the root.** A single > flag over the group cannot express "this library is published read-only and that @@ -1670,6 +1671,12 @@ states: - **`available`** — computed as `not ejected and is_live()`. This is what clients and the indexer see. +A third, **`paused`**, is the operator's alone and independent of both: the root is +readable and served, and its indexing is stopped (§6.3). Persisted in `roster.db` +beside `ejected`, for the same reason: a pause that a restart undid would start the +scan the operator stopped. Removing a root forgets its pause, so a directory added +back under the same name starts indexed. + The distinction matters: between clicking eject and physically unplugging, `is_live()` is still true, and without `ejected` the availability sweep would immediately flip the root back. @@ -1755,6 +1762,24 @@ reason to show progress. `index_progress` is deliberately **not** sealed: counters only, every 2 s. +**A walk looks at its root before every file.** A root removed from the set or +ejected while it is walked is not read to the end: its entries are dropped, or +frozen on a drive that is leaving. The walk reads the root from the current set, +not the object it started with, because a reload replaces every root. + +**A pause outlasts the walk it stops.** Reconcile indexes every file the index +lacks, so a walk that was merely cancelled is redone at the next tick. A paused +root is therefore not watched, not swept by reconcile, and not hashed by any walk: + +- **A walk that meets a pause finishes from the hash cache alone.** What the node + already hashed stays listed; nothing new is read. Stopping dead is not enough, + because the index is rebuilt at every start and a rescan drops the root's + entries before it walks — either would leave a paused root half empty. +- **Resuming runs the reconcile sweep over that root, never a scan.** A scan + replaces every entry it meets, and with them what the enrichment passes put + there; the sweep indexes only what the index lacks. The request returns once + the walk is queued, and the progress band shows it. + ### 6.4 Uploads Five protections, and they are the substance: @@ -1989,7 +2014,7 @@ before its key is linked or a group exists. ``` meshbay-node status meshbay-node group list|add|remove -meshbay-node root list|add|remove|set|eject|plug +meshbay-node root list|add|remove|set|eject|plug|pause|resume meshbay-node gek init|rotate meshbay-node operator pair meshbay-node member list|invite [--link]|cancel|revoke|unpin -- cgit v1.2.3