# Bekannte Schwachstellen, Risiken & Technische Schulden (Flaws) Diese Datei dokumentiert eine ehrliche und schonungslose technische Analyse aller bekannten Limitierungen, Architektur-Kompromisse, Skalierungsrisiken und UX-Schwachstellen in **MindSift**. Sie dient als verbindlicher Backlog für zukünftige Refactorings und Architektur-Meilensteine. --- ## 📌 Übersicht & Risikomatrix | ID | Bereich | Problem / Schwachstelle | Schweregrad | Risiko im Produktivbetrieb | |---|---|---|---|---| | **FLAW-01** | Workspace Sync | Kein echtes CRDT/OT – Lock-Heartbeat-Modell mit Last-Write-Wins | **Gelöst (v2.1)** | Behoben durch Yjs CRDT-Engine & blockbasierte kollaborative Bearbeitung | | **FLAW-02** | Storage | Dual-Storage Drift (SQLite vs. Dateisystem) | **Obsolet / Gelöst** | Kein Risiko: MindSift nutzt Pure-DB (keine `.md`-Dateien auf Platte) | | **FLAW-03** | Netzwerk / WS | Proxy-Inkompatibilität bei WebSocket-Upgrades & Polling-Fallback | **Mittel** | Verzögerte Synchronisierung (1–3s Polling) in Firmennetzwerken | | **FLAW-04** | Datenbank / Suche | Volltextsuche via `LIKE '%query%'` statt SQLite FTS5 | **Mittel** | Performance-Einbrüche bei wachsenden Notizbeständen (>10.000 Blöcke) | | **FLAW-05** | Frontend DOM | Fehlende Virtualisierung im Outliner-Baum | **Mittel** | Frame-Drops und Lags bei Dokumenten mit >500 Blöcken auf Mobilgeräten | | **FLAW-06** | SQLite Concurrency | Single-Writer-Bottleneck bei hohem parallelem Schreibaufkommen | **Mittel** | `SQLITE_BUSY` Fehler bei vielen gleichzeitigen Team-Nutzern | | **FLAW-07** | Benachrichtigungen | Fehlende Dead-Letter-Queue für Webhook- & E-Mail-Alarme | **Niedrig** | Verlorene Benachrichtigungen bei temporärem Ausfall von Drittsystemen | | **FLAW-08** | Sicherheit / Auth | Statische JWT-Tokens ohne rotierende Refresh-Tokens & Blacklisting | **Niedrig** | Keine sofortige Sitzungsinvalidierung bei Rechteentzug | | **FLAW-09** | Mobile UX | Viewport-Verschiebung durch Virtual-Keyboard auf iOS/Android | **Niedrig** | Verdeckte Eingabefelder und springende Bottom-Navigationsleisten | | **FLAW-10** | Outliner UX | Kein hierarchischer Undo/Redo-Stack für Strukturmutationen | **Mittel** | Versehentliches Löschen/Einrücken von Blöcken nicht per `Ctrl+Z` rückgängig machbar | --- ## 🔍 Detaillierte Analyse & Maßnahmen ### FLAW-01: [GELÖST / IMPLEMENTIERT] Yjs Outliner CRDT & Multi-User Kollaboration - **Status:** **Gelöst via Yjs CRDT Engine (Lösung i)** - **Betroffene Komponenten:** [`api/websocket.md`](./api/websocket.md), [`modules/workspace.md`](./modules/workspace.md), `internal/ws/hub.go`, `internal/storage/sqlite.go`, `frontend/src/lib/crdt.ts`, `frontend/src/features/workspace/WorkspaceView.tsx` - **Ursache (Historisch):** MindSift nutzte ein kooperatives Dokumenten-Lease-Locking mit Last-Write-Wins. Bei parallelem Zugriff oder Netztrennung konnten Änderungen überschrieben werden. - **Implementierte Lösung (Yjs CRDT):** 1. **Block-Level Yjs CRDT:** Implementierung eines vollwertigen CRDT-Modells mit `Y.Doc`. Die Blockreihenfolge wird über ein `Y.Array` (Block-IDs) konfliktfrei mit dem YATA-Algorithmus aufgelöst. Inhalte und Metadaten (Text, Level, Checked, Collapsed) sind in `Y.Map` gekapselt. 2. **Wegfall von Ganzdokument-Sperren:** Keine blockierenden Sperren mehr (`isLockedByOther` entfernt). Mehrere Nutzer können zeitgleich am selben Dokument schreiben. 3. **Dual-Persistence:** CRDT-Zustände und Delta-Updates werden binär/Base64 in SQLite (`document_crdt_state`, `document_crdt_updates`) persistiert. Zur Aufrechterhaltung relationaler Indizes (Global To-Dos, Tag-Indizierung, Suche) werden Blöcke parallel in `document_blocks` materialisiert. 4. **Live-Awareness & Presence:** Block-Fokus und Cursor-Zustand werden via `CRDT_AWARENESS` übertragen. Nutzer sehen ein Live-Badge im Header (`Live (N)`) sowie farbige Kollaborator-Pillen an aktiven Blöcken. 5. **REST- & Proxy-Sicherheit:** Ergänzend zu WebSockets stehen Endpunkte `GET /api/v1/documents/:id/crdt` und `PUT /api/v1/documents/:id/crdt` bereit. --- ### FLAW-02: [OBSOLET / GELÖST] Dual-Storage Drift (SQLite vs. Dateisystem) - **Status:** **Entkräftet & Behoben (Pure Database Architecture)** - **Klarstellung:** In früheren Entwürfen wurde angedacht, Notizen zusätzlich als Markdown-Dateien auf dem Dateisystem zu spiegeln. MindSift speichert Notizen und Blöcke jedoch **zu 100% relational in SQLite** (`documents` und `document_blocks`). - **Verifikation:** - Es existieren **keine** Markdown-Dateien auf der Festplatte. - Das Go-Backend schreibt keine Dateien in ein Notizverzeichnis. - Unnötige Verzeichniserstellungen (`docDir`/`workspace`) wurden aus `internal/storage/sqlite.go` vollständig entfernt. - **Ergebnis:** Es gibt keinen Dual-Storage und somit keinen File-Drift oder externe Dateikonflikte. Notizen verhalten sich vollständig transaktional und konsistent innerhalb der SQLite-Datenbank. --- ### FLAW-03: Proxy-Inkompatibilität bei WebSocket-Upgrades - **Betroffene Komponenten:** [`api/websocket.md`](./api/websocket.md), `frontend/src/lib/stores/websocket.js` - **Ursache:** In restriktiven Firmennetzwerken oder über bestimmte Reverse-Proxies (Corporate SSL-Termination, Firewalls) schlägt der HTTP-Upgrade-Header `Upgrade: websocket` fehl (`NS_ERROR_CONNECTION_REFUSED` oder `400 Bad Request`). - **Folgen:** Der Client schaltet in den HTTP-Polling-Fallback um (alle 2 Sekunden). Dadurch: - Werden Sperren (*Document Locks*) und Online-Statusanzeigen verzögert übertragen. - Entsteht unnötige Serverlast durch kontinuierliche HTTP-Requests. - **Empfohlene Lösung:** 1. Sicherstellen von standardmäßigen Nginx/Traefik-Snippets in der Dokumentation (`proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";`). 2. Optionale Implementierung von Server-Sent Events (SSE) als unidirektionale Echtzeit-Alternative für Lesevorgänge bei blockiertem WebSocket. --- ### FLAW-04: Volltextsuche via `LIKE '%query%'` - **Betroffene Komponenten:** [`database.md`](./database.md), `internal/api/search.go`, `internal/api/thoughts.go` - **Ursache:** Suchabfragen über Gedanken, Notizblöcke und Aufgaben nutzen aktuell einfaches SQL: ```sql WHERE content LIKE '%' || ? || '%' ``` - **Folgen:** `LIKE '%...'` kann B-Tree-Indizes prinzipbedingt nicht nutzen und erzwingt einen sequentiellen Full Table Scan. Bei Datenbanken mit Zehntausenden Blöcken führt dies zu CPU-Spitzen und trägen Suchreaktionen. - **Empfohlene Lösung:** 1. Einrichten einer virtuellen SQLite FTS5-Tabelle (`document_blocks_fts`) mit Tokenizer (`unicode61` oder `trigram`). 2. Automatische Synchronisierung über SQLite Trigger (`AFTER INSERT`, `AFTER UPDATE`, `AFTER DELETE`). --- ### FLAW-05: Fehlende Virtualisierung im Outliner-Baum - **Betroffene Komponenten:** [`modules/workspace.md`](./modules/workspace.md), `frontend/src/lib/components/Outliner.svelte` - **Ursache:** Jeder Block im Notizbaum wird als eigenes DOM-Element gerendert. Enthält ein Dokument 500 bis 2000 Blöcke (z. B. lange Projektjournale oder Besprechungsprotokolle), befinden sich tausende verschachtelte `div`- und `contenteditable`-Elemente gleichzeitig im DOM. - **Folgen:** Auf älteren Smartphones oder bei schnellem Tippen kommt es zu spürbaren Input-Latenzen und Ruckeln beim Scrollen. - **Empfohlene Lösung:** 1. Implementierung einer virtuellen Fensterung (Virtual Windowing) für Blöcke außerhalb des Sichtbereichs. 2. Automatisches Einklappen (*Collapsing*) tiefer Hierarchieebenen beim initialen Laden langer Dokumente. --- ### FLAW-06: SQLite Concurrency & Single-Writer-Bottleneck - **Betroffene Komponenten:** [`architecture.md`](./architecture.md), [`database.md`](./database.md), `internal/storage/sqlite.go` - **Ursache:** Obwohl SQLite mit `PRAGMA journal_mode = WAL;` parallele Leser erlaubt, kann zu jedem Zeitpunkt immer nur **ein einziger Thread** schreiben. Bei intensiver gleichzeitiger Nutzung (z. B. 20 Teammitglieder tippen gleichzeitig in Listen und Notizen, während im Hintergrund Webhook-Events einlaufen) können Schreib-Transaktionen aufeinander warten. - **Folgen:** Risiko von `database is locked (SQLITE_BUSY)`-Fehlern, wenn der Busy-Timeout überschritten wird. - **Empfohlene Lösung:** 1. Setzen von `PRAGMA busy_timeout = 5000;` (5 Sekunden Wartezeit vor Fehlerwurf). 2. Kapselung aller Schreibzugriffe über einen dedizierten Go-Channel / Single-Worker-Pool oder Migration auf PostgreSQL bei größeren Installationen. --- ### FLAW-07: Fehlende Dead-Letter-Queue für Benachrichtigungen - **Betroffene Komponenten:** [`modules/tasks.md`](./modules/tasks.md), `internal/scheduler/alarm_ticker.go` - **Ursache:** Der Alarm-Ticker überprüft minütlich fällige Aufgaben und sendet Webhooks bzw. E-Mails. Schlägt der HTTP-Post an den externen Webhook fehl (z. B. Zielserver vorübergehend offline), wird der Fehler lediglich ins Log geschrieben. - **Folgen:** Der Alarm wird nicht erneut versucht und die Benachrichtigung geht dauerhaft verloren. - **Empfohlene Lösung:** 1. Einführung einer Tabelle `notification_outbox` mit Spalten `retry_count`, `next_retry_at`, `status`. 2. Exponentieller Backoff (1m, 5m, 15m, 1h) und Status-Markierung (*failed*, *delivered*). --- ### FLAW-08: Statische JWT-Tokens ohne Blacklisting - **Betroffene Komponenten:** [`architecture.md`](./architecture.md), `internal/api/auth.go` - **Ursache:** Nach dem Login wird ein JWT ausgestellt, das clientseitig im `localStorage` gespeichert wird. Der Server prüft lediglich die kryptografische Signatur des Tokens, führt aber keine serverseitige Sitzungsliste. - **Folgen:** Wird einem Benutzer die Berechtigung entzogen oder ändert er sein Passwort, bleibt ein bereits ausgestellter Token bis zu seinem Ablaufdatum (z. B. 7 Tage) gültig. - **Empfohlene Lösung:** 1. Einführung einer `revoked_tokens`-Tabelle oder einer `token_version`-Spalte in der `users`-Tabelle. 2. Implementierung von kurzlebigen Access-Tokens (15 Min.) in Verbindung mit rotierenden Refresh-Tokens in `httpOnly`-Cookies. --- ### FLAW-09: Mobile Viewport-Verschiebung durch Virtual-Keyboard - **Betroffene Komponenten:** `frontend/src/routes/+layout.svelte`, `frontend/src/lib/components/BottomNav.svelte` - **Ursache:** Öffnet sich auf iOS Safari oder Android Chrome die Bildschirmtastatur, ändert der Browser die Höhe des sichtbaren Viewports dynamisch. Feste CSS-Attribute wie `height: 100vh` oder `fixed bottom-0` führen dazu, dass Eingabefelder unter die Tastatur rutschen oder die Navigationsleiste den Bildschirm verdeckt. - **Folgen:** In der mobilen Triage- und Aufgabenansicht muss der Nutzer manuell scrollen, um die Speichern-Buttons zu erreichen. - **Empfohlene Lösung:** 1. Nutzung der modernen `window.visualViewport`-API in Svelte. 2. Dynamisches Setzen einer CSS-Variable `--vvh` (`visualViewport.height`) zur präzisen Anpassung von Modals und Bottom-Sheets. --- ### FLAW-10: Kein hierarchischer Undo/Redo-Stack im Outliner - **Betroffene Komponenten:** [`modules/workspace.md`](./modules/workspace.md), `frontend/src/lib/components/Outliner.svelte` - **Ursache:** Standardmäßiges Browser-`Ctrl+Z` funktioniert nur innerhalb eines einzelnen aktiven Textfelds (`contenteditable`). Strukturelle Aktionen – wie das Verschieben von Blöcken via Drag & Drop, Einrücken (`Tab`), Ausrücken (`Shift+Tab`) oder das Löschen ganzer Unterbäume – sind im Standard-Browser-Undo-Stack nicht enthalten. - **Folgen:** Verschiebt ein Nutzer versehentlich einen Block samt 10 Unterpunkten, kann dies nicht mit `Ctrl+Z` rückgängig gemacht werden. - **Empfohlene Lösung:** 1. Implementierung eines expliziten In-Memory Command-Stacks im Frontend (Command-Pattern: `execute()` / `undo()`). 2. Aufzeichnung aller Block-Mutationen (Insert, Indent, Move, Delete) mit Revert-Payloads. --- ## 🧭 Verwandte Dokumente - 🏗️ [Gesamtarchitektur](architecture.md) - 💾 [Datenbank-Design & Tabellenschemata](database.md) - 🔌 [WebSocket & Live-Protokoll](api/websocket.md) - 🧠 [Workspace-Modul (Outliner)](modules/workspace.md) - 🛡️ [Rechte & Freigabesystem](sharing-and-permissions.md)