04 — Information Architecture (WS / Realtime Module)
- 1. Transport
- 2. Identity model
- 3. Room model
- 4. Event taxonomy
- 5. Consumer mapping (web UI)
- 6. Naming conventions for the web client
The transport contract of the realtime layer: namespaces, rooms, envelope, naming. Mirrors 00-shared/05 at the wire level; module facts here are all source-grounded.
1. Transport
| Aspect | Value | Source |
|---|---|---|
| Protocol | Socket.io over WebSocket (engine.io fallback transports) | ws.gateway.ts:10-11 |
| Namespace | /ws | ws.gateway.ts:21 |
| CORS | origin: '*', credentials: true | ws.gateway.ts:22 |
| Payload wire format | JSON (socket.io default) | — |
| Heartbeat | socket.io defaults (pingInterval 25 s / pingTimeout 20 s) — not tuned in source | gap → 14_QA_Checklist.md |
2. Identity model
A connected socket carries (source ws.gateway.ts:14-18,46-48):
AuthenticatedSocket { userId, tenantId, roles[] } ← from verified JWT (payload.sub, tenantId, roles)
- Tenant scope is fixed at connect time by the JWT (
ws.gateway.ts:47); it is never supplied by the client on the wire. This is the security boundary of the whole module. roles[]are carried but unused in routing today (gap; see12).
3. Room model
| Room | Created | Who joins | Purpose | Source |
|---|---|---|---|---|
tenant:{tenantId} | implicit, at connect | every socket of the tenant | tenant-wide fan-out of domain events | ws.gateway.ts:50 |
| arbitrary room string | on subscribe | any authenticated socket | entity-level delivery (e.g. class:10-A, user:{id}) | ws.gateway.ts:63-68 |
Rules:
subscribe/unsubscribeaccept any string (type-checked only:typeof room !== 'string'returns silently —ws.gateway.ts:65,71). No allow-list, no RBAC check → gap (see12).- Rooms are per-connection and die with the socket; clients must re-
subscribeafter every reconnect (13_State_Management.md). - There is no per-user room in source; user-scoped delivery today = tenant room +
client-side filter, or an explicit
subscribeto a convention likeuser:{id}(planned convention, not enforced).
4. Event taxonomy
Server→client events are domain event types relayed by the bridge, wrapped in an
envelope (ws-bridge.service.ts:17-21):
{
"eventType": "notification.created",
"occurredAt": "2026-08-03T09:00:00.000Z",
"payload": { }
}
- Names use dot notation — blueprint convention:
attendance.updated,payment.completed,notification.created,homework.published(API_STANDARDS.md:642-658). - The underlying
DomainEventalso carriesactorIdandcorrelationId(events/domain-event.interface.ts:1-8) — not forwarded by the bridge today. - Client→server events in source: exactly
subscribeandunsubscribe(ws.gateway.ts:63-74).
5. Consumer mapping (web UI)
| Domain event family (examples from blueprint §28) | UI consumer | Status |
|---|---|---|
notification.created | bell badge, toast | convention — modules to emit (planned) |
payment.completed | fee dashboard tiles | planned |
attendance.updated | attendance lists/dashboard | planned |
homework.published | homework lists | planned |
communication.* | message threads | planned (IMPLEMENTATION_PLAN.md:119) |
transport.* | live tracking | planned (IMPLEMENTATION_PLAN.md:229) |
| job progress | async-op status (API_STANDARDS.md:579) | planned |
6. Naming conventions for the web client
- Channel constants in one file (
realtime_channels.dart) — mirror server room strings. - Event constants mirror server eventType strings 1:1 (no client-side renaming).
- Envelope is the only accepted server→client shape; anything else is a protocol violation.