Architecture
System context
The active stack is a Nuxt 3 application running as a Docker container on the nisc-ingestion Azure VM, backed by an Azure Functions v4 API and PostgreSQL. Telemetry arrives from Aruba APs (via Event Hub), Windows Desktop Gateways (via HTTP), or the Node BLE scanner utility. Real-time updates are fanned out through a self-hosted Centrifugo relay on the same VM (replaced Azure Web PubSub — see Realtime Relay). Authentication uses Okta SSO via nuxt-oidc-auth. Docs are built into the same frontend deploy.
Cloud architecture
flowchart LR
Devices[BLE devices / badges / phones] --> Ingest[BLE collection layer]
Ingest --> EH[Azure Event Hub<br/>nisc-eventhub-stream]
Ingest --> ApiIngest[/raddec/ingest & /desktop-gateway/*]
EH --> Func[Azure Functions API]
ApiIngest --> Func
Func --> PG[(PostgreSQL / TimescaleDB)]
Func --> Centrifugo[Centrifugo relay<br/>nisc-ingestion VM]
Func --> ACS[Azure Communication Services / Twilio]
Func --> Blob[Azure Storage<br/>personnel-photos]
Nuxt[Nuxt 3 / Azure VM Docker] --> Func
Nuxt --> Centrifugo
Docs[VitePress docs output] --> NuxtAzure resources defined in the repo
deployments/main.bicep and deployments/main.json define a stack that can include:
- Azure Static Web App
- Azure Function App
- PostgreSQL Flexible Server with TimescaleDB-related extensions
- Azure Event Hubs (Aruba ingestion path)
- Azure Storage Account (function runtime, blob containers for photos)
- optional Azure Communication Services (emergency comms)
- optional Redis Cache (in-memory caching layer; backend degrades gracefully if absent)
- optional Application Insights
- Linux VM hosting
barnowl-aruba+nginx+centrifugofor ingestion and realtime fanout
Deprecated — do not provision
Azure Web PubSub, Azure IoT Hub S1, and the Device Provisioning Service are listed in legacy Bicep templates but are no longer used at runtime. Web PubSub was replaced by the self-hosted Centrifugo relay in PR #335. IoT Hub + DPS have been idle since the Aruba Event Hub path went live; the last IoT-Hub-coupled API surfaces (the receivers/discover endpoint and azure-iothub SDK dependency) were removed in 2026-04. The Bicep resources themselves still need to be deprovisioned in a follow-up. See docs/deployment/realtime-migration.md in the repo for the decommission runbook.
Local development architecture
flowchart LR
Browser --> Nginx[nginx :8090]
Nginx --> NuxtDocker[Nuxt container :3000 -> host 3005]
Nginx --> ApiDocker[API container :80 -> host 7071]
ApiDocker --> Postgres[(PostgreSQL :5433)]
ApiDocker --> Redis[(Redis :6379)]
ApiDocker --> Azurite[Azurite :10000-10002]Key local behaviors
docker-compose.ymlruns PostgreSQL, Azurite, API, Nuxt, nginx, Redis, and optional tooling.- Direct local Nuxt development still uses
npm run devon port3006. - The docs site compiles into
nuxt-frontend/public/docs, so docs ship with the app build.
Networking architecture
Ingestion-side network model
Telemetry can enter through several paths currently represented in code:
- Azure/IoT pipeline: IoT/Event-based input handled by
static-web-app/api/src/routes/iotHub.js - Desktop gateway path: Windows service posts config, heartbeats, and raddec-style observations to
desktop-gateway/*endpoints - BLE scanner path:
ble-scanner/scanner.jsposts to/api/raddec/ingest - Phone beacon path: mobile enrollment and station flows register phone-derived beacon identities
Desktop gateway site detection
The Windows gateway only scans when it determines it is on an approved network. The config route supplies:
- allowed SSIDs
- allowed BSSIDs
- allowed DNS suffixes
- allowed subnets
- Aruba OUI prefixes
- off-site grace period
- recheck interval
This is the current network-safety control that prevents laptop-based gateways from reporting observations from home or off-site networks.
Frontend-to-backend request model
sequenceDiagram
participant U as User browser / mobile shell
participant N as Nuxt app
participant F as Azure Functions API
participant D as PostgreSQL
participant R as Centrifugo (VM)
U->>N: Navigate / interact
N->>F: HTTP request or Nitro proxy call
F->>D: Query/update state
F-->>N: JSON response
F->>R: Broadcast live update when needed
R-->>N: WebSocket event
N-->>U: Updated UIPareto Anywhere lineage
The repo still carries the pareto-anywhere-azure package identity and homepage metadata, but the implementation has evolved into a broader NISC monorepo with:
- a modern Nuxt 3 frontend
- a dedicated Azure Functions API
- desktop and mobile delivery targets
- custom emergency, weather, phone beacon, and desktop gateway workflows
Use the Pareto name as lineage and domain context, but treat this repository as a NISC product codebase rather than a small upstream adapter package.