Files
MindSift/Doku/flaws.md
T
sandrodach 3480859d8a
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 8s
docs: add comprehensive technical wiki documentation and flaws analysis; remove legacy BRAIN.md
2026-10-26 13:27:35 +01:00

12 KiB
Raw Blame History

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 Kritisch Zeilenverlust bei gleichzeitigen Bearbeitungen oder Netzschwankungen
FLAW-02 Storage Dual-Storage Drift (SQLite document_blocks vs. Dateisystem .md) Hoch Externe Änderungen an .md werden nicht automatisch in SQLite gespiegelt
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: Kein echtes CRDT/OT – Lock-Heartbeat & Last-Write-Wins

  • Betroffene Komponenten: api/websocket.md, modules/workspace.md, internal/ws/hub.go, frontend/src/lib/components/Outliner.svelte
  • Ursache: MindSift nutzt zur Vermeidung von Konflikten eine kooperative Bearbeitungssperre (Document Lease Lock, 30 Sekunden TTL mit Heartbeat). Wenn zwei Nutzer dasselbe Dokument geöffnet haben, blockiert der aktive Schreiber das Bearbeiten für andere. Verliert ein Nutzer jedoch kurzzeitig die WebSocket-Verbindung oder tippt während des Übergangs zum Polling-Fallback, werden Änderungen per REST via Last-Write-Wins (PUT /api/v1/documents/:id) gespeichert.
  • Folgen: Wie in früheren Versionen beobachtet, können bei parallelem Schreiben Zeilen überschrieben werden oder Strukturhierarchien (Parent-IDs) verloren gehen.
  • Empfohlene Lösung:
    1. Langfristige Migration auf ein echtes blockweises CRDT-Framework (wie Yjs oder Automerge).
    2. Kurzfristig: Optimistisches Block-Locking (Sperre nur auf Block-Ebene block_id statt des gesamten Dokuments) und atomare Versionsprüfung (Optimistic Concurrency Control mit version_seq).

FLAW-02: Dual-Storage Drift (SQLite vs. Dateisystem .md)

  • Betroffene Komponenten: database.md, internal/storage/sqlite.go, internal/storage/markdown_sync.go
  • Ursache: MindSift pflegt Notizen primär in SQLite (documents, document_blocks), exportiert bzw. spiegelt sie jedoch als reine .md-Dateien auf das Dateisystem (data/notes/), um Exportierbarkeit und lokale Lesbarkeit zu gewährleisten.
  • Problem: Es existiert kein systemweiter File-Watcher (fsnotify). Bearbeitet der Nutzer eine Notizdatei direkt auf dem Host (z. B. via Obsidian, VS Code, Git-Sync oder SSH), bekommt die SQLite-Datenbank dies nicht mit. Beim nächsten Web-Edit überschreibt SQLite die Datei auf der Festplatte.
  • Empfohlene Lösung:
    1. Integrieren eines Background-File-Watchers mit fsnotify für das Datenverzeichnis.
    2. Implementieren einer Hash-Prüfung (sha256 der Datei vs. DB-Metadaten) vor dem Speichern. Bei Abweichung: Nutzerkonflikt-Dialog anzeigen.

FLAW-03: Proxy-Inkompatibilität bei WebSocket-Upgrades

  • Betroffene Komponenten: 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, internal/api/search.go, internal/api/thoughts.go
  • Ursache: Suchabfragen über Gedanken, Notizblöcke und Aufgaben nutzen aktuell einfaches 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, 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, 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, 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, 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, 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