CPI Documentation
Architecture

Architecture

System architecture overview including MQTT pipeline, database design, authentication flow, and real-time data path.

System Overview

CPI is a monorepo built with Turborepo and pnpm workspaces containing:

ComponentTechnologyRole
APINestJS 11, TypeORM 0.3REST API, business logic, MQTT handling
AdminReact 19, React Router v7, ShadCN UIWeb dashboard (SPA mode)
MobileExpo SDK 54, React NativeiOS/Android monitoring app
SharedZod schemas, TypeScript enumsCross-app type safety
API TypesOrval-generated React Query hooksType-safe API calls
UIShadCN component libraryShared UI components
ConfigESLint 9 + TypeScript configsShared tooling configs

MQTT Telemetry Pipeline

The real-time data flow from device to dashboard:

Device                Mosquitto          NestJS API             Admin/Mobile
  |                      |                   |                      |
  |-- MQTT publish ----> |                   |                      |
  |   (telemetry)        |-- forward ------> |                      |
  |                      |                   |-- MqttService         |
  |                      |                   |   (receives msg)      |
  |                      |                   |-- EventEmitter         |
  |                      |                   |   emit 'mqtt.telemetry'|
  |                      |                   |                       |
  |                      |                   |-- TelemetryListener    |
  |                      |                   |   1. Verify HMAC       |
  |                      |                   |   2. Check nonce       |
  |                      |                   |   3. Insert TimescaleDB|
  |                      |                   |   4. Check alert rules |
  |                      |                   |                       |
  |                      |                   |-- WebsocketsGateway    |
  |                      |                   |   emit to room         |
  |                      |                   |   'device:{id}'  ----> |
  |                      |                   |                  Socket.IO

Key Components

MqttModule (global) — connects to Mosquitto on :1883, subscribes to cpi/+/telemetry, cpi/+/status, cpi/+/ack. Forwards messages via @nestjs/event-emitter.

TelemetryListener — handles mqtt.telemetry and mqtt.device-status events. Verifies HMAC signatures, checks nonce uniqueness, inserts raw telemetry into TimescaleDB hypertable, and evaluates alert rules.

WebsocketsGateway — Socket.IO gateway with JWT auth (supports both cookie and Bearer token). Clients join room device:{id} and receive telemetry:update and device:status events in real-time.

Database Architecture

PostgreSQL + TimescaleDB

The database uses PostgreSQL 16 with the TimescaleDB extension for time-series telemetry data.

Entities (TypeORM, auto-loaded):

EntityDescription
UserAdmin users with role-based access (ADMIN, OPERATOR, VIEWER)
DeviceIoT devices with lifecycle status, auth method, metadata
AlertConfigurable alert rules with thresholds
CommandLogAudit trail of commands sent to devices
NotificationLogDelivery tracking for push/email/SMS notifications
PushTokenExpo push notification tokens per user

Telemetry hypertable — raw SQL with TimescaleDB time_bucket() for aggregation queries. Created in the initial TypeORM migration.

Data Flow

TypeORM Entities  →  @InjectRepository(Entity)  →  Repository<Entity>
Telemetry (raw SQL)  →  DataSource.query()  →  time_bucket() aggregation

Migrations auto-run on startup via migrationsRun: true in the TypeORM config.

Authentication Flow

Web (Admin Dashboard)

Login POST /auth/login
  → Validate credentials
  → Issue JWT
  → Set httpOnly cookie (access_token)
  → Subsequent requests: cookie extracted by JwtAuthGuard

Mobile App

Login POST /auth/login
  → Response includes JWT in body
  → Stored in Expo SecureStore
  → Sent as Bearer token via axios interceptor
  → Socket.IO: auth.token in handshake

Global Guards

Registered in AuthModule via APP_GUARD:

  1. JwtAuthGuard — extracts JWT from cookie or Bearer header, validates, attaches user to request
  2. RolesGuard — checks @Roles() decorator against user role

The @Public() decorator skips JWT auth for specific endpoints.

Real-Time Architecture (Socket.IO)

Admin Dashboard

useSocket()  // Shared Socket.IO connection (cookie auth)
  → Joins room 'device:{id}'
  → Receives 'telemetry:update' events
useDeviceTelemetry() merges historical REST + live Socket.IO data

Mobile App

useSocket()  // Bearer token via socket.handshake.auth.token
  → Same room-based pattern
  → Auto-disconnects on app background
useDeviceTelemetry() identical merge pattern

WsAuthAdapter

Custom Socket.IO adapter that overrides createIOServer from IoAdapter:

  • Sets CORS origins
  • JWT auth middleware validates token on connection
  • Supports both cookie (access_token) and Bearer token (auth.token)

Type Generation Pipeline

NestJS DTOs  →  Swagger decorators  →  OpenAPI JSON  →  Orval  →  React Query hooks

                                                              @cpi/api-types package

                                                         Admin + Mobile import hooks

Run pnpm orval to regenerate types after API changes.

On this page