# SSE Implementation Guide ## Quick Start This guide will help you implement and test the SSE-based session architecture. ## Step 1: Integration with server.js Add these lines to your existing `/home/uroma/obsidian-web-interface/server.js`: ```javascript // After existing imports (around line 12) const eventBus = require('./services/event-bus'); const sseManager = require('./services/sse-manager'); const sessionRoutes = require('./routes/session-routes'); const sseRoutes = require('./routes/sse-routes'); // After existing middleware (around line 270) // Register new API routes app.use('/api', sessionRoutes); app.use('/api', sseRoutes); // Add monitoring endpoint app.get('/api/debug/metrics', (req, res) => { res.json({ eventBus: eventBus.getMetrics(), sse: sseManager.getStats(), timestamp: Date.now() }); }); // Update graceful shutdown (around line 2600) const originalCleanup = /* existing cleanup logic or null */; process.on('SIGTERM', async () => { console.log('[Server] Starting graceful shutdown...'); // Cleanup SSE connections sseManager.cleanup(); // ... existing cleanup ... process.exit(0); }); ``` ## Step 2: Modify ClaudeService to emit events In `/home/uroma/obsidian-web-interface/services/claude-service.js`: ```javascript // Add at top of file const eventBus = require('./event-bus'); // Find the section where Claude output is handled // Replace callback-based approach with EventBus emits // Example: When output is received handleSessionOutput(sessionId, output) { // Old way: // this.emit('session-output', { sessionId, output }); // New way: eventBus.emit('session-output', { sessionId, type: 'stdout', content: output, timestamp: Date.now() }); } // Example: When operations are detected handleOperationsDetected(sessionId, operations, response) { eventBus.emit('operations-detected', { sessionId, operations, response: response.substring(0, 500) + '...' }); } ``` ## Step 3: Test SSE Endpoint ### Test 1: Basic SSE Connection ```bash # Terminal 1: Start server cd /home/uroma/obsidian-web-interface npm start # Terminal 2: Create a test session curl -X POST http://localhost:3010/claude/api/claude/sessions \ -H "Content-Type: application/json" \ -d '{"workingDir":"/home/uroma"}' # Note the session ID from response # Terminal 3: Test SSE connection (replace SESSION_ID) curl -N http://localhost:3010/api/session/SESSION_ID/events ``` ### Test 2: SSE with curl (watch events) ```bash # Connect to SSE and watch for events curl -N -H "Accept: text/event-stream" \ http://localhost:3010/api/session/SESSION_ID/events ``` ### Test 3: Send command via REST API ```bash # Send a command curl -X POST http://localhost:3010/api/session/SESSION_ID/prompt \ -H "Content-Type: application/json" \ -d '{"command":"ls -la"}' # Watch for output in your SSE connection (Test 2) ``` ## Step 4: Browser Testing Create a test HTML file `/home/uroma/obsidian-web-interface/public/test-sse.html`: ```html SSE Test

SSE Test

``` Access it at: `http://localhost:3010/test-sse.html` ## Step 5: Monitor Metrics ```bash # Check EventBus and SSE metrics curl http://localhost:3010/api/debug/metrics # Expected response: { "eventBus": { "eventsEmitted": 1234, "eventsByType": { "session-output": 800, "session-error": 5, ... }, "listenerCounts": { "session-output-session-123": 1, ... }, "activeListeners": 10 }, "sse": { "totalSessions": 3, "totalConnections": 5, "sessions": { "session-123": 2, "session-456": 1, ... }, "totalCreated": 50, "totalClosed": 45, "activeHeartbeats": 5 }, "timestamp": 1234567890 } ``` ## Step 6: Test Reconnection 1. Start SSE connection in browser 2. Kill and restart server 3. Verify automatic reconnection with exponential backoff 4. Check browser console for reconnection logs ## Step 7: Load Testing ```javascript // In browser console, run: const connections = []; for (let i = 0; i < 10; i++) { const sessionId = 'session-123'; // Use a real session ID const stream = new SessionEventStream(sessionId); connections.push(stream); } // Check metrics fetch('/api/debug/metrics').then(r => r.json()).then(console.log); // Cleanup connections.forEach(s => s.disconnect()); ``` ## Step 8: nginx Configuration If using nginx as reverse proxy, add this configuration: ```nginx # In your nginx server block location /api/session/ { # Disable buffering for SSE proxy_buffering off; proxy_cache off; # Pass to backend proxy_pass http://localhost:3010; proxy_http_version 1.1; proxy_set_header Connection ''; proxy_set_header Cache-Control no-cache; proxy_set_header X-Accel-Buffering no; # Increase timeouts for long-lived connections proxy_read_timeout 86400s; # 24 hours proxy_send_timeout 86400s; # Ensure no buffering proxy_buffering off; } ``` ## Testing Checklist - [ ] SSE connection established successfully - [ ] Events received in real-time - [ ] Multiple clients can connect to same session - [ ] Reconnection works on connection drop - [ ] Heartbeat prevents timeout - [ ] Metrics endpoint returns correct data - [ ] Session validation works (404 for invalid session) - [ ] Command sent via REST API produces output via SSE - [ ] No memory leaks after extended use - [ ] Works through nginx reverse proxy ## Troubleshooting ### Issue: SSE connection closes immediately **Solution:** Check nginx configuration, ensure `X-Accel-Buffering: no` is set. ### Issue: No events received **Solution:** Check session ID is valid and session exists. Check browser console for errors. ### Issue: Frequent reconnections **Solution:** Check network stability, increase `heartbeatTimeout` in client options. ### Issue: Memory usage increasing **Solution:** Check EventBus listeners are properly unsubscribed on disconnect. ## Next Steps After successful testing: 1. Update existing UI to use SSE instead of WebSocket 2. Add feature flag for gradual rollout 3. Monitor production metrics 4. Deprecate WebSocket endpoint 5. Remove legacy code after migration complete