Root cause proven end to end: a pre-Authentik account that lost its profiles row (deactivate clears it, reactivate does not recreate it) crashes the displayname write path, so it never gets a display name and the Element Call widget never initialises. Fixed with a cross-checked INSERT; open_id_tokens went 0 -> 6 and the call joined. Records the ruled-out suspects, what led to the solution, and the lessons - chief among them: compare old accounts against freshly provisioned ones, and reproduce in a cleartext room before blaming crypto. Also notes the account-takeover finding (gitops#61) surfaced along the way.
103 lines
6.2 KiB
Markdown
103 lines
6.2 KiB
Markdown
# 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.
|
|
|
|
## 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.
|