254be1b8da
Key-Backup-Flows und Geräteverwaltung waren schon implementiert (nur nicht abgehakt). Neue Sicherheitslücke dokumentiert: sqlcipher_flutter_libs ist eine ungenutzte Abhängigkeit, die Krypto-DB liegt unverschlüsselt vor. Bewusst nicht blind gefixt (Migrationsrisiko für Bestandsinstallationen), Frage an Bernd zur Priorität ergänzt.
219 lines
13 KiB
Markdown
219 lines
13 KiB
Markdown
# Pyramid – Arbeitsprotokoll
|
||
|
||
Neueste Einträge OBEN. Jede Claude-Session schreibt hier nach jedem abgeschlossenen
|
||
Schritt (und beim Abbruch mitten im Schritt den Zwischenstand). Format:
|
||
|
||
```
|
||
## JJJJ-MM-TT – Kurztitel
|
||
**Erledigt:** was fertig ist (mit Commit-Hash)
|
||
**Offen/Nächster Schritt:** der konkrete nächste Handgriff
|
||
**Stolperfallen:** was schiefging und wie es gelöst wurde (damit es nie zweimal passiert)
|
||
**Fragen an Bernd:** (optional) gesammelte Richtungsfragen, die nicht blockieren
|
||
```
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M1: Restliche Punkte geprüft – 3 schon vorhanden, 1 echte Lücke gefunden
|
||
|
||
**Erledigt:**
|
||
- **Logout-Warnung bei fehlendem Key-Backup** (Commit b5762ea): neue Funktion
|
||
`isKeyBackupMissing(client)` in `bootstrap_dialog.dart` (prüft Cross-Signing +
|
||
`keyManager.isCached()`). Beide Logout-Dialoge in `settings_modal.dart` zeigen
|
||
jetzt eine rote Klartext-Warnung statt des neutralen Textes, wenn kein Backup
|
||
eingerichtet ist.
|
||
- **Drei ROADMAP-Punkte waren bereits umgesetzt** (nur nicht abgehakt) – durch
|
||
Code-Lektüre verifiziert, nicht neu gebaut:
|
||
- Key-Backup einrichten/wiederherstellen: `bootstrap_dialog.dart` deckt beides
|
||
über den `Bootstrap`-Zustandsautomaten des matrix-SDK ab (`askNewSsss` für
|
||
Neueinrichtung, `askUnlockSsss` fragt auf einem neuen Gerät nach dem
|
||
Recovery-Key). Wird automatisch getriggert, wenn Cross-Signing fehlt
|
||
(`app_shell.dart:_triggerBootstrap`).
|
||
- Geräte-/Sessionverwaltung: `_SessionsSection` in `settings_modal.dart` –
|
||
Liste, Umbenennen, SAS/QR-Geräteverifizierung, Einzel- und
|
||
Massen-Abmeldung (mit Passwort-Reauth bei UIA-Anforderung).
|
||
- **Echte Sicherheitslücke gefunden:** `sqlcipher_flutter_libs` ist eine
|
||
Abhängigkeit in `pubspec.yaml`, wird aber **nirgends im Code benutzt**.
|
||
`matrix_client.dart` (Haupt-App) und `background_push.dart` (Push-Hintergrund)
|
||
öffnen `pyramid.sqlite` beide über den normalen, unverschlüsselten
|
||
`sqflite`/`sqflite_common_ffi`-Factory. Die Krypto-DB (Access-Token,
|
||
gepickelter Olm-Account, Megolm-Sessions, komplette Nachrichtenhistorie)
|
||
liegt also **im Klartext** auf der Platte – nur durch Androids App-Sandbox
|
||
geschützt, nicht durch eine eigene Verschlüsselung.
|
||
|
||
**Offen/Nächster Schritt:** SQLCipher-Anbindung NICHT blind auf dem Pi
|
||
umgesetzt – siehe Stolperfalle unten. Braucht einen eigenen, vorsichtig
|
||
geplanten PC-Termin: Backup der Test-DB, Migrationscode (bestehende
|
||
Klartext-DB einmalig nach SQLCipher überführen), Test auf einem Gerät mit
|
||
echten Daten, erst dann Rollout.
|
||
|
||
**Fragen an Bernd:**
|
||
- Priorität der SQLCipher-Lücke: Soll das vor M2 (Refactoring) angegangen
|
||
werden, oder reicht die App-Sandbox als Schutz erstmal aus (Angreifer bräuchte
|
||
Root oder physischen Zugriff + ADB, um die Datei überhaupt zu lesen)? Die
|
||
Migration bestehender Installationen (Uta!) ist der aufwändige/riskante Teil,
|
||
nicht die Verschlüsselung selbst.
|
||
|
||
**Stolperfallen:**
|
||
- Eine DB-Verschlüsselungs-Migration ist genau die Art Änderung, die laut
|
||
CLAUDE.md nicht "mal eben" auf dem Pi ohne Testgerät gemacht werden darf:
|
||
scheitert die Migration (falscher Schlüssel, falsche PRAGMA-Reihenfolge,
|
||
Timing zwischen Haupt-App und Push-Hintergrund-Client), sind Utas
|
||
Nachrichten und Krypto-Schlüssel unwiederbringlich weg. Deshalb hier nur
|
||
dokumentiert, nicht implementiert.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M1: Push-Regression vermutlich dieselbe Ursache wie Random-Logout
|
||
|
||
**Erledigt:** Vor dem Blick in den Push-Code die Vermutung geprüft, ob die
|
||
Push-Entschlüsselungs-Regression und der Uta-Random-Logout-Bug (siehe Eintrag
|
||
weiter unten) zusammenhängen – Ergebnis: **sehr wahrscheinlich ja, exakt
|
||
dieselbe Ursache.**
|
||
|
||
- `client.getEventByPushNotification(...)` (aufgerufen in `_bgDecryptAndShow`,
|
||
`background_push.dart:84`) ruft als ALLERERSTES `ensureNotSoftLoggedOut()`
|
||
auf (matrix-Paket `client.dart:1878`). Diese Funktion prüft, ob der
|
||
Access-Token in < 1 Minute abläuft, und löst dann `_handleSoftLogout()` aus
|
||
— **dieselbe Funktion**, die ohne Refresh-Token (unser Zustand vor Commit
|
||
9dc83f7) sofort einen echten `client.logout()` auslöst (siehe Eintrag unten).
|
||
- Der Hintergrund-Client aus `_buildClient()` liest **dieselbe** `pyramid.sqlite`
|
||
wie der Haupt-Client. Ein `logout()`/`clear()` aus dem Hintergrund-Isolat
|
||
würde also die komplette Session (inkl. Krypto-Schlüssel) für die GESAMTE
|
||
App zerstören – nicht nur für den einen Push. Das erklärt, warum das Problem
|
||
als dauerhafte Regression erscheint ("seit dem letzten Update nie wieder"):
|
||
Nach dem ersten Auto-Logout-Ereignis (ausgelöst beim Sync ODER beim
|
||
Entschlüsseln eines Pushs) sind die Schlüssel weg, jede folgende
|
||
Entschlüsselung schlägt fehl → `BgEngine` zeigt nur noch den
|
||
„Neue Nachricht"-Platzhalter (Fallback nach 6 s), nie mehr den echten Text.
|
||
- Der bereits committete Fix (`refreshToken: true` beim Login, 9dc83f7) behebt
|
||
damit vermutlich BEIDE Symptome gleichzeitig: der Refresh-Token liegt in der
|
||
gemeinsamen DB, also kann auch der Hintergrund-Client ihn lesen und einen
|
||
drohenden Soft-Logout durch stillen Refresh abwenden, statt die Session zu
|
||
zerstören.
|
||
|
||
**Offen/Nächster Schritt:** Gehört zum selben Praxistest wie der
|
||
Random-Logout-Punkt oben – zusätzlich beobachten, ob nach einer Weile
|
||
(mehrere Stunden/Tage) echte Push-Inhalte statt Platzhalter ankommen. Schlägt
|
||
das fehl, liegt eine zweite, unabhängige Ursache vor (dann `docs/NOTIFICATIONS.md`
|
||
+ Memory „Pyramid Push" erneut konsultieren, insbesondere die 3 dort gelisteten
|
||
Fallstricke der Hintergrund-Entschlüsselung).
|
||
|
||
**Stolperfallen:** Diese Verbindung wurde rein durch Lesen des matrix-SDK-
|
||
Quellcodes hergeleitet (`~/.pub-cache/hosted/pub.dev/matrix-6.2.0/lib/src/client.dart`),
|
||
nicht durch Beobachtung auf einem echten Gerät – bewusst als Vermutung
|
||
markiert, bis der PC-Praxistest sie bestätigt.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M1: Uta-Random-Logout – Ursache gefunden + Fix (UNGETESTET/Pi)
|
||
|
||
**Erledigt:**
|
||
- Alle Logout-Auslöser im Code kartiert:
|
||
- 3 bewusste Nutzer-Logout-Buttons in `settings_modal.dart` (alle hinter
|
||
Bestätigungsdialog) – kein Kandidat für "random"
|
||
- Der eigentliche Verdächtige: `matrix`-Paket (v6.2.0) `Client._innerSync()`
|
||
behandelt `M_UNKNOWN_TOKEN` vom `/sync`-Endpunkt so: bei `soft_logout: true`
|
||
→ `_handleSoftLogout()` → versucht `refreshAccessToken()` → **das SDK
|
||
hatte nie einen Refresh-Token angefordert** (`client.login(...)` in
|
||
`login_notifier.dart` rief ohne `refreshToken: true` auf) → `refreshAccessToken()`
|
||
wirft sofort "No refresh token available" → Catch-Block ruft `await logout()`
|
||
auf: ein **echter, destruktiver Logout**, ausgelöst rein durch einen normalen
|
||
Soft-Logout (z. B. abgelaufenes Access-Token), ganz ohne Nutzerinteraktion.
|
||
Quelle: `~/.pub-cache/hosted/pub.dev/matrix-6.2.0/lib/src/client.dart:2374-2384`
|
||
und `:2496-2509`.
|
||
- **Logging (Commit 63e4919):** neue `lib/core/auth_log.dart` schreibt jeden
|
||
Login-Status-Wechsel (`client.onLoginStateChanged`) sowie SDK-Warnungen/-Fehler
|
||
(`Logs().onLog`) in `auth_log.txt` im App-Support-Verzeichnis – übersteht
|
||
App-Neustart. Zusätzlich loggen alle 3 Nutzer-Logout-Buttons sich selbst, damit
|
||
sich künftig unterscheiden lässt: Nutzer-Logout vs. SDK-Auto-Logout. Reine
|
||
Zusatzfunktion, keine Verhaltensänderung.
|
||
- **Fix (Commit 9dc83f7):** `client.login(...)` fordert jetzt `refreshToken: true`
|
||
an. Damit kann das SDK einen Soft-Logout künftig durch stillen Token-Refresh
|
||
überstehen, statt die Sitzung zu zerstören. Unterstützt der Server keine
|
||
Refresh-Tokens, bleibt das Verhalten exakt wie vorher (`response.refreshToken`
|
||
ist dann `null`) – der Fix kann also nichts verschlimmern.
|
||
|
||
**Offen/Nächster Schritt:**
|
||
1. **Praxistest auf dem PC (zwingend vor Haken in ROADMAP!):** Login → Nachrichten
|
||
senden/lesen → Logout → erneuter Login → alte verschlüsselte Nachrichten noch
|
||
lesbar? Dabei prüfen, ob `auth_log.txt` (App-Support-Verzeichnis) die
|
||
Login-Status-Wechsel sauber mitschreibt.
|
||
2. Danach: den nächsten Punkt in M1 angehen (Push-Entschlüsselungs-Regression).
|
||
3. **Wichtig für Bernd/Uta:** Der Fix wirkt nur für NEUE Logins. Utas bereits
|
||
bestehende Sitzung bekommt erst nach einem einmaligen manuellen Neu-Login
|
||
einen Refresh-Token und ist bis dahin weiterhin anfällig. Sobald der PC-Test
|
||
grün ist, sollte Uta sich einmal aus- und wieder einloggen.
|
||
|
||
**Stolperfallen:**
|
||
- Konnte auf dem Pi nicht mit echtem Login/Logout getestet werden (kein GUI,
|
||
kein Matrix-Testaccount zur Hand) – Fix beruht auf genauem Lesen des
|
||
matrix-SDK-Quellcodes, nicht auf Beobachtung im Betrieb. Deshalb zwingend
|
||
vor dem nächsten Haken in ROADMAP am PC nachprüfen.
|
||
- `chat_provider.dart:42` nutzt weiterhin das deprecated `Timeline.onUpdate`
|
||
(Re-Entschlüsseln nach Schlüssel-Empfang) – hängt mit demselben Sync-Bereich
|
||
zusammen, aber bewusst nicht angefasst, um nicht zwei riskante Änderungen im
|
||
selben Bereich gleichzeitig ungetestet zu committen.
|
||
|
||
---
|
||
|
||
## 2026-07-03 – M0 abgeschlossen: Ordnung, flutter analyze sauber, README
|
||
|
||
**Erledigt:**
|
||
- Liegen gebliebene Deprecation-Fixes aus einer abgebrochenen Session committet
|
||
(`withOpacity`→`withValues`, `activeColor`→`activeThumbColor`, `cacheExtent`→
|
||
`ScrollCacheExtent`, `PublicRoomsChunk`→`PublishedRoomsChunk`) – Commit 057ac1e
|
||
- Restliche `flutter analyze`-Hinweise behoben – Commit 401bd6a:
|
||
- `document_viewer.dart`: `Matrix4.translate/scale` → `translateByDouble`/
|
||
`scaleByDouble` (PDF-Zoom, gleiches Verhalten)
|
||
- `rooms_panel.dart`: `onReorder` → `onReorderItem` (Index-Korrektur jetzt intern,
|
||
manuelles `newIdx--` entfernt); `implementation_imports`-Hinweis kommentiert
|
||
(SpaceChild wird vom `matrix`-Paket nicht öffentlich exportiert, kein Fix möglich)
|
||
- `voip_manager.dart`: `getSources()`-Override kommentiert (Pflicht-Override der
|
||
abstrakten `MediaDevices`-Klasse, die die Methode selbst deprecated hat)
|
||
- `settings_modal.dart`: zwei `BuildContext`-nach-`await`-Stellen mit
|
||
`context.mounted` abgesichert (Geräte umbenennen, Passwortabfrage bei Massenlogout)
|
||
- **Bewusst NICHT gefixt:** `chat_provider.dart:42` (`Timeline.onUpdate` deprecated,
|
||
Ersatz wäre `client.onSync` + Raum-Filter). Hängt am E2EE-Redecrypt-Pfad
|
||
(Re-Entschlüsseln nach Schlüssel-Empfang) – laut CLAUDE.md heiliger Bereich,
|
||
Umbau nur mit dediziertem Login→Nachrichten→Logout→Login-Test, nicht nebenbei.
|
||
- `lib/features/calls/` (Leiche) geprüft: existiert nicht mehr, nur `lib/features/call/`
|
||
mit den echten Dateien – Punkt war bereits erledigt, nur nicht abgehakt.
|
||
- README.md durch echte Projektbeschreibung ersetzt (Funktionsumfang, Build, Struktur) – Commit 74d38fd
|
||
- ROADMAP.md: M0 komplett abgehakt.
|
||
|
||
**Offen/Nächster Schritt:** M1, erster Punkt – Uta-Random-Logout-Bug untersuchen
|
||
(Logging an allen Logout-Pfaden einbauen, dann Ursache finden). Direkt danach die
|
||
Push-Entschlüsselungs-Regression (`docs/NOTIFICATIONS.md` + Memory „Pyramid Push"
|
||
vorher lesen).
|
||
|
||
**Stolperfallen:**
|
||
- Kein `test/`-Ordner vorhanden → `flutter test` läuft ins Leere, keine automatisierte
|
||
Absicherung. `flutter analyze` lief sauber (0 Fehler/Warnungen), aber die Lint-Fixes
|
||
in `document_viewer.dart` (PDF-Zoom) und `rooms_panel.dart` (Drag-Reorder der
|
||
Raumliste) sind **UNGETESTET** in echter UI – nächster Windows-Lauf sollte kurz
|
||
PDF-Zoom und Raum-Umsortieren per Drag prüfen.
|
||
- Der `ignore:`-Kommentar für einen deprecated-Aufruf muss auf der Zeile *unmittelbar*
|
||
über der betroffenen Codezeile stehen, nicht nur "in der Nähe" – sonst wirkt er nicht
|
||
(bei `voip_manager.dart` erst falsch über `@override` gesetzt, dann korrigiert).
|
||
|
||
---
|
||
|
||
## 2026-07-03 – Arbeitsstruktur angelegt + Vollbackup (Setup, noch kein Code)
|
||
|
||
**Erledigt:**
|
||
- Vollbackup inkl. Git-Historie: `MatrixPi\backups\pyramid-kopie_backup_2026-07-03.tar.gz` (510 MB)
|
||
- `CLAUDE.md` (Arbeitsregeln), `ROADMAP.md` (M0–M7 nach Bernds Prioritäten), dieses Protokoll
|
||
- Klargestellt: `C:\Users\nordm\pyramid - Kopie` ist das EINZIGE aktuelle Repo;
|
||
`C:\Users\nordm\pyramid` und `MatrixPi\pyramid` sind veraltete April-Stände (Commit c9af913)
|
||
|
||
**Offen/Nächster Schritt:** M0, Punkt 2 – die ~140 uncommitteten Dateien mit `git status`
|
||
sichten und in thematische Commits aufteilen (diese drei neuen Dateien mit committen),
|
||
dann `git push origin master`.
|
||
|
||
**Stolperfallen:**
|
||
- Letzter Commit ist vom 2026-04-28 (d706ace), gearbeitet wurde aber bis mindestens
|
||
2026-06-12 → zwischen Commits und Realität liegen 6 Wochen. Genau deshalb: ab jetzt
|
||
kleine Commits nach jedem Schritt.
|
||
- Git-Remote (Gitea auf dem Pi) enthält Zugangsdaten in der URL – funktioniert, aber
|
||
nur erreichbar, wenn der Pi läuft (unterwegs: WireGuard nötig).
|