diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 325a3c5..2e46d1a 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1770,6 +1770,29 @@ share an active group with the target node**. Otherwise any authenticated user could make a third party's machine allocate peer connections on demand (**H6**). The address in a NAT-punch request must match the caller's source address. +**The offer ceilings bound a member and never an ordinary one** (**AV30**). They +are sized against an account with twenty groups on three devices, because a +refused offer is indistinguishable, to the reader, from a node that is down: + +- **Per account and per node, a budget**: a burst of 120 offers, refilled at two a + second (`OFFER_BURST`, `OFFER_REFILL_PER_S`). This is what bounds a member's cost + to one machine, and it spares the others — an account that has spent it on one + node still reaches every other. It is counted by account, not by address, + because a mobile carrier puts many subscribers behind one IPv4 address. +- **Per account, 32 offers pending** at once, across devices and nodes — an offer + is pending for the node's round trip, or up to the fifteen-second answer timeout + when a connected node is silent. +- **Per address and per node, 600 a minute**: a coarse guard in front of + authentication, and nothing more. +- **Every refusal carries `Retry-After`, and a client sends the same offer again** + on 429, 502 or 503 — the hub busy or restarting — with growing, jittered waits + (`postOffer` in `transport.js`). Any other failure, 404 for a node that is not + 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. + **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 rule skipped itself — membership, group status and the public-group gate @@ -2731,11 +2754,21 @@ still lets silent nodes hold every place at once. The ordering is advisory — storage that refuses, a private window or a first visit all fall back to the hub's own order, which costs that one sweep and then corrects itself. +**Search reaches each group with one connection, through one pool.** The sweep, +the warm-up and a tile asking for its group all go through `ConnectionPool`, +which negotiates at most six at once (`MAX_IN_FLIGHT`) and keeps what the sweep +opened for the tiles, up to its size; a group past that connects when a tile of +it scrolls into view. So a visit costs one offer per group, a refresh costs none +for a connection that answers a short ping, and a connection whose index is being +read is never the one evicted. Separate ceilings for each of these add up behind +each other's backs, past what the hub admits per account (§7.2). + **Search dials many nodes at once, so its deadline measures stalling rather than elapsed time.** Opening one group from the sidebar waits out the transport's own -budgets; Search opens up to a poolful, three at a time, and waits for the slowest -of each batch — so the deadline it imposes is what a page of unreachable groups -costs in spinner, and raising it to accommodate a slow link taxes every dead one. +budgets; in Search a dead group holds one of the pool's places for the deadline — +so the deadline is what a page of unreachable groups costs in spinner, and +raising it to accommodate a slow link taxes every dead one. A hub that refuses an +offer for load is answering, and each retry counts as progress. A node that is not there reports no progress and fails in one window. A node that answers ICE, then opens a channel, buys another window at each step, to a ceiling, because a deadline that only ever resets has none. A flat budget cannot tell a @@ -3119,6 +3152,7 @@ had already been asked. | **AV27** | **A free-text third-party search is bounded per member and per node** (§6.5). `tmdb_search_req` spends the *operator's* credential, which TMDB rates and the whole group's automatic matching depends on, so one member holding a search box degrades the library for everyone. Per member and not per connection — three tabs is one person — and kept in the group context so a reconnect does not reset it. The refusal is an error, because an empty result list is what "no such film" looks like | | **AV29** | **An invitation link is bounded on both halves and its mail on the sender** (§3.4, §7.3). Twenty outstanding per group on the node (bearer codes) and on the hub (tickets); and because a link mail reaches an address the hub has no relationship with, at the request of anyone who owns a group, it is counted **per sending account per day** (`mail.invite_link_daily_cap`, 10), under the recipient and instance bounds and outside the recovery reserve (`invite_link` is not a recovery purpose) | | **AV28** | **How many node keys one account may announce is bounded** (§7.2). Each is a row plus an IP-log row under a one-year retention, so an account in a loop writes a year of storage on the operator's disk having paid only for signatures. Proof of possession (**M8**) settles whose key it is and not how many. Counted only where a row is added: re-announcing a key already held keeps working at the ceiling, or a node that reached it could never refresh its address again | +| **AV30** | **What one member's offers cost a node is bounded per account and per node, and the bound admits the heaviest ordinary account** (§7.2). Each offer makes the node allocate a peer connection. A budget of 120 per node refilled at two a second bounds a member there without touching their other nodes, and it is counted by account because a mobile carrier shares one IPv4 address among many subscribers. Pending offers are capped at 32 per account. Both refusals carry `Retry-After` and the client retries them, because a refused offer otherwise reads as a node that is down | ### 13.6 Chat design findings |