Decouple ghost key-recipient enumeration from the shared bot Client (follow-up to #44) #50
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Follow-up to #44 (deferred, low priority).
#44 replaced the per-ghost full
matrix_sdk::Clientwith a send-onlyOlmMachine, but left one thread on the bridge bot's sharedClient:enumerate_key_recipients(crates/matrix-rtc/src/rtc/ghost.rs) still reads the room'sm.call.memberstate throughclient.get_room(...).get_state_events_staticto build the key-send recipient list.CallRecipients/distribute_key_taskthread thatClientin just for this read.This was deliberately deferred in #44 because that
clientis the shared, bridge-wide bot client (one/syncfor the whole bridge, driving the subscriber) — not a per-ghost FD/sync cost. So it does not affect #44's acceptance criteria (zero per-ghost crypto FDs, no per-ghost/sync). It's a decoupling/cleanliness item, not a scaling fix.Scope
Convert
enumerate_key_recipientsto read room state via the same raw CS-API masquerade the rest of the ghost path already uses:GET /_matrix/client/v3/rooms/{room_id}/statewithbearer_auth(as_token)+?user_id=<ghost_mxid>(the ghost is already joined), reusingcs_url.org.matrix.msc3401.call.member, deserialize eachcontentintoCallMemberEventContent, reuse the existingactive_memberships(...)->(sender, device_id)logic and theis_bridge_excluded_identity/bot_user_idexclusions unchanged.clientfield fromCallRecipientsand theClientarg fromdistribute_key_task.Why bother
Fully decouples the ghost media-key path from the matrix-rust-sdk
Client, so the ghost subsystem depends only on the appservice raw-HTTP surface. Minor, but tidy once #44 lands; not worth doing before #44 is live-verified.Optional, separate
#44's body also notes a per-room shared device-tracker (collapse
/keys/queryfrom O(ghosts) to O(1) per room). That is a different optimization and is already tracked in #44's description; not part of this issue.Shipped on master
e66eb1a(fast-forward ofrefactor/issue-50-recipient-decoupling).What changed (
crates/matrix-rtc/src/rtc/ghost.rsonly):enumerate_key_recipientsnow reads the room'sorg.matrix.msc3401.call.memberstate via a raw masqueradedGET /_matrix/client/v3/rooms/{roomId}/state(cs_url+bearer_auth(as_token)+?user_id=<ghost_mxid>— the ghost is a joined member), exactly as scoped.recipients_from_room_state(type filter → ghost/legacy-ghost/bot exclusions →active_membershipsper(user, device)→ dedup), with 3 new unit tests (fixtures built via the ruma constructors, so wire-shape parity is guaranteed): active-humans-only, expired/empty/redacted skipped, dedupe + legacy-format + malformed-event tolerance.CallRecipientsdrops itsClientfield (carries the ghost MXID instead);distribute_key_taskloses itsClientparam.run_ghostkeeps itsClient— still needed byinvite_ghost, which deliberately cannot masquerade (out of scope here, as the issue says).Err, whichdistribute_key_loopalready idles through.Verified: full workspace cargo test + clippy
-D warningsgreen (matrix-rtc 70 tests).Live-tested 2026-07-07 on staging, encrypted Dark Voice room (Olm transport, cold-start stores):
ghost media key sent recipients=["@dark:nether.im:ihjNlBVX90"] transport="olm" delivered=1on every send — the raw-state enumeration produced exactly the human(user, device), bot + ghosts excluded;reached NO recipient devicescount 0; membership-change re-key latency 316–493 ms (the #51 fast path intact); user confirmed Discord→Matrix audio decrypts in Element Call. Prod restored to alpha.13 baseline afterwards (all device ids reused).Unreleased — rides in the next tag. The optional per-room shared device-tracker stays tracked in #44's description.