Skip to content

Signal Service

@nx/signal is the platform's single real-time edge: an end-to-end-encrypted WebSocket server (/stream) that other services push to via Redis pub/sub, plus a Kafka-fed activity-notification pipeline that resolves recipients, persists ActivityNotification rows (in @nx/core), and pushes them to per-recipient rooms. It owns no schema of its own — only seed migrations for its permissions.

1. Quick Reference

PropertyValue
Package@nx/signal
CodeSVC-00090-SIGNAL
TypeMicroservice
RuntimeBun
Base ClassVerifierApplication
Locationpackages/signal
Base Path/v1/api/signal
WebSocket Path/stream (requireEncryption: true)
Dev Port31090
Container Port3000 (external 31090)
Snowflake ID9
DB Schemanone of its own — reuses @nx/core tables (ActivityNotification)
Binding Namespace@nx/signal

2. Purpose & Scope

IncludedExcluded
Encrypted WebSocket server (/stream, ECDH P-256 + AES-256-GCM)Persisting WebSocket connections (in-memory per instance, lost on restart)
Cross-instance / cross-service fan-out via Redis pub/subProducing domain events (signal only consumes + pushes)
REST client management (list, get, broadcast, room/client send, disconnect)Authoring notification content for arbitrary event types (only PAYMENT_SUCCESS today)
Kafka activity-notification pipeline (resolve → persist → push)Owning the ActivityNotification schema (centralized in @nx/core)
Recipient-scoped notification REST API (list, count, mark-read)Email / SMS / push-notification channels
JWT verification via remote JWKS (Identity)Issuing tokens (delegated to @nx/identity)

3. Tech Stack

External:

LibraryPurpose
@venizia/ignisIoC container, DI, BaseComponent, WebSocketComponent, JWKSVerifierTokenService
@venizia/ignis-helpersECDH, WebSocketServerHelper, WebSocketEmitter, KafkaConsumerHelper
@platformatic/kafkaKafka (de)serializers + SASL types for the notification consumer
honoHTTP server framework (via IGNIS)
@hono/zod-openapiOpenAPI generator from Zod schemas
zodRequest/response schema validation
drizzle-orm / pgDB access via PostgresCoreDataSource (notification persistence + authz)

Internal:

PackagePurpose
@nx/coreVerifierApplication, createAppConfig, bootstrapApplication, centralized repositories/models (ActivityNotification), KafkaTopics, WebSocketTopics/WebSocketRooms, RedisConnectionFactory, BaseSocketEventService

4. Project Structure

packages/signal/
├── src/
│   ├── application.ts                  # VerifierApplication subclass
│   ├── index.ts                        # bootstrapApplication()
│   ├── migrate.ts                      # bootstrapMigration()
│   ├── common/                         # BindingKeys, RestPaths, WebSocketClientInfoSchema
│   ├── components/
│   │   ├── signal.component.ts         # Top-level orchestrator component
│   │   ├── websocket.component.ts      # NxWebSocketComponent (ECDH + Redis + handlers)
│   │   └── notification/               # NotificationKafkaComponent + socket-event service
│   ├── controllers/
│   │   ├── websocket-clients/          # WebSocketClientController (+ permissions)
│   │   └── activity-notifications/     # ActivityNotificationController
│   ├── datasources/                    # PostgresCoreDataSource binding
│   ├── errors/                         # WorkerErrors
│   ├── migrations/processes/           # 2 seed migrations (permissions, role-permissions)
│   ├── models/requests/                # zod query schemas
│   ├── services/                       # SignalEventService, ActivityNotification(Worker)Service
│   └── utilities/                      # content-decorator (markdown → content/html)
├── package.json
└── tsconfig.json

5. Architecture

Detail: see Architecture.

6. Domain Snapshot

Signal owns no tables. It reads/writes the centralized ActivityNotification table from @nx/core.

Full ERD + entity tables: see Domain Model.

7. Surface Summary

REST controllers — full reference rendered live from /v1/api/signal/doc/openapi.json (Scalar viewer at /doc, gateway portal):

ControllerBase pathNotes
WebSocketClientController/socket/websocket/clientsstatus (public) + list / get / broadcast / room-send / client-send / disconnect (auth + WebSocketClient.* permission)
ActivityNotificationController/notificationsJWT, recipient-scoped: list / count / mark-read / mark-all-read

Async surface — full reference in API Events:

DirectionChannelCount
InboundKafka1 (SIGNAL_ACTIVITY_NOTIFICATION)
OutboundKafka0
OutboundWebSocket1 topic (observation/signal/notification/created) + ad-hoc broadcast/room/client sends

8. Components

ComponentFilePurpose
SignalComponentsrc/components/signal.component.tsOrchestrator — registers SignalEventService + ActivityNotificationService, WebSocketClientController + ActivityNotificationController, and the two sub-components below
NxWebSocketComponentsrc/components/websocket.component.tsRedis, ECDH encryption, /stream options, 6 handler bindings, IGNIS WebSocketComponent, WebSocketEmitter
NotificationKafkaComponentsrc/components/notification/component.tsBinds notification services, starts the SIGNAL_ACTIVITY_NOTIFICATION consumer (autocommit off, fallbackMode: latest, graceful shutdown)

9. Services

ServiceFileOne-liner
SignalEventServiceservices/signal-event.service.tsFaçade over WS server/emitter (broadcast, room/client send, introspection, disconnect)
ActivityNotificationServiceservices/activity-notification.service.tsRecipient-scoped read/update over ActivityNotificationRepository (list, count, mark-read, mark-all)
ActivityNotificationWorkerServiceservices/activity-notification-worker.service.tsKafka handler — resolve recipients, build content, persist rows, push WS
NotificationSocketEventServicecomponents/notification/socket-event.service.tsBaseSocketEventService subclass; emits to per-recipient rooms

10. Repositories

All re-exported from @nx/core; signal owns no schema. Registered in preConfigure() (+ rebound by NotificationKafkaComponent).

RepositoryTableSourceCustom Methods
ActivityNotificationRepositoryActivityNotification@nx/core— (plain SoftDeletableRepository)
PolicyDefinitionRepositoryPolicyDefinition@nx/corefindUserIdsInOrganizer, findUserIdsInMerchant (recipient resolution)
PermissionRepositoryPermission@nx/core— (seed migration)
RoleRepositoryRole@nx/core— (seed migration)

11. Entry Points

FilePurpose
src/index.tsService entry → bootstrapApplication()
src/migrate.tsMigration entry → bootstrapMigration() (seeds permissions + role-permissions)
src/application.tsApplication extends VerifierApplication

12. Configuration

Env vars + seeded data: see Configuration.

13. Operations

Deployment + observability + security + runbook: see Operations.

Concepts — why/how:

Reference — lookup:

Features — deep dives:

Decisions:

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