diff options
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/MESHBAY_DESIGN.md | 8 | ||||
| -rw-r--r-- | docs/MESHBAY_NODE_PROTOCOL.md | 9 |
2 files changed, 14 insertions, 3 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md index 78a3261..d2bec10 100644 --- a/docs/MESHBAY_DESIGN.md +++ b/docs/MESHBAY_DESIGN.md @@ -1046,6 +1046,14 @@ requester and it therefore grants nothing across accounts. - The denylist is consulted for user, `jti` **and** group. - The node **refuses connections when it holds no group key** — there is no `gek_required: false` bypass (**NS8**). +- **The roster must admit the account for the group** before a session opens, + after the proof and whatever the token says (`not_authorized_for_group`). The + key proves possession, the token proves the hub's view of membership, and + neither is the node's own answer: without this, a member revoked or unpinned + here but still on the hub kept a full session with the key they held, and was + handed the chat epoch their removal had just opened. A removal, from any door + (MNP, the node page, the CLI), also opens a new chat epoch in each group the + person could read and closes every connection they hold. **Refusals carry a code**, not only a sentence, because a client can act on a code. `not_a_member` means the hub did not count the account a member when it minted the diff --git a/docs/MESHBAY_NODE_PROTOCOL.md b/docs/MESHBAY_NODE_PROTOCOL.md index 70047b2..47f508b 100644 --- a/docs/MESHBAY_NODE_PROTOCOL.md +++ b/docs/MESHBAY_NODE_PROTOCOL.md @@ -413,7 +413,8 @@ fails if a transport skips a step. |-------------------------------------------------------------->| | 8. rebuild binding; | | refuse if empty; | - | compare_digest(proof) | + | compare_digest(proof); | + | roster admits the user | | 9. session authenticated: | | frame limit -> 64 MiB, | | peer registry, audit | @@ -480,6 +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 | `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 @@ -2294,8 +2296,9 @@ walks through the gate meant to stop it. filename and no path anywhere in them (§11.1a). * **Rotation is the only thing that removes access.** Revoking a member stops the node serving the next key; the current key and anything already downloaded stay readable. - A chat epoch is opened at the same time, which stops them reading what is said next — - not what was said before, which they could already read. + A chat epoch is opened at the same time, and their connections are closed and refused + from then on (the handshake consults the roster), which stops them reading what is + said next — not what was said before, which they could already read. --- |