aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/MESHBAY_DESIGN.md4
-rw-r--r--docs/MESHBAY_NODE_PROTOCOL.md6
2 files changed, 6 insertions, 4 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 2efedca..6e90402 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -378,7 +378,9 @@ Four properties, each load-bearing:
account in one group — except an invitation link's, which names its account
when it is redeemed (below) — stored only as `sha256(code)`. A password KDF over 40
uniformly random bits would buy nothing. Guessing is bounded by 5 attempts per
- connection and a node-wide lockout, and every attempt is an audit event.
+ connection and by wrong-code limits per account and node-wide — consulted only
+ when a code is tried, never for a device the node already pinned — and every
+ attempt is an audit event.
3. **The node's roster is the authority**, not hub membership. A hub that invents
an account, adds it to a group and mints it a token gets
`not_authorized_for_group`.
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md
index ce99a40..2e5aca3 100644
--- a/docs/MESHBAY_NODE_PROTOCOL.md
+++ b/docs/MESHBAY_NODE_PROTOCOL.md
@@ -620,7 +620,7 @@ immediately after these three, so the table below is the window, exhaustively.
|---|---|---|
| `keypair_bundle_fetch` | the client's own identity keys for this node live in an encrypted bundle stored on it | counts against `MAX_PRE_PROOF_FETCHES` = 4; audited |
| `gek_bundle_fetch` | the wrapped group key is what the proof is computed with | same counter |
-| `join_request` | a first-time member holds no group key at all. Accepted after the proof as well — an operator pairing a browser is already connected — because its authority comes from the pairing code and the signature, never from the session state | 5 attempts per connection, 20 failures per 600 s node-wide |
+| `join_request` | a first-time member holds no group key at all. Accepted after the proof as well — an operator pairing a browser is already connected — because its authority comes from the pairing code and the signature, never from the session state | 5 attempts per connection; wrong codes: 5 per account and 20 node-wide per 600 s, consulted only when a code is tried |
Device linking (§9) is **not** in this window. `device_add_request` and every message
after it are answered only on an authenticated session, and the device budget of 5
@@ -822,7 +822,6 @@ Evaluated in order (`_do_join_request`):
| Condition | Outcome |
|---|---|
| `join_attempts >= 5` on this connection | `error: Too many attempts` |
-| `>= 20` node-wide failures in 600 s | `error: Pairing temporarily locked`, audited `join_throttled` |
| key not 32 raw bytes, or bad base64 | `join_result{ok:false, reason:"invalid_keys"}` |
| `\|ts - now\| > 120` | `stale_request` |
| `group_id` non-empty and != session group | `group_mismatch` |
@@ -831,6 +830,7 @@ Evaluated in order (`_do_join_request`):
| account has devices here, this key is not one | `unknown_device` — the way in is a device-add (§9), not a new invite |
| device known, no member row, group policy `open` | member row created (`approved_by: "open-join"`) |
| device known, a **pending invite** exists for this user | code required even for a known device; `code_required` / `code_invalid` on failure |
+| a code is about to be tried and this account has `>= 5`, or the node `>= 20`, wrong codes in 600 s | `error: Pairing temporarily locked`, audited `join_throttled`. Only `code_invalid` counts, and nothing else is gated by it: every member reconnecting gets the key through this message, so a lock checked before recognition let one member refuse it to everybody |
| device known, not an active member of the session group, a code offered | redeemed like any code — an invitation **link** reaches here from someone pinned through another group, or removed and invited back; `code_invalid` on failure |
| device known, member row resolved | `join_result{ok, recognised:true, role}` + wrapped GEK |
| unknown device, no code, policy `open` | pin TOFU, admit, wrap (`via: "tofu"`, audited) |
@@ -2366,7 +2366,7 @@ LP(x) = uint32be(len(x)) || x every field, no exceptions
| Code lifetimes (default, settable) | invitation 7 d, operator pairing 24 h, device request 1 h | `roster.py` |
| `MAX_PRE_PROOF_FETCHES` | 4 per connection | `webrtc/dispatch.py` |
| `MAX_JOIN_ATTEMPTS` | 5 per connection | `webrtc/admission.py` |
-| `MAX_JOIN_FAILURES_WINDOW` / `JOIN_FAILURE_WINDOW` | 20 / 600 s, node-wide | ” |
+| `MAX_JOIN_FAILURES_PER_ACCOUNT` / `MAX_JOIN_FAILURES_WINDOW` / `JOIN_FAILURE_WINDOW` | 5 per account / 20 node-wide / 600 s, wrong codes only | ” |
| Device attempts | 5 per connection | ” |
| `MAX_DEVICES_PER_USER` | 5 | `roster.py` |
| `MAX_LINK_INVITES_PER_GROUP` | 20 unredeemed invitation links | `roster.py` |