| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A node that hosts no groups sends no `group_ids` on its hub socket, and the
hub resolved the claim with `set(claimed_groups or authorized)` — so "I host
nothing" arrived as "I host every group this account belongs to", other
members' included. Such a node can serve none of them: it holds no GEK, and
its own handshake refuses them with "Group not hosted on this node".
`/v1/groups/{id}/nodes` answers in registration order and `_node_groups` is
in-memory, so which node a client was sent to depended on who reconnected
first after a hub restart. GroupPage took `nodes[0]` with no fallback. On
2026-09-11 a hub deploy at 20:14 reshuffled the registry, a second member's
unconfigured node won the race, and a group stopped opening for everyone in
it with its only real host online throughout. Any member could take one of
their groups down, by accident, by leaving an empty node running.
Four changes, because no one of them is sufficient:
- the hub never widens an absent claim, and `update_groups` goes through
the same ceiling as registration — it assigned its list verbatim, so the
bound that makes C2 hold at authentication was one message wide
- the node states the empty set rather than omitting the field
- the refusal carries `not_hosted`, so a client can tell "try the next
node" from "you, here, must do something first"
- GroupPage walks the list instead of indexing into it
The three lines involved date from 13, 20 and 23 August and each is
defensible alone. The defect is in the seam, which is where the last two
also were: a falsy empty collection must never mean "unspecified".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T4YmK41VsEURWFdop4EEeT
|
|
|
MESHBAY_NODE_PROTOCOL.md and playlists.md move into the repository, where a
reader can follow them.
The protocol reference was written against the tree before the cleanup landed
and described three things that no longer exist. Corrected here rather than
left to be discovered:
* member_upload is gone from the handshake ack and from the message
catalogue. Whether a member may write is a property of each root, and a
summary field beside the authoritative one is a second source for one
question — whichever the reader consults first decides it.
* video_root, audio_root and photo_roots are gone from the ack, the signed-op
table and the message reference. app_directories is not "the general form"
of three narrower ops any more; it is the only one. What survives is their
storage key on the node, because that is a key on an operator's disk rather
than on the wire, and a node upgraded into this has to find its own
configuration.
* The sender-key implementation the chat section pointed at has been deleted,
along with the ratchet. The argument for deriving a key per device stands on
its own now instead of pointing at a module to compare against.
playlists.md is added as written — a design for a feature that is not built, and
the first one to need per-account state spanning several groups on several
nodes. Its decided shape is summarised in MESHBAY_DESIGN.md section 9.10.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVoHVCcfBqud6ZjG4db3y7
|