rtc: Commet callers in encrypted rooms are silent toward Discord — their io.element.call.encryption_keys lacks member.id, the typed to-device handler drops it without a log #135
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
nether/nether-voicebridge#135
Loading…
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?
Observed 2026-09-27 22:44–22:47 UTC, prod, Dark Voice (
!kKwK98PeeZHpAGx8tx). @dark joined from "Phone - Commet" (devicemZ3kATp88n, Commet's experimental encrypted-call support). They heard Discord; Discord heard nothing from them.Evidence (prod journal)
LiveKit: E2EE state changed identity=@dark:nether.im:mZ3kATp88n state=MissingKey— and it never left that state.track subscribed before its peer media key — will re-subscribe when the key lands (#73)fired, but noreceived io.element.call.encryption_keys to-deviceand noinstalled peer media keyline for the whole session (both areinfo!).ghost media key sent … transport="olm" delivered=1for all 7 ghosts — hence they could hear us._@voicebridge_as:nether.im_dark-voice-9w6i5s4q_m.calland @dark's membership both well-formed; no key material posted as room events.member_id=<uuid>).Root cause
Commet's key sender (
commet/lib/client/matrix/components/voip_room/matrix_livekit_encryption_key_provider.dart,sendKeyToParticipants) emitswith no
member.id. OurEncKeyMember(crates/matrix-rtc/src/rtc/keys.rs) declaredid: String(required). matrix-sdk's typedadd_event_handlerskips the handler when content deserialization fails and logs only undermatrix_sdk::event_handler=warn, which prod'sRUST_LOGdoesn't enable → no install, no log, one-way audio. Element Call / matrix-js-sdk do sendmember.id(a UUID), which is why EC users work.member.idis only logged by us — the peer's LiveKit identity issender:claimed_device_id— so requiring it bought nothing.Fix (branch
fix/commet-encryption-keys-member-id)member.id→Option<String>(#[serde(default)]);sent_tsgets#[serde(default)]too (js-sdk marks itsent_ts?). Wire shape we send is unchanged (id: Some("{device_id}_m.call")).AnyToDeviceEvent + RawEventhandler re-parses anyio.element.call.encryption_keysevent and WARNs with the content's field names (never values) when it doesn't fit our type — so the next client-shape gap is loud.Residual / verification
Tuwunel prod logs record nothing for to-device sends at the current level, so "Commet actually emitted the event to the bridge device" is inferred from its source, not observed. After deploy, a Commet join should show
received io.element.call.encryption_keys to-device … member_id=-followed byinstalled peer media keyand the #73 heal line; if instead the new WARN fires, the shape diverges further; if neither fires, Commet didn't target our device (it only sends to devices in itsuserDeviceKeyscache).Upstream note for Commet tracked in the companion issue.
io.element.call.encryption_keysomitsmember.id(and other interop notes vs matrix-js-sdk) #136Live-verified on prod 2026-09-28 01:25 UTC, v0.3.7 — operator joined Dark Voice from Commet (desktop device
inMftF8sdw) and was heard on Discord.Journal, Dark Voice bridge (new pid, no WARN/ERROR):
member_id=-= the Commet event with nomember.id, now accepted. A ~3 sMissingKey→Okblip at 01:25:19–21 matches Commet's membership-triggered key rotation (new index used after its fixed 5 s delay) — self-healed, noted in #136. The tripwire WARN did not fire, so Commet's shape is otherwise within our type.Shipped in v0.3.7 (prod-deployed 01:02 UTC). Closing.