πŸ“‹ Implementation Status

SignalR real-time updates are planned but not yet implemented. Current dashboard updates require page refresh or polling. This document outlines the proposed architecture for live updates.

βœ— Not Implemented ⚠ Planned Q3 2026 High Priority

πŸ—οΈ Proposed SignalR Architecture

SignalR Real-time Architecture (Proposed) ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ CURRENT STATE (Polling) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ Angular Dashboard API Database β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ GET /dashboard β”‚ β”‚ β”‚ β”‚ │────────────────►│ β”‚ β”‚ β”‚ β”‚ β”‚ SELECT... β”‚ β”‚ β”‚ β”‚ │───────────────────────►│ β”‚ β”‚ β”‚ │◄───────────────────────│ β”‚ β”‚ │◄─────────────────│ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ (Wait 30s) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ GET /dashboard β”‚ β”‚ β”‚ β”‚ │────────────────►│ β”‚ β”‚ β”‚ β”‚ ...repeat... β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ ⚠ Problems: β”‚ β”‚ β€’ Wasted resources (constant polling) β”‚ β”‚ β€’ Delayed updates (30s polling interval) β”‚ β”‚ β€’ Server load (N clients Γ— M requests) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ↓ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ PROPOSED STATE (SignalR) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Agent β”‚ β”‚ SignalR Hub β”‚ β”‚ Redis β”‚ β”‚ β”‚ β”‚ (Tray) β”‚ β”‚ (Backplane) β”‚ β”‚ (Pub/Sub)β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Session Events β”‚ β”‚ β”‚ β”‚ β”‚ (AI detected) β”‚ β”‚ β”‚ β”‚ │──────────────────────────────►│ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Broadcast to Group β”‚ β”‚ β”‚ β”‚ β”‚ (OrganizationId) β”‚ β”‚ β”‚ β”‚ │───────────────────────────►│ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ │◄───────────────────────────│ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Angular β”‚ β”‚ API Server β”‚ β”‚ Database β”‚ β”‚ β”‚ β”‚ Dashboard │◄─────────────│ (Hub Host) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ WebSocket β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ Hub Groups (Tenant Isolation): β”‚ β”‚ β€’ group:org-123 β†’ Users in organization 123 β”‚ β”‚ β€’ group:admin β†’ All admin users β”‚ β”‚ β€’ group:superadmin β†’ SuperAdmin only β”‚ β”‚ β”‚ β”‚ Benefits: β”‚ β”‚ β€’ Instant updates (WebSocket push) β”‚ β”‚ β€’ Reduced server load (persistent connections) β”‚ β”‚ β€’ Scalable with Redis backplane β”‚ β”‚ β€’ Bidirectional communication (notifications, alerts) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“‘ Real-time Event Types

EventSourceTarget GroupPayloadUse Case
SessionStarted Agent org:{orgId} { sessionId, userId, startTime } Live activity feed
SessionEnded Agent org:{orgId} { sessionId, duration, aiUsageTime } Real-time dashboard KPI update
AIDetected Agent org:{orgId} { tool, duration, userId } AI usage tracking
DashboardUpdate Backend org:{orgId} { metrics, trends } Aggregated dashboard refresh
ExportCompleted ExportJob user:{userId} { jobId, downloadUrl } Export ready notification
Alert System org:{orgId} or user:{userId} { type, message, severity } System notifications
UserJoined Auth org:{orgId} { userId, email, timestamp } Activity monitoring

πŸ” Security Model

SignalR Authentication & Authorization ═══════════════════════════════════════════════════════════════════════════════ WebSocket Connection Establishment: ─────────────────────────────────────────────────────────────────────────────── 1. Client Requests Connection Upgrade GET /hubs/analytics?access_token=eyJhbG... Headers: β€’ Authorization: Bearer {JWT} β€’ X-Organization-Id: org-123 β”‚ β–Ό 2. Server Validates JWT β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β€’ Verify signature (RS256) β”‚ β”‚ β€’ Check expiration β”‚ β”‚ β€’ Extract claims: β”‚ β”‚ - sub (UserId) β”‚ β”‚ - org (OrganizationId) β”‚ β”‚ - role β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό 3. Authorize Hub Connection β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ [Authorize(Roles = "Admin,Manager")]β”‚ β”‚ β”‚ β”‚ β€’ Check role claim β”‚ β”‚ β€’ Verify org matches request β”‚ β”‚ β€’ Validate subscription status β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό 4. Add to Tenant Group β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ await Groups.AddToGroupAsync( β”‚ β”‚ Context.ConnectionId, β”‚ β”‚ $"org:{orgId}"); β”‚ β”‚ β”‚ β”‚ // User now receives all events β”‚ β”‚ // broadcast to org:{orgId} β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ 5. Connection Established WebSocket UPGRADE 101 Switching Protocols Tenant Isolation in SignalR: ─────────────────────────────────────────────────────────────────────────────── β€’ Each user joins group: "org:{theirOrganizationId}" β€’ Hub methods verify Context.User belongs to target org β€’ SuperAdmins can join multiple groups or "superadmin" group β€’ Messages filtered server-side before broadcast

βš™οΈ Implementation Components

1. AnalyticsHub (Server)
public class AnalyticsHub : Hub { [Authorize] public async Task JoinOrganizationGroup() { var orgId = Context.User.GetOrgId(); await Groups.AddToGroupAsync( Context.ConnectionId, $"org:{orgId}"); } public async Task BroadcastDashboardUpdate( string orgId, DashboardMetrics metrics) { await Clients.Group($"org:{orgId}") .SendAsync("DashboardUpdate", metrics); } }
2. Angular Service (Client)
@Injectable() export class RealtimeService { private hubConnection: HubConnection; connect() { this.hubConnection = new HubConnectionBuilder() .withUrl('/hubs/analytics', { accessTokenFactory: () => this.getJwtToken() }) .build(); this.hubConnection.on('DashboardUpdate', (metrics) => this.updateDashboard(metrics)); return this.hubConnection.start(); } }
3. Event Publisher
public class SessionEventPublisher { private readonly IHubContext _hub; public async Task PublishSessionStarted( Session session) { await _hub.Clients .Group($"org:{session.OrganizationId}") .SendAsync("SessionStarted", new { session.Id, session.UserId, session.StartTime }); } }
4. Redis Backplane
// Program.cs builder.Services.AddSignalR() .AddStackExchangeRedis("connectionString", options => { options.Configuration.ChannelPrefix = "AIUsagePlatform"; }); // Enables multiple API servers to share connections

πŸ“Š Scaling Architecture

SignalR Scaling with Redis Backplane ═══════════════════════════════════════════════════════════════════════════════ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ MULTIPLE API SERVERS β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ API Server 1 β”‚ β”‚ Redis β”‚ β”‚ API Server 2 β”‚ β”‚ β”‚ β”‚ (SignalR) │◄────►│ Backplane │◄──────►│ (SignalR) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β€’ Connections β”‚ β”‚ β€’ Pub/Sub β”‚ β”‚ β€’ Connections β”‚ β”‚ β”‚ β”‚ β€’ Hub instances β”‚ β”‚ β€’ Scale-out β”‚ β”‚ β€’ Hub instances β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ WebSocket connections β”‚ β”‚ β”‚ β–Ό β–Ό β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Angular β”‚ β”‚ Angular β”‚ β”‚ β”‚ β”‚ Client A β”‚ β”‚ Client B β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ When Server 1 broadcasts to group "org:123": β”‚ β”‚ 1. Server 1 publishes to Redis channel "org:123" β”‚ β”‚ 2. Redis forwards to Server 2 β”‚ β”‚ 3. Server 2 delivers to its connected clients in org:123 β”‚ β”‚ β”‚ β”‚ Result: Client B receives message even though connected to Server 2 β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

⚠️ Implementation Risks

⚠
Connection Limits
IIS/Web server has concurrent connection limits. May need dedicated SignalR service or Azure SignalR Service.
⚠
Memory Usage
Each WebSocket connection consumes memory. 10K concurrent connections = significant RAM usage.
⚠
Proxy/WebSocket Support
Corporate proxies may block WebSockets. Need fallback to Server-Sent Events or Long Polling.

βœ… Implementation Checklist

Phase 1: Infrastructure
  • β–‘ Add SignalR NuGet package
  • β–‘ Configure Redis backplane
  • β–‘ Setup WebSocket support in IIS
  • β–‘ Configure CORS for WebSocket
Phase 2: Hub Implementation
  • β–‘ Create AnalyticsHub class
  • β–‘ Implement authentication
  • β–‘ Add tenant group management
  • β–‘ Create event publisher service
Phase 3: Frontend
  • β–‘ Add SignalR client library
  • β–‘ Create RealtimeService
  • β–‘ Subscribe to dashboard events
  • β–‘ Handle reconnection logic
Phase 4: Integration
  • β–‘ Hook session events to SignalR
  • β–‘ Hook export completion events
  • β–‘ Add notification system
  • β–‘ Load testing (10K+ connections)