aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_NODE_PROTOCOL.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/MESHBAY_NODE_PROTOCOL.md')
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md52
1 files changed, 48 insertions, 4 deletions
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index f3fcbb9..47c3636 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -1,6 +1,6 @@
# MeshBay Node Protocol (MNP)
-**Wire version:** `3.0` — `meshbay_common/__init__.py` (`MNP_VERSION`)
+**Wire version:** `3.1` — `meshbay_common/__init__.py` (`MNP_VERSION`)
**Oldest peer accepted:** `3.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`,
@@ -620,6 +620,46 @@ nodes.
operator who cracks the copy on their own disk gets a key that opens nothing
anywhere else.
+### 7.1a Per-account blobs (MNP 3.1)
+
+The same shape as a keypair bundle with a different payload — playlists today
+(`docs/playlists.md` §8). The node stores bytes it cannot read for an account it
+already holds a bundle for, so this adds **no new trust boundary**.
+
+```
+ C N
+ |-- user_blob_list {v} ---------------------------->| which kinds exist here
+ |<- user_blob_list_resp {v, blobs: [{kind, rev}]} --| revisions only, no payload
+ | |
+ |-- user_blob_fetch {v, kind} ---------------------->|
+ |<- user_blob_resp {v, kind, rev|null, |
+ | blob_enc|null} -------------------------------|
+ | |
+ |-- user_blob_store {v, kind, rev, blob_enc} ------->|
+ |<- ack {v, detail: "user_blob_stored"} ------------|
+ | |
+ |-- user_blob_delete {v, kind} --------------------->|
+```
+
+* **`kind` is a namespace, validated against a pattern**: `playlists` is the
+ manifest, `playlist:<id>` is one playlist's tracks. That is what lets one
+ playlist be rewritten without re-uploading the whole collection, and it is a
+ pattern rather than "anything" so the table does not become a key/value store
+ for whatever a client feels like writing.
+* **`blob_enc` is msgpack `bin`, not base64.** These run to hundreds of
+ kilobytes, where base64 is a third of every write.
+* **`user_id` comes from the authenticated session, never from the message.** A
+ `user_id` in the body would let any member read or overwrite any other
+ member's blob.
+* **Caps refuse, never truncate**: 64 KB for the manifest, 1 MB for one body,
+ 8 MB per account per node, each with a stated reason. A truncating cap loses
+ tracks silently, which is the failure the design exists to prevent.
+* A `fetch` for a kind never written answers `null`, not an error: that is the
+ ordinary state of a node the reader has just joined.
+* The node keeps **no history**. The client is the authority on which revision
+ is current and holds its own copy; a node keeping older revisions would mean
+ the node deciding, which is exactly what it must not do.
+
### 7.2 Wrapped group key
```
@@ -1840,6 +1880,10 @@ it back (§3.5).
| `keypair_bundle_fetch` / `_resp` | C→N / N→C | pre | the caller's encrypted identity bundle |
| `keypair_bundle_store` | C→N | auth | back up (or re-wrap) that bundle |
| `keypair_bundle_delete` | C→N | auth | withdraw the backup |
+| `user_blob_store` | C→N | auth | write one per-account blob (playlists) |
+| `user_blob_fetch` / `user_blob_resp` | C→N / N→C | auth | read one, or `null` |
+| `user_blob_list` / `user_blob_list_resp` | C→N / N→C | auth | which kinds, at what revision — never a payload |
+| `user_blob_delete` | C→N | auth | drop one |
| `gek_bundle_fetch` / `_resp` | C→N / N→C | pre | the caller's wrapped GEK |
| `join_request` / `join_result` | C→N / N→C | pre | pin or recognise an identity; wrap the GEK |
| `invite_create` / `invite_result` | C→N / N→C | signed | issue a one-time pairing code |
@@ -1942,8 +1986,8 @@ message:
## 13. Versioning and compatibility
-MNP versions independently of the package version. Current: **`3.0`**; oldest peer
-accepted: **`3.0`**.
+MNP versions independently of the package version. Current: **`3.1`**; oldest peer
+accepted: **`3.0`** — 3.1 is additive, so the floor does not move with it.
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
@@ -2133,7 +2177,7 @@ LP(x) = uint32be(len(x)) || x every field, no exceptions
| Constant | Value | Source |
|---|---|---|
-| `MNP_VERSION` | `3.0` | `meshbay_common/__init__.py` |
+| `MNP_VERSION` | `3.1` | `meshbay_common/__init__.py` |
| `MNP_MIN_SUPPORTED` | `3.0` | `handshake.py` |
| `NONCE_LEN` | 32 bytes (both handshake nonces) | `handshake.py` |
| `ADMIN_CHALLENGE_TTL` | 120 s | `adminop.py` |