WebSocket Service
Relevant source files
The following files were used as context for generating this wiki page:
The WebSocket service provides the real-time, bidirectional communication layer between the monGARS backend (FastAPI) and the frontend web interface. Its primary purpose is to stream chat responses from the assistant to the user, providing an interactive and responsive experience. The service is designed with security in mind, utilizing a ticket-based authentication system to authorize connections.
The entire real-time event system is built on a few key components: a secure ticket issuer (ws_ticket.py), a central connection manager (ws_manager.py), and a decoupled event bus (ui_events.py) that allows various parts of the backend to publish events to the UI without being directly coupled to the WebSocket implementation. This architecture ensures that the chat UI can receive streaming updates, history replays, and other real-time notifications.
Sources: README.md, monGARS/api/AGENTS.md, monGARS/api/ws_manager.py, monGARS/api/ws_ticket.py
Architecture Overview
The WebSocket system involves the client (browser), the Django webapp, and the FastAPI backend. The client first authenticates with the backend to get a JWT. It then uses this JWT to request a short-lived, single-use WebSocket ticket. This ticket is used to establish the WebSocket connection, which is then managed by the WebSocketManager on the server.
The following diagram illustrates the high-level flow of establishing a connection and broadcasting a message.
Connection Lifecycle
The connection process is secured by a ticket-based system to prevent unauthorized WebSocket connections.
- Ticket Request: The authenticated client makes a
POSTrequest to/api/v1/auth/ws/ticketto obtain a single-use ticket. - Ticket Issuance: The backend, using
TicketSigner, generates a signed, short-lived ticket containing the user's identity and an expiration time. - Connection Attempt: The client uses this ticket to open a WebSocket connection to the
/ws/{ticket}endpoint. - Ticket Verification: The server verifies the ticket's signature and expiration. If valid, the connection is accepted and registered with the
WebSocketManager. - Communication: The client and server can now exchange messages. The server primarily uses the connection to broadcast events to the client.
- Disconnection: When the client disconnects, the
WebSocketManagerremoves the connection from its active pool.
The sequence diagram below details this flow.
Core Components
Ticket-Based Authentication
To secure the WebSocket endpoint, monGARS uses a ticket system instead of relying on long-lived JWTs directly in the WebSocket URL.
Sources: monGARS/api/ws_ticket.py, monGARS/api/AGENTS.md
TicketSigner
This class is responsible for creating and verifying tickets. It uses a secret key for signing and has a configurable expiration time for tickets.
issue_ticket(user_id): Creates a signed payload containing theuser_id, anexp(expiration) timestamp, and asub(subject) set to "ws-ticket".verify_ticket(ticket): Decodes the ticket, validates its signature and expiration, and returns the payload if valid. It raises aWebSocketExceptionon failure.
Sources: monGARS/api/ws_ticket.py:21-63
Ticket Issuance Endpoint
The client obtains a ticket by calling this endpoint.
- Endpoint:
POST /api/v1/auth/ws/ticket - Authentication: Requires a valid JWT bearer token.
- Response: A JSON object containing the single-use ticket.
{ "ticket": "a.signed.ticket" }
Sources: webapp/static/js/src/services/http.js:34-45
Connection Management
The WebSocketManager is the central hub for managing all active WebSocket connections. It is designed as a singleton to be shared across the application.
Sources: monGARS/api/ws_manager.py, monGARS/api/AGENTS.md
WebSocketManager Class
This diagram shows the structure of the WebSocketManager.
Sources: monGARS/api/ws_manager.py:17-29
Key methods include:
connect(websocket, user_id): Adds a new WebSocket connection to a set associated with theuser_id.disconnect(websocket): Removes a WebSocket from all user connection sets.broadcast(message, user_id): Sends a message to all active connections for a specificuser_id.on_event(event): A handler that listens forUIEvents from theUIEventBusand callsbroadcastto forward them to the relevant user.
Sources: monGARS/api/ws_manager.py:31-72
Event Broadcasting
The system uses a decoupled event bus, UIEventBus, to allow different parts of the application to send messages to the UI without knowing about the WebSocketManager.
UIEvent: A Pydantic model representing a message to be sent to the UI. It includesuser_id,eventtype, and adatapayload.UIEventBus: A simple pub/sub class withpublishandsubscribemethods. TheWebSocketManagersubscribes itson_eventmethod to this bus upon initialization.- When another service (e.g., the conversational AI core) needs to send an update, it publishes a
UIEventto the global event bus. TheWebSocketManagerreceives this event and broadcasts it to the appropriate client(s).
This flow is shown below:
Client-Side Implementation
The frontend, built with vanilla JavaScript, handles the WebSocket lifecycle from fetching a ticket to rendering streamed messages.
Sources: webapp/static/js/src/ui/chatUi.js, webapp/static/js/src/services/http.js
Fetching a Ticket
The http.js service includes a function to get a ticket from the API.
// webapp/static/js/src/services/http.js
async function fetchTicket() {
const resp = await authorisedFetch("/api/v1/auth/ws/ticket", {
method: "POST",
});
if (!resp.ok) {
throw new Error(`Ticket error: ${resp.status}`);
}
const body = await resp.json();
if (!body || !body.ticket) {
throw new Error("Ticket response invalide");
}
return body.ticket;
}
Sources: webapp/static/js/src/services/http.js:34-45
Handling Streaming Messages
The chatUi.js module manages the rendering of real-time messages.
startStream(): Creates a new message row in the UI with a blinking cursor to indicate that the assistant is "typing".appendStream(delta): Appends the incoming textdeltato the message buffer and re-renders the message content with Markdown processing.endStream(): Finalizes the message, removes the cursor, and updates the message timestamp. It also handles displaying errors if the stream terminates unexpectedly.
This allows for a smooth, character-by-character streaming effect in the chat interface.
Sources: webapp/static/js/src/ui/chatUi.js:1003-1078
Configuration
The WebSocket service can be configured via environment variables.
| Setting | Description | Default | Source |
|---|---|---|---|
WS_ALLOWED_ORIGINS | Comma-separated list of origins allowed to open WebSocket sessions. | Not specified | README.md |
WS_ENABLE_EVENTS | A feature flag to enable or disable the WebSocket event streaming entirely. | True | README.md, monGARS/api/AGENTS.md |
WS_RATE_LIMIT_MAX_TOKENS | The maximum number of tokens in the bucket for rate limiting connections. | Not specified | README.md |
WS_RATE_LIMIT_REFILL_SECONDS | The time in seconds to refill one token in the rate-limiting bucket. | Not specified | README.md |
SECRET_KEY | The secret key used by TicketSigner to sign and verify WebSocket tickets. | Not specified | monGARS/api/ws_ticket.py:23 |
Sources: README.md, monGARS/api/AGENTS.md, monGARS/api/ws_ticket.py:23
Summary
The monGARS WebSocket service is a robust and secure system for enabling real-time features in the web application. By using a ticket-based authentication flow, it ensures that only authorized clients can connect. The centralized WebSocketManager and decoupled UIEventBus create a clean, maintainable architecture for broadcasting server-sent events, which is essential for the streaming chat functionality at the core of the user experience. The client-side implementation complements this by efficiently handling the connection lifecycle and rendering streamed data for a responsive UI.