Skip to content

PRD: Device signal & notifications

ModuleDevice (CORE-04)PRD IDPRD-MON-001
StatusIn-progressOwnerDevice / Signal squad
Date2026-05-21Versionv0.1
Packages@nx/signal · @nx/outreach · @nx/coreURDMON

TL;DR

Gives KICKO a server-side activity-notification backbone: a Kafka consumer ingests activity events, a worker resolves the right recipient users within an organizer/merchant scope, persists a notification record, and pushes it live to connected clients over WebSocket. The result - administrators see device and platform activity surface in near real time instead of polling, and the first live use case (inquiry-submitted) lights up end to end.

1. Context & Problem

Device Identity & Health Monitoring (MON) needs the platform to capture activity and surface it to administrators in near real time. Today there is no server-side notification backbone: events happen in commerce/outreach but nothing fans them out to the right users or pushes them to a live UI, so administrators cannot watch activity as it happens.

This increment builds that backbone in @nx/signal - an end-to-end activity-notification pipeline (Kafka consumer → worker → persisted record → WebSocket emit) - together with recipient resolution scoped to an organizer/merchant and a stable WebSocket transport. It is the real-time transport that monitoring and activity surfacing ride on; heartbeat/health-status reporting itself (URD-MON-001/002) and remote device actions (URD-MON-004/005) are tracked separately.

2. Goals & Non-Goals

Goals

  • A Kafka-fed activity-notification pipeline in @nx/signal: consumer → worker → persisted record → WebSocket emit.
  • Recipient resolution - notifications fan out to the correct users within an organizer/merchant scope.
  • A stable WebSocket transport (rooms + topics + socket-event service) with a fixed room/topic naming (signal/notification, NOTIFICATION_CREATED).
  • Notification read-state: REST endpoints to fetch notifications and mark them read.
  • First real-time use case end to end: inquiry-submitted WebSocket notification in @nx/outreach.

Non-Goals

  • Heartbeat emission and the 15-minute offline threshold (URD-MON-001/002).
  • Remote deactivate / remote data wipe (URD-MON-004/005).
  • The per-device health dashboard UI (the consuming client surface).
  • Porting the notification toolkit out of @nx/signal into a shared package.

3. Success Metrics

MetricTarget / signal
Delivery latencyActivity event → client WebSocket emit in near real time (sub-second under normal load)
Recipient accuracyNotifications reach exactly the users within the organizer/merchant scope; no cross-tenant leakage
DurabilityEvery emitted notification has a persisted record; read-state survives reconnect
CoverageFirst live path (inquiry-submitted) verified end to end; pipeline reusable for future event types

4. Personas & Use Cases

PersonaGoal in this feature
Administrator / OwnerSee platform & device activity surface live without polling
Connected client (web/app)Receive notifications over a live WebSocket and reflect read-state
Outreach handlerBe alerted the moment an inquiry is submitted

Core scenarios: an activity event lands on Kafka → the worker resolves recipients within the organizer/merchant scope and persists a notification → the record is pushed over WebSocket to connected clients → a client fetches its notifications and marks them read; an inquiry submission emits a live WebSocket notification through the same transport.

5. User Stories

  • As an administrator, I want activity to appear in my UI as it happens, so that I can monitor the platform without refreshing.
  • As a connected client, I want to receive notifications over a live WebSocket, so that I see updates the instant they occur.
  • As a connected client, I want to fetch my notifications and mark them read, so that read-state is consistent across sessions.
  • As an outreach handler, I want a live notification when an inquiry is submitted, so that I can respond promptly.
  • As the platform, I want notifications to fan out only to users within the right organizer/merchant scope, so that no tenant sees another's activity.

6. Functional Requirements

#RequirementURD ref
FR-1A Kafka consumer ingests activity events into the notification pipelineURD-MON-003
FR-2A worker resolves recipient user IDs within an organizer/merchant scopeURD-MON-003
FR-3Each notification is persisted as an activity-notification record in @nx/coreURD-MON-003
FR-4A WebSocket transport (rooms + topics + socket-event service) pushes the notification to connected clients live, on room signal/notification / topic NOTIFICATION_CREATEDURD-MON-003
FR-5REST endpoints fetch a user's notifications and mark them readURD-MON-003
FR-6Inquiry submission in @nx/outreach emits a WebSocket notification to interested clientsURD-MON-003

Full requirement text and acceptance criteria live in the Device URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Tenancy & authzRecipient resolution is scoped per organizer/merchant; notifications never cross tenant boundaries
DurabilityEvery WebSocket emit is backed by a persisted notification record; read-state is retrievable after reconnect
Performance / scaleKafka decouples ingest from fan-out; the worker handles bursts without blocking producers
Transport stabilityFixed room/topic naming (signal/notification, NOTIFICATION_CREATED) so clients subscribe against a stable contract
i18nUser-facing notification labels are bilingual ({ en, vi }) where surfaced

8. UX & Flows

Key surfaces: the notification component lives in @nx/signal (components/notification - consumer, worker, socket-event service, rooms, topics) with REST in controllers/activity-notifications; the inquiry path lives in @nx/outreach (components/websocket, controllers/inquiry). The consuming dashboard UI is out of scope for this increment.

9. Data & Domain

EntityRole
activity-notificationThe persisted notification record (recipient, payload, read-state) in @nx/core
Notification component@nx/signal Kafka consumer, worker, socket-event service, rooms, topics
WebSocket room / topicsignal/notification room and NOTIFICATION_CREATED topic - the live delivery channel
Recipient resolutionPolicyDefinitionRepository lookup of user IDs within an organizer/merchant

Conceptual only - full schema lives in the developer domain model.

10. Dependencies & Assumptions

Depends on

  • @nx/core - owns the activity-notification schema/model/repository, the Kafka topic + types, and recipient resolution via PolicyDefinitionRepository.
  • Kafka - the ingest seam between activity sources and the notification worker.
  • WebSocket transport - the live delivery channel to connected clients.
  • @nx/outreach - the first event producer (inquiry submission).

Assumptions

  • Activity sources publish events onto the agreed Kafka topic.
  • Organizer/merchant membership data is available for recipient resolution.
  • Clients maintain a WebSocket connection and subscribe to the signal/notification room.

11. Risks & Open Questions

Risk / questionMitigation / status
Recipient resolution could leak across tenantsResolution scoped per organizer/merchant; verified against membership lookup
WebSocket disconnects could drop live notificationsEvery emit is backed by a persisted record; clients re-fetch read-state on reconnect
Room/topic naming drift between producers and clientsNaming fixed to signal/notification / NOTIFICATION_CREATED as a stable contract
Notification toolkit lives in @nx/signal, not sharedAccepted for this increment; extraction to a shared package is a future consideration
MON heartbeat/health still pendingOut of scope here; this increment delivers the transport only (see §2)

12. Release Plan & Launch Criteria

AspectPlan
PhaseP2 (MON is a P2, In-progress feature)
RolloutAll merchants; no feature flag
MigrationNew activity-notification entity; no data migration
Launch criteriaActivity event → recipient resolution → persisted record → WebSocket emit verified end to end; inquiry-submitted path verified; read-state survives reconnect
MonitoringNotification volume, delivery latency, WebSocket connection health, recipient-resolution correctness

13. FAQ

Does this deliver device heartbeat / health status? No - this increment delivers the real-time notification transport that monitoring rides on. Heartbeat emission and the offline threshold (URD-MON-001/002) are tracked separately.

How are recipients chosen? The worker resolves user IDs within the relevant organizer/merchant scope via PolicyDefinitionRepository; notifications never cross tenant boundaries.

What happens if a client is disconnected when an event fires? The notification is still persisted; the client re-fetches its notifications and read-state over REST on reconnect.

What room/topic do clients subscribe to? Room signal/notification, topic NOTIFICATION_CREATED - a fixed, stable contract.

Why is the inquiry-submitted path in here? It is the first real-time use case proving the transport end to end, emitted from @nx/outreach over the same WebSocket backbone.

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.