summaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_NODE_PROTOCOL.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_NODE_PROTOCOL.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_NODE_PROTOCOL.md')
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md38
1 files changed, 28 insertions, 10 deletions
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index e7fce90..d03b7eb 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -1,6 +1,6 @@
# MeshBay Node Protocol (MNP)
-**Wire version:** `5.0` — `meshbay_common/__init__.py` (`MNP_VERSION`)
+**Wire version:** `6.0` — `meshbay_common/__init__.py` (`MNP_VERSION`)
**Oldest peer accepted:** `4.0` — `handshake.py` (`MNP_MIN_SUPPORTED`)
**Normative implementation:** `meshbay-common` (`protocol.py`, `handshake.py`,
`groupbox.py`, `chatbox.py`, `adminop.py`, `join.py`, `device.py`, `crypto.py`,
@@ -1165,9 +1165,7 @@ broadcast, every connected peer in the group learns the change without reconnect
| `tmdb_override` | `file_id=..,tmdb_id=..,media_type=..` | operator | `tmdb_override_ack{file_id, tmdb_id, media_type}` | yes |
| `tmdb_rematch` | `file_id=..` | operator | `tmdb_rematch_ack{file_id}` | yes |
| `musicbrainz_enabled` | `enabled` | operator | `musicbrainz_enabled_ack{enabled}` | yes |
-| `root_add` | `{path, name, kind, writable, removable}` | operator | `root_add_ack` | no |
| `root_remove` | the root name | operator | `root_remove_ack` | no |
-| `root_update` | `<root>:rw=on\|off,rem=on\|off` | operator | `root_update_ack` | yes |
| `root_eject`, `root_plug` | the root name | operator | `root_eject_ack` / `root_plug_ack` | yes |
| `app_directories` | `<app>:<dir>,<dir>,...` | operator | `app_directories_ack{app, dirs}` | yes |
| `chat_directory` | the path | operator | `chat_directory_ack{path}` | yes |
@@ -1175,14 +1173,28 @@ broadcast, every connected peer in the group learns the change without reconnect
| `search_listed` | `on\|off` | operator | `search_listed_ack{listed}` | yes |
| `transfer_limits` | `d=<n>,u=<n>` | operator | `transfer_limits_ack{limits}` | yes |
| `chat_epoch` | `group_id` | operator | `chat_epoch_ack{epoch}` | yes |
-| `group_attach` | `{name, shared_dir, writable}` | operator | `group_attach_ack` | no |
| `group_detach` | the group name | operator | `group_detach_ack` | no |
**Upload policy is not in this table**, and that is the design: whether a member may
-write is a property of each root (`root_update`), not a switch over the group. A single
+write is a property of each root (its `writable` flag), not a switch over the group. A single
group-wide flag cannot express "this library is published read-only and that folder is a
drop box", which is the ordinary arrangement.
+**Nothing in this table widens what the node shares**, and that is the design too.
+Adding a directory to a group, hosting a new group over a directory, and switching
+a root's `writable` or `removable` flag are done on the node's own machine — the
+desktop application over the loopback API, which asks in a native dialog before it
+shares a folder or opens one to writes, or the CLI — and never over MNP. Until 6.0
+they were the signed ops `root_add`, `group_attach` and `root_update`. A signature
+proves that the operator's key signed, not that the operator 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 have shared any folder on the
+operator's machine, writable, from anywhere. What is left here narrows (`root_remove`,
+`root_eject`) or restores what the operator already shared (`root_plug`). The
+operator still sees the table from any browser: it rides in `index_sync` and
+`index_delta`, which is also how a change made on the node's machine reaches every
+open page.
+
`app_directories` is the **only** way an application's folders are set: one message
for every application, keyed by the app's own registry name, so adding an application
adds no message type, no signed op and no handler.
@@ -2128,7 +2140,6 @@ it back (§3.5).
| `chat_directory` / `_ack` | C→N / N⇒C | signed | where chat attachments are written |
| `chat_link_preview` / `_ack` | C→N / N⇒C | signed | whether the node unfurls posted links |
| `search_listed` / `_ack` | C→N / N⇒C | signed | whether members' cross-group Search lists this group |
-| `root_update` / `_ack` | C→N / N⇒C | signed | a root's `writable` / `removable` flags |
| `root_eject` / `_ack`, `root_plug` / `_ack` | C→N / N⇒C | signed | take a removable root offline, put it back |
| `gek_rotate` / `_ack` | C→N / N→C | signed | node generates a new group key |
| `apps_enabled` / `_ack` | C→N / N⇒C | signed | which group apps are shown |
@@ -2138,8 +2149,8 @@ it back (§3.5).
| `tmdb_override` / `_ack` | C→N / N⇒C | signed | correct a wrong automatic match |
| `tmdb_rematch` / `_ack` | C→N / N⇒C | signed | drop one file's cached match |
| `musicbrainz_enabled` / `_ack` | C→N / N⇒C | signed | per-group MusicBrainz on/off |
-| `root_add` / `_ack`, `root_remove` / `_ack` | C→N / N→C | signed | add or remove a shared directory |
-| `group_attach` / `_ack`, `group_detach` / `_ack` | C→N / N→C | signed | start or stop hosting a group |
+| `root_remove` / `_ack` | C→N / N→C | signed | remove a shared directory |
+| `group_detach` / `_ack` | C→N / N→C | signed | stop hosting a group |
| `node_status` / `_ack` | C→N / N→C | auth (operator) | all groups, roots, daemon state |
| `roster_read` / `_ack` | C→N / N→C | auth (operator) | pinned identities and members |
| `denylist_read` / `_ack` | C→N / N→C | auth (operator) | current refusals |
@@ -2182,7 +2193,7 @@ message:
## 13. Versioning and compatibility
-MNP versions independently of the package version. Current: **`5.0`**; oldest peer
+MNP versions independently of the package version. Current: **`6.0`**; oldest peer
accepted: **`4.0`**.
4.0 is the floor: a member presents a short-lived node-audience token bound to one node
@@ -2197,6 +2208,13 @@ selection, per-account blobs.
four fail with a refusal and everything else works; no node accepts the old subjects,
so nothing is left unsigned on either side. That is why the floor did not move.
+6.0 is a MAJOR that removes three signed operations — `root_add`, `root_update`,
+`group_attach` — rather than changing any (§10.4: nothing over MNP widens what a node
+shares). A 5.x client that sends one gets no answer, as for any unknown type, and
+everything else it does still works; the floor stays at 4.0. The desktop application
+also refuses to sign them, so a node older than 6.0 cannot be driven into them by a
+script in its page either.
+
The two numbers are separate on purpose. `MNP_VERSION` says what this build speaks;
`MNP_MIN_SUPPORTED` says what it will talk to, and moving the second is a decision about
whether an older peer can still do anything useful:
@@ -2414,7 +2432,7 @@ LP(x) = uint32be(len(x)) || x every field, no exceptions
| Constant | Value | Source |
|---|---|---|
-| `MNP_VERSION` | `5.0` | `meshbay_common/__init__.py` |
+| `MNP_VERSION` | `6.0` | `meshbay_common/__init__.py` |
| `MNP_MIN_SUPPORTED` | `4.0` | `handshake.py` |
| `MNP_AUD` / `HUB_API_AUD` | `meshbay:mnp` / `meshbay:hub-api` | `tokens.py` |
| MNP token lifetime | 900 s | `meshbay-hub/auth.py` (`issue_mnp_token`) |