Defer key bundle downloads on server-initiated joins (MSC4509) - #5426
Defer key bundle downloads on server-initiated joins (MSC4509)#5426ara4n wants to merge 4 commits into
Conversation
|
@ara4n where can we read about this |
|
msc4509 it appears. Please can such unspecced features be gated behind some sort of opt-in from the application (which in turn should gate it behind a labs flag) |
If the server joins us to a room we were invited to (e.g. auto-accepting a knock, synapse#16307), joinRoom never runs, so the MSC4268 key bundle sent by the inviter was silently dropped ('Not yet accepting key bundle for room where we are not awaiting a bundle') and pre-join history stayed undecryptable. Watch our own invite->join transitions and run the same markRoomAsPendingKeyBundle/maybeAcceptKeyBundle dance as joinRoom.
An accepted knock can be auto-joined straight past the invited state (the invite coalesced out of sync), and the invite-time record is in-memory only, so recover the inviter from the join event's unsigned.prev_sender — the same Synapse extension getDMInviter already relies on. Mirrors the equivalent rust-sdk fix.
A server-initiated join (e.g. an auto-accepted knock) lands on all of our user's devices in the same sync instant, so each would eagerly download the MSC4268 room key bundle. Instead, only download it when the server designates this device as the claimer (a new org.matrix.msc4509.key_bundle_claim to-device message, only trusted from our own user), or lazily upon the first undecryptable event in the room. Client-initiated joins via joinRoom keep the eager behaviour. The MSC4509 behaviour is unstable, so it is opt-in via the new unstableMSC4509KeyBundleClaim client option: when unset (the default), key_bundle_claim messages are ignored and server-initiated joins keep the eager per-device download.
0b22fe2 to
3176257
Compare
Covers the eager (default) path incl. the unsigned.prev_sender fallback, and the MSC4509 deferral: designation claims, designations racing ahead of the join, the undecryptable-event fallback, sender/room_id validation, leave cleanup, and the error paths.
| /** Map from room ID to the user who invited us, for rooms with a pending invite. | ||
| * Used to do the key-bundle bookkeeping if the server joins us to the room itself | ||
| * (e.g. auto-accepting a knock, synapse#16307) rather than via `joinRoom`. */ | ||
| private readonly pendingKeyBundleInviters = new Map<string, string>(); | ||
|
|
||
| /** Map from room ID to inviter, for rooms the server joined us to whose key bundle | ||
| * we are deferring (MSC4509): the bundle is only downloaded if the server designates | ||
| * us as the claimer, or lazily on the first undecryptable event in the room. */ | ||
| private readonly lazyKeyBundleRooms = new Map<string, string>(); | ||
|
|
||
| /** Room IDs for which an `org.matrix.msc4509.key_bundle_claim` designation arrived | ||
| * before we processed the corresponding join. */ | ||
| private readonly earlyKeyBundleClaims = new Set<string>(); |
There was a problem hiding this comment.
MatrixClient is already well over 7000 lines long and has far too many properties. Please can we move all the logic and these fields out to a different class, which can be gated as whole behind unstableMSC4509KeyBundleClaim?
| // MSC4509: the server may designate this device as the one which should eagerly | ||
| // download a room key bundle after a server-initiated join (see above). Only our | ||
| // own homeserver can deliver a to-device message with our own user as sender. | ||
| this.on(ClientEvent.ToDeviceEvent, (event) => { |
There was a problem hiding this comment.
As Sonar points out, ToDeviceEvent is deprecated.
| // MSC4509 disabled: download eagerly, as `joinRoom` would. | ||
| await cryptoBackend.maybeAcceptKeyBundle(member.roomId, finalInviter); |
There was a problem hiding this comment.
Let's not sneak this change of behaviour in as part of implementing an unstable MSC.
| * If the room's join hasn't been processed yet (a designation racing ahead of the | ||
| * join in the same sync), remember the designation for the join handler instead. | ||
| */ | ||
| private claimLazyKeyBundle(roomId: string, reason: string): void { |
There was a problem hiding this comment.
This is a somewhat misleading name, given it won't actually "claim" the bundle if we haven't been invited yet.
| this.logger.info(`Claiming deferred key bundle for ${roomId}: ${reason}`); | ||
| cryptoBackend.maybeAcceptKeyBundle(roomId, inviter).catch((e) => { | ||
| this.logger.error(`Error accepting deferred key bundle for room ${roomId}:`, e); | ||
| }); |
There was a problem hiding this comment.
Presumably this means that if the attempt to download the bundle fails (eg the server gives a 500), we'll never actually do the download? Is that a known problem?
| if (member.membership === KnownMembership.Invite) { | ||
| const inviter = event.getSender(); | ||
| if (inviter) this.pendingKeyBundleInviters.set(member.roomId, inviter); |
There was a problem hiding this comment.
This seems like it will fail in the case of a gappy sync, since we might not see the invite event?
A server-initiated join (e.g. an auto-accepted knock) lands on all of our user's devices in the same sync instant, so each would eagerly download the MSC4268 room key bundle. Instead, only download it when the server designates this device as the claimer (a new org.matrix.msc4509.key_bundle_claim to-device message, only trusted from our own user), or lazily upon the first undecryptable event in the room. Client-initiated joins via joinRoom keep the eager behaviour.
The MSC4509 behaviour is unstable, so it is opt-in via the new
unstableMSC4509KeyBundleClaimclient option: when unset (the default), key_bundle_claim messages are ignored and server-initiated joins keep the eager per-device download. Element Web enables it behind ashare_history_on_autojoinfeature flag (PR to follow).