From c330900a3ab7d4851d269086f2488dc8b1d8b1b6 Mon Sep 17 00:00:00 2001 From: Christian Gick Date: Sun, 22 Feb 2026 08:23:17 +0200 Subject: [PATCH] fix(voice): wait for key rotation via nio sync, not HTTP fetch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io.element.call.encryption_keys events are Megolm-encrypted in this room (appear as m.room.encrypted). The HTTP fetch cannot decrypt them — only the nio sync client can via Olm/Megolm decryption. Change the post-connect rotation poll to check self._caller_all_keys directly (updated by on_encryption_key() via nio sync) instead of calling _fetch_encryption_key_http() which always returns nothing in encrypted rooms. Also extends wait to 10s and adds progress logging every 2s. Co-Authored-By: Claude Sonnet 4.6 --- voice.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/voice.py b/voice.py index a0b1076..a54734f 100644 --- a/voice.py +++ b/voice.py @@ -258,22 +258,23 @@ class VoiceSession: len(self.lk_room.remote_participants)) # Element Call rotates its encryption key when bot joins the LiveKit room. - # We fetched the pre-join key above; now wait for EC to generate and publish - # the post-join rotated key via Matrix timeline. + # EC sends the new key via Matrix (Megolm-encrypted); nio sync will decrypt it + # and call on_encryption_key(), which updates self._caller_all_keys. + # NOTE: HTTP fetch is useless here — keys are Matrix-E2EE encrypted (m.room.encrypted). pre_max_idx = max(self._caller_all_keys.keys()) if self._caller_all_keys else -1 - logger.info("Polling for EC key rotation (pre-join max_idx=%d)...", pre_max_idx) - for _attempt in range(10): # poll up to 5s (10 × 0.5s) + logger.info("Waiting for EC key rotation via nio sync (current max_idx=%d)...", pre_max_idx) + for _attempt in range(20): # up to 10s (20 × 0.5s) await asyncio.sleep(0.5) - await self._fetch_encryption_key_http() new_max = max(self._caller_all_keys.keys()) if self._caller_all_keys else -1 if new_max > pre_max_idx: self._caller_key = self._caller_all_keys[new_max] logger.info("Key rotated: index %d→%d (%d bytes)", pre_max_idx, new_max, len(self._caller_key)) break - logger.debug("Key rotation poll %d: max_idx still %d", _attempt + 1, new_max) + if _attempt % 4 == 3: # log every 2s + logger.info("Key rotation wait %ds: max_idx still %d", (_attempt + 1) // 2, new_max) else: - logger.warning("No key rotation after 5s — using pre-join key[%d]", pre_max_idx) + logger.warning("No key rotation after 10s — using pre-join key[%d]", pre_max_idx) # Set per-participant keys via key provider kp = self.lk_room.e2ee_manager.key_provider