SSEBroadcaster for Real-Time UI Updates: Architecture and Implementation in Claude-Mem
The SSEBroadcaster in Claude-Mem manages Server-Sent Events (SSE) connections by maintaining a Set of active Express Response objects and broadcasting JSON payloads directly to all connected clients in a single pass, enabling instantaneous UI synchronization without polling overhead.
The Claude-Mem repository implements a lightweight real-time communication layer using the SSEBroadcaster class to push live updates to the viewer UI. This architecture eliminates traditional polling overhead by leveraging Server-Sent Events (SSE) to stream domain-specific events directly from the worker service to connected browsers. Understanding the SSEBroadcaster for real-time UI updates reveals how Claude-Mem maintains synchronized state across multiple viewer clients with minimal latency.
Core Architecture Layers
The SSEBroadcaster operates through three distinct layers that manage client lifecycle, message distribution, and service integration.
Client Management Layer
At the foundation, the SSEBroadcaster maintains a Set<SSEClient> containing active Express Response objects. When a browser connects to the /stream endpoint defined in src/services/worker/http/routes/ViewerRoutes.ts, the broadcaster invokes addClient(res) to register the connection. The implementation attaches a close event listener to automatically trigger removeClient when the browser disconnects, preventing memory leaks. Upon successful registration, the broadcaster immediately transmits a connected event to confirm the SSE stream is active.
Event Broadcasting Layer
The broadcast(event) method in src/services/worker/SSEBroadcaster.ts handles message distribution through a single-pass iteration over the client set. For each event, the method constructs a JSON payload enriched with a server-side timestamp, formats it according to the SSE protocol, and writes directly to each client's response stream using client.write(data). This direct-write approach ensures all connected viewers receive identical snapshots simultaneously without intermediate queuing or batching delays.
Integration Points
The SSEBroadcaster serves as a shared dependency across multiple high-level services:
- ViewerRoutes: Creates the
/streamendpoint and pushes initialinitial_loadandprocessing_statusevents upon client connection. - SessionEventBroadcaster: Translates domain actions into SSE events including
new_prompt,session_started,observation_queued, andsession_completed. - WorkerService: Invokes
broadcastProcessingStatus()to emit real-time queue depth updates viaprocessing_statusevents whenever the internal state changes.
Real-Time Data Flow
The SSEBroadcaster facilitates a continuous data pipeline from server to browser:
- Client Connection: Browser opens
EventSourcetoGET /stream→ ViewerRoutes callsSSEBroadcaster.addClient(res). - Warm-up Payload: Server immediately sends
initial_load(project list) and currentprocessing_status. - Event Trigger: Business logic fires (e.g., new prompt arrives) → SessionEventBroadcaster calls
SSEBroadcaster.broadcast(). - Client Update: All connected browsers receive the event via open EventSource, update React state, and re-render UI instantly.
- Disconnection: Browser closes tab →
closeevent triggersSSEBroadcaster.removeClient, cleaning up the response object.
Because the broadcaster writes directly to the raw Response stream, there is no JSON-API wrapper or polling overhead; the UI stays in sync with the worker's internal state with minimal latency.
Implementation Examples
Registering an SSE Client
When a viewer connects to the stream endpoint, the Express response is configured for SSE and registered with the broadcaster:
// In src/services/worker/http/routes/ViewerRoutes.ts
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
this.sseBroadcaster.addClient(res);
Broadcasting Custom Events
Any service can emit real-time updates by invoking the broadcast method with a typed event payload:
// Example: Notifying the UI when an observation is stored
this.sseBroadcaster.broadcast({
type: 'observation_saved',
observationId: 'obs-12345',
project: 'claude-mem',
timestamp: Date.now()
});
Client-Side Event Consumption
The browser connects to the SSE endpoint and listens for specific event types to update the UI state:
const evtSource = new EventSource('http://localhost:37777/stream');
evtSource.addEventListener('new_prompt', (ev) => {
const data = JSON.parse(ev.data);
// Update React state with the new prompt information
updateUI(data);
});
Broadcasting Processing Status
The worker service automatically emits queue status updates whenever the processing state changes:
// Inside WorkerService.broadcastProcessingStatus()
this.sseBroadcaster.broadcast({
type: 'processing_status',
isProcessing: this.isProcessing,
queueDepth: this.queue.length
});
Summary
- The SSEBroadcaster maintains a
Setof active ExpressResponseobjects to manage persistent SSE connections. - The
broadcast()method writes JSON payloads directly to all connected clients in a single pass, ensuring synchronized real-time updates. - Integration points include ViewerRoutes for the
/streamendpoint, SessionEventBroadcaster for domain events, and WorkerService for processing status. - Automatic cleanup via
removeClientprevents memory leaks when browsers disconnect from the SSE stream.
Frequently Asked Questions
How does SSEBroadcaster handle client disconnections?
The SSEBroadcaster attaches a close event listener to each Express Response object upon registration. When a browser closes the connection or navigates away, the close event fires and automatically invokes removeClient to delete the response from the internal Set, preventing memory leaks and stale connections.
What types of events can be broadcast through SSEBroadcaster?
The SSEBroadcaster supports any JSON-serializable event payload. Common event types used in Claude-Mem include connected, initial_load, processing_status, new_prompt, session_started, observation_queued, session_completed, and observation_saved. The type field in the payload allows the client-side EventSource to route events to appropriate handlers.
How does the SSEBroadcaster differ from WebSocket implementations?
Unlike WebSockets, which require bidirectional message framing and protocol negotiation, the SSEBroadcaster leverages HTTP's native Server-Sent Events protocol. This approach uses standard Express Response objects with text/event-stream headers, eliminating the need for separate WebSocket libraries or connection upgrade logic. The architecture is unidirectional (server-to-client), making it ideal for broadcasting state updates without client-to-server messaging overhead.
Where is the SSEBroadcaster instantiated in the Claude-Mem codebase?
The SSEBroadcaster is instantiated as a singleton within the worker service architecture and injected into dependent services. It is created in the service composition layer and passed to ViewerRoutes for the HTTP endpoint handling, SessionEventBroadcaster for domain event translation, and WorkerService for processing status updates. This dependency injection pattern ensures a single source of truth for all SSE communications across the worker service.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →