Files
synapse/tests
Kegan DougalandClaude Opus 5 cac8b6e343 Learn, verify and use MSC4430 member links
A member key names no server, so a room full of them is a room whose members
cannot be routed to, served to, or shown to anyone. What a key stands for is
the linkage information on its `m.room.member` event, and this teaches the
server to read it: another server's member can now join one of our member key
rooms over federation, and we learn who they are from the event that put them
there.

A member key's link is the linkage information on its *current*
`m.room.member` event, and nothing else. A member who has left has no link,
because the event that removed them was somebody else's and asserts nothing
about them. Making the mapping a function of the room's current state means
every server that has the state agrees on it, no history is needed to compute
it, and an event arriving late or by backfill cannot change it.

Two signatures decide what a claim is worth. The member key's own makes the
claim theirs, and without it the claim is ignored entirely: on an event the
member sent, the `sender` signature is already that; on an event somebody
else sent about them, it is the signature under `state_key`. The current
server signing key of the claimed domain makes the claim *verified*, and that
is the half that needs the network, so it is the half that can fail for
reasons that are nobody's fault: a domain that does not answer leaves the link
unverified rather than refused, and unverified links are re-checked on a small
bounded loop so that a member whose server was briefly unreachable does not
stay invisible until they next change their membership.

What a link is used for depends on which half holds. The claimed domain is
where the room's traffic goes, verified or not: an unverified claim is
somewhere to send to, and grants nothing. Only a verified link makes a server
a member server — able to be told the room's history, to be served its state,
to be treated as present in it — because anybody can claim anybody's domain.
Events from a key with no verified link are soft failed, so their state
changes still count (membership and power levels are decided by keys, not by
who holds them) but their messages are not shown to clients.

Joining a room of this kind works from either end. A joining server does not
know the room's version until `/make_join` answers, so it cannot know whether
to name its user by ID or by key: it sends the user ID in the path and the key
in `org.matrix.msc4430.member_key`, which a server that has never heard of
MSC4430 ignores. The key is held in memory until the room turns out to want
it, so joining a room without member keys leaves nothing behind, and it is
stored before the join is sent, so a join that succeeds is never one we cannot
sign for afterwards. A resident server accepts a key in either place and
refuses a bare user ID for such a room. Because the sender of a membership
event is a key and not a user ID, the check that the sender belongs to the
requesting server no longer applies: the origin is simply who relayed it, and
what the member claims about their own server is in the event, signed by them.

The end-to-end tests build the remote half by hand, so that a server can be
made to vouch for its member, decline to, vouch for a different one, or lie
about somebody else's. They cover a verified join, an unverified one, a
re-check that succeeds on a later attempt, a member key that moves to another
server and takes its member with it, and a moderator who bans a member while
naming a domain they control and changes nothing about who that member is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-16 14:02:14 +02:00
..
2026-09-18 12:00:45 +01:00