# πŸ“‘ 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 & Proxy-Resilienz (Dual-Transport Architektur) In restriktiven Firmennetzwerken, hinter Unternehmens-Proxies (z.B. Zscaler, BlueCoat) oder fehlerhaft konfigurierten Reverse-Proxies wird der `Upgrade: websocket`-Header hΓ€ufig verworfen (`NS_ERROR_CONNECTION_REFUSED`, Code 1006 / 1002 / 1008). MindSift lΓΆst dieses Problem durch eine **vollwertige Dual-Transport-Architektur** ([FLAW-03](../flaws.md)): ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MindSift Frontend β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β–Ό β–Ό Standard: WebSockets Fallback: SSE + REST (wss://.../api/v1/ws) (https://.../api/v1/events) β”‚ β”‚ β”‚ [Proxy Upgrade Block (Code 1006/Timeout)]β”‚ β”‚ ────────────────────────────────────────> β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Gorilla WS Conn β”‚ β”‚ Server-Sent Events β”‚ β”‚ Bidirektionaler β”‚ β”‚ (text/event-stream) β”‚ β”‚ TCP-WebSocket β”‚ β”‚ Standard HTTPS Port β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Realtime Hub β”‚ β”‚ (Multiplexing WS & SSE) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### 1. Automatischer SSE-Fallback (`GET /api/v1/events`) - Scheitert der WebSocket-Handshake oder blockiert ein Proxy das Upgrade, wechselt MindSift innerhalb von 3,5 Sekunden automatisch auf einen **Server-Sent Events (SSE) Stream**. - Da SSE ein standardmÀßiger HTTP/1.1- bzw. HTTP/2-GET-Request ΓΌber Standard-Port 443/80 ist (`Content-Type: text/event-stream`), wird er von jedem Proxy und jeder Firewall ungehindert durchgeleitet. - Header `X-Accel-Buffering: no` und `Cache-Control: no-cache` verhindern Pufferung im Proxy; periodische Ping-Kommentare (`: ping\n\n`) alle 15 Sekunden halten Timeouts fern. - Der Client bleibt dadurch **vollstΓ€ndig echtzeitfΓ€hig** (`Live (SSE)`). ### 2. REST-Endpunkte fΓΌr Upstream-Aktionen im SSE-Modus Da SSE unidirektional (Server βž” Client) ist, stellt MindSift fΓΌr SchreibvorgΓ€nge im SSE-Modus REST-Γ„quivalente bereit: - `PUT /api/v1/documents/:id/crdt`: UnterstΓΌtzt sowohl volles `state` (Compaction) als auch inkrementelles `update` (Delta). Der Hub persistiert das Update und broadcastet es sofort an alle anderen WS- und SSE-Clients im Dokumentenraum. - `POST /api/v1/documents/:id/awareness`: ÜbertrΓ€gt Cursor- und Blockfokus-PrΓ€senzen. - `DELETE /api/v1/documents/:id/awareness`: Informiert Peers beim Verlassen des Dokuments. - `POST /api/v1/events/rooms`: Erlaubt SSE-Clients, Dokument- oder ListenrΓ€umen dynamisch beizutreten (`join`) oder sie zu verlassen (`leave`). ### 3. Verbindungs-Indikator - **GrΓΌn pulsierend:** Live verbunden ΓΌber WebSockets (`Live (WebSocket)`). - **TΓΌrkis pulsierend:** Live verbunden ΓΌber Server-Sent Events (`Live (Server-Sent Events / Proxy-Stream)`). - **Gelb/Amber:** TemporΓ€re Unterbrechung mit automatischem HTTP-Polling-Schutz. AusfΓΌhrliche Konfigurationsbeispiele fΓΌr Nginx, Traefik, Caddy, Cloudflare und Apache finden sich im separaten Handbuch: πŸ‘‰ **[Reverse-Proxy-Konfigurationshandbuch](../reverse-proxy.md)**