--- type: aar status: harvested date: 2026-08-11 related: [] --- # AAR — `@apo` konnte nicht telefonieren: fehlende Synapse-`profiles`-Zeile **Datum:** 2026-08-11 · **Beteiligt:** sorb + Mac-Session · **Stack:** Synapse, MAS, Authentik, Element Web / Element Call, MatrixRTC (K3s-Cluster) **Auftrag:** `@apo` kann sich anmelden und schreiben, aber **kein Call kommt zustande** — Grundursache finden und beheben, ohne weiter zu raten. ## 1. Ergebnis **Behoben und verifiziert:** - `@apo` telefoniert wieder. Grundursache belegt: dem Konto fehlte die Zeile in Synapses `profiles`-Tabelle. Fix war ein einzelnes `INSERT` der Registrierungs-Default-Zeile, an zwei gesunden Konten (`clark`, `calltest01`) gegengeprüft. - Gegenprobe nach dem Fix: `displayname` gesetzt (vorher keine Zeile), `open_id_tokens` **0 → 6**, aktives `org.matrix.msc3401.call.member` im Raum. - Dokumentiert: Runbook `docs/troubleshooting/CALLS-FEHLEN-PROFILE-ZEILE.md` im gitops-Repo (inkl. Index-Eintrag), Merksatz im Session-Gedächtnis. **Nebenbefund, separat behoben:** - **Kontoübernahme-Lücke:** Der MAS-Upstream-Provider stand auf `claims_imports.localpart.on_conflict: add` — bei Localpart-Kollision verknüpfte MAS die neue Upstream-Identität mit einem **bestehenden** Konto (inkl. Dienstkonten ohne Upstream-Link). Auf `on_conflict: fail` umgestellt (gitops `ef04d86`, nach git.lab gepusht), dokumentiert als [gitops#61](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/61), `priority:high`. Ausgelöst durch die live reproduzierte case-sensitive Dublette `boje`/`Boje`; das Zweitkonto `boje` (Authentik-ID 11) wurde gelöscht. **Deployment verifiziert:** Das SOPS-Values-Secret aktualisierte Flux, aber MAS lief noch mit der alten Config im Speicher (Pod älter als die Änderung) — erst ein `rollout restart` machte `fail` aktiv. „Committet" ≠ „deployed" ≠ „aktiv". ## 2. Die Kausalkette (belegt, nicht vermutet) | Glied | Beleg | |---|---| | `@apo` hat **keine `profiles`-Zeile** | `SELECT count(*) … = 0`, während `clark`/`sorb`/`calltest01` je eine haben | | Displayname-Setzen crasht | `PUT …/displayname → 500`, `TypeError: 'NoneType' object is not subscriptable` in `_check_profile_size` (`storage/databases/main/profile.py:354`) — `txn.fetchone()` liefert `None`, `row[0]` fliegt | | kein Displayname → Widget-Init bricht ab | Call-Klick erzeugte **null** Server-Aktivität: kein `openid/request_token`, kein `call.member`; Browser-Log damals „Messaging present but not yet started" (iframe meldet nie `ContentLoaded`) | | kein Widget → kein Token → keine SFU | `@apo` als einziger aktiver Nutzer mit **0** Einträgen in `open_id_tokens` (die nicht geprunt werden) | Herkunft der fehlenden Zeile: `@apo` ist ein **Vor-Authentik-Konto**, das durch sechs Identitäts-Resets ging. Deaktivieren löscht in Synapse das Profil, Reaktivieren legt es nicht neu an. `frank` (noch älter, nie zurückgesetzt) behielt seine Zeile. Ob einer der früheren manuellen Eingriffe der auslösende Reset war, ist nicht mehr zweifelsfrei zu klären — die Zeile ist jetzt wieder da. ## 3. Was ausgeschlossen wurde (gemessen) | Verdacht | Warum entkräftet | |---|---| | Krypto / Cross-Signing (18 Pseudo-Geräte aus 6 Resets) | Testraum ist **unverschlüsselt** → Call braucht keine Krypto; `clark` telefoniert mit ebenfalls zurückgesetzten Schlüsseln | | Server-Call-Pfad (SFU, RTC-Auth, OpenID-Endpoint) | `calltest01`/`sorb` bekommen sauber 200 auf `openid/request_token` und die Federation-Auflösung | | `@apo`s Token / Session | `/sync` läuft durchgehend mit 200, Messaging intakt | | Login-Verknüpfung MAS↔Authentik | `subject` = Authentik-`uid` `2fafe38b…`, korrekt | ## 4. Was zur Lösung geführt hat - **Der Sprung von „welcher Nutzer telefoniert nicht" zu „welche *Tabelle* ist anders".** Der Durchbruch war die `open_id_tokens`-Abfrage über *alle* aktiven Nutzer: `@apo` = 0, alle anderen zweistellig+. Ein Vergleich statt einer Einzelbetrachtung. - **Ein unverschlüsselter Testraum** hat das größte Ablenkungsfeld (Cross-Signing) in einem Schritt geschlossen. - **Der Live-Mitschnitt beim echten Call-Klick** zeigte die Abwesenheit jeder Aktivität — nicht ein Fehler, sondern *nichts* war der Befund. - **Der Nutzer-Hinweis „Anzeigename konnte nicht gesetzt werden"** lieferte den 500er mit vollständigem Stacktrace — die letzte Meile von Korrelation zu Ursache. - **Der entscheidende Kontext kam von sorb:** „`apo` ist ein Alt-Konto von vor der Authentik-Integration." Das lenkte die Suche von „angesammelter Müll" auf „Migrations-/Provisionierungs-Lücke". ## 5. Lehren für die Zukunft 1. **Bei Call-Problemen zuerst `open_id_tokens` je Nutzer vergleichen.** 0 bei einem sonst aktiven Konto ist das schnellste, eindeutigste Alarmsignal und trennt Client- von Server-Ursache in einer Abfrage. 2. **Immer im unverschlüsselten Raum reproduzieren, bevor man Krypto verdächtigt.** Das schließt einen ganzen Ursachenblock kostenlos aus. 3. **„Nichts passiert" ist ein Messergebnis, kein Sackgassen-Signal.** Die Abwesenheit eines `openid`-Aufrufs hat den Fehler lokalisiert, nicht ein Fehlercode. 4. **Alt-/mehrfach-zurückgesetzte Konten gegen frisch provisionierte diffen, nicht nur gegen die Erwartung.** Der Unterschied war eine *fehlende* Zeile — sichtbar nur im direkten Vergleich mit `clark`/`calltest01`. 5. **Jeder DB-Schreib strukturiert: betroffene Zeile vorher anzeigen, an einem gesunden Konto gegenprüfen, per `INSERT … ON CONFLICT DO NOTHING` statt Überschreiben.** Das ist die direkte Konsequenz aus den früheren unstrukturierten MAS-Eingriffen dieses Vorgangs — und diesmal eingehalten. 6. **Beiläufige Symptome ernst nehmen:** die Dublette `boje`/`Boje` beim Testkonto-Anlegen war der Faden, der die Kontoübernahme-Lücke (gitops#61) aufdeckte — ein Sicherheitsfund, der ohne den `@apo`-Vorgang unentdeckt geblieben wäre. ## 6. Offen / Folgetodos - **gitops#61** (`on_conflict`-Härtung) ist gepusht und rollt über Flux; der case-insensitive Eindeutigkeits-Check im `matrix-invitation`-Prompt-Stage (damit der Nutzer schon bei der Registrierung statt erst beim Login scheitert) ist dort als bewusst offener Rest vermerkt. - Verwaiste Altlasten bei `@apo` (10 `local_notification_settings` für längst gelöschte Geräte, 18 Cross-Signing-Pseudoeinträge) sind **kosmetisch** und wurden bewusst **nicht** angefasst — sie haben mit dem Call-Problem nichts zu tun, und ein weiterer Eingriff widerspräche der Lehre oben.