Skip to main content

ADR-006: Notifications WebSocket

  • Status: Accepted
  • Date: 2026-04-16
  • Author: CTO Agent (KALA-133)

Context

AgentCore already had WebSocket infrastructure for agent-tasks progress. New product flows needed real-time user notifications for approvals, mentions, and generated documents. The team had to choose between extending the existing WebSocket plugin, adding Server-Sent Events, or relying only on REST polling.

Decision

Add /ws/notifications to the existing Fastify WebSocket plugin.

The notification channel:

  1. authenticates with the same JWT model as REST;
  2. supports a query token or first-message auth;
  3. subscribes to notification events for the authenticated userId;
  4. sends notification.created events when producers call emitNotification();
  5. keeps durable state in the notifications table.

Alternatives Considered

REST polling only

Rejected. Polling is simple, but it adds latency to approval and document-ready workflows and increases request volume for active dashboards.

Separate SSE endpoint

Rejected for now. SSE is a good fit for server-to-client events, but the app already has authenticated WebSocket plumbing and tests. Adding another transport would duplicate auth and lifecycle logic.

Extend /ws/agent-tasks

Rejected. Agent task progress is namespace/task scoped. Notifications are user-inbox scoped. Keeping separate paths avoids mixing subscription semantics.

Consequences

Positive

  • Reuses the existing WebSocket plugin and JWT verification path.
  • Keeps notification delivery tied to current user identity.
  • REST inbox remains the source of truth after reconnects.
  • Tests can exercise the socket with the same injected WebSocket helpers.

Negative

  • The current EventEmitter broadcast is process-local.
  • Multi-replica production needs Redis pub/sub or another shared event bus.
  • Clients still need REST sync for missed events while disconnected.

Implementation Notes

  • src/lib/notifications.ts owns emitNotification() and subscribeUserNotifications().
  • src/plugins/websocket.ts registers /ws/notifications.
  • Producers should create the database row first, then emit the event.
  • The socket closes with code 4001 on auth timeout or invalid token.