Files
pyramid/ROADMAP.md
T
Bernd Steckmeister 3c7eb9ddca refactor: M2-Call-Pilot – Module call_signaling + voice_channel mit Fassaden (Entscheidung 2)
- CallSignalingService/callSignalingProvider und VoiceChannelService/
  voiceChannelProvider als einzige oeffentliche Schnittstellen
- voip_manager.dart / livekit_call_manager.dart in ihre Module verschoben,
  Logik unangetastet (nur Klassen-Koepfe, @override, Imports, Provider-Namen)
- Alle Konsumenten entkoppelt; Kompositions-Punkt matrix_client.dart als
  dokumentierte Ausnahme. analyze sauber, 29 Tests gruen, Windows-Start ok.
  Call-Klick-/Geraetetest steht aus (PROGRESS.md)
2026-07-06 12:42:59 +02:00

265 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
- [x] Vollbackup vor dem Refactoring (2026-07-03 → `MatrixPi\backups\pyramid-kopie_backup_2026-07-03.tar.gz`)
- [x] Die ~140 uncommitteten Dateien sichten, in thematische Commits aufteilen, zu Gitea pushen
(Sicherungs-Commit 25ed765 + Folge-Commits bis 74d38fd; Arbeitsbaum ist jetzt sauber)
- [x] `flutter analyze` komplett sauber bekommen und sauber halten (nur noch 1 bewusst
zurückgestellter Hinweis, siehe PROGRESS.md 2026-07-03)
- [x] `lib/features/call/` vs. `lib/features/calls/` (leer?) klären Leiche entfernen
(geprüft: `calls/` existiert nicht mehr, nur `call/` mit den echten Dateien)
- [x] README.md durch echte Projektbeschreibung ersetzt (74d38fd)
- [x] **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 f9979e4 schon
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 mit 891f348 (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.
- [x] **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.
- [x] **REGRESSION: Push-Benachrichtigungen werden nicht mehr entschlüsselt**
UNGETESTET (Pi), sehr wahrscheinlich DIESELBE Ursache wie der
Uta-Random-Logout-Bug (bereits mit 9dc83f7 mitgefixt), 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) dann `docs/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 in
`test/app_database_test.dart` fahren 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 in `docs/SQLCIPHER_MIGRATION.md`;
erst nach grünem Gerätetest Release + Utas Gerät (vorher ihr Key-Backup
prüfen!), dann abhaken.
`sqlcipher_flutter_libs` steht zwar in `pubspec.yaml`, wird aber nirgends
benutzt `matrix_client.dart` und `background_push.dart` öffnen die
`pyramid.sqlite` (Access-Token, gepickelter Olm-Account, Nachrichten) über
den normalen `sqflite`/`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 in `docs/SQLCIPHER_MIGRATION.md`** (2026-07-03, gegen Code und
SDK verifiziert) PC-Termin kann direkt damit starten.
- [x] Key-Backup einrichten-Flow: bereits vorhanden (`bootstrap_dialog.dart`,
wird automatisch getriggert wenn Cross-Signing fehlt, `app_shell.dart`)
- [x] Key-Backup wiederherstellen-Flow: bereits vorhanden (`bootstrap_dialog.dart`,
`BootstrapState.askUnlockSsss` fragt nach Recovery-Key auf neuem Gerät)
- [x] Logout-Schutz: Warnung mit Klartext-Folgen, wenn Key-Backup fehlt (b5762ea)
- [x] Geräte-/Sessionverwaltung in den Einstellungen: bereits vorhanden
(`_SessionsSection` in `settings_modal.dart` Liste, Umbenennen, Verifizieren
per SAS/QR, Abmelden, Massen-Abmeldung mit Passwort-Reauth)
- [x] **SICHERHEIT: Dashboard-Admin-Endpoints ERLEDIGT: Loch geschlossen,
Heimnetz-only ist laut Bernd der Endzustand** (Fable-5-Review (o);
Entscheidung `ANTWORTEN_BERND.md` Nr. 3, 2026-07-05).
`server.py` nimmt `/api/ban`, `/api/unban`, `/api/toggle-registration`,
`/api/create-invite`, `/api/run-stats` jetzt 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 in `docs/DASHBOARD_AUTH_HARDENING.md`.
Zusatz (Fable-5-Review (p), 2026-07-04): `server.py` lä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 auf `conduit.toml`,
`send_admin`-Readback) ist mit `ADMIN_LOCK` geschlossen, 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ße `Content-Length` überall abgelehnt (kein Thread-Hänger,
ban/unban 4-KB-Deckel), Einladungs-Token per `secrets` statt `random`.
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-Leser
`registration-status` antwortet bei internen Fehlern jetzt generisch
statt mit `str(e)` (hätte den `conduit.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
verbliebenen `str(e)`-Antworten LAN-gegated, absichtlich).
**Abschluss (2026-07-05, `ANTWORTEN_BERND.md` Nr. 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 in `docs/DASHBOARD_AUTH_HARDENING.md` dokumentiert.
Nicht weiter daran arbeiten.**
- [x] **LiveKit-Token-Route server-seitig gebaut + verifiziert** (Fable-5-Review (n),
PROGRESS.md 2026-07-04). `POST /api/livekit-token` läuft live in `server.py`
(whoami-Auth, stdlib-HS256-Minting aus `livekit.yaml`), headless geprüft
(401/400/413/200, JWT-Signatur unabhängig gegen `livekit.yaml` gü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), siehe `docs/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-Token
`J5lax…` (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, damit
`apiSecret` aus `lib/core/livekit_token.dart` verschwindet (App holt nur das JWT).
**Umsetzungsplan liegt fertig in `docs/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-diagnostics` denselben
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.dart` benutzt diesen. Der
**Admin-Token ist damit nicht mehr im App-Code** (per `grep` bestä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.md` Nr. 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)
## 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.
- [x] Ist-Analyse geschrieben (PROGRESS.md 2026-07-03): Gott-Datei `settings_modal.dart`
(5541 Zeilen/50+ Klassen), Call-Schicht ohne gemeinsame Fassade (4 Module
importieren `voice_channel.dart` direkt), `app_state.dart` als Kopplungspunkt
aller Features, keine gemeinsame Storage-Schnittstelle (12 direkte
`SharedPreferences`-Zugriffe), toter Code (`message_bubble.dart`) + Duplikate
(`_formatTime`/`_formatDate` 3-fach). 8 Modul-Kandidaten dokumentiert.
- [x] 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ßerdem `settings` (aufgesplittet), `verification` (neu, aus Settings
herausgelöst), `storage` (Ausbau `settings_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_signaling` und `voice_channel` mit Fassaden-Interfaces +
Providern existieren (`lib/features/call_signaling/`,
`lib/features/voice_channel/`), alle Konsumenten gehen nur noch über
`callSignalingProvider`/`voiceChannelProvider` kein UI-Import der
Implementierungen mehr (Details/Abweichungen: Status-Kopf in
`docs/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;
außerdem stehen die inneren Aufräumarbeiten (UI-State aus den Managern
heraus, `call_ui`-Modul) noch aus.
- [ ] Einheitliches State-Management (Riverpod konsequent, keine Misch-Patterns)
- [x] Toten Code & Duplikate entfernen, Ordnerstruktur vereinheitlichen
(PROGRESS.md 2026-07-03: `message_bubble.dart` tot entfernt, doppelte
`_formatTime` in `message_group.dart` zusammengeführt; alle 6 in der
Ist-Analyse gefundenen Gott-Dateien per `part`/`part of` in
thematische Dateien aufgeteilt `widgets/settings_modal.dart`
(5541→270 Zeilen, 12 Teile unter `widgets/settings/`),
`features/chat/message_group.dart` (2775→30 Zeilen, 7 Teile unter
`features/chat/message/`), `features/rooms/rooms_panel.dart`
(2555→31 Zeilen, 8 Teile unter `features/rooms/panel/`),
`features/spaces/space_admin_dialog.dart` (2278→19 Zeilen, 6 Teile
unter `features/spaces/admin/`), `features/chat/chat_view.dart`
(2116→33 Zeilen, 6 Teile unter `features/chat/view/`),
`features/chat/document_viewer.dart` (1773→20 Zeilen, 5 Teile unter
`features/chat/document/`) jeweils verifiziert per automatisiertem
Blockvergleich gegen das Original und `flutter analyze`, siehe
PROGRESS.md-Einträge. `features/call/voice_channel.dart` (1123 Zeilen)
und `layout/app_shell.dart` (1073 Zeilen) bewusst NICHT mit angefasst
beide hängen eng mit der noch unentschiedenen Call-Fassade bzw. dem
Bootstrap/Login-Pfad zusammen, siehe „Call-Schicht entwirren" unten.
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).