# 📡 WebSocket & Echtzeit-Synchronisation MindSift implementiert eine bidirektionale Echtzeit-Synchronisationsschicht auf Basis von WebSockets ([`internal/ws/hub.go`](../../internal/ws/hub.go)). Sie sorgt dafür, dass Änderungen an Aufgaben, Triage-Karten, Notizen und geteilten Einkaufslisten ohne manuelles Neuladen sofort auf allen geöffneten Geräten und bei allen Kollaborationspartnern sichtbar werden. --- ## 🔌 1. Verbindung & Handshake ### Verbindungs-Endpunkte ``` wss:///api/v1/ws?token= wss:///api/v1/ws/lists/?token= ``` ### Authentifizierung & Upgrade - Die Authentifizierung erfolgt über das JWT-Token im Query-Parameter `?token=...` oder den `Authorization: Bearer`-Header während des HTTP-Upgrade-Requests. - Der Server extrahiert die `userID` und registriert die Verbindung im internen `Hub`. - Der `Upgrader` akzeptiert alle Origins (`CheckOrigin: return true`), um volle Kompatibilität mit Reverse-Proxies (Traefik, Nginx, Caddy, Cloudflare) und Docker-Netzwerken zu gewährleisten. --- ## 🏛️ 2. Hub-Architektur & Rooms Der WebSocket-Hub unterteilt Verbindungen in drei Abstraktionsebenen: ``` ┌─────────────────────────────────────────────────────────────────┐ │ WebSocket Hub │ ├─────────────────┬─────────────────────────────┬─────────────────┤ │ allClients │ userRooms │ rooms │ │ (Alle aktiven │ (Pro Benutzer-ID) │ (Pro Liste/ │ │ Verbindungen) │ Cross-Device Sync für │ Space) │ │ │ Tasks, Triage & Workspace │ Geteilte Liste │ └────────┬────────┴──────────────┬──────────────┴────────┬────────┘ ▼ ▼ ▼ Presence Stats TASK_CREATED ITEM_TOGGLED DOC_LOCKED TASK_UPDATED ITEM_CREATED DOC_UNLOCKED DOC_UPDATED LIST_UPDATED ``` 1. **`allClients` (Global):** Hält jede aktive Client-Verbindung. Ermöglicht Broadcasts systemweiter Ereignisse (z.B. globale Dokument-Sperrungen). 2. **`userRooms[userID]` (Benutzer-Räume):** Sendet Events an alle Geräte desselben Benutzers (z.B. Desktop und Smartphone). Erzeugt ein Webhook oder Quick Dump auf dem Smartphone einen Eintrag, taucht dieser zeitgleich auf dem Desktop auf. 3. **`rooms[listID]` (Kollaborations-Räume):** Multiplexing für geteilte Listen. Alle Mitglieder einer Liste empfangen Aktionen (wie Abhaken, Löschen, Umsortieren) in Echtzeit. --- ## 📨 3. Nachrichten-Format (Envelope) Alle WebSocket-Nachrichten werden als JSON mit einheitlicher Hülle übertragen: ```json { "event": "ITEM_TOGGLED", "listId": "lst_12345", "payload": { "itemId": "itm_987", "isCompleted": true, "completedBy": "usr_sandro", "completedByName": "Sandro" }, "timestamp": "2026-10-26T13:30:00Z" } ``` --- --- ## ⚡ 4. CRDT-Echtzeitprotokoll (Yjs Collaborative Editing) Seit Version 2.1 unterstützt MindSift **echte, konfliktfreie Mehrbenutzer-Kollaboration** für Notizen via Yjs CRDT. Clients tauschen binäre Delta-Updates und Live-Awareness-Informationen über dedizierte WebSocket-Nachrichten aus: ### CRDT-Nachrichtentypen | Event | Richtung | Zweck / Payload | |---|---|---| | `CRDT_JOIN` | Client ➔ Server | Tritt dem Dokumentenraum (`doc:{docId}`) bei. | | `CRDT_INIT` | Server ➔ Client | Sendet den aktuellen Basiszustand (`stateBlob`) und offene Delta-Updates (`updates[]`) an den Client. | | `CRDT_UPDATE` | Bidirektional | Überträgt inkrementelle binäre Yjs-Updates (Base64-kodiert). Server speichert in `document_crdt_updates` und verteilt an alle anderen Raumnutzer. | | `CRDT_COMPACT` | Client ➔ Server | Komprimiert mehrere Updates zu einem konsolidierten Snapshot in `document_crdt_state`. | | `CRDT_AWARENESS` | Bidirektional | Überträgt Live-Präsenz (Fokus auf `blockId`, Benutzername, Farbe). | | `CRDT_AWARENESS_LEAVE` | Bidirektional | Signalisiert, dass ein Nutzer den Fokus verloren hat oder das Dokument verlassen hat. | | `CRDT_LEAVE` | Client ➔ Server | Verlässt den Dokumentenraum explizit. | ```mermaid sequenceDiagram autonumber actor UserA as Benutzer A participant WS as WebSocket Hub & SQLite actor UserB as Benutzer B UserA->>WS: CRDT_JOIN (docId: "doc_123") WS-->>UserA: CRDT_INIT (stateBlob, updates) UserB->>WS: CRDT_JOIN (docId: "doc_123") WS-->>UserB: CRDT_INIT (stateBlob, updates) UserA->>WS: CRDT_UPDATE (docId, updateBlob) WS->>WS: Append in document_crdt_updates WS->>UserB: CRDT_UPDATE (docId, updateBlob) Note over UserB: Yjs wendet Delta atomar an (0 Konflikte) UserA->>WS: CRDT_AWARENESS (docId, focusedBlockId) WS->>UserB: CRDT_AWARENESS (docId, user, focusedBlockId) Note over UserB: Avatar-Badge erscheint am Block ``` --- ## 🔒 5. Abwärtskompatibles Document-Lease-Locking Vor Version 2.1 nutzte MindSift ein serverseitiges Lease-Locking mit 30-Sekunden-Heartbeats. Durch die CRDT-Engine wurde die blockierende Bearbeitungssperre (`isLockedByOther`) abgelöst: - **Aktuelles Verhalten:** Mehrere Benutzer mit Bearbeitungsrechten können zeitgleich dasselbe Dokument bearbeiten. - **Presence & Locks:** Die Events `LOCK_DOC` und `HEARTBEAT_DOC` bleiben abwärtskompatibel erhalten und dienen nun der reinen Präsenzanzeige (z.B. gelber Statusindikator „wird gerade von X angesehen"). - **Schreibschutz:** Ein echter Schreibschutz (`isReadOnly`) greift ausschließlich noch dann, wenn der Nutzer nur über Leserechte (`permission === 'read'`) für die Notiz oder den Ordner verfügt. --- ## 🔄 5. Fallback-Strategie & Reconnect In strikten Firmennetzwerken oder bei Proxy-Problemen (z.B. fehlende `Upgrade`-Header bei HTTP/1.1) kann eine WebSocket-Verbindung fehlschlagen: 1. **Exponentielles Backoff:** Das Frontend ([`frontend/src/lib/api.ts`](../../frontend/src/lib/api.ts)) versucht automatisch die Wiederverbindung nach 1s, 2s, 4s, 8s, bis maximal 30s. 2. **HTTP-Polling Fallback:** Geteilte Listen und Notizen aktivieren im Hintergrund ein periodisches Polling (5s Intervall), falls der WebSocket-Status nicht `CONNECTED` ist. 3. **Status-Indikator:** Ein dezenter grüner bzw. gelber Indikator oben rechts im Header zeigt dem Benutzer jederzeit den aktuellen Live-Verbindungsstatus an.