aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-10-01 10:34:26 +0200
committerChristophe Besson <cbesson@gmail.com>2026-10-01 10:34:26 +0200
commit15e117673d2303bf476d4f78699e47913ce1aec0 (patch)
treec70655684c1b0c41b5aeeb18ae4e4843ac72e375 /docs
parentcf4fdda3523015248b3ff1f3e928ce9e69cc5a12 (diff)
downloadmeshbay-15e117673d2303bf476d4f78699e47913ce1aec0.tar.gz
fix(node): one member holds a share of the node, sized past real use
128 peer sessions on the node, at most 64 per account (the hub names the account with each offer; the node's own account is not counted). One account plays at most half the stream slots, rounded up, and runs two subtitle extractions at once. Frames after the handshake are 8 MiB (was 64), decoded with per-container bounds, and a frame refused for either ends the session instead of jamming its buffer (F-16). Sized for the heaviest real member: twenty groups on one node, three devices and a tab, up to 52 sessions. Measured: ~0.15 MiB and one fd per idle session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'docs')
-rw-r--r--docs/MESHBAY_DESIGN.md11
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md13
2 files changed, 17 insertions, 7 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 609c751..1f2e43d 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -1898,7 +1898,7 @@ Node page:
| `invite_ttl_hours` | 168 | how long a member invitation stays valid. Not an invitation link, which is fixed at seven days (§3.4) |
| `pair_ttl_hours` | 24 | how long an operator pairing code stays valid |
| `device_request_ttl_minutes` | 60 | how long a device request waits for approval. Comfort, not security: the code is bound to the keys by its hash |
-| `max_concurrent_streams` | 8 | simultaneous video streams. One process per viewer, ~50 MB each; a slot is held for the length of a film, so this counts viewers |
+| `max_concurrent_streams` | 8 | simultaneous video streams. One process per viewer, ~50 MB each; a slot is held for the length of a film, so this counts viewers. One account plays at most half of them, rounded up — three screens in one home fit, and no member alone takes every slot — and runs at most two subtitle extractions at once, which take the same slots |
| `max_concurrent_downloads` | 8 | node-wide download leases (§5.5) |
| `max_concurrent_uploads` | 8 | node-wide upload leases. A separate pool from downloads, because the two cost different things and one queue for both makes each cap meaningless |
| `max_upload_gb` | 8 | the largest single upload, §6.4. Fractions are allowed, and it is read per chunk so a change reaches an upload already running |
@@ -2020,8 +2020,13 @@ refused offer is indistinguishable, to the reader, from a node that is down:
connected above all, fails at once, so a dead node costs no time.
The node bounds its own total separately (`MAX_PEER_SESSIONS` in
-`webrtc_server.py`), which is the limit that protects the machine whatever the
-number of members.
+`webrtc_server.py`, 128), which is the limit that protects the machine whatever
+the number of members, and **one account holds at most half of it** (64, from
+the account the hub names with each offer). The heaviest real member — twenty
+groups on one node, three devices and a spare tab — holds up to 52: each device
+keeps up to twelve connections for search and music, plus the open group page.
+The node's own account is not counted; it is the operator's machine. An idle
+connected session costs about 0.15 MiB and one file descriptor.
**A node registered for no group shares one with nobody**, and is refused rather
than exempted. Written as "check membership if the node claims any group", the
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index 9e51dc9..676b96f 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -102,7 +102,8 @@ Identical on every transport:
| Bound | Value | Where |
|---|---|---|
| Max frame **before** the client's group-key proof | 64 KiB | `PRE_HANDSHAKE_MAX_MSG` |
-| Max frame **after** the proof | 64 MiB | `MAX_MSG` |
+| Max frame **after** the proof | 8 MiB | `MAX_MSG` — eight times the largest message a client sends (a sealed playlist blob, 1 MiB) |
+| Decoding a frame | arrays 100 000, maps 10 000, strings 1 MiB, binaries 8 MiB, no extension types | `UNPACK_LIMITS` |
| File chunk (plaintext) | 1 MiB | `CHUNK_SIZE` |
| Video segment (plaintext, before encryption) | 256 KiB | `STREAM_SEGMENT_SIZE` |
| Upload chunk sent by the browser | 48 KiB | fits the aiortc SCTP limit after msgpack overhead |
@@ -115,6 +116,10 @@ into it, holding that much memory per connection for as long as it liked; a hund
such connections is the node's memory, from peers that have proved nothing. Exceeding
the limit is a hard protocol error and the buffer raises rather than truncating —
truncating would hand a parser a valid-looking prefix of something it never received.
+A frame refused for its size or its decoding ends the session: the buffer still starts
+with it, so nothing after it could be read. The decoding bounds exist because a frame of
+many tiny elements decodes to many times its size in memory; with them, an 8 MiB frame
+costs tens of megabytes at worst.
### 3.3 Versioning field
@@ -245,7 +250,7 @@ answer, and a discriminator of the message's own (`file_id`, `url`, `upload_id`,
v
+-------------------------+
| AUTHENTICATED | `_user_id` / `_group_id` set,
- | full message set | frame limit raised to 64 MiB
+ | full message set | frame limit raised to 8 MiB
+-----------+-------------+
| channel closes
v
@@ -416,7 +421,7 @@ fails if a transport skips a step.
| compare_digest(proof); |
| roster admits the user |
| 9. session authenticated: |
- | frame limit -> 64 MiB, |
+ | frame limit -> 8 MiB, |
| peer registry, audit |
| |
| 10. handshake_ack |
@@ -2422,7 +2427,7 @@ LP(x) = uint32be(len(x)) || x every field, no exceptions
| Device attempts | 5 per connection | ” |
| `MAX_DEVICES_PER_USER` | 5 | `roster.py` |
| `MAX_LINK_INVITES_PER_GROUP` | 20 unredeemed invitation links | `roster.py` |
-| `PRE_HANDSHAKE_MAX_MSG` / `MAX_MSG` | 64 KiB / 64 MiB | `webrtc/core.py` / `webrtc/limits.py` |
+| `PRE_HANDSHAKE_MAX_MSG` / `MAX_MSG` | 64 KiB / 8 MiB | `webrtc/core.py` / `webrtc/limits.py` |
| `CHUNK_SIZE` | 1 MiB | `webrtc/limits.py` |
| `DOWNLOAD_BUFFER_HIGH` | 2 MiB | `webrtc/files.py` |
| `MAX_UPLOAD_BYTES` | 8 GiB, default only — `max_upload_gb` overrides it per node | `webrtc/upload_handlers.py` |