docs(PROJ-11): TLS infra done, end-to-end verified over public domains
Nginx Proxy Manager now terminates TLS for matrix.perlbach24.de and talkhub.perlbach24.de with real Let's Encrypt certs. talkhub's backend .env updated (SYNAPSE_PUBLIC_BASE_URL, APP_BASE_URL). Full chat flow re-verified end-to-end against the public HTTPS domains: login, token mint, room resolve, message send — all 200s, event id returned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0143yYvjesjHndBACoweyK67
This commit is contained in:
1 parent
8f6e05481b
commit
c200fafd03
1 file changed
+121
-2
+121
-2
@@ -115,8 +115,10 @@ Kein neuer Datenspeicher in der TalkTime-Datenbank nötig — Mitglied-zu-Matrix
|
||||
- **Frontend:** `matrix-js-sdk` — offizielle Matrix-Client-Bibliothek, übernimmt Verbindungsaufbau, Nachrichtenabgleich, Lesebestätigungen
|
||||
- **Backend:** keine neuen Pakete — nutzt die bereits vorhandene Synapse-Anbindung aus [[PROJ-9]]
|
||||
|
||||
### Offene Infrastruktur-Voraussetzung
|
||||
Damit der Browser Synapse direkt erreichen kann, muss der Matrix-Server auch außerhalb des internen Netzes (bzw. für alle Nutzer-Endgeräte) erreichbar sein, inklusive TLS — aktuell (Testaufbau) läuft Synapse nur auf der internen LXC-IP ohne TLS. Das ist eine Deployment-Voraussetzung für den produktiven Einsatz dieses Features, keine Backend-/Frontend-Codeänderung.
|
||||
### Infrastruktur-Voraussetzung — ERLEDIGT (2026-09-11)
|
||||
~~Damit der Browser Synapse direkt erreichen kann, muss der Matrix-Server auch außerhalb des internen Netzes erreichbar sein, inklusive TLS.~~ Erledigt: Nginx Proxy Manager (192.168.66.9) terminiert TLS für `matrix.perlbach24.de` → `192.168.66.7:8008` und `talkhub.perlbach24.de` → `192.168.66.8:80`, beide mit echten Let's-Encrypt-Zertifikaten. `backend/.env` auf `talkhub` zeigt jetzt `SYNAPSE_PUBLIC_BASE_URL=https://matrix.perlbach24.de` und `APP_BASE_URL=https://talkhub.perlbach24.de`.
|
||||
|
||||
Kompletter Flow end-to-end über die öffentlichen TLS-Domains verifiziert: Login → `POST /api/chat/token` (liefert `homeserverUrl: https://matrix.perlbach24.de`) → `GET /api/chat/room` → Nachricht per `PUT .../send/m.room.message` direkt an `https://matrix.perlbach24.de` gesendet, HTTP 200, Event-ID zurückbekommen. Damit ist auch **BUG-1 sinngemäß behoben** (Backend läuft mit korrekten Werten) — die `.env.example`-Dokumentation selbst fehlt aber weiterhin (Permission-Sperre, siehe oben), das ist nur noch ein Doku-Nachtrag, keine funktionale Lücke mehr.
|
||||
|
||||
---
|
||||
|
||||
@@ -275,3 +277,120 @@ Nebenbei behoben: `createMatrixClient()` lädt `matrix-js-sdk` jetzt per dynamis
|
||||
**Nicht in diesem Pass behoben (niedrigere Priorität laut QA):** BUG-5 (Polling-Backoff), BUG-6 (History-Visibility-Migration für Bestandsräume), BUG-7 (Bundle-Größe — durch den BUG-4-Fix bereits spürbar verbessert, aber nicht Ziel dieses Fixes), BUG-8 (Helmet/CORS — pre-existing aus PROJ-1, nicht PROJ-11-Scope), BUG-9/BUG-10.
|
||||
|
||||
Empfehlung: erneut `/qa` laufen lassen, sobald Synapse per TLS von außen erreichbar ist und BUG-1 (Env-Doku) manuell nachgezogen wurde.
|
||||
|
||||
---
|
||||
|
||||
## QA Re-Test (2026-09-11, Runde 2)
|
||||
|
||||
**Getestet gegen:** commit `8f6e054` (fix(PROJ-11): address QA BUG-2/3/4, partially mitigate BUG-1)
|
||||
**Testart:** Code-Verifikation der Fixes, Backend-/Frontend-Typecheck, Production-Build (Bundle-Messung), Live-Probe gegen `matrix.perlbach24.de`, Red-Team-Review der neu hinzugekommenen Datenpfade.
|
||||
**Einschränkung:** `matrix.perlbach24.de` antwortet aktuell mit **HTTP 502 (openresty)** — weder `/_matrix/client/versions` noch `user_directory/search` erreichbar. Ein Live-Funktionstest des Chats sowie die Verifikation der BUG-2-Mitigation auf der laufenden Instanz waren daher erneut nicht möglich. Cross-Browser/Responsive weiterhin nur statisch bewertet.
|
||||
|
||||
### Status der Vorrunden-Bugs
|
||||
|
||||
| Bug | Vorher | Jetzt | Bewertung |
|
||||
|-----|--------|-------|-----------|
|
||||
| BUG-1 (Critical) Env-Doku | offen | **weiterhin offen** | Nicht in `8f6e054` enthalten. `backend/.env.example` liegt auch für mich außerhalb der Permission-Sandbox (Read + `git diff` beide verweigert) — ich kann den aktuellen Inhalt **nicht verifizieren**. Im Working Tree ist die Datei als `M` markiert, d.h. es gibt eine uncommittete lokale Änderung. Falls das der Fix ist: **er ist nicht committet** und geht bei jedem Clone/Deployment verloren. Bleibt P0. |
|
||||
| BUG-2 (High) Tenant-Isolation | offen | **teilgemildert, wie deklariert** | `synapse/homeserver.yaml.sample` enthält `user_directory.enabled: false` samt ehrlicher Begründung. Live-Verifikation nicht möglich (502). Restrisiko (direktes Anschreiben bei bekannter Matrix-ID) korrekt als offene Architekturentscheidung dokumentiert statt als „fixed" deklariert — gutes Vorgehen. Bleibt offen, jetzt aber als bewusste, dokumentierte Entscheidung. |
|
||||
| BUG-3 (High) Kick-Handling | offen | **BEHOBEN** | `use-team-chat.ts:135-143` hört auf `RoomEvent.MyMembership`, filtert korrekt auf den eigenen `roomId`, setzt `status="removed"`; `TeamChat.tsx` rendert dafür einen eigenen Alert-Zustand vor dem `ready`-Pfad, Verlauf + Composer verschwinden. Korrekt. |
|
||||
| BUG-4 (High) Ungelesen-Zähler | offen | **BEHOBEN (mit Folgefehlern)** | `use-team-unread-counts.ts` + Badge auf jeder Kachel in `TeamsPage.tsx:173-180`, `matrixRoomId` kommt aus `GET /api/teams`. Akzeptanzkriterium 6 damit erfüllt. Die Umsetzung bringt aber vier neue Befunde, siehe BUG-11 bis BUG-14. |
|
||||
| BUG-7 (Medium) Bundle | offen | **NICHT verbessert, in einem Punkt verschlechtert** | siehe BUG-11 |
|
||||
| BUG-5/6/8/9/10 | offen | offen (bewusst nicht adressiert) | unverändert gültig |
|
||||
|
||||
Typechecks `backend` und `frontend` (`tsc --noEmit`) laufen sauber. Production-Build erfolgreich.
|
||||
|
||||
### Neue Bugs aus dem Bugfix-Pass
|
||||
|
||||
**BUG-11 (High) — matrix-js-sdk lädt jetzt schon beim Öffnen der Teams-Liste statt erst beim Chat**
|
||||
Build-Messung bestätigt die Chunk-Aufteilung (`TeamDetail` 853 kB → 27 kB, SDK separat in `browser-index-*.js` 829,59 kB / 229 kB gzip + `matrix_sdk_crypto_wasm_bg-*.wasm` 7.833 kB). Der Effekt ist aber praktisch aufgehoben: `useTeamUnreadCounts` wird in `TeamsPage` **unbedingt** gemountet und ruft `createMatrixClient()` auf, sobald ein Team mit `matrixRoomId` existiert — der dynamische Import feuert also bereits beim Rendern der Teamliste. Vorher lud die SDK erst beim Öffnen eines Teams. Netto: derselbe 829-kB-Chunk, nur **früher** und jetzt auch für Nutzer, die nie einen Chat öffnen. Das Lazy-Loading von `TeamDetail` ist dadurch weitgehend wirkungslos.
|
||||
Repro: Teams-Seite laden, kein Team anklicken → Netzwerk-Tab zeigt `browser-index-*.js` (829 kB).
|
||||
Priorität: P1 — Regression gegenüber dem Zustand vor dem Fix, auf Mobilfunk spürbar.
|
||||
|
||||
**BUG-12 (High) — App-weiter Unread-Client behandelt Token-Ablauf und Sync-Fehler überhaupt nicht**
|
||||
`use-team-unread-counts.ts` reagiert nur auf `PREPARED`/`SYNCING`; es gibt keinen `ERROR`-Zweig und keinen Refresh-Pfad wie in `useTeamChat` (dort `fetchChatCredentials(..., { refresh: true })`). Der gemintete Token lebt 1 h. Danach:
|
||||
(a) Die Badges frieren still ein — Nutzer bekommen neue Nachrichten nicht mehr angezeigt und merken nichts davon (genau der Zweck des Features fällt aus).
|
||||
(b) matrix-js-sdk retryt `/sync` mit dem toten Token unbegrenzt weiter, weil der Client nie gestoppt wird — derselbe Fehlermodus wie BUG-5(b), jetzt aber auf einer Seite, die dauerhaft offen bleibt. Ein offener Tab erzeugt so unbegrenzt 401-Sync-Requests gegen Synapse.
|
||||
Repro: Teams-Seite über eine Stunde offen lassen → Badges aktualisieren nicht mehr, Netzwerk-Tab zeigt endlose fehlschlagende `/sync`-Calls.
|
||||
Priorität: P1.
|
||||
|
||||
**BUG-13 (Medium) — `GET /api/teams` gibt `matrixRoomId` aller Teams der Organisation an jedes Mitglied heraus**
|
||||
Die Route prüft nur `requesterIsOrganizationMember`, nicht die Team-Mitgliedschaft. Bisher war die Matrix-Raum-ID durch `GET /api/chat/room` geschützt (Join auf `teamMembers` + `status='active'`). Jetzt bekommt jedes Vereinsmitglied die Raum-IDs **aller** Teams seines Vereins, auch derer, denen es nicht angehört. Kein direkter Zugriff möglich (`createRoom` nutzt `preset: "private_chat"`, also `join_rule: invite` — ein `POST /_matrix/client/v3/join/<roomId>` mit dem gemitteten Token wird abgelehnt, verifiziert am Code, nicht live), aber in Kombination mit dem nicht scopebaren Token aus BUG-2 ist es unnötige Angriffsfläche und ein Existenz-/Metadaten-Leak über Teamgrenzen hinweg.
|
||||
Erwartet: `matrixRoomId` nur für Teams ausliefern, in denen der Anfragende aktives Mitglied ist (sonst `null`).
|
||||
Priorität: P2.
|
||||
|
||||
**BUG-14 (Medium) — `leftJoin` auf `chat_rooms` kann Teams doppelt zurückgeben**
|
||||
`chat_rooms.teamId` hat keinen Unique-Constraint (`backend/src/db/schema.ts:249`), nur `matrixRoomId` ist unique. Existieren für ein Team zwei Räume (z.B. durch einen fehlgeschlagenen/wiederholten `create_team_room`-Sync-Job), liefert der `leftJoin` das Team zweimal. Folgen: doppelte Kacheln in `TeamsPage`, doppelte React-Keys (`key={team.id}`), und `roomIdToTeamId` in `use-team-unread-counts.ts` mappt beide Räume auf dieselbe Team-ID, wobei der zuletzt einsortierte den Zähler überschreibt statt ihn zu addieren. Zusätzlich filtert der Join nicht auf `scope='team'`.
|
||||
Erwartet: Join auf `scope='team'` einschränken und Eindeutigkeit erzwingen (Unique-Index auf `chat_rooms.team_id` bei `scope='team'`).
|
||||
Priorität: P2.
|
||||
|
||||
**BUG-15 (Medium) — Zwei parallele Matrix-Clients mit demselben Token verschärfen das Rate-Limit-Risiko**
|
||||
Ist ein Team geöffnet, laufen `useTeamUnreadCounts` und `useTeamChat` gleichzeitig, beide mit eigenem `/sync` auf denselben Nutzer-Token (im Code als bewusster Tradeoff kommentiert). Jeder ruft beim Start `POST /api/chat/token` (Limit 10/min). Dazu kommt der 15-s-Poll aus BUG-5 (4 Token-Calls/min) und ein Teardown+Neuaufbau des Unread-Clients bei jeder Änderung der `roomIds` (z.B. nach `refresh()` im Anschluss an „Team anlegen"). Ein Nutzer, der zügig durch mehrere Teams klickt, während Provisioning noch läuft, kann das 10/min-Limit erreichen → 429 → Chat zeigt einen generischen Verbindungsfehler statt „bitte kurz warten".
|
||||
Priorität: P2.
|
||||
|
||||
**BUG-16 (Low) — `refreshCounts` prüft `cancelled` nicht**
|
||||
`use-team-unread-counts.ts:59-67`: Die an `Room.timeline`/`Room.receipt` gehängte `refreshCounts` ruft `setCounts` ohne `cancelled`-Guard. Beim Unmount werden zwar `removeAllListeners()`/`stopClient()` aufgerufen, ein bereits in Flight befindlicher Callback kann aber noch ein `setState` auf einer unmounteten Komponente auslösen. Zusätzlich: schlägt `startClient()` nach dem Registrieren der Listener fehl, wird der Client im `catch` nicht gestoppt (Leak eines syncenden Clients).
|
||||
Priorität: P3.
|
||||
|
||||
**BUG-17 (Low) — `getUnreadNotificationCount()` ist in matrix-js-sdk deprecated**
|
||||
Verwendet in `use-team-chat.ts:99` und `use-team-unread-counts.ts:64`. Aktuelle SDK-Versionen empfehlen `getRoomUnreadNotificationCount(NotificationCountType.Total)`; die Deprecated-API kann bei einem SDK-Update wegfallen. Außerdem sind die in `use-team-unread-counts.ts` hartcodierten Event-Namen-Strings (`"Room.timeline"` usw., Kommentar erklärt das Warum nachvollziehbar) nicht typgeprüft — ein SDK-Rename bricht die Badges **still**, ohne Typecheck-Fehler.
|
||||
Priorität: P3.
|
||||
|
||||
### Akzeptanzkriterien (Neubewertung)
|
||||
|
||||
| # | Kriterium | Runde 1 | Runde 2 |
|
||||
|---|-----------|---------|---------|
|
||||
| 1 | Chat-Tab in Team-Detailansicht | PASS | PASS |
|
||||
| 2 | Backend liefert Token, kein Passwort/Admin-Token im Frontend | PASS | PASS |
|
||||
| 3 | `/sync` via matrix-js-sdk, kein eigener WebSocket-Server | PASS | PASS |
|
||||
| 4 | Alle Raum-Mitglieder dürfen senden | PASS | PASS |
|
||||
| 5 | Volle Historie für neue Mitglieder | PASS m. Vorbehalt | PASS m. Vorbehalt (BUG-6 unverändert offen) |
|
||||
| 6 | Ungelesen-Zähler pro Raum + Read Receipts | **FAIL** | **PASS** — Badge auf jeder Kachel; Einschränkung: friert nach 1 h ein (BUG-12) |
|
||||
| 7 | Fehlendes `matrixUserId` → Hinweis + Polling | PASS | PASS |
|
||||
| 8 | Kein Medien-Upload, kein Push | PASS | PASS |
|
||||
|
||||
**Summe: 7 PASS, 1 PASS mit Vorbehalt, 0 FAIL** (vorher 6/1/1).
|
||||
|
||||
### Edge Cases (Neubewertung)
|
||||
|
||||
| Edge Case | Runde 1 | Runde 2 |
|
||||
|-----------|---------|---------|
|
||||
| `matrixUserId = null` | PASS | PASS |
|
||||
| `create_team_room` nicht gelaufen | PASS | PASS |
|
||||
| Synapse nicht erreichbar, kein Flut-Retry | FAIL | **FAIL** — unverändert (BUG-5), zusätzlich neuer Dauer-Retry-Pfad (BUG-12b) |
|
||||
| Token abgelaufen beim Senden → 1 Retry | PASS | PASS (nur im Detail-Chat; Unread-Client hat keinen Refresh, BUG-12a) |
|
||||
| Rauswurf bei offenem Chat | FAIL | **PASS** |
|
||||
| Lange Historie → Pagination | PASS | PASS |
|
||||
|
||||
### Security-Audit (Delta)
|
||||
|
||||
| Prüfung | Ergebnis |
|
||||
|---------|----------|
|
||||
| Auth-Bypass / IDOR auf Chat-Routen | PASS — unverändert |
|
||||
| **Neu:** Metadaten-Leak über `GET /api/teams` | **FAIL** — BUG-13, Raum-IDs fremder Teams |
|
||||
| **Neu:** Verwertbarkeit der geleakten Raum-ID | MITIGIERT — `preset: private_chat` ⇒ invite-only, Join wird abgelehnt (statisch verifiziert) |
|
||||
| Tenant-Isolation des Tokens | TEILGEMILDERT — `user_directory.enabled: false` in der Sample-Config, live nicht verifizierbar (502) |
|
||||
| Transportsicherheit Browser ↔ Synapse | **FAIL** — BUG-1 unverändert; `matrix.perlbach24.de` liefert derzeit 502 |
|
||||
| Security-Header / CORS | **FAIL** — BUG-8 unverändert |
|
||||
| XSS / SQL-Injection / Input-Validierung | PASS — unverändert, keine neuen ungeprüften Eingaben |
|
||||
| Token in Logs/DB/Response | PASS — unverändert |
|
||||
|
||||
### Regressionstest
|
||||
|
||||
| Feature | Ergebnis |
|
||||
|---------|----------|
|
||||
| PROJ-1 Auth | PASS |
|
||||
| PROJ-3 Mitgliederverwaltung | PASS |
|
||||
| PROJ-5 Mannschaftsverwaltung | **RISIKO** — `GET /api/teams` hat ein neues Feld **und** durch den `leftJoin` eine geänderte Zeilenkardinalität (BUG-14). Jeder weitere Konsument der Route ist betroffen. Zusätzlich startet allein das Öffnen der Teams-Liste jetzt einen Matrix-`/sync` (vorher erst beim Öffnen eines Teams). |
|
||||
| PROJ-8 Audit Log | PASS — zusätzlich zu beachten: der Unread-Client mintet beim Laden der Teams-Seite einen Token, was ggf. mehr `chat.token_issued`-Einträge erzeugt (Cache dämpft das) |
|
||||
| PROJ-9 Synapse-Integration | **RISIKO unverändert** — `initial_state` + `preset: private_chat` weiterhin nicht gegen eine laufende Synapse verifiziert (502) |
|
||||
| PROJ-10 Deployment | **RISIKO** — BUG-1 unverändert; Synapse-Endpoint aktuell 502 |
|
||||
|
||||
### Fazit Runde 2
|
||||
|
||||
**Production-ready: NEIN.**
|
||||
|
||||
- Fortschritt ist real: 2 von 3 High-Bugs sauber behoben, ein Akzeptanzkriterium von FAIL auf PASS, alle 8 Kriterien jetzt mindestens bestanden.
|
||||
- Blocker unverändert: **BUG-1 (Critical)** ist nicht committet und nicht verifizierbar; Synapse ist von außen nicht erreichbar (502), damit ist das Feature derzeit ohnehin nicht betreibbar.
|
||||
- Der BUG-4-Fix hat **zwei neue P1-Befunde** eingeführt (BUG-11 Bundle-Regression, BUG-12 kein Token-Refresh/Fehlerbehandlung im App-weiten Client) sowie drei P2 (BUG-13/14/15).
|
||||
|
||||
**Empfohlene Fix-Reihenfolge:** BUG-1 (committen + verifizieren) → Synapse-TLS-Erreichbarkeit → BUG-12 → BUG-11 → BUG-13/14 → BUG-2 (Architekturentscheidung) → BUG-5/15 → BUG-6/8 → Rest.
|
||||
Reference in new issue
Block a user