aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-02 10:20:09 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-02 10:20:09 +0200
commite4f61771131be635b9e81a19203a00707b4b19df (patch)
treed80e4edafbeade3c27137e6753140e6585a26b9b /docs/MESHBAY_DESIGN.md
parente941cc4c39c38a12220153ea572bd4c7bb92fde0 (diff)
downloadmeshbay-e4f61771131be635b9e81a19203a00707b4b19df.tar.gz
feat(mnp): sharing a folder is decided on the node's machine only (MNP 6.0)
root_add, root_update and group_attach leave MNP: adding a directory and switching writable/removable go through the loopback API (native dialog in the desktop app) or the CLI. The operator's Settings tab still lists the roots from any browser, read-only. The desktop app refuses to sign those ops; a loopback flag change now reaches open pages (publish_roots). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md21
1 files changed, 16 insertions, 5 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index eec354a..e145ed4 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 5.0** (oldest peer accepted 4.0),
+> Wire versions at the time of writing: **MNP 6.0** (oldest peer accepted 4.0),
> **MHP 0.1**, packages **0.17.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.
@@ -99,7 +99,7 @@ opens them.
│ └─────────┘
MHP 0.1 │ signalling (SDP/ICE, <1 KB), presence, revocation push MHP
│
- ┌────┴────┐ MNP 5.0 ┌──────────┐
+ ┌────┴────┐ MNP 6.0 ┌──────────┐
│ node │◄──────── WebRTC DataChannel / QUIC ──────────►│ client │
└─────────┘ index, file chunks, streams, chat, admin └──────────┘
holds the files browser SPA or desktop
@@ -1451,8 +1451,8 @@ checks the version its peer declared and **branches on none of it**.
**The floor is not necessarily the current version, and what is added above it is why.**
It is `MNP_MIN_SUPPORTED` in `handshake.py`, and it is the last MAJOR that had to
refuse at the handshake: 3.1–3.4 were added above the 3.0 floor without moving it,
-and 5.0, a MAJOR confined to four signed operations that a peer across the break
-refuses to sign, sits above the 4.0 floor. So a
+and 5.0 and 6.0 — MAJORs confined to a few signed operations, four whose subjects
+changed and then three removed — sit above the 4.0 floor. So a
peer can be reachable and still not do something the current version can, and the
client has to cope with that — **by reading the peer's own answer, never by comparing
version numbers**.
@@ -1541,7 +1541,17 @@ Five consequences, none optional:
- `writable = true` means any group member may upload there.
Several roots may be writable and none need be — a fully read-only group is valid.
-The operator toggles this with a signed op.
+
+**What widens the sharing is decided on the node's own machine.** Adding a root,
+hosting a group over a directory, and switching `writable` or `removable` go through
+the loopback API (in the desktop application, behind a native dialog for anything
+that shares a folder or opens one to writes) or the CLI — never over MNP, since 6.0.
+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.
> **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
@@ -3802,6 +3812,7 @@ process runs it — `systemctl --user` on Linux, Task Scheduler on Windows.
| **The Create group wizard calls two hooks after an early return** | `CreateGroupWizard` (`create-group-page.js`) returns during node detection, before its `useRef`/`useEffect` for provisioning, so the hook count changes between renders. Preact tolerates a list that grows, and nothing is known to break; `test_hook_ordering.py` checks declaration order, not this. Found while tracing the frozen-fields report, which had another cause (`ask.js`) |
| **A node key is read from the terminal or the desktop client, never a browser** | **Accepted.** `meshbay-node status` on the node's own machine and Node → Overview in the desktop client are the two places the key can be read; the Node page is Electron-only, because `platform.node` resolves to "not available" without the bridge, and no hub route exposes the key. The create-group wizard links it automatically over that same bridge, so the manual paste in **Profile → Link Node** exists for the operator who runs the node from a terminal and the hub from a browser — who has a terminal by definition. Anyone linking a node is already at a shell prompt, so a browser-reachable copy would buy nothing and widen what the hub knows about the node |
| **Listing a group's folders walks every root on the event loop** | `index_sync_message` (`transport/wire.py`) builds its `dirs` field with `list_dirs`, an `rglob("*")` over every root, and nothing sends it off the loop: the WebRTC `index_sync` handler, the daemon's index push and QUIC all call it inline. So each index request from any member is a directory walk of the whole library that every other peer on the node waits behind. `test_disk_io_off_loop.py` never saw it, because it reads the transport's own modules and the walk is one call away in `wire.py`. Found by widening what that test reads, not by a symptom |
+| **A loopback eject or plug reaches open pages late** | `ops.eject_root` and `ops.plug_root` flip the live set and tell nobody; over MNP the broadcast `root_eject_ack` / `root_plug_ack` is what moves every open table. So an eject made from the desktop application or the CLI shows on members' pages only with the next index push — for a plug, the end of its rescan; for an eject, whatever changes next. `ops.update_root` had the same silence and now calls `DirectoryIndexer.publish_roots`; the same call belongs in these two. Found while moving the `writable`/`removable` switches to the loopback door (MNP 6.0) |
| **The transcoded-seek test passes without transcoding** | `test_a_transcoded_video_keeps_accurate_seeking` (`test_stream_seek_audio_alignment.py`) forces the re-encode branch by swapping the module's `BROWSER_INCOMPATIBLE_VIDEO_CODECS`, then checks only that the result has no audio gap. The copy path also leaves no gap on that clip, so pointing the swap at a module the streaming code does not read still passes: the test cannot tell that the branch it is named after never ran. It should assert the re-encode happened (the `re-encoding` log line, or the encoder in the ffmpeg argv). Found by breaking the swap on purpose while moving the streaming code |
---