diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-30 12:00:58 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-30 12:00:58 +0200 |
| commit | 1684fcb64531eaf2e9bb8567ba4bdcbada6469d8 (patch) | |
| tree | e6f5135087323d9e7a0b5e47bc93118c0db4df4d /docs/MESHBAY_NODE_PROTOCOL.md | |
| parent | 5612dbbac41609b3f84784f57f1de538262db9a9 (diff) | |
| download | meshbay-1684fcb64531eaf2e9bb8567ba4bdcbada6469d8.tar.gz | |
docs: state hosting, invitations, the roster check and link previews as they are
Present-tense statements of what holds, in place of before/after phrasing:
host designation (§7.2, AV32), invitations (§3.4, §7.3, AV33), the roster
check at the handshake (§5.2, protocol §6.3), the wrong-code lock (protocol
§8.3), and what the link-preview gate does and does not refuse (§6.5).
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.md | 10 |
1 files changed, 6 insertions, 4 deletions
diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md index 47f508b..b760354 100644 --- a/docs/MESHBAY_NODE_PROTOCOL.md +++ b/docs/MESHBAY_NODE_PROTOCOL.md @@ -481,7 +481,7 @@ absent. `verify_proof` compares with `hmac.compare_digest`. | not on the denylist for `user_id`, `jti` **or** `group_id` | `Token revoked` | all three targets, and persisted to disk: a revocation that a restart forgets is not one | | `group_id ∈ token.groups` | `Not a member of this group`, code `not_a_member` | the membership check itself — a token is proof of an account, never of a group | | `group_id ∈ node.hosted_groups` | `Group not hosted on this node`, code `not_hosted` | the hub may hand a client several nodes for one group, and only some of them host it | -| *(after the proof)* the roster admits `sub` for `group_id` — an active member row, or the node-wide operator row | `This node has not admitted you to this group`, code `not_authorized_for_group` | the key proves possession and the token the hub's view; the node's own answer is the roster. Without it, someone revoked here but still a hub member kept a session with the key they held | +| *(after the proof)* the roster admits `sub` for `group_id` — an active member row, or the node-wide operator row | `This node has not admitted you to this group`, code `not_authorized_for_group` | the key proves possession and the token the hub's view; the node's own answer is the roster. Someone revoked here but still a hub member, holding the key, is refused a session | `AuthorizedPeer` carries `user_id`, `group_id`, `username`, `jti` — and deliberately **no user public key**. `username` is read from a `username` claim that neither the MNP @@ -494,12 +494,14 @@ node's roster. The hub certifies accounts, not keys. minted the token. The MNP token is minted for each connection and names **only** that connection's group (`POST /v1/nodes/mnp-token {node_pk, group_id}`; `groups` is empty for a non-member), so the operator it is handed to learns nothing of the member's -other groups. A stale `groups` claim is therefore no longer the usual cause; the +other groups. A stale `groups` claim is therefore not the usual cause; the client still refreshes its session once and retries on that code before telling someone who was just invited that they are not a member. `not_hosted` is the client's signal to try the **next** node the hub offered for the -group rather than to report a failure. `/v1/groups/{id}/nodes` returns every node +group rather than to report a failure. The hub offers only nodes whose account owns +the group or that the group's owner approved as hosts — a member's node holds the +group key and would pass this handshake like the real host. `/v1/groups/{id}/nodes` returns every node registered for the group, in hub registration order, and that order is not a ranking: a node listed first is not necessarily one that holds the group's files. Refusing without a code made this indistinguishable from a refusal the reader has to act on, @@ -834,7 +836,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 | +| 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 applied before recognition would 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) | |