Der erste Befund beruhte auf einem durch head-limit abgeschnittenen grep; message_media.dart (am wenigsten geprueft laut PC_TESTPLAN 3) nutzt den Cache ebenfalls, inkl. statischem Wrapper ohne ref-Zugang. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
20 KiB
Pyramid – Roadmap
Die lange Liste. Wird von oben nach unten abgearbeitet (Arbeitszyklus siehe CLAUDE.md).
Reihenfolge = Priorität: erst Ordnung & Sicherheit (Datenverlust-Risiken!), dann das
große Refactoring, dann Calls/Streaming/Chat-Features, dann Feinschliff.
Punkte abhaken ([x]), wenn erledigt UND in PROGRESS.md protokolliert.
M0 – Backup & Ordnung
-
Vollbackup vor dem Refactoring (2026-07-03 →
MatrixPi\backups\pyramid-kopie_backup_2026-07-03.tar.gz) -
Die ~140 uncommitteten Dateien sichten, in thematische Commits aufteilen, zu Gitea pushen (Sicherungs-Commit
25ed765+ Folge-Commits bis 74d38fd; Arbeitsbaum ist jetzt sauber) -
flutter analyzekomplett sauber bekommen und sauber halten (nur noch 1 bewusst zurückgestellter Hinweis, siehe PROGRESS.md 2026-07-03) -
lib/features/call/vs.lib/features/calls/(leer?) klären – Leiche entfernen (geprüft:calls/existiert nicht mehr, nurcall/mit den echten Dateien) -
README.md durch echte Projektbeschreibung ersetzt (
74d38fd) -
Fable-5-Qualitäts-Review aller bisherigen Autopilot-Commits (Bernds Wunsch, VOR allem anderen): ERLEDIGT – siehe Abschnitt „Fable-5-Review" oben in PROGRESS.md. (a) alle 6 Gott-Datei-Aufteilungen unabhängig verifiziert (Zeilen-Multiset, part-Bezüge): korrekt, verlorene Erklärkommentare waren mit
f9979e4schon wiederhergestellt; (b) Uta-Logout-/Push-Fix gegen matrix-SDK 6.2.0 verifiziert: Analyse richtig, Lücke (SDK eskaliert Refresh-Fehler zu logout()+clear()) war mit891f348(onSoftLogout-Guard) geschlossen, Guard heute gegenverifiziert; (c) Lint-Fixes verhaltensäquivalent (onReorderItem gegen Flutter-SDK geprüft, kein Off-by-One); (d) abgehakte Punkte stimmen mit dem Code überein. Alles weiterhin UNGETESTET (Pi) → PC-Praxistest bleibt. -
Uta-Random-Logout untersuchen: UNGETESTET (Pi) – Ursache im matrix-SDK gefunden + Fix committet (
63e4919,9dc83f7), siehe PROGRESS.md 2026-07-03. Nächster PC-Lauf MUSS den vollen Kreislauf praktisch prüfen (Login → Nachrichten → Logout → erneuter Login → alte Nachrichten lesbar?), erst dann gilt der Punkt als wirklich erledigt. -
REGRESSION: Push-Benachrichtigungen werden nicht mehr entschlüsselt – UNGETESTET (Pi), sehr wahrscheinlich DIESELBE Ursache wie der Uta-Random-Logout-Bug (bereits mit
9dc83f7mitgefixt), siehe PROGRESS.md 2026-07-03. Auf echtem Gerät prüfen: nach Neu-Login (für den Refresh-Token) eine Weile laufen lassen und beobachten, ob Push-Nachrichten wieder mit echtem Text statt nur „Neue Nachricht"-Platzhalter ankommen. Falls das Problem weiterhin auftritt, liegt eine zweite, unabhängige Ursache vor (z. B. fehlende Megolm-Schlüssel/Key-Sharing) – danndocs/NOTIFICATIONS.md+ Memory „Pyramid Push" erneut konsultieren und die dort gelisteten 3 Fallstricke der Hintergrund-Entschlüsselung gegenprüfen. -
Sichere Speicherung von Access-Token & Krypto-DB: LÜCKE GEFUNDEN. Stand 2026-07-06: UMGESETZT + auf Windows praxisgetestet – neue Storage-Fassade
lib/core/app_database.dart(SQLCipher auf Android/Windows/Linux, Schlüssel im Keystore, Klartext-Migration mit Backup/Verifikation/atomarem Tausch, Push-Isolate-Marker). Bernds echte Windows-DB wurde erfolgreich migriert (PROGRESS.md 2026-07-06), Tests intest/app_database_test.dartfahren die Migration mit echtem SQLCipher. Bleibt offen, weil Android UNGETESTET ist (Migration auf Gerät, Push-Isolate liest verschlüsselte DB, secure_storage im Background-Isolate) – Testplan Punkt 4 indocs/SQLCIPHER_MIGRATION.md; erst nach grünem Gerätetest Release + Utas Gerät (vorher ihr Key-Backup prüfen!), dann abhaken.sqlcipher_flutter_libssteht zwar inpubspec.yaml, wird aber nirgends benutzt –matrix_client.dartundbackground_push.dartöffnen diepyramid.sqlite(Access-Token, gepickelter Olm-Account, Nachrichten) über den normalensqflite/sqflite_common_ffi-Factory, also unverschlüsselt auf der Platte. Bewusst NICHT blind auf dem Pi gefixt: eine Umstellung auf SQLCipher braucht eine Migration für bereits bestehende Klartext-DBs (Utas Gerät!) – ohne Testgerät hier zu riskant für „Kein Datenverlust". Siehe PROGRESS.md 2026-07-03 + Frage an Bernd. Umsetzungsplan liegt fertig indocs/SQLCIPHER_MIGRATION.md(2026-07-03, gegen Code und SDK verifiziert) – PC-Termin kann direkt damit starten. -
Key-Backup einrichten-Flow: bereits vorhanden (
bootstrap_dialog.dart, wird automatisch getriggert wenn Cross-Signing fehlt,app_shell.dart) -
Key-Backup wiederherstellen-Flow: bereits vorhanden (
bootstrap_dialog.dart,BootstrapState.askUnlockSsssfragt nach Recovery-Key auf neuem Gerät) -
Logout-Schutz: Warnung mit Klartext-Folgen, wenn Key-Backup fehlt (
b5762ea) -
Geräte-/Sessionverwaltung in den Einstellungen: bereits vorhanden (
_SessionsSectioninsettings_modal.dart– Liste, Umbenennen, Verifizieren per SAS/QR, Abmelden, Massen-Abmeldung mit Passwort-Reauth) -
SICHERHEIT: Dashboard-Admin-Endpoints – ERLEDIGT: Loch geschlossen, Heimnetz-only ist laut Bernd der Endzustand (Fable-5-Review (o); Entscheidung
ANTWORTEN_BERND.mdNr. 3, 2026-07-05).server.pynimmt/api/ban,/api/unban,/api/toggle-registration,/api/create-invite,/api/run-statsjetzt NUR noch aus Heimnetz/localhost an (Socket-IP + Cloudflare-Header-Check); Fremde aus dem Internet bekommen 403. Headless verifiziert (öffentlich 403, Heimnetz ok, App-Endpoints unverändert), siehe PROGRESS.md 2026-07-04. Bernds Dashboard läuft im Heimnetz unverändert:http://192.168.178.71:8080/stats.html. Offen bleibt: Will Bernd die Admin-Buttons auch von unterwegs? Dann Weg A (Cloudflare Access, empfohlen – schließt auch die weiterhin öffentliche Nutzerlisten-Ansicht) oder Weg B (DASH_TOKEN), Plan + Restrisiken indocs/DASHBOARD_AUTH_HARDENING.md. Zusatz (Fable-5-Review (p), 2026-07-04):server.pyläuft jetzt multithreaded mit 30-s-Socket-Timeout – ein einzelner langsamer Client kann den Server (und damit/api/livekit-token) nicht mehr blockieren; headless verifiziert. Zusatz (Fable-5-Review (q), 2026-07-04): Der durch (p) eingeführte Nebenläufigkeits-Race in den Admin-Routen (Read-Modify-Write aufconduit.toml,send_admin-Readback) ist mitADMIN_LOCKgeschlossen, das NUR die Admin-Mutationen serialisiert – die öffentlichen Routen bleiben nebenläufig; headless verifiziert (2× parallel = 2× Laufzeit, öffentlicher Pfad 11 ms). Zusatz (Fable-5-Review (r), 2026-07-04): drei Restbefunde gefixt –registration-status-Leser jetzt unter ADMIN_LOCK (Truncate-Write-Race), negative/übergroßeContent-Lengthüberall abgelehnt (kein Thread-Hänger, ban/unban 4-KB-Deckel), Einladungs-Token persecretsstattrandom. Zusatz (Fable-5-Review (s), 2026-07-04): ban/unban-Body wird jetzt VOR dem ADMIN_LOCK gelesen (hängender Body-Read konnte den Lock 30 s festhalten), und die öffentlichen Routen antworten bei internen Fehlern generisch statt mit Exception-Text (Info-Leak); Details ins Journal. Headless verifiziert. Zusatz (Fable-5-Review (t), 2026-07-04): auch der öffentliche GET-Leserregistration-statusantwortet bei internen Fehlern jetzt generisch statt mitstr(e)(hätte denconduit.toml-Pfad geleakt); headless verifiziert inkl. 500-Beweis. Zusatz (Fable-5-Review (u), 2026-07-04): (t)-Fix gegengelesen – korrekt, kein Befund; die server.py-Härtungskette ist damit KONVERGIERT (alle verbliebenenstr(e)-Antworten LAN-gegated, absichtlich). Abschluss (2026-07-05,ANTWORTEN_BERND.mdNr. 3): KEIN Fernzugriff gewünscht – die A/B-Frage ist GESCHLOSSEN, das Heimnetz-Gate ist der Endzustand (unterwegs nutzt Bernd WireGuard). Die Restrisiken des Gates (öffentliche Nutzerlisten-Ansicht, theoretisches LAN-CSRF) sind bewusst akzeptiert und indocs/DASHBOARD_AUTH_HARDENING.mddokumentiert. Nicht weiter daran arbeiten. -
LiveKit-Token-Route server-seitig gebaut + verifiziert (Fable-5-Review (n), PROGRESS.md 2026-07-04).
POST /api/livekit-tokenläuft live inserver.py(whoami-Auth, stdlib-HS256-Minting auslivekit.yaml), headless geprüft (401/400/413/200, JWT-Signatur unabhängig gegenlivekit.yamlgültig, Identität = geprüfte user_id). Offen: Client-Umstellung (livekit_token.dart/livekit_call_manager.dart) + Secret-Rotation – beides PC/Gerät (heiliger Call-Pfad), siehedocs/LIVEKIT_TOKEN_MIGRATION.md. -
SICHERHEIT: geleakte Secrets rotieren + aus dem Client holen (Fable-5-Review (k), PROGRESS.md 2026-07-04). Das Gitea-Repo ist öffentlich (
private:false), und es lagen vier aktive Secrets drin – teils schon bereinigt (release.ps1, Admin-Doku), aber: - Rotieren (Pflicht, sie sind bereits geleakt): Matrix-Server-Admin-TokenJ5lax…(noch gültig, voller Admin!), Gitea-Release-Token, LiveKit-apiKey/apiSecret, Giphy-Key. Alte jeweils widerrufen. - LiveKit-Token server-seitig minten: kleiner Token-Endpoint auf dem Pi, damitapiSecretauslib/core/livekit_token.dartverschwindet (App holt nur das JWT). Umsetzungsplan liegt fertig indocs/LIVEKIT_TOKEN_MIGRATION.md(2026-07-04, gegen Code + Pi-Konfig geschrieben) – braucht echten Call-Test auf einem Gerät. - E2EE-Diagnose-Upload: ERLEDIGT (2026-07-04). Der Dashboard-Server auf dem Pi (/home/steggi/matrix/server.py) benutzte für/api/e2ee-diagnosticsdenselben String wie den Matrix-Admin-Token – deshalb steckte er im Client. Jetzt hat das Diagnose-Endpoint einen eigenen, eng begrenzten Token (DIAG_TOKEN, hängt nur an den Log an, keine Admin-Rechte);settings_encryption.dartbenutzt diesen. Der Admin-Token ist damit nicht mehr im App-Code (pergrepbestätigt). Server + Client umgestellt und verifiziert (neuer Token 200, alter Admin-Token 403). Der Admin-Token selbst muss trotzdem noch rotiert werden (steht oben, Bernds Sache). - Optional: Git-History-Rewrite (Bernds Entscheidung, Repo ist auch Backup). - Stand 2026-07-05 (ANTWORTEN_BERND.mdNr. 4): Rotation und History-Rewrite ruhen BEWUSST – beides bleibt Bernds manuelle Sache, NICHT vom Autopilot anstoßen oder nachfragen. Der Punkt bleibt nur als Merkposten offen; Autopilot-bearbeitbar ist hier nichts mehr. - ACHTUNG bei „Repo einfach privat schalten": Der In-App-Updater (lib/core/update_checker.dart) holt Releases anonym über die öffentliche Gitea-API (/api/v1/repos/steggi/pyramid/releases). Repo privat = Utas App findet keine Updates mehr (und ein Fix ließe sich nicht mehr per Update verteilen – Henne-Ei). Erst Updater-Strategie klären, dann Sichtbarkeit ändern. -
Härtetest dokumentieren: Login → Nachrichten → Logout → Login neu → alles noch lesbar (gehört zum ausstehenden PC-Praxistest der beiden Bugfixes oben; kompletter Ablauf inkl. aller aufgelaufenen UNGETESTET-Punkte steht jetzt in
docs/PC_TESTPLAN.md– dort abarbeiten und abhaken) Teilstand 2026-07-06 (Autopilot, siehe PC_TESTPLAN): flutter test grün (29), Session über 3 Starts + DB-Migration stabil eingeloggt, E2EE-Empfang live belegt, auth_log unauffällig; App läuft seit 12:43 für die Langzeit-Beobachtung weiter. Offen (Klick/Bernd): Senden, Logout-Warnung, Re-Login mit Lesbarkeits-Check.
M2 – Das große Refactoring (erst nach M0/M1!)
Ziel: gleiche Funktionen, saubere modulare Struktur – als Fundament für alles Folgende. Leitprinzip (Bernd): austauschbare Bausteine – jedes Modul muss sich komplett neu schreiben lassen, ohne dass andere Module angefasst werden müssen.
- Ist-Analyse geschrieben (PROGRESS.md 2026-07-03): Gott-Datei
settings_modal.dart(5541 Zeilen/50+ Klassen), Call-Schicht ohne gemeinsame Fassade (4 Module importierenvoice_channel.dartdirekt),app_state.dartals Kopplungspunkt aller Features, keine gemeinsame Storage-Schnittstelle (12 direkteSharedPreferences-Zugriffe), toter Code (message_bubble.dart) + Duplikate (_formatTime/_formatDate3-fach). 8 Modul-Kandidaten dokumentiert. - Modul-Schnitt definiert (
docs/M2_MODULE_SCHNITT.md, PROGRESS.md 2026-07-03): „Calls" ist architektonisch zwei getrennte Module (call_signaling= Matrix-1:1-VoIP,voice_channel= LiveKit-SFU-Kanäle) +call_ui; außerdemsettings(aufgesplittet),verification(neu, aus Settings herausgelöst),storage(Ausbausettings_prefs.dart),auth,push,chat_timeline/rooms. Umsetzungsreihenfolge vorgeschlagen, wartet auf Bernds Go für den Call-Pilot (Frage in PROGRESS.md). - Call-Schicht entwirren: EINE klare Zuständigkeit pro Klasse (Signalisierung /
LiveKit-Verbindung / UI-State sauber getrennt)
Teilschritt erledigt (2026-07-06, M2-Pilot laut Entscheidung 2):
Module
call_signalingundvoice_channelmit Fassaden-Interfaces + Providern existieren (lib/features/call_signaling/,lib/features/voice_channel/), alle Konsumenten gehen nur noch übercallSignalingProvider/voiceChannelProvider– kein UI-Import der Implementierungen mehr (Details/Abweichungen: Status-Kopf indocs/M2_MODULE_SCHNITT.md). App startet, 29 Tests grün, Sync/E2EE laufen. UNGETESTET: echter 1:1-Call und Voice-Channel-Join per Klick (braucht Gerät bzw. interaktiven Test) – deshalb nicht abgehakt. Weitere Teilschritte erledigt (2026-07-06, zweite PC-Session):call_uiist jetzt ein eigenes Modul (lib/features/call_ui/,b2335da) und die voice_*-Prefs laufen über dieVoicePrefs-Fassade incore/settings_prefs.dartstatt direktem SharedPreferences-Zugriff (d22f9c2) – beides verhaltensgleich, analyze/Tests grün, App-Start geprüft. Bewusst zurückgestellt bis Bernds Klick-Test des Piloten (PC_TESTPLAN Abschnitt 8) grün ist: UI-State aus den Managern herauslösen – kein zweiter ungetesteter Logik-Umbau auf dem heiligen Call-Pfad. - Einheitliches State-Management (Riverpod konsequent, keine Misch-Patterns)
Ist-Analyse + Umbauplan liegen in
docs/M2_STATE_MANAGEMENT.md(2026-07-06): Riverpod ist schon dominant (~50 Provider); 4 Misch-Patterns identifiziert, Schrittfolge definiert. Schritte 1–2 (Call-Manager-UI-State, Voice-Provider-Umzug) GESPERRT bis Bernds Klick-Test (PC_TESTPLAN Abschnitt 8); MediaCache-Umstellung zurückgestellt (hängt an der Chat-Timeline, Korrektur im Doc). Push bleibt bewusst wie er ist (Isolate-Zwang). ToterincomingCallProviderbereits entfernt (41bcaf2). - Toten Code & Duplikate entfernen, Ordnerstruktur vereinheitlichen
(PROGRESS.md 2026-07-03:
message_bubble.darttot entfernt, doppelte_formatTimeinmessage_group.dartzusammengeführt; alle 6 in der Ist-Analyse gefundenen Gott-Dateien perpart/part ofin thematische Dateien aufgeteilt –widgets/settings_modal.dart(5541→270 Zeilen, 12 Teile unterwidgets/settings/),features/chat/message_group.dart(2775→30 Zeilen, 7 Teile unterfeatures/chat/message/),features/rooms/rooms_panel.dart(2555→31 Zeilen, 8 Teile unterfeatures/rooms/panel/),features/spaces/space_admin_dialog.dart(2278→19 Zeilen, 6 Teile unterfeatures/spaces/admin/),features/chat/chat_view.dart(2116→33 Zeilen, 6 Teile unterfeatures/chat/view/),features/chat/document_viewer.dart(1773→20 Zeilen, 5 Teile unterfeatures/chat/document/) – jeweils verifiziert per automatisiertem Blockvergleich gegen das Original undflutter analyze, siehe PROGRESS.md-Einträge.features/call/voice_channel.dart(1123 Zeilen) undlayout/app_shell.dart(1073 Zeilen) waren zunächst bewusst NICHT mit angefasst – beide hängen eng mit der damals unentschiedenen Call-Fassade bzw. dem Bootstrap/Login-Pfad zusammen. Nachtrag 2026-07-06: Nach dem Call-Piloten istcall_ui/voice_channel.dartjetzt ebenfalls geteilt (5 part-Dateien untercall_ui/view/, zeichenidentisch verifiziert,95d9b07);app_shell.dartbleibt wegen des Bootstrap/Login-Pfads (heilig) weiter unangetastet. Ebensosettings/settings_encryption.dart(1470 Zeilen, größte verbliebene Teildatei) in 4 part-Dateien untersettings/encryption/geteilt (zeichenidentisch verifiziert,6128953) – Vorbereitung für das spätere verification-Modul (Schnitt-Doc Schritt 3), Code selbst unangetastet. UNGETESTET (Pi) im Sinne von „nicht in echter UI gesehen" – nächster Windows-Lauf sollte alle 6 betroffenen Bereiche einmal durchklicken (zusätzlich zu den 5 unten: eine PDF/Text/Archiv/Bild-Datei im Chat öffnen, Vollbild-PDF-Viewer inkl. Thumbnail-Leiste und Speichern-Button prüfen), siehe „Fragen an Bernd"/Nächster-Schritt in den jeweiligen PROGRESS.md-Einträgen) - Nach JEDEM Schritt: App läuft, Kernflows getestet, Commit
M3 – Calls: Logik, UI & Audio (Discord als Referenz)
- Voice-Channel-Logik robust machen: Beitreten/Verlassen/Wechseln ohne Hänger, klarer Zustand wer drin ist, sauberes Verhalten bei Verbindungsabbruch
- Call-UI aufräumen: aufgeräumter, intuitiver, konsistent (Mini-Call-Widget, Vollansicht)
- Windows: Audiogeräte-Erkennung reparieren (Mikrofon/Ausgabe erkennen und in den Einstellungen auswählbar machen – geht aktuell gar nicht)
- Noise Suppression als optionale Einstellung (z. B. RNNoise/LiveKit-Audio-Filter)
- Lautstärke pro Teilnehmer einstellbar (Discord-Style, persistent pro Nutzer)
- Weitere Feinjustierung: Eingangsempfindlichkeit/Gate, Echo-Unterdrückung an/aus, Push-to-Talk prüfen – von Discords Sprach-Einstellungen inspirieren lassen
- Anruf-Benachrichtigung auch bei gekillter App (Push-Architektur nutzen)
M4 – Streaming (Bildschirmübertragung)
- Framerate verbessern: Ursache messen (Encoder? Auflösung? Simulcast-Settings? Software- statt Hardware-Encoding?), dann gezielt fixen
- Auflösung/Qualität wählbar für den Streamer (z. B. 720p/1080p + FPS-Wahl)
- Qualität wählbar für den Empfänger (LiveKit-Simulcast-Layer auswählen)
- Sauberes Degradieren bei schwacher Bandbreite statt Ruckel-Chaos
M5 – Chat-Funktionen
- Sprachnachrichten aufnehmen & abspielen (MSC3245-kompatibel, wie Element)
- Bilder speichern → richtige Galerie: über MediaStore/Downloads statt in den unzugänglichen App-Ordner (Android Scoped Storage korrekt nutzen)
- Medien senden/empfangen generell prüfen (Fortschritt, Vorschau, Videos)
M6 – Release & Verteilung
- Google-Warnung beim Installieren untersuchen: Play-Protect-Meldung seit der Signatur-Einführung analysieren (Signing-Konfig prüfen; unbekannte Signatur + Sideload löst das oft aus; ggf. Play-Protect-Appeal oder saubere v2/v3-Signierung)
- Release-Skript prüfen/aktualisieren (Windows + Android), Versionierung sauber
- In-App-Updater testen (existiert laut Git-Historie)
M7 – Design & Feinschliff
- Rooms-Icon überarbeiten – passt stilistisch nicht zu den anderen Buttons
- Design-Konsistenz-Pass über alle Icons/Buttons
- Onboarding: erster Start erklärt Server-Wahl und Verifikation verständlich
- Themes/Darkmode-Feinschliff, Schriftgrößen
Ideen-Parkplatz (unsortiert, erst nach M7 bewerten)
- Threads, Sticker, Standort teilen, mehrere Konten
- Watch-Together (z. B. Jellyfin gemeinsam schauen)
Bernd: „Das ist nur der Anfang meiner Liste" – neue Punkte einfach hier in den passenden Meilenstein eintragen (oder Claude sagen, er soll sie einsortieren).