feat: Phase 3 — E2EE calls, Jitsi conferencing, help tab, web security model

- LiveKit call E2EE: CallE2EEManager exchanges encryption keys via Matrix
  to-device events (m.rtc.encryption_keys) for interop with Element X
- Olm bootstrapped in index.html before Flutter init; main.dart logs result
- Encrypted messages shown with lock icon and informative fallback text
- Profile screen: key restore dialog + security setup (cross-signing/backup)
- Jitsi feature: welcome screen (public, no login), conference tab, full-screen
  embed via JitsiMeetExternalAPI, JitsiLink parser for all common link formats
- Help tab: expandable cards for encryption, video calls, account management
- Web security model: no session persistence — device ID only across visits
- Media auth: MSC3916 authenticated endpoint for avatars (Synapse 1.120+)
- Router: welcome route as public landing page; jitsi route as public
- Manifest/index.html: M8Chat branding, dark theme colours

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 05:28:14 +10:00
parent a241d0a7ab
commit b95471b0b4
27 changed files with 2076 additions and 226 deletions

View File

@@ -18,12 +18,12 @@
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta name="description" content="M8Chat — secure chat and conferencing.">
<!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="m8chat_app">
<meta name="apple-mobile-web-app-title" content="M8Chat">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
@@ -49,12 +49,43 @@
}
</script>
<!--
Olm (E2EE) disabled for Phase 1 web builds. The Matrix SDK gracefully
skips encryption when Olm is not loaded — unencrypted rooms work fine.
E2EE requires persistent Olm account storage (IndexedDB) to avoid
device key conflicts on /keys/upload (400). Deferred to Phase 3.
The olm.js and olm.wasm files remain deployed for future use.
Olm (E2EE): load olm.js synchronously, await Olm.init() for WASM, THEN
inject flutter_bootstrap.js so the Matrix SDK finds global Olm ready.
If Olm fails to load (e.g. WASM blocked by browser), Flutter boots anyway
without encryption — unencrypted rooms still work.
-->
<script src="flutter_bootstrap.js" async></script>
<script src="olm.js"></script>
<script>
(function() {
function bootFlutter() {
var s = document.createElement('script');
s.src = 'flutter_bootstrap.js';
s.async = true;
document.body.appendChild(s);
}
// Ensure Olm is accessible on both window and globalThis.
// dart2js may resolve bare 'Olm' via different global references.
if (typeof Olm !== 'undefined') {
window.Olm = Olm;
if (typeof globalThis !== 'undefined') globalThis.Olm = Olm;
if (typeof self !== 'undefined') self.Olm = Olm;
}
if (typeof Olm !== 'undefined' && Olm.init) {
console.log('[M8Chat] Olm.js loaded, calling Olm.init()...');
Olm.init().then(function() {
console.log('[M8Chat] Olm.init() SUCCESS — E2EE available');
console.log('[M8Chat] window.Olm =', window.Olm);
console.log('[M8Chat] typeof Olm.Account =', typeof Olm.Account);
bootFlutter();
}).catch(function(e) {
console.warn('[M8Chat] Olm init FAILED, booting without E2EE:', e);
bootFlutter();
});
} else {
console.warn('[M8Chat] olm.js did NOT load — typeof Olm =', typeof Olm);
bootFlutter();
}
})();
</script>
</body>
</html>