# Saraswati Domain — Technical Specifications

Saraswati is the advanced-technology bounded context of the Oshun monorepo. It
covers design, manufacturing, deployment, operations, compliance, and market
intelligence across 17 technology business units (BUs): electric vehicles,
batteries, solar, IoT, electronics, pharma, robotics, drones, telecom, medical
devices, fintech hardware, security, e-waste, additive manufacturing, satellite
communications, market intelligence, and financials.

This is an implemented domain. The specification below is grounded in the
TypeScript source under `libs/saraswati/*` and `libs/contracts/saraswati`. The
phase backlog is `TODOS/phase-60.md`.

This document is a precise, field-level reference for every entity, enum,
schema, guard, factory, table, route, and middleware in the domain. It is
intentionally exhaustive: developers implementing a new feature, reviewers
auditing compliance fields, or architects tracing a cross-domain contract should
be able to find the authoritative definition here rather than hunting through
source files.

## Package Inventory

There is no `apps/saraswati/*` or `services/saraswati/*` package. The domain is
implemented as 24 libraries under `libs/saraswati/` plus one contracts package.
The grouping below — contracts, foundation, business units, cross-BU dashboards
— mirrors the architectural layering described in `architecture.md`.

### Contracts

- `@contracts/saraswati` (`libs/contracts/saraswati`) — cross-domain integration
  schemas and adapters.

### Domain foundation

- `@saraswati/core` (`libs/saraswati/core`) — shared TypeScript types, Zod
  schemas, branded ID types, type guards, factory functions.
- `@saraswati/db` (`libs/saraswati/db`) — Drizzle ORM schema, connection
  pooling, migrations, seed data, Redis cache key namespaces.
- `@saraswati/gateway` (`libs/saraswati/gateway`) — Hono HTTP API gateway,
  GraphQL, gRPC service definitions, MQTT topic hierarchy, WebSocket/SSE,
  CQRS/event-sourcing, Kafka event-bus abstraction, middleware.

### Business-unit capability libraries

Each library below owns the full lifecycle for its technology vertical,
including domain-specific entities, enums, Zod schemas, type guards, and factory
functions.

- `@saraswati/ev` — vehicle design, powertrain, battery pack, chassis,
  certification, production, fleet, V2G, ADAS.
- `@saraswati/battery` — cell catalog, module engineering, testing, production,
  stationary storage, second-life, next-gen chemistry trackers.
- `@saraswati/solar` — panel manufacturing, installation, PAYGO, solar resource,
  SHS designer, testing.
- `@saraswati/iot` — device design, connectivity, platform, smart city,
  dashboard, zero-trust.
- `@saraswati/electronics` — assembly, SMT, testing, production, DFM, AI
  (digital twin, predictive maintenance, quality inspection).
- `@saraswati/pharma` — batch, formulation, GMP, QC, regulatory, drug discovery.
- `@saraswati/robotics` — household, construction, waste-sorting, core, SOTA
  (surgical, swarm).
- `@saraswati/drones` — agriculture, delivery, survey, flight, fleet, data, SOTA
  swarm planner.
- `@saraswati/telecom` — network, site, construction, equipment, maintenance, 6G
  tracker.
- `@saraswati/medical` — design, biocompatibility, quality, regulatory,
  post-market.
- `@saraswati/fintech-hw` — POS, ATM, biometric, field service.
- `@saraswati/security` — CCTV, access, analytics, SOTA (quantum-safe,
  blockchain provenance).
- `@saraswati/ewaste` — collection, recovery, circular economy, compliance.
- `@saraswati/additive` — design, material, applications, fleet.
- `@saraswati/satellite` — network, service, terminal, SOTA LEO planner.
- `@saraswati/market-intel` — tech markets, EV/energy, intelligence.
- `@saraswati/financials` — business-unit financial models, portfolio.

### Cross-BU dashboard / operations libraries

These four libraries aggregate views across the 17 business-unit libraries. They
contain no business logic of their own; they compose and project data for
operators and managers who need a unified view.

- `@saraswati/command` — technology operations dashboard: KPI aggregation,
  manufacturing status, supply chain, quality, financial dashboard, regulatory
  tracker, alert center (phase tasks 60.21.1.x).
- `@saraswati/fleet` — vehicle and drone fleet management: fleet overview, EV
  fleet health, drone mission control, charging infrastructure, fleet scheduler,
  fleet analytics, driver/pilot performance (60.21.2.x).
- `@saraswati/factory` — manufacturing management: electronics MES, pharma
  dashboard, battery assembly, solar manufacturing, print farm, production
  planning, NCR manager (60.21.3.x).
- `@saraswati/iot-platform` — IoT platform device management: device registry,
  device detail, smart-city operations, rule engine, OTA manager, data export
  (60.21.4.x).

## Branded ID Types (`@saraswati/core/ids.ts`)

Branded IDs are the primary defense against passing the wrong type of ID to a
function. Because each BU has its own entity graph, accidental cross-BU ID
confusion (e.g., passing a `DroneId` where a `VehicleId` is expected) would
cause silent data corruption. Branding makes these mistakes into compile-time
type errors.

`@saraswati/core` defines compile-time branded ID types so that, for example, a
`DroneId` cannot be passed where a `VehicleId` is expected. Each is
`Brand<string, '...'>` over a unique symbol.

Vehicle and mobility: `VehicleId`, `VehicleDesignId`, `ChargingStationId`,
`FleetOperatorId`.

Battery and energy: `BatteryId`, `BatteryCellId`, `BatteryModuleId`,
`BatteryPackId`, `SolarPanelId`, `SolarInstallationId`, `SolarHomeSystemId`.

IoT and electronics: `DeviceId`, `IoTDeviceId`, `GatewayId`, `PCBDesignId`,
`ProductionOrderId`.

Pharma and medical: `PharmaceuticalId`, `BatchRecordId`, `MedicalDeviceId`,
`RegulatorySubmissionId`.

Robotics and drones: `RobotId`, `RobotTaskId`, `DroneId`, `FlightPlanId`.

Telecom and satellite: `TelecomTowerId`, `TowerSiteId`, `SatelliteId`,
`VSATTerminalId`, `GroundStationId`.

E-waste and manufacturing: `EWasteCollectionId`, `PrintJobId`, `PrinterId`.

Fintech and security: `FintechTerminalId`, `SecuritySystemId`.

Cross-domain: `OrganizationId`, `CustomerId`, `TechnicianId`, `ProjectId`.

ID helpers: `createVehicleId()`, `createBatteryCellId()`,
`createBatteryModuleId()`, `createBatteryPackId()`, `createDeviceId()`,
`createIoTDeviceId()`, `createDroneId()`, `createRobotId()`,
`createFlightPlanId()`, `createRobotTaskId()`, `createMedicalDeviceId()`,
`createPharmaceuticalId()`, `createFintechTerminalId()`,
`createSecuritySystemId()`, `createChargingStationId()`,
`createSolarInstallationId()`, `createPrintJobId()`, `createTelecomTowerId()`,
`createVSATTerminalId()` each return `randomUUID()`. `isValidId(id)` validates a
UUID v4 string against `UUID_REGEX`. Asserting casters `asVehicleId`,
`asDroneId`, `asRobotId`, `asIoTDeviceId`, `asBatteryPackId` throw `TypeError`
if the input is not a valid UUID.

## Core Domain Objects (`@saraswati/core`)

`@saraswati/core/index.ts` re-exports six type modules: `vehicle.ts`,
`energy.ts`, `iot.ts`, `pharma.ts`, `robotics.ts`, `telecom.ts`,
`manufacturing.ts`. Every interface, enum, Zod schema, type guard, and factory
function below is defined in that source.

The sections that follow enumerate each entity's fields in full, because the
field-level precision matters for downstream consumers (gateway validation,
persistence mapping, contract adapters, and compliance audits). For each entity,
status enum values are listed in their natural lifecycle order to show how an
artifact progresses from creation to retirement.

### Vehicle and mobility (`vehicle.ts`)

This module defines all electric-vehicle entity types. The `Vehicle` entity is
the root of the EV hierarchy; `FleetVehicle` extends it for operational
contexts; `ChargingStation` is a separate entity for charging infrastructure.

#### `Vehicle` (task 60.2.1.1)

Base electric-vehicle record. Fields: `id: VehicleId`, `vin` (ISO 3779),
`vehicleType: VehicleType`, `status: VehicleStatus`, `designCode`,
`designVersion`, `powertrain: Powertrain`, `chassis: ChassisDesign`,
`batteryPackId: BatteryPackId`, `batterySwappable`, `nominalRangeKm`,
`topSpeedKmh`, optional `certification: VehicleCertification`,
`productionBatch`, `productionDate`, `assemblyLine`, `paygoEnabled`, optional
`paygoStatus` (`active` | `suspended` | `completed` | `defaulted`),
`paygoProvider`, `colorCode`, `trimLevel`.

`VehicleType` (9 values): `motorcycle`, `tricycle_cargo`, `tricycle_passenger`,
`tricycle_utility`, `minibus`, `city_bus`, `intercity_bus`,
`delivery_van_light`, `delivery_van_medium`.

`VehicleStatus` (8 values, the EV lifecycle): `design` (engineering, no
artifact) → `prototype` (first builds) → `homologation` (type-approval review) →
`production` (released to manufacturing) → `active_fleet` (in service) →
`maintenance` (service window) → `end_of_life` → `decommissioned`.

#### `Powertrain` (task 60.2.1.2)

The powertrain encodes the full motor-to-wheel drivetrain in structured data,
enabling the `PowertrainConfigurator` to compute efficiency maps and the
compliance system to verify drive-cycle emissions.

Motor, controller, transmission, and drive-cycle efficiency. Holds
`motorType: MotorType`, motor power/torque/speed fields, `motorEfficiencyMap`
(2-D `efficiencyMatrix` indexed by speed and torque), `motorTorqueCurve`
(peak/continuous torque vs. speed), `motorThermalLimits` (`maxWindingTempC`,
`maxMagnetTempC`, `thermalTimeConstantMin`), `controllerManufacturer/Model`,
`controllerParams` (FOC parameters: currents, voltage, switching frequency,
d-/q-axis inductance, flux weakening), `regenerativeBrakingEnabled`,
`maxRegenTorqueNm`, `regenRecoveryPercent`, `transmissionType` (`single_speed` |
`two_speed` | `cvt`), `gearRatios`, `differentialType` (`open` | `limited_slip`
| `torque_vectoring`), `finalDriveRatio`, `drivetrainEfficiencyPercent`,
`co2EmissionsGKm` (well-to-wheel).

`MotorType` (5 values): `bldc_hub`, `bldc_mid_drive`, `pmsm`, `induction`,
`switched_reluctance`.

#### `ChassisDesign` (task 60.2.1.3)

Frame geometry, weight distribution, suspension, braking, wheels/tyres,
aerodynamics. `FrameMaterial` (5): `steel_tubular`, `steel_monocoque`,
`aluminum_space_frame`, `chromoly`, `composite`. `SuspensionType` (7):
`telescopic_fork`, `dual_shock`, `monoshock`, `double_wishbone`,
`mcpherson_strut`, `trailing_arm`, `torsion_beam`. `BrakeType` (4):
`disc_hydraulic`, `disc_mechanical`, `drum`, `combined_braking`. `BrakingSystem`
carries `absEnabled`, `combinedBrakingSystem` (CBS, an India/Ghana regulatory
requirement for two/three-wheelers), `regenBrakingIntegrated`, and an optional
`stoppingDistanceMFromKmh100`.

#### `VehicleCertification` and `CertificationRecord` (task 60.2.1.4)

These types capture the type-approval workflow that every vehicle must pass
before it can be sold in a given market. The `recallHistory` field ensures that
post-sale safety actions remain on the vehicle's compliance record.

`HomologationStandard` (10 values): `DVLA_GHANA_EV`, `UN_ECE_R10` (EMC),
`UN_ECE_R100` (Li-ion EV battery safety), `UN_ECE_R48` (lighting),
`UN_ECE_R13_H` (passenger braking), `UN_ECE_R78` (motorcycle braking),
`ECOWAS_EV_STANDARD`, `IEC_61851` (EV charging), `ISO_11451` (EMI immunity),
`ISO_6469` (EV safety). `CertificationRecord` has `standard` (a
`HomologationStandard` or free string), `certificationNumber`, `certifyingBody`,
`issueDate`, `expiryDate`, `documentRef`, and `status` in `valid` | `expired` |
`pending` | `withdrawn`. `VehicleCertification` aggregates DVLA-Ghana fields,
type-approval fields, UN-ECE type-approval number, a `certifications` array, a
`nextRenewalDue` date, and a `recallHistory` array of
`{ recallNumber, description, affectedVehicles, remedyDate, status }` where
status is `open` | `completed`.

#### `ChargingStation` (task 60.2.1.5)

`id: ChargingStationId`, station code/name/operator, location (address,
city/region/country, `lat`/`lng`), `connectors: ChargingConnector[]`,
`maxStationPowerKw`, `hasDcFastCharge`, power-supply flags (`gridConnected`,
`solarBackup`, `batteryBackupKwh`, `generatorBackupKva`), live status
(`isOnline`, `availablePorts`, `totalPorts`, `lastUpdated`),
`pricing: ChargingPricing`, optional `networkConfig: ChargingNetworkConfig`,
`v2gCapable`, `demandResponseEnabled`, `accessControl` (`public` | `private` |
`fleet_only` | `subscription`), `openingHours`.

`ConnectorType` (10): `Type_1_SAE_J1772`, `Type_2_IEC_62196`, `CCS_Combo_1`,
`CCS_Combo_2`, `CHAdeMO`, `GB_T_20234`, `Tesla_NACS`, `Schuko_CEE`,
`Three_Pin_UK`, `CEE_Blue_16A`. `ChargingPricing.model` is `per_kwh` |
`per_hour` | `per_session` | `free`. `ChargingNetworkConfig.ocppVersion` is
`1.6` | `2.0` | `2.0.1`; payment methods are drawn from `mobile_money`, `card`,
`rfid`, `app`, `qr_code`.

#### `FleetVehicle` (task 60.2.1.6)

`FleetVehicle` adds the fields needed to run a vehicle as part of an operated
fleet rather than a manufacturer's inventory. Lifetime counters and the
`lifetimeEmissionsSavedKgCo2` field support sustainability reporting.

Extends `Vehicle` with `fleetOperatorId: FleetOperatorId`, `fleetName`, optional
`latestTelematics: VehicleTelematics`, `currentAssignment: DriverAssignment` and
`assignmentHistory`, lifetime usage counters (`totalOdometerKm`,
`totalChargeCount`, `totalEnergyConsumedKwh`, `lifetimeEmissionsSavedKgCo2`),
service scheduling (`nextServiceDueDate`, `nextServiceDueKm`, `serviceHistory`),
insurance fields, `dailyRevenueTarget`, and `currency`. `VehicleTelematics`
holds timestamp, GPS, `speedKmh`, `heading`, `socPercent`, `estimatedRangeKm`,
`odometricKm`, `isCharging`, `chargingStationId`, `doorLocked`, `engineOn`,
`tripId`, `driverBehaviorScore`. `DriverAssignment.shiftType` is `day` | `night`
| `split` | `flexible`.

#### Vehicle schemas, guards, factories

`VehicleTypeSchema` (Zod enum, 9 values). `ChargingStationSchema` validates a
charging-station input with bounded `lat`/`lng`, `country` length 2, `currency`
length 3, nested connector and pricing objects; `ChargingStationInput` is its
inferred type. Guards: `isVehicle`, `isFleetVehicle` (checks `fleetOperatorId`),
`isChargingStation` (delegates to `ChargingStationSchema.safeParse`). Factories:
`createDefaultMotorEfficiencyMap()` (5×5 BLDC-hub efficiency grid),
`createDefaultBrakingSystem()`.

### Energy and battery (`energy.ts`)

This module defines the three-level battery hierarchy (cell → module → pack),
the `SolarPanel` and `SolarHomeSystem` types, and the `EnergyStorageSystem` type
for stationary deployments. The `calculateExpectedPanelOutput` helper embeds the
NOCT correction formula used by the solar gateway's generation forecast.

#### `Battery` (task 60.2.2.1)

`id: BatteryId`, `serialNumber`, `chemistry: BatteryChemistry`,
`applicationType: BatteryApplicationType`, rated electrical specs (voltages,
`ratedCapacityAh`, `ratedEnergyKwh`, currents, `maxCRate`), lifecycle state
(`stateOfHealth` 0–100 with `<80` end-of-life threshold, `stateOfCharge`,
`stateOfSafety`, `cycleCount`, `totalEnergyThroughputKwh`), a degradation model
(`degradationRatePercentPerCycle`, `degradationRatePercentPerYear`,
`predictedEolDate`, `predictedRemainingCycles`), thermal limits, and status
flags (`isHealthy`, `faultCodes`, `lastDiagnosticDate`, `certifications`).

`BatteryChemistry` (6): `lfp`, `nmc`, `nca`, `lco`, `lto`, `sodium_ion`.
`BatteryCellFormFactor` (6): `cylindrical_18650`, `cylindrical_21700`,
`cylindrical_26650`, `prismatic`, `pouch`, `blade`. `BatteryApplicationType`
(9): `ev_motorcycle`, `ev_tricycle`, `ev_bus`, `ev_van`, `solar_home_system`,
`commercial_storage`, `grid_scale`, `telecom_backup`, `ess_residential`.

#### `BatteryCell` (task 60.2.2.2)

Per-cell specification: `id: BatteryCellId`, `cellModelCode`, `manufacturer`,
`countryOfOrigin`, `chemistry`, `formFactor`, electrical specs, performance
(`maxChargeCRate`, `maxDischargeCRate`, `cycleLife` to 80% retention,
`calendarLifeYears`), thermal (`thermalRunawayThresholdC`,
`thermalRunawayPropagationRisk` in `low` | `medium` | `high`), physical
dimensions, optional `unitCostUsd`, optional `testData: CellTestData`, and
certification flags (`un383Certified`, `iec62133Certified`). `CellTestData`
records lab test results including `un383Pass` and `iec62133Pass`.

#### `BatteryModule` (task 60.2.2.3)

`id: BatteryModuleId`, `moduleCode`, `cellId`, cell configuration
(`cellsInSeries`, `cellsInParallel`, `totalCells`, `cellConfiguration` such as
`"7s2p"`), computed pack-relevant specs, thermal management
(`thermalManagementType`, `coolingMedium`, `coolantFlowRateLitresMin`,
`heaterWatts`, `thermalManagementRange`), `bms: BMSSpec`, physical fields,
`enclosureIpRating`, `busbarsPlate` (`copper` | `nickel_plated_copper` |
`aluminum`), and optional state. `ThermalManagementType` (4): `air`, `liquid`,
`phase_change_material`, `none`. `BalancingType` (3): `passive`, `active`,
`hybrid`. `BMSSpec` covers chipset, firmware, balancing, `socAlgorithm`
(`coulomb_counting` | `ekf` | `ampere_hour_with_correction`), `sohAlgorithm`
(`capacity_measurement` | `internal_resistance` | `degradation_model`),
protection thresholds, and `communicationInterface` (`can` | `lin` | `i2c` |
`spi` | `rs485`).

#### `BatteryPack` (task 60.2.2.4)

`id: BatteryPackId`, `packCode`, optional `serialNumber`, `moduleId`, module
arrangement, pack-level electrical specs, `coolingSystem: PackCoolingSystem`,
`safety: PackSafetyFeatures`, structural fields (`enclosureType` in
`cell_to_pack` | `module_to_pack` | `cell_to_body`; `structuralMaterial`),
physical/energy-density fields, lifecycle state, and a second-life block:
`secondLifeStatus` (`in_vehicle` | `second_life_stationary` |
`awaiting_assessment` | `recycling`) and `secondLifeMinimumSoh`.
`PackSafetyFeatures` tracks `un38_3Certified`, `ece_r100Certified`,
`iec62619Certified`, venting, fire suppression (`fireSuppressantType` in
`aerosol` | `dry_powder` | `fm200` | `novec`), and thermal-propagation barriers.

#### `SolarPanel` (task 60.2.2.5)

`id: SolarPanelId`, model/manufacturer, `panelType: SolarPanelType`, STC
electrical specs (1000 W/m², 25 °C, AM 1.5), temperature coefficients, NOCT
performance, degradation (`firstYearDegradationPercent`,
`annualDegradationPercent`, `performanceWarrantyYears`,
`performanceWarrantyMinPercent`, `productWarrantyYears`), physical dimensions,
electrical-safety voltages, certification flags (`iecCertified` for IEC 61215,
`iecSafetyCertified` for IEC 61730, `tuvCertified`, `lightingGlobalCertified`),
and cost. `SolarPanelType` (6): `monocrystalline_perc`,
`monocrystalline_topcon`, `polycrystalline`, `thin_film_cigs`, `thin_film_cdte`,
`bifacial`.

#### `SolarHomeSystem` (task 60.2.2.6)

`id`, `skuCode`, `name`, `tier: SHSTier`, components (`panel`, `panelCount`,
`totalPanelWp`, battery chemistry/capacity/energy, `chargeControllerType` (`pwm`
| `mppt`), `chargeControllerRatingA`, optional `inverterWatts`), load
configuration (`maxDailyConsumptionWh`, `autonomyDays`, `peakLoadW`,
`includedLoads: SHSLoad[]`, `totalDailyConsumptionWh`), optional
`paygo: PAYGOConfig`, quality fields (`qualityStandard`, `veraSolCertified`,
`qualityTierPerLightingGlobal`), pricing, and `warrantyYears`. `SHSTier` (5):
`tier_1` through `tier_5`, the IEC TR 61836 / Lighting Global multi-tier
framework. `PAYGOConfig` carries `lockEnabled` (remote disable on missed
payment), `paymentPlatform`, `freeDays`, `paymentFrequency` (`daily` | `weekly`
| `monthly`), and the finance terms in GHS.

#### `EnergyStorageSystem` (task 60.2.2.7)

Stationary storage record: `id`, `systemCode`, `application: ESSApplication`,
battery specs, `performance: ESSPerformanceSpec`, power electronics
(`inverterTopology` in `on_grid` | `off_grid` | `hybrid`, `gridFormingCapable`,
`uninterruptibleSwitchoverMs`), management flags (`bmsIntegration`,
`emsEnabled`, `scadaInterface`), physical fields (`formFactor` in `rack_mounted`
| `wall_mounted` | `floor_standing` | `containerized`), safety, grid-connection
fields (`antiIslandingProtection`), lifecycle, and `lcoe`. `ESSApplication` (8):
`residential_self_consumption`, `commercial_peak_shaving`, `industrial_ups`,
`telecom_backup`, `grid_frequency_regulation`, `grid_peak_shaving`,
`off_grid_community`, `hybrid_solar_diesel_replacement`.

#### Energy schemas, guards, factories

`BatteryChemistrySchema` (Zod enum, 6 values), `BatterySchema`,
`SolarPanelSchema` (with `efficiencyPercent` capped at 30 and degradation
bounds). Guards: `isBatteryHealthy` (`stateOfHealth >= 80`, `isHealthy`, no
fault codes), `isBatteryEndOfLife` (`stateOfHealth < 80`),
`isSolarPanelWithinWarranty`.
`calculateExpectedPanelOutput(panel, ambientTempC, irradianceWm2)` applies a
NOCT cell-temperature and irradiance correction:
`peakPowerWp × irradianceFactor × tempFactor`. Factory:
`createDefaultESSPerformance()` (94% round-trip efficiency, 200 ms response,
3500 cycles at rated DOD).

### IoT and electronics (`iot.ts`)

This module covers IoT devices, sensor readings, connectivity profiles, PCB
designs, electronic assemblies, and smart-city assets. It is the largest type
module in the domain because IoT and electronics span both high-frequency
telemetry (millions of readings per day) and slow-moving design artifacts (PCB
revisions that change rarely but carry extensive compliance metadata).

#### `IoTDevice` (task 60.2.3.1)

`id: IoTDeviceId`, `deviceId` (EUI-64 or UUID), `name`,
`deviceType: IoTDeviceType`, manufacturer/model, `firmwareVersion`,
`hardwareVersion`, `connectivity: ConnectivityProfile`, optional `gatewayId`,
deployment fields (site, address, lat/lng, date, `deploymentHeightM`),
`telemetrySchema: TelemetrySchema`, `alertRules: AlertRule[]`, live status
(`isOnline`, `lastSeenAt`, `lastTelemetryAt`, `batteryLevelPercent`,
`signalStrengthDbm`, `rssi`, `snr`), maintenance fields, and ownership
(`ownerOrganizationId`, `projectId`).

`IoTDeviceType` (14): `air_quality_sensor`, `water_meter`, `electricity_meter`,
`waste_bin_sensor`, `street_light_controller`, `traffic_sensor`, `flood_sensor`,
`weather_station`, `structural_monitor`, `soil_sensor`, `gateway`,
`edge_compute`, `tracking_device`, `environmental_monitor`. `TelemetrySchema` is
a record of field name →
`{ type, unit, sensorType, min?, max?, precision?, description }`. `AlertRule`
has `field`, `operator` (`>`, `>=`, `<`, `<=`, `==`, `!=`, `change_exceeds`),
`threshold`, `severity` (`critical` | `high` | `medium` | `low` | `info`),
`notificationChannels` (subset of `email`, `sms`, `push`, `webhook`), and
`cooldownMinutes`.

#### `ConnectivityProfile` (task 60.2.3.5)

The connectivity profile allows the same `IoTDevice` entity to cover a LoRaWAN
soil-moisture sensor and a 5G edge-compute node without a proliferation of
subtypes. The optional secondary and fallback protocols model devices with
redundant radios.

`primaryProtocol`, optional `secondaryProtocol` and `fallbackProtocol`, optional
`lorawanConfig`, `nbIotConfig`, `wifiConfig`, quality metrics
(`targetUplinkIntervalSec`, `maxTransmissionLatencyMs`, `minRssiDbm`), data
fields (`payloadFormatVersion`, `payloadEncoding` in `binary` | `json` | `cbor`
| `protobuf`, `maxPayloadBytes`), and uptime targets. `ConnectivityProtocol`
(12): `lorawan`, `nb_iot`, `lte_m`, `sigfox`, `zigbee`, `zwave`, `ble`, `wifi`,
`ethernet`, `cellular_4g`, `cellular_5g`, `satellite`. `LoRaWANConfig` carries
`devEui`, `appEui`, `appKey` (stored encrypted), `activationMode` (`OTAA` |
`ABP`), `spreadingFactor` (7–12), `bandwidthKhz` (125 | 250 | 500), `codingRate`
(`4/5`–`4/8`), and `networkServerId`.

#### `SensorReading` (task 60.2.3.2)

The `sequenceNumber` and `uplinkId` fields implement requirement 2 (telemetry
identity) — they allow the ingest pipeline to detect and discard duplicates and
to reconstruct the exact order of readings from a device without relying on
wall-clock timestamps alone.

`timestamp` (ISO 8601 with milliseconds), `deviceId: IoTDeviceId`,
`sensorType: SensorType`, `value` (number, string, or boolean), `unit`,
`quality: SensorQuality`, optional calibration fields (`rawValue`,
`calibrationOffset`, `calibrationFactor`, `lastCalibrationDate`), and context
(`sequenceNumber` for ordering/deduplication, `batteryLevelPercent`,
`signalStrengthDbm`, `uplinkId` — the LoRaWAN frame counter or message ID).
`SensorType` (28 values, including `temperature`, `humidity`, `pressure`,
`pm2_5`, `pm10`, `co2`, `voc`, `no2`, `co`, `o3`, `noise_db`, `water_flow`,
`water_level`, `soil_moisture`, `soil_temperature`, `solar_irradiance`,
`wind_speed`, `rainfall`, `fill_level`, `vibration`, `tilt`, `acceleration`,
`gps_latitude`, `gps_longitude`, `power_kwh`, `current_a`, `voltage_v`,
`binary_event`). `SensorQuality` (5): `good`, `uncertain`, `bad`,
`sensor_error`, `out_of_range`.

#### `PCB` (task 60.2.3.3)

`id: PCBDesignId`, `designCode`, `projectName`, `revisionNumber`, optional
`customerId`, PCB specs (`layerCount`, dimensions, `surfaceFinish`,
`copperWeightOz`, trace/spacing minima, `ipcClass`, `tgC`), assembly fields
(`smdComponents`, `throughHoleComponents`, `hasBga`, `hasFinePitchMm`,
`doubleSided`, `hasConformalCoating`), BOM (`bomRevision`,
`bomItems: BOMItem[]`, `totalBomCostUsd`), test requirements (`ictRequired`,
`aoiRequired`, `xrayRequired`, `functionalTestRequired`, `testCoveragePercent`),
optional `latestTestResults: PcbTestResult[]`, file references, `status`
(`draft` | `under_review` | `approved` | `in_production` | `obsolete`), and
standards flags (`ulCertified`, `ceCertified`, `fccCertified`). `IpcClass` is
`Class_1` | `Class_2` | `Class_3`. `SurfaceFinish` (7): `HASL`,
`HASL_lead_free`, `ENIG`, `ENEPIG`, `OSP`, `Immersion_Tin`, `Immersion_Silver`.
`BOMItem` carries reference designators, MPN, `mslLevel` (Moisture Sensitivity
Level 1–6), `roHsCompliant`, and approved alternates. `PcbTestResult.testType`
is `AOI` | `ICT` | `FCT` | `X-Ray` | `Visual`.

#### `ElectronicAssembly` (task 60.2.3.4)

`id`, `assemblyCode`, `version`, `pcbId`, optional `enclosure: EnclosureSpec`,
`cableHarnesses: CableHarness[]`, `externalConnectors`, test references,
assembly metadata (`toolsRequired`, `estimatedAssemblyTimeMin`), environmental
ratings, and `status` (`design` | `prototype` | `production` | `obsolete`).
`EnclosureSpec.mountingType` is `din_rail` | `wall_mount` | `rack_mount` |
`desktop` | `handheld` | `panel_mount`.

#### `SmartCityAsset` (task 60.2.3.6)

`id`, `assetCode`, `assetType: SmartCityAssetType`, location (lat/lng, address,
`ward`, `district`, `city`), optional `iotDeviceId`, `isSmartEnabled`, free-form
`specs`, `status` (`active` | `inactive` | `fault` | `maintenance` |
`decommissioned`), inspection dates, ownership, smart-capability flags
(`remotlyControlled`, `dataCollectionEnabled`, `alertingEnabled`), install date.
`SmartCityAssetType` (9): `street_light`, `waste_bin`, `water_meter`,
`air_quality_node`, `traffic_light`, `parking_sensor`, `noise_monitor`,
`flood_gauge`, `weather_station`.

#### IoT schemas, guards, factories

`SensorReadingSchema`, `IoTDeviceSchema`. Guards: `isIoTDevice`,
`isSensorReading`, `isDeviceOnline` (default 10-minute offline threshold),
`isSensorReadingInRange` (rejects `bad`/`sensor_error` quality, then checks
against the device's telemetry-schema `min`/`max`). Factory:
`createLoRaWANConnectivityProfile(devEui, appEui, uplinkIntervalSec)`.

### Pharmaceutical and medical (`pharma.ts`)

This module covers both pharmaceuticals and medical devices because their
compliance concerns overlap (regulatory submissions, GMP, ISO standards) and
they share the `RegulatorySubmission` cross-BU type. The clinical evaluation
types implement the ISO 14971 risk-analysis structure required by EU MDR.

#### `Pharmaceutical` (task 60.2.4.1)

`id: PharmaceuticalId`, `productCode`, `internationalNonproprietaryName` (INN),
`brandName`, `dosageForm: DosageForm`, `strength`, `route: PharmaceuticalRoute`,
`therapeuticClass` (ATC Level 3), `atcCode` (WHO ATC code),
`apiComposition: APIComposition[]`, `excipients: ExcipientList[]`, regulatory
fields (`fdaGhanaRegistrationNumber`, `fdaGhanaRegistrationExpiry`,
`nafdacNigeriaNumber`, `whoPrequalificationNumber`, `regulatoryStatus`), quality
fields (`pharmacopoeiaCompliance`, `shelfLifeMonths`, `storageConditions`,
`packagingType`, `primaryPackagingMaterial`), schedule flags (`isNarcotic`,
`isControlledSubstance`, `scheduleClass`), and Ghana-specific flags
(`essentialMedicineGhana`, `nhisListed`).

`Pharmaceutical.regulatoryStatus` (7): `not_started`, `in_preparation`,
`submitted`, `under_review`, `approved`, `rejected`, `expired`. `DosageForm` has
25 values (`tablet`, `coated_tablet`, `sustained_release_tablet`, `capsule`,
`hard_capsule`, `soft_gel_capsule`, `oral_solution`, `oral_suspension`, `syrup`,
`elixir`, `injection_solution`, `injection_suspension`, `powder_for_injection`,
`cream`, `ointment`, `gel`, `suppository`, `dry_powder_inhaler`,
`metered_dose_inhaler`, `transdermal_patch`, `eye_drops`, `ear_drops`,
`nasal_spray`, `granules`, `sachets`, `lyophilized_powder`).
`PharmaceuticalRoute` has 13 values (`oral`, `parenteral_iv`, `parenteral_im`,
`parenteral_sc`, `topical`, `inhalation`, `rectal`, `vaginal`, `ophthalmic`,
`otic`, `nasal`, `transdermal`, `sublingual`).

#### `BatchRecord` (task 60.2.4.2)

The batch record is the primary GMP artifact. It must be traceable to its
pharmaceutical, its equipment (with qualification status), and every deviation
raised during production. The qualified-person sign-off field implements the
EU-style QP release requirement.

`id: BatchRecordId`, `batchNumber`, `productId`, batch details (`batchSize`,
`batchUnit`, `theoreticalYield`, `actualYield`, `yieldPercent`), schedule dates,
`shelfLifeMonths`, manufacturing fields (`manufacturingSite`,
`manufacturingRoom`, `equipmentUsed: EquipmentUsed[]`,
`processParameters: ProcessParameter[]`), personnel (`productionSupervisorId`,
`qcAnalystId`, `qualifiedPersonId` — the EU QP or equivalent),
`qcResults: QCTestResult[]`, `qcStatus` (`pass` | `fail` | `conditional_pass` |
`retest` | `pending`), release fields, `status` (`planned` | `in_production` |
`qc_testing` | `released` | `rejected` | `quarantine`),
`deviations: Deviation[]`, `changeControls`, recall flags, and storage fields.
`EquipmentUsed.qualificationType` is `OQ` | `PQ` | `DQ` | `IQ`. `Deviation.type`
is `critical` | `major` | `minor`.

#### `MedicalDevice` (task 60.2.4.3)

`id: MedicalDeviceId`, `deviceCode`, `name`, `genericName`,
`deviceClass: MedicalDeviceClass`, `intendedUse`, `indicationsForUse`,
classification codes (`gmdnCode`, `unspscCode`, `isoMdnCode`), device-character
flags (`isInVitroDiagnostic`, `isImplantable`, `isSterile`,
`sterilizationMethod`, `hasElectronics`, `hasSoftware`, `softwareSafetyClass` in
`A` | `B` | `C` per IEC 62304, `contactWithPatient`, `contactDuration`,
`invasive`, `surgicallyInvasive`), regulatory fields
(`regulatoryPathway: RegulatoryPathway`, `regulatoryStatus`, authority numbers),
manufacturing-quality flags (`iso13485Certified`, `gmpCertified`), and
post-market fields (`udiCode`, `udiIssuerAgency` in `GS1` | `HIBCC` | `ICCBBA`,
`pmsRequired`, `pmcfRequired`, `vigilanceReportingRequired`).

`MedicalDeviceClass` (4): `class_i`, `class_iia`, `class_iib`, `class_iii`.
`RegulatoryPathway` (10): `fda_510k`, `fda_pma`, `fda_de_novo`,
`eu_mdr_class_i`, `eu_mdr_notified_body`, `who_prequalification`,
`ghana_fda_class_i`, `ghana_fda_class_ii`, `ghana_fda_class_iii`,
`iso_13485_only`. `MedicalDevice.regulatoryStatus` (5): `not_started`,
`in_preparation`, `submitted`, `approved`, `rejected`.

#### `ClinicalEvaluation` (task 60.2.4.4)

`evaluationId`, `deviceId`, `evaluationDate`, `clinicalEvaluatorId` (medically
qualified per EU MDR), `clinicalDataSources: ClinicalDataSource[]`, optional
`equivalentDeviceClaim`, state-of-the-art fields, `risks: RiskEntry[]` (ISO
14971 risk analysis), `overallResidualRisk` (`acceptable` | `unacceptable`),
benefit-risk fields (`benefitRiskConclusion` in `positive` | `negative` |
`inconclusive`), and outcome (`clinicalEvaluationConclusion` in
`device_conforms` | `requires_additional_data` | `negative`). `RiskEntry`
carries `probability` and `severity` on 1–5 ISO 14971 scales and a computed
`riskScore`.

#### `GMPFacility` (task 60.2.4.5)

`id`, `facilityCode`, `name`, `siteAddress`, `licenseNumber`, `licenseExpiry`,
`cleanRooms: CleanRoomSpec[]`, `totalCleanRoomAreaM2`,
`qualifiedEquipment: GmpEquipment[]`, GMP-certification flags
(`whoGmpCertified`, `fdaGhanaApproved`, `iso9001Certified`),
utilities-qualification flags (`purifiedWaterSystemValidated`,
`wfiSystemValidated`, `hvacSystemValidated`, `compressedAirQualityVerified`,
`nitrogenQualityVerified`), capacity fields, personnel counts
(`qualifiedPersonCount`, `pharmacistsCount`, `qcPersonnelCount`), and inspection
fields (`lastInspectionOutcome` in `satisfactory` | `deficiencies_observed` |
`warning_letter` | `import_alert`, `outstandingCAPAs`).
`CleanRoomSpec.isoClassification` is `ISO_5` | `ISO_6` | `ISO_7` | `ISO_8` (ISO
14644); `euGmpClassification` is `A` | `B` | `C` | `D` (EU GMP Annex 1).

#### Pharma schemas, guards, factories

`MedicalDeviceClassSchema`, `PharmaceuticalSchema` (the ATC code is validated
against `/^[A-Z]\d{2}[A-Z]{2}\d{2}$/`). Guards: `isMedicalDeviceClass`,
`requiresNotifiedBody` (true for `class_iib` and `class_iii`),
`requiresClinicalTrial` (true for `class_iii` or `surgicallyInvasive`),
`isBatchReleaseReady` (`qcStatus === 'pass'`, `status === 'qc_testing'`, all
deviations closed or minor). Helpers: `calculateBatchYieldPercent`,
`isPharmaceuticalExpired`, `daysToExpiryFromDate`.

### Robotics and drones (`robotics.ts`)

This module covers both robotics and drones because their operational patterns
are similar — task scheduling, telemetry, safety checks, and compliance — and
they share the geo-coordinate and waypoint types. The GCAA 400-m altitude cap
enforced in `isFlightPlanSafe` is a hard invariant, not a configurable
threshold.

#### `Robot` (task 60.2.5.1)

`id: RobotId`, `robotId`, `serialNumber`, `name`, `robotType: RobotType`,
`model`, `manufacturer`, firmware/hardware versions, hardware
(`actuators: Actuator[]`, `sensors: RobotSensor[]`,
`navigationMode: NavigationMode`, `aiCapabilities: AICapability[]`, `aiChip`,
`computeUnit`), power fields, physical fields, communication
(`communicationProtocols`, `remoteControlEnabled`, `telemetryEnabled`), `status`
(`active` | `inactive` | `maintenance` | `charging` | `fault` |
`decommissioned`), deployment fields, performance metrics
(`totalOperatingHours`, `totalTasksCompleted`, `averageTaskSuccessRate`,
`uptimePercent`), and safety fields (`safetyStandard` such as ISO 10218 / ISO
15066, `emergencyStopEnabled`, `collisionAvoidanceEnabled`,
`geofencingEnabled`).

`RobotType` (10): `household_cleaning`, `household_cooking_assist`,
`household_eldercare`, `construction_rebar_tying`, `construction_bricklaying`,
`construction_plastering`, `construction_surveying`, `waste_sorting_optical`,
`waste_sorting_magnetic`, `waste_sorting_ai`. `NavigationMode` (8): `slam`,
`gps`, `beacon`, `hybrid_slam_gps`, `visual_odometry`, `lidar`, `teleoperated`,
`fixed_path`. `Actuator.type` is `servo` | `stepper` | `bldc` | `pneumatic` |
`hydraulic` | `linear_actuator`. `RobotSensor.type` has 13 values including
`lidar_2d`, `lidar_3d`, `camera_rgb`, `camera_depth`, `camera_thermal`,
`hyperspectral`, `x_ray`, `magnetic`.

#### `RobotTask` (task 60.2.5.2)

`id: RobotTaskId`, `taskCode`, `robotId`, `taskType: RobotTaskType`, `priority`
(`critical` | `high` | `medium` | `low`), `status: RobotTaskStatus`,
`parameters` (work area, waypoints, repeat count, speed/force limits, material
type, quality threshold, custom params), scheduling fields, location context,
results (`completionPercent`, `outputMetrics`, `qualityScore`), and error/safety
fields (`errorCode`, `safetyEventsCount`, `humanInterventionRequired`).
`RobotTaskType` (13): `clean_floor`, `clean_surface`, `cook_meal`,
`serve_medication`, `tie_rebar`, `lay_bricks`, `apply_plaster`, `survey_site`,
`sort_waste_optical`, `sort_waste_magnetic`, `transport_material`, `charge`,
`diagnostic`. `RobotTaskStatus` (7): `queued`, `assigned`, `in_progress`,
`paused`, `completed`, `failed`, `cancelled`.

#### `Drone` (task 60.2.5.3)

`id: DroneId`, `droneId`, `serialNumber`, `droneType: DroneType`, `model`,
`manufacturer`, `firmwareVersion`, GCAA registration fields
(`gcaaRegistrationNumber`, `registrationExpiry`, `operatorCertificationNumber`,
insurance fields), performance specs (payload, takeoff/empty weight, flight time
with and without payload, `maxRangeKm`, `maxHoverAltitudeM`, `maxSpeedMs`,
`maxWindSpeedMs`, `maxRainfallTolerance` in `none` | `light` | `moderate`),
hardware (`motorSpec`, `flightController`, `sensorSuite`), power, communication,
`status` (`active` | `inactive` | `maintenance` | `grounded` | `in_flight` |
`decommissioned`), position/usage counters, `availablePayloads`, and compliance
flags (`remoteIdEnabled`, `utmEnabled`). `DroneType` (6):
`multirotor_quadcopter`, `multirotor_hexacopter`, `multirotor_octocopter`,
`fixed_wing`, `vtol_fixed_wing`, `single_rotor`.
`DroneFlightController.redundancyLevel` is `none` | `dual_gps` |
`triple_redundant`.

#### `FlightPlan` (task 60.2.5.4)

The flight plan captures everything GCAA requires for a safe, authorized sortie:
airspace class, BVLOS authorization, geofence geometry, weather minimums, and
the return-to-home battery threshold. The gateway rejects any plan that violates
the 400-m AGL cap at creation time, not just at runtime.

`id: FlightPlanId`, `planCode`, `droneId`, `missionType`, `pilotId`,
`pilotLicenseNumber`, airspace-authorization fields (`gcaaFlightPermitNumber`,
`permitExpiry`, `operationalArea` polygon), flight geometry
(`homeBase: GeoCoordinate`, `waypoints: Waypoint[]`, `totalDistanceKm`,
`estimatedDurationMin`, min/max altitude), airspace fields
(`airspaceRestrictions: AirspaceRestriction[]`, `airspaceClass` `A`–`G`,
`vlosRequired`, `bvlosAuthorized`, `nightFlightAuthorized`), safety parameters
(`geofenceEnabled`, `geofenceGeometry`, `rthAltitudeM`, `rthTrigger` in
`low_battery` | `signal_loss` | `both`, `minReturnBatteryPercent`,
`maxWindSpeedAutoLandMs`), `weatherRequirements: WeatherRequirements`, payload
configuration, grid/survey parameters, `status` (`draft` | `pending_approval` |
`approved` | `ready` | `active` | `completed` | `cancelled` | `aborted`),
schedule timestamps, and results. `Waypoint` extends `GeoCoordinate` with
`speedMs`, `loiterTimeS`, `action` (`take_photo` | `start_video` | `stop_video`
| `spray` | `land` | `rtl` | `survey_row`), `heading`, `gimbalPitchDeg`.
`AirspaceRestriction.type` is `ctr` | `tma` | `atz` | `restricted` | `danger` |
`prohibited` | `temporary`.

#### `DronePayload` (task 60.2.5.5)

`id`, `payloadCode`, `name`, `payloadType: DronePayloadType`, manufacturer,
model, weight, power requirement, `mountType` (`top_mount` | `bottom_gimbal` |
`side_mount` | `belly_hook`), type-specific `specs`, capacity fields
(`tankCapacityL`, `maxCargoKg`), `compatibleDroneModels`, and `status`
(`available` | `attached` | `maintenance` | `decommissioned`).
`DronePayloadType` (10): `camera_rgb`, `camera_multispectral`, `camera_thermal`,
`lidar_scanner`, `radar`, `agricultural_sprayer`, `delivery_box`, `cargo_hook`,
`loudspeaker`, `gas_detector`.

#### Robotics schemas, guards, factories

`RobotTypeSchema` (10 values), `DroneSchema`, `WaypointSchema`,
`FlightPlanSchema` (requires at least two waypoints). Guards: `isDroneAirworthy`
(`status === 'active'`, battery `>= 30%`, `batteryFlightCycles < 200`),
`isFlightPlanSafe` (current wind within limit, `maxAltitudeM <= 400` — the Ghana
GCAA general altitude cap — and `minReturnBatteryPercent >= 15`),
`isRobotReadyForTask` (`status === 'active'`, battery `>= 20%`, no current
task). Factories: `createDefaultWeatherRequirements(droneType)`,
`estimateDroneFlightTime(drone, payloadWeightKg, windSpeedMs)`.

### Telecom and satellite (`telecom.ts`)

This module covers telecom tower infrastructure and satellite communications.
Both categories depend on geospatial positioning (PostGIS geometry columns) and
regulatory permits — NCA for towers, NCA certification for ground stations —
because their physical siting is governed by spectrum and land-use regulation.

#### `TelecomTower` (task 60.2.6.1)

`id: TelecomTowerId`, `siteId`, `name`, `towerType: TowerType`, location
(address, `community`, `district`/`region`/`country`, lat/lng,
`elevationAmslM`), `structural: TowerStructuralSpec`, `tenants: TowerTenant[]`,
`maxTenantCount`, `antennas: TowerAntenna[]`, `backhaulConnections` (each with
`type` `microwave` | `fiber` | `satellite`, `capacityGbps`, `provider`,
`primaryLink`), `powerSystem: TowerPowerSystem`, land-lease fields, `status`
(`active` | `inactive` | `under_construction` | `maintenance` |
`decommissioned`), inspection dates, and revenue fields.
`TowerStructuralSpec.upliftAvailableKg` is the remaining structural capacity for
new tenants; `TowerAntenna.polarization` is `horizontal` | `vertical` |
`cross_polar`. `TowerType` (8): `monopole`, `guyed_mast`,
`self_supporting_lattice`, `rooftop_monopole`, `rooftop_frame`,
`ground_based_lattice`, `camouflaged_tree`, `camouflaged_flagpole`.

#### `TowerSite` (task 60.2.6.2)

`id`, `siteId`, location, land-owner fields, land-lease fields, access fields
(`accessRoadType` in `tarmac` | `gravel` | `dirt` | `none`,
`accessRoadCondition`, `gatedAccess`, `securityGuard`), utilities, permit fields
(`buildingPermitNumber`, `ncaConstructionPermitNumber`, `epaClearanceNumber`),
optional `environmentalAssessment: EnvironmentalAssessment`, site-survey dates,
and `status` (`surveyed` | `acquired` | `permitted` | `under_construction` |
`operational` | `decommissioned`). `EnvironmentalAssessment` records ICNIRP /
Ghana EPA radiation compliance.

#### `Satellite` (task 60.2.6.3)

`id`, `noradId`, `name`, `operator`, `orbitType: SatelliteOrbitType`, orbital
parameters, coverage fields (`coverageRegions`, `coverageArea` polygon),
`uplinkBands` and `downlinkBands` (arrays of `FrequencyBand`), bandwidth fields
(`totalCapacityGbps`, `availableCapacityGbps`, `beamType` in `wide_beam` |
`spot_beam` | `steerable_beam`, `numberOfBeams`), `roundTripLatencyMs`, and
operational fields (`launchDate`, `expectedEndOfLifeDate`, `currentStatus` in
`operational` | `degraded` | `retired`). `SatelliteOrbitType` (4): `leo`, `meo`,
`geo`, `heo`. `FrequencyBand` (8): `l_band`, `s_band`, `c_band`, `x_band`,
`ku_band`, `ka_band`, `v_band`, `q_band`.

#### `VSATTerminal` (task 60.2.6.4)

`id: VSATTerminalId`, `terminalId`, `serialNumber`, antenna fields
(`antennaType` in `dish_parabolic` | `flat_panel_esim` | `phased_array`,
diameter, gain, transmit power), frequency fields, modem fields, service fields
(`serviceProvider`, `satelliteName`, `serviceType` in `star` | `mesh` |
`hybrid`), bandwidth fields (`downloadMbps`, `uploadMbps`,
`contractedBandwidthMbps`, `contendedRatio`, `burstableUpTo`), installation
fields (customer, address, lat/lng, `azimuthDeg`, `elevationDeg`), `status`
(`active` | `inactive` | `maintenance` | `fault`), `uptimePercent`,
`lastHeartbeatAt`, `signalLevelDb`, and billing fields.

#### `GroundStation` (task 60.2.6.5)

`id: GroundStationId`, `stationCode`, `name`, `operatedBy`, location,
`antennas: GroundStationAntenna[]`, processing fields (`processingCapacity`,
`satelliteModems`, `demodulatorCount`), `networkConnections` (each with `type`
`fiber` | `microwave` | `ip_vpn`), power fields, regulatory fields
(`ncaCertified`, `ncaCertExpiry`, `ncaLicenseNumber`), `status` (`operational` |
`maintenance` | `offline`), maintenance dates, and service fields
(`activeTerminals`, `totalBandwidthMbps`). `GroundStationAntenna.type` is
`parabolic_dish` | `phased_array` | `turnstile`.

#### Telecom schemas, guards, factories

`TelecomTowerSchema`, `VSATTerminalSchema`. Guards: `isTowerCapacityAvailable`
(checks `structural.upliftAvailableKg` against an additional payload),
`isVSATTerminalOnline` (default 15-minute threshold). Helper:
`calculateTowerRevenue` (sums tenant monthly rent). Factory:
`createDefaultPowerSystem(gridAvailable, solarKwp)`.

### E-waste, additive, security, fintech, and shared (`manufacturing.ts`)

This module groups the five remaining business-unit entity types along with the
shared cross-BU types (`ManufacturingOrder`, `QualityTestResult`,
`RegulatorySubmission`, `MaintenanceRecord`, `SupplyChainEvent`,
`MarketDataPoint`) that are used across multiple business units. The shared
types are defined here rather than in their primary BU because they are truly
cross-cutting — a `RegulatorySubmission` applies equally to pharmaceuticals,
medical devices, and vehicles.

#### `EWasteItem` (task 60.2.7.1)

`itemId`, `itemType: EWasteItemType`, brand/model/age, `weightKg`, `condition`
(`working` | `partially_working` | `broken` | `crushed`),
`hazardClass: HazardClass`, optional
`materialCompositionEstimate: MaterialCompositionEstimate`, chain-of-custody
fields (`serialNumber`, `dataWipingRequired`, `dataWipingCertificate`), and
`processingRoute` (`refurbishment` | `component_harvest` | `material_recovery` |
`safe_disposal`). `EWasteItemType` (17 values from `mobile_phone` to
`telecom_equipment`). `HazardClass` (5): `non_hazardous`, `low_hazard`,
`medium_hazard`, `high_hazard`, `restricted_export` (Basel Convention).
`MaterialCompositionEstimate` itemizes recoverable masses including `gold_g`,
`silver_g`, `palladium_g`, `cobalt_kg`, `lithium_kg`, `lead_kg`, `mercury_g`.

#### `RecoveredMaterial` (task 60.2.7.2)

`id`, `recoveryBatchCode`, `collectionId: EWasteCollectionId`, `recoveryDate`,
`processingFacilityId`, material spec (`materialType: RecoverableMaterial`,
`grade: MaterialGrade`, `weightKg`, `purityPercent`), assay fields, value fields
(`spotPriceUsdPerKg`, `estimatedValueUsd`, `actualSaleValueGhs`, buyer/sale
fields), Basel-Convention fields (`exportPermitNumber`, `destinationCountry`),
and a `chainOfCustody` array. `RecoverableMaterial` (14): `gold`, `silver`,
`palladium`, `platinum`, `copper`, `aluminum`, `steel`, `cobalt`,
`lithium_carbonate`, `nickel`, `plastic_abs`, `plastic_hdpe`, `glass`,
`ferrites`. `MaterialGrade` (6): `doré`, `refined_99_5`, `refined_99_9`,
`commercial_grade`, `industrial_grade`, `scrap`.

#### `PrintJob` (task 60.2.7.3)

`id: PrintJobId`, `jobCode`, `printerId: PrinterId`, optional `customerId`,
`projectName`, `partName`, `quantity`, design-file references, print parameters
(`material: PrintMaterial`, `layerHeightMicron`, `infillPercent`,
`infillPattern` in `grid` | `honeycomb` | `gyroid` | `triangles` | `lines` |
`rectilinear`, `shellCount`, `printSpeedMms`, temperatures, support settings),
sliced dimensions, estimates, quality requirements (`dimensionalToleranceMm`,
`surfaceRoughnessRaUm`, `requiresPostProcessing`, `postProcessingSteps`,
`requiresInspection`), and results (`status` in `queued` | `printing` | `paused`
| `completed` | `failed` | `cancelled`, `qualityResult` in `pass` | `fail` |
`conditional_pass`). `PrintMaterial` (14 values, including
`metal_stainless_316l`, `metal_titanium_ti64`, `metal_aluminum_almg`,
`metal_inconel_625`, `concrete`).

#### `AdditiveManufacturingMaterial` (task 60.2.7.4)

`id`, `materialCode`, `name`, `materialType: PrintMaterial`, `manufacturer`,
`diameter` (`1.75mm` | `2.85mm` | `spool` | `powder_kg`), mechanical/thermal
properties, drying requirements, available colors, cost fields, and stock fields
(`quantityKgOnHand`, `reorderPointKg`, `supplier`, `leadTimeDays`).

#### `SecuritySystem` (task 60.2.7.5)

`id: SecuritySystemId`, `systemCode`, `name`, `systemType` (`cctv_only` |
`access_control_only` | `intrusion_only` | `integrated`), `customerId`, install
fields, location, CCTV fields (`cameras: SecurityCamera[]`, `totalCameras`,
`activeCameras`, `vmsServer`, `nvrModel`, `storageCapacityTb`, `retentionDays`),
access-control fields (`accessPoints: AccessControlPoint[]`), `alarmZones` (each
with `type` `perimeter` | `interior` | `panic` | `fire` | `flood`), AI-analytics
fields, monitoring fields, and `status` (`active` | `inactive` | `maintenance` |
`alarm`). `SecurityCamera.type` is `fixed` | `ptz` | `panoramic` | `fisheye` |
`thermal`; `AccessControlPoint.authMethods` is a subset of `pin`, `card`,
`fingerprint`, `face`, `iris`, `qr`.

#### Fintech hardware (`POSTerminal`, `ATM`, `Kiosk`, `BiometricDevice` —

tasks 60.2.8.1–60.2.8.4)

`FintechTerminalType` (9): `pos_countertop`, `pos_mobile`, `pos_pin_pad`,
`atm_standalone`, `atm_drive_through`, `kiosk_banking`, `kiosk_government`,
`kiosk_telecom`, `kiosk_retail`.

`POSTerminal`: `id: FintechTerminalId`, `terminalId`, `serialNumber`,
`terminalType` (the three `pos_*` values), manufacturer/model/firmware, PCI
fields (`pciPtsCertNumber`, `pciPtsCertExpiry`, `pciPtsVersionApproved`,
`pciDssCompliant`, `emvL1Certified`, `emvL2Certified`, `emvContactless`),
payment-method flags (`acceptsChip`, `acceptsMagstripe`, `acceptsContactless`,
`acceptsQrCode`, `acceptsMobileMoney`, `mobileMoneySupportedNetworks`),
connectivity, hardware, deployment fields, `status` (`active` | `inactive` |
`fault` | `decommissioned`), and usage counters.

`ATM`: `id`, `terminalId`, `serialNumber`, `terminalType` (`atm_standalone` |
`atm_drive_through`), PCI fields (`pciHsmCertNumber`, `pciDssCompliant`),
cash-management fields (`cashCassettes`, `cashCapacityUnits`,
`denominationsSupported`, `cashLevelPercent`, `lowCashAlert`), biometric flags,
hardware, network, deployment fields, `status` (`active` | `inactive` | `fault`
| `out_of_cash` | `maintenance`), and performance fields.

`Kiosk`: `id`, `terminalId`, `serialNumber`, `terminalType` (the four `kiosk_*`
values), `purpose`, hardware capability flags, OS/software platform,
connectivity, deployment fields, `status` (`active` | `inactive` | `fault` |
`maintenance`).

`BiometricDevice`: `id`, `deviceCode`, manufacturer/model, `modality`
(`fingerprint` | `face` | `iris` | `palm_vein` | `finger_vein` | `multi_modal`),
sensor specs, performance metrics (`falseMatchRate`, `falseNonMatchRate`,
`failureToEnrollRate`, `enrollmentTimeS`, `verificationTimeMs` per ISO/IEC
19795), standards-compliance flags (`iso19794Compliant`, `fips201Compliant`,
`fidoCertified`), operating conditions, and `applications`.

#### Shared cross-BU domain types (task 60.2.9.x)

These six types appear in the domain model wherever a concept applies across
multiple business units. They are defined once in `manufacturing.ts` and
re-exported from the `@saraswati/core` barrel.

`ManufacturingOrder<T>` (60.2.9.1) — generic production order with `productSpec`
of type `T`, `billOfMaterials` (each line has `status` `available` | `on_order`
| `shortage`), `qualityRequirements`, traceability fields, and `status` (`draft`
| `confirmed` | `material_procurement` | `in_production` | `quality_check` |
`completed` | `cancelled`).

`QualityTestResult` (60.2.9.2) — `parameters` array with per-parameter
`specification`, `measuredValue`, `pass`, and `uncertainty`; `overallResult`
(`pass` | `fail` | `conditional_pass` | `retest`); optional certificate fields
and `dispositionDecision` (`accept` | `reject` | `rework` | `scrap` |
`concession`).

`RegulatorySubmission` (60.2.9.3) — `productType` (`pharmaceutical` |
`medical_device` | `vehicle` | `food` | `chemical`), `authority`,
`submissionType` (`initial_registration` | `renewal` | `variation` |
`line_extension` | `labeling_change` | `type_approval`), `requiredDocuments`,
`status` (9 values: `not_started`, `in_preparation`, `submitted`,
`under_review`, `additional_info_required`, `approved`, `rejected`, `withdrawn`,
`expired`), fees, and a `queries` array.

`MaintenanceRecord` (60.2.9.4) — `maintenanceType` (`scheduled` | `corrective` |
`predictive` | `inspection` | `overhaul` | `emergency`), usage-at-maintenance
fields, `partsReplaced`, `softwareUpdated`/`newFirmwareVersion`, cost fields,
findings, and next-due fields.

`SupplyChainEvent` (60.2.9.5) — `eventType` (11 values from `purchase_order`
through `disposal`), location, material reference, actor, transaction reference,
value, `qualityStatus` (`passed` | `failed` | `hold` | `pending`), and `status`
(`planned` | `in_transit` | `completed` | `exception`).

`MarketDataPoint` (60.2.9.6) — `businessUnit`, `metricName`, `metricCategory`
(`market_size` | `market_share` | `growth_rate` | `price` | `demand` |
`competition` | `regulatory` | `technology`), `geography`, `timePeriod`,
`value`, `unit`, `source`, `confidenceLevel` (0–1), `isProjected`,
`projectionCagr`, and optional competitor fields.

Shared schemas: `ManufacturingOrderSchema`, `QualityTestResultSchema`,
`MarketDataPointSchema`. Guards: `isQualityTestPassed`,
`isManufacturingOrderComplete`, `isRegSubmissionApproved`,
`isRegSubmissionExpired`, `isHighHazardEWaste`. Helpers: `estimateEWasteValue`
(values a composition estimate against per-material USD/kg prices, with
documented default prices), `createDefaultMaintenanceRecord`.

## Persistence (`@saraswati/db`)

`@saraswati/db` is the single data layer for the entire domain. All persistence
decisions — table structure, indexes, geospatial columns, time-series
hypertables, vector columns, connection pools, and cache TTLs — are centralized
here. The separation between the OLTP pool (`getPool`) and the telemetry-ingest
pool (`getTelemetryPool`) exists because telemetry writes are high-frequency and
should not block ordinary CRUD operations.

The domain database is `saraswati` (PostgreSQL with the PostGIS, TimescaleDB,
and pgvector extensions). The connection URL is the `SARASWATI_DATABASE_URL`
environment variable. The schema is defined with Drizzle ORM in
`libs/saraswati/db/src/schema.ts`.

### Shared enums

These four enums are reused across many tables to avoid per-table duplication.
Per-BU enums with domain-specific values are listed separately below.

`saraswati_status` (`active`, `inactive`, `maintenance`, `decommissioned`,
`pending`); `saraswati_priority` (`critical`, `high`, `medium`, `low`);
`saraswati_quality_status` (`pass`, `fail`, `conditional_pass`, `retest`,
`pending`); `saraswati_regulatory_status` (`not_started`, `in_preparation`,
`submitted`, `under_review`, `additional_info_required`, `approved`, `rejected`,
`withdrawn`, `expired`).

### Tables

All tables use a `gen_random_uuid()` UUID primary key and `created_at` /
`updated_at` timestamps. Tables, grouped by schema area:

- Vehicle records (60.1.2.1) — `saraswati_vehicle_designs`,
  `saraswati_vehicles`, `saraswati_charging_stations`,
  `saraswati_vehicle_service_history`.
- Battery (60.1.2.2) — `saraswati_battery_cells`, `saraswati_battery_modules`,
  `saraswati_battery_packs`, and the TimescaleDB hypertable
  `saraswati_battery_telemetry`.
- Solar (60.1.2.3) — `saraswati_solar_panels`, `saraswati_solar_home_systems`,
  `saraswati_solar_installations`, and the hypertable
  `saraswati_solar_telemetry`.
- IoT (60.1.2.4) — `saraswati_iot_devices` and the hypertable
  `saraswati_iot_telemetry`.
- Electronics (60.1.2.5) — `saraswati_pcb_designs`,
  `saraswati_production_orders`, `saraswati_component_inventory`.
- Pharmaceutical (60.1.2.6) — `saraswati_pharmaceuticals`,
  `saraswati_batch_records`, `saraswati_gmp_facilities`.
- Robotics (60.1.2.7) — `saraswati_robots`, `saraswati_robot_tasks`,
  `saraswati_robot_maintenance_logs`.
- Drones (60.1.2.8) — `saraswati_drones`, `saraswati_flight_plans`.
- Telecom (60.1.2.9) — `saraswati_telecom_towers`.
- Medical devices (60.1.2.10) — `saraswati_medical_devices`,
  `saraswati_regulatory_submissions`, `saraswati_post_market_events`.
- Fintech hardware (60.1.2.11) — `saraswati_fintech_terminals`.
- Security systems (60.1.2.12) — `saraswati_security_systems`,
  `saraswati_alarm_events`.
- E-waste (60.1.2.13) — `saraswati_ewaste_collections`,
  `saraswati_recovered_materials`.
- Additive manufacturing (60.1.2.14) — `saraswati_printers`,
  `saraswati_print_jobs`.
- Satellite (60.1.2.15) — `saraswati_vsat_terminals`,
  `saraswati_ground_stations`.
- Market intelligence (60.1.2.16) — `saraswati_market_intelligence`.
- Financial models (60.1.2.17) — `saraswati_financial_models`.

### Per-table enums

The schema defines additional Postgres enums beyond the four shared ones,
including `saraswati_vehicle_type`, `saraswati_vehicle_status`,
`saraswati_motor_type`, `saraswati_battery_chemistry`,
`saraswati_battery_cell_form_factor`, `saraswati_solar_panel_type`,
`saraswati_shs_tier`, `saraswati_iot_device_type`,
`saraswati_connectivity_protocol`, `saraswati_production_order_status` (15
values: `draft`, `confirmed`, `material_procurement`, `ready_to_produce`,
`smt_in_progress`, `smt_complete`, `tht_in_progress`, `tht_complete`,
`aoi_inspection`, `ict_testing`, `functional_testing`, `rework`,
`quality_approved`, `shipped`, `cancelled`), `saraswati_dosage_form` (14
values), `saraswati_robot_type`, `saraswati_robot_task_status`,
`saraswati_drone_type`, `saraswati_mission_type` (11 values: `aerial_survey`,
`photogrammetry`, `lidar_mapping`, `agricultural_spraying`,
`infrastructure_inspection`, `pipeline_inspection`, `delivery`,
`search_and_rescue`, `security_patrol`, `environmental_monitoring`,
`telecoms_relay`), `saraswati_tower_type`, `saraswati_medical_device_class`,
`saraswati_fintech_terminal_type`, `saraswati_ewaste_item_type`,
`saraswati_hazard_class`, `saraswati_printer_technology` (8 values: `fdm`,
`sla`, `sls`, `dmls`, `mjf`, `binder_jetting`, `dlp`, `concrete_printing`),
`saraswati_orbit_type`, `saraswati_frequency_band`.

### Keys, indexes, and constraints

Each table carries a natural-key unique index — `design_code`, `vin`,
`serial_number`, `station_code`, `cell_model_code`, `module_code`, `pack_code`,
`order_number`, `device_id`, `batch_number`, `facility_code`, `robot_id`,
`task_code`, `drone_id`, `plan_code`, `site_id`, `device_code`,
`submission_number`, `terminal_id`, `system_code`, `event_id`,
`collection_code`, `recovery_batch_code`, `printer_id`, `job_code`,
`station_code`, `data_point_code`, `model_code` — plus secondary indexes on
status, type, customer/fleet, geographic, and date columns. Foreign keys link
the hierarchy: vehicles → vehicle designs and battery packs; battery packs →
modules → cells; battery packs → vehicles; solar installations → solar home
systems → panels; production orders → PCB designs; batch records → and
regulatory submissions → pharmaceuticals; robot tasks and maintenance logs →
robots; flight plans → drones; regulatory submissions and post-market events →
medical devices; alarm events → security systems; recovered materials → e-waste
collections; print jobs → printers; battery/solar/IoT telemetry → their parent
records.

### Drizzle relations

`schema.ts` declares Drizzle `relations` for the parent/child graph above:
`vehicleDesignsRelations`, `vehiclesRelations`, `batteryPacksRelations`,
`batteryModulesRelations`, `solarHomeSystemsRelations`,
`solarInstallationsRelations`, `iotDevicesRelations`, `pcbDesignsRelations`,
`pharmaceuticalsRelations`, `robotsRelations`, `dronesRelations`,
`medicalDevicesRelations`, `securitySystemsRelations`,
`eWasteCollectionsRelations`, `printersRelations`.

### Time-series storage (TimescaleDB)

Three hypertables hold telemetry. Continuous aggregates roll up raw readings
into hourly and daily summaries so dashboard queries do not scan the full raw
table. Retention policies evict old raw data after the policy window while
keeping aggregates indefinitely.

`saraswati_battery_telemetry` (partition key `time`, 1-day chunks) records
per-pack SoC/SoH, voltage, current, power, min/max/avg temperatures, cell
voltages, balancing state, `error_flags`, and charging state.
`saraswati_solar_telemetry` (7-day chunks) records per-installation panel power,
battery voltage/current/SoC, load power, irradiance, ambient/panel temperature,
daily yield, and PAYGO status. `saraswati_iot_telemetry` (7-day chunks) records
per-device JSONB `readings`, battery level, signal strength, sequence number,
LoRaWAN frame port, raw payload, and processing latency. `migrations.ts` creates
these hypertables via `createHypertables()`, builds three continuous aggregates
(`saraswati_iot_telemetry_hourly`, `saraswati_battery_telemetry_daily`,
`saraswati_solar_telemetry_daily`) via `createContinuousAggregates()`, and sets
retention policies via `setupRetentionPolicies()`: raw IoT telemetry 90 days,
raw battery telemetry 30 days, raw solar telemetry 90 days (aggregates kept).

### Geospatial storage (PostGIS)

`createSpatialIndices()` adds a SRID-4326 `geom` POINT column, populates it from
the table's lat/lng pair, and creates a GiST index for seven tables:
`saraswati_charging_stations`, `saraswati_vehicles`, `saraswati_telecom_towers`,
`saraswati_iot_devices`, `saraswati_solar_installations`, `saraswati_drones`,
`saraswati_vsat_terminals`.

### Vector storage (pgvector)

pgvector columns support three AI/ML use cases that require nearest-neighbor
search over high-dimensional embeddings. The `ivfflat` cosine-similarity index
makes similarity queries fast at scale.

`addVectorColumns()` adds embedding columns and ivfflat cosine-similarity
indexes: `saraswati_pcb_designs.design_embedding` (`vector(1536)`, for
defect-detection clustering), `saraswati_ewaste_collections.item_embedding`
(`vector(1536)`, for material classification), and
`saraswati_iot_devices.telemetry_pattern_embedding` (`vector(768)`, for anomaly
detection).

### Extensions and setup

`installExtensions()` creates `postgis`, `postgis_topology`, `timescaledb`,
`vector`, and `pgcrypto`. `runDatabaseSetup()` runs, in order:
`installExtensions`, `createHypertables`, `createContinuousAggregates`,
`addVectorColumns`, `createSpatialIndices`, `setupRetentionPolicies`.

### Connection pooling

The two-pool design separates concerns: the OLTP pool handles ordinary requests
and can be safely saturated by concurrent API traffic without blocking the
telemetry ingest path.

`getPool()` returns a shared `pg` Pool keyed off `SARASWATI_DATABASE_URL` with
defaults of 20 max / 2 min connections, a 30-second idle timeout, a 10-second
connection timeout, a 30-second statement timeout, and
`application_name = 'saraswati_service'`. `getDb()` wraps it in a Drizzle
instance. `getTelemetryPool()` is a separate, higher-throughput pool (30 max / 5
min connections, 10-second statement timeout,
`application_name = 'saraswati_telemetry_ingest'`) for high-frequency telemetry
writes. `getPoolStats()` exposes total/idle/waiting counts; `closeConnections()`
drains both pools on shutdown.

### Seed data

`seed.ts` seeds reference data (task 60.1.2.19): reference battery cells (real
commercially available cells used in the African EV market, e.g. the
`CATL-LFP-280AH-3V2` prismatic LFP cell), solar panels, and market-intelligence
data points.

### Redis caching (`cache.ts`)

`CACHE_KEYS` defines per-BU cache key namespaces under the `saraswati:` prefix
for all BUs. `CACHE_TTL` defines TTLs in seconds, from very short
(`REALTIME_TELEMETRY` 5 s, `DRONE_POSITION` 3 s, `VEHICLE_POSITION` 10 s,
`ALARM_STATUS` 15 s, `TERMINAL_HEARTBEAT` 30 s) through dashboard
(`DASHBOARD_REALTIME` 30 s, `FLEET_STATUS` 60 s) and operational
(`PRODUCTION_STATUS` 300 s, `PRINTER_STATUS` 120 s) to reference data
(`MARKET_DATA` 1 h, `BATTERY_SPECS` and `SOLAR_PANEL_SPECS` 24 h,
`MATERIAL_PRICES` 30 min). `PUBSUB_CHANNELS` defines Redis pub/sub channel names
for IoT, battery, EV, solar, drone, security, manufacturing, and financial
events. `RATE_LIMIT_KEYS` defines rate-limit key builders for telemetry ingest,
API requests, and SMS alerts. The module also exports cache payload types:
`CachedDronePosition`, `CachedVehiclePosition`, `CachedBatteryTelemetry`,
`CachedSolarInstallation`, `CachedFleetDashboard`.

## API Surface (`@saraswati/gateway`)

The gateway is a Hono application (`libs/saraswati/gateway/src/app.ts`). It
mounts all 17 business units under `/api/v1/{bu}/`. The exported `app` is the
composed application; individual BU route modules are also exported for
composition.

The middleware chain, system endpoints, and BU routing described below are the
same for every business unit. Understanding the chain once means understanding
every BU's request lifecycle.

### Global middleware chain

Middleware runs in declaration order. Each layer adds a capability: security
headers, CORS, distributed tracing, structured logging, Prometheus metrics, rate
limiting, and optional JWT auth. The order matters — tracing and logging must
wrap rate limiting and auth so that rejected requests are still attributed.

In order: `secureHeaders()`; CORS (allows `localhost:3000/5173/4200`,
`saraswati.oshun.io`, `dashboard.oshun.io`); distributed tracing
(`tracing('@saraswati/gateway')`); structured logging with correlation IDs
(`structuredLogger`); Prometheus HTTP metrics (`httpMetrics`); a global rate
limit of 500 requests/minute per IP on `/api/*`; and optional JWT auth
(`optionalAuth`) on `/api/*`.

### System endpoints

These endpoints support infrastructure concerns — health checks for
load-balancer probes, metrics for Prometheus scraping, OpenAPI docs for client
generation, and the circuit-breaker snapshot for operations dashboards.

- `GET /health` — returns `status`, service name, version, the list of 17
  business units, and `buCount: 17`.
- `GET /ready` — readiness check; returns 503 if any circuit breaker is open.
- `GET /metrics` — Prometheus exposition (`text/plain; version=0.0.4`).
- `GET /openapi.json` — the `SARASWATI_OPENAPI_SPEC` OpenAPI document.
- `GET /graphql/schema` — the GraphQL SDL.
- `POST /graphql` — executable GraphQL endpoint for cross-BU dashboard queries
  and mutations; resolved by `executeSaraswatiGraphQL`.
- `GET /circuit-breakers` — circuit-breaker health snapshot.
- `GET /ws/status`, `GET /ws/sse/:channel`, `POST /ws/inject` — WebSocket/SSE
  status, the SSE stream, and a development-only event injector.

### Business-unit routing

Under `/api/v1`, each BU is mounted with a per-BU rate limit and then its route
module: `/ev`, `/battery`, `/solar`, `/iot` (with a separate higher limit on
`/iot/telemetry`), `/electronics`, `/pharma`, `/robotics`, `/drones`,
`/telecom`, `/medical`, `/fintech-hw`, `/security`, `/ewaste`, `/additive`,
`/satellite`, `/market-intel`, `/financials`. WebSocket routes are mounted at
the root.

### Representative endpoints

The EV route module (`/api/v1/ev`) is representative of the pattern every BU
follows. Reading it once gives a reader the mental model for all 17 BUs. Every
route is guarded by `requireBUAccess('ev')`; write routes additionally require a
minimum role.

- `GET /ev/designs`, `GET /ev/designs/:id` — list and fetch vehicle designs.
- `POST /ev/designs` (`saraswati_engineer`) — create a design, validated by a
  Zod `CreateVehicleDesignSchema`.
- `PATCH /ev/designs/:id/status` (`saraswati_manager`) — transition a design
  through `draft` | `in_review` | `approved` | `deprecated`.
- `GET /ev/fleet`, `GET /ev/fleet/:vin` — paginated fleet listing and
  per-vehicle lookup.
- `POST /ev/fleet/assign` (`saraswati_manager`) — assign a vehicle to an
  operator/driver.
- `POST /ev/fleet/telemetry` (`saraswati_technician`) — ingest a telemetry
  reading, validated by `VehicleTelemetrySchema`.
- `GET /ev/fleet/:vehicleId/telemetry` — recent readings for a vehicle.
- `GET /ev/charging-stations`, `GET /ev/charging-stations/:id`,
  `POST /ev/charging-stations` (`saraswati_engineer`).
- `GET /ev/analytics/fleet-summary`, `GET /ev/analytics/range-estimate`.

The remaining BUs follow the same shape (`libs/saraswati/gateway/src/routes/`):
electronics (PCB designs, production orders, component inventory, yield
analytics); robotics (robots, robot tasks with single-active-task enforcement,
telemetry, analytics); drones (drones, flight plans with a GCAA 400-m altitude
check on creation, flight-plan execution, analytics); telecom (towers, tenant
provisioning with an `upliftAvailableKg` capacity check, analytics); medical
(devices with a notified-body flag for class IIb/III, regulatory submissions);
fintech-hw (terminals, deployments, analytics); security (systems, alarm
ingestion, active alarms); ewaste (collections with precious-metal value
estimation, processing with material recovery, analytics); additive (printers,
print jobs with printer-availability enforcement, analytics); satellite (VSAT
terminals, ground stations, analytics); market-intel (data points, the Ghana
EV-market reference dataset, competitors); financials (financial models with NPV
computation and a Newton-Raphson IRR estimate, model summaries, portfolio
roll-up). The financials routes additionally require the `saraswati_manager`
role on every endpoint.

The current route modules persist into in-memory `Map`s rather than
`@saraswati/db`; that data layer is built and verified independently.

### Authentication and RBAC (`middleware/auth.ts`)

The six-level role hierarchy ensures that read-only viewers cannot trigger
writes, field technicians cannot change business-unit configuration, and only
managers and above can access the financials BU. The `buScopes` claim enables
multi-tenant deployments where a user has access to only a subset of the 17
business units.

JWT bearer tokens. `JWTPayload` has `sub`, `email`, `role`, optional `buScopes`,
optional `siteId`, `iat`, `exp`. `parseJWT` base64url-decodes the payload,
rejects expired tokens, and requires the `sub`/`email`/`role` claims and a known
role.

`SaraswatiRole` (6, lowest to highest clearance): `saraswati_viewer` (1,
read-only), `saraswati_technician` (2, field ops), `saraswati_engineer` (3,
domain configuration), `saraswati_manager` (4, BU management),
`saraswati_director` (5, cross-BU oversight), `super_admin` (6).
`ROLE_HIERARCHY` maps each to its numeric level.

`SaraswatiBU` (17): `ev`, `battery`, `solar`, `iot`, `electronics`, `pharma`,
`robotics`, `drones`, `telecom`, `medical`, `fintech_hw`, `security`, `ewaste`,
`additive`, `satellite`, `market_intel`, `financials`. An empty `buScopes` array
means unrestricted access.

Middleware: `optionalAuth()` (sets auth context if a valid token is present),
`authenticate()` (rejects requests without a valid token), `requireRole(min)`
(403 if the user's level is below the minimum), `requireBUAccess(bu)` (403 if a
scoped user lacks the BU). `getAuthContext(c)` retrieves the context;
`createMockToken` builds a test-only JWT.

### Rate limiting (`middleware/rate-limit.ts`)

Per-BU rate limits reflect each BU's expected traffic pattern. Telemetry
endpoints receive the highest limits because IoT and battery ingest is
continuous; financials and fintech-hw receive the lowest because they are
low-volume, high-sensitivity endpoints.

`SlidingWindowRateLimiter` implements a sliding-window counter with periodic
cleanup. `BU_RATE_LIMITS` defines per-BU profiles (requests per 60-second
window): telemetry endpoints `iot_telemetry` 10,000, `battery_telemetry` and
`solar_telemetry` 5,000; operations `ev`/`battery`/`solar`/`iot` 500,
`electronics`/`robotics`/`drones`/`telecom`/`satellite`/`security` 300,
`ewaste`/`additive` 200; regulated `pharma`/`medical` 200; strict `fintech_hw`
and `financials` 60, `market_intel` 100; `default` 100. `rateLimitForBU(bu)`
applies the matching profile and emits `X-RateLimit-Limit`,
`X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After` headers; over
limit returns HTTP 429 with a `RATE_LIMITED` error body.

### Other middleware

`middleware/` also provides Prometheus metrics (`httpMetrics`, `registry`, and
BU-specific recorders such as `recordEVFleetMetrics`, `recordBatteryMetrics`),
OpenTelemetry-style tracing (`tracing`, `startChildSpan`, W3C `traceparent`
parsing), structured logging (`structuredLogger`, `Logger`, correlation IDs),
and circuit breakers (`CircuitBreaker`, `callWithBreaker`, `CircuitOpenError`,
`getCircuitBreakerHealth`) with `CircuitState` open/closed/half-open semantics.

### GraphQL (`graphql/`)

The GraphQL endpoint exists specifically to serve the cross-BU dashboard
libraries, which need to query multiple business units in a single round trip.
REST routes do not cross BU boundaries; GraphQL does.

`SARASWATI_GRAPHQL_SCHEMA` (task 60.1.3.2) is the SDL for cross-BU queries. Its
`Query` type exposes `saraswatiOverview`, `fleetSummary` (unified across EVs,
drones, and robots), `energyDashboard`, `manufacturingPipeline`,
`supplyChainEvents`, `marketIntelSummary`, `regulatoryCompliance`, and per-BU
queries (`evFleet`, `batteryHealth`, `solarPortfolio`, `iotNetwork`,
`droneFleet`). Its `Mutation` type exposes operations such as `assignVehicle`,
`triggerBatteryAssessment`, and `activateSolarPaygo`. `executeSaraswatiGraphQL`
resolves operations; `validateQueryFields` checks requested fields.

### gRPC service definitions (`proto/services.ts`)

Task 60.1.3.5. TypeScript Protocol-Buffer message and service interfaces for
four real-time services: `IRoboticsService` (`ROBOTICS_PROTO` — motor-control
commands and sensor feedback), `IDroneService` (`DRONES_PROTO` — flight
telemetry and command/control), `IIoTGatewayService` (`IOT_GATEWAY_PROTO` —
high-frequency edge-gateway telemetry ingestion), and `IManufacturingService`
(`MANUFACTURING_PROTO` — machine-to-machine process control). Message types
include `RobotCommand`, `RobotTelemetry`, `DroneTelemetry`, `DroneCommand`,
`IoTUplink`, `QualityGateRequest`/`QualityGateResponse`, and `ProductionEvent`,
with shared `GRPCTimestamp`, `GRPCLocation`, and `GRPCStatus` (gRPC status codes
0–16) types.

### MQTT topic hierarchy (`mqtt.ts`)

Task 60.1.3.6. MQTT is used for device-to-gateway communication where the broker
fan-out model fits better than HTTP request-response — particularly for IoT
devices that push telemetry asynchronously. `MQTT_TOPICS` builds topics on the
pattern `saraswati/{siteId}/{bu}/{entityId}/{stream}` — for example
`saraswati/{siteId}/ev/{vehicleId}/telemetry`,
`saraswati/{siteId}/battery/{packId}/alerts`,
`saraswati/{siteId}/iot/{deviceEUI}/{sensorType}`,
`saraswati/{siteId}/drone/{droneId}/commands`, plus the global
`saraswati/gateway/status` and `saraswati/paygo/{systemId}` topics. `TOPIC_QOS`
sets per-topic MQTT QoS levels. The module exports message types
(`MQTTEVTelemetry`, `MQTTBatteryAlert`, `MQTTSolarTelemetry`,
`MQTTPAYGOCommand`, `MQTTIoTUplink`, `MQTTDroneCommand`,
`MQTTManufacturingEvent`), serialization helpers, and an `IMQTTClient`
abstraction.

### API versioning (`versioning/api-versioning.ts`)

Task 60.21.5.5. URI-based versioning (`/api/v1/`, `/api/v2/`) with content
negotiation fallback (`Accept: application/vnd.saraswati.v2+json`). `APIVersion`
records `label`, `semver`, `status` (`current` | `deprecated` | `sunset` |
`preview`), and lifecycle dates. The deprecation policy emits RFC 8594 `Sunset`
and `Deprecation` headers and an RFC 8288 `Link` header to the migration guide;
versions are sunset at least six months after deprecation. `BreakingChange`
classifies breaking vs. non-breaking changes.

## Events

### WebSocket / SSE channels (`routes/websocket.ts`)

Task 60.1.3.4. WebSocket/SSE channels serve consumers that need sub-second
updates — fleet management UIs tracking vehicle positions, factory dashboards
watching production line events, security operations centers monitoring alarm
feeds. Each channel is typed so that subscribers know the exact shape of every
message.

A `PubSubBus` fans messages out to subscribers. `WSChannel` (8): `ev_telemetry`,
`battery_alerts`, `solar_live`, `iot_stream`, `drone_tracking`,
`manufacturing_events`, `security_alarms`, `system_health`. Each `WSMessage`
carries `channel`, `event`, `timestamp`, and `payload`. Typed publishers:
`publishEVTelemetry`, `publishBatteryAlert`, `publishSolarEvent`,
`publishIoTReading`, `publishDronePosition`, `publishManufacturingEvent`,
`publishSecurityAlarm`. The SSE endpoint `GET /ws/sse/:channel` streams a
channel as `text/event-stream` with a 1-second heartbeat and a 5-minute
auto-close.

### Kafka event-bus abstraction (`events/kafka-event-bus.ts`)

Task 60.21.5.3. Kafka is the durable, high-throughput backbone for events that
need to survive gateway restarts, be consumed by multiple independent downstream
services, or be retained for compliance. The partition and retention strategy is
tuned per event category to balance throughput, ordering, and storage cost.

Topics follow `saraswati.{bu}.{category}`, where `BUCode` is the 17-BU set and
`EventCategory` (5) is `telemetry`, `commands`, `events`, `alerts`, `audit`.
`buildTopicRegistry()` generates a `TopicConfig` for every (BU, category) pair:
telemetry topics have 16 partitions, round-robin partitioning, and 7-day
retention; command topics 4 partitions, entity-keyed, 30-day retention; event
topics 4 partitions, entity-keyed, log-compacted (event sourcing); alert topics
8 partitions, site-keyed, 14-day retention; audit topics 4 partitions,
entity-keyed, 1-year retention (compliance). All topics use replication factor 3
and a 1-MiB max message size. `computePartition(key, n)` implements a
Kafka-compatible Murmur2 hash. `buildDLQTopicName(topic)` produces the
`saraswati.dlq.{topic}` dead-letter topic; messages are routed to the DLQ after
`maxRetries` failures with failure metadata appended. `SaraswatiEventBus` is an
in-process implementation mirroring Kafka semantics (partitions, consumer
groups, offsets, consumer lag, DLQ) for testing and local development; the
documented production path is to back the same interface with `kafkajs` or
`@confluentinc/kafka-javascript`.

### CQRS / event sourcing (`cqrs/cqrs.ts`)

Task 60.21.5.4. CQRS separates the write path (commands that change state) from
the read path (queries that read projections). This matters in Saraswati because
telemetry ingest is write-heavy and dashboard queries are read-heavy; keeping
them on the same path would cause contention. Separates command (write) and
query (read) paths. `Command` carries `commandType`, `aggregateId`,
`correlationId`, optional `causationId`, `issuedAt`, `issuedBy`. `DomainEvent`
carries `eventId`, `eventType`, `aggregateId`, `aggregateType`,
`sequenceNumber`, `occurredAt`, `causationCommandId`, `payload`. `CommandResult`
returns `success`, the raised `events`, and validation `errors`. Command
handlers append events to an event store; aggregates are reconstituted by
replaying events, with snapshots taken every `SNAPSHOT_INTERVAL` events to bound
replay time. Read models are projections updated asynchronously for eventual
consistency.

## Cross-Domain Integration Contracts (`@contracts/saraswati`)

`@contracts/saraswati` exports five integration modules, each defining Zod
schemas plus an adapter class. The module index re-exports `brigid.ts`,
`asase.ts`, `freya.ts`, `cybele.ts`, `maat.ts`.

Each contract exists because a neighboring domain needs a specific slice of
Saraswati's data model but should not depend directly on Saraswati's internal
types. The Zod schema defines the shape of the cross-domain message; the adapter
class converts between Saraswati's internal entities and the contracted form.
This means Saraswati can evolve its internal model without breaking its
neighbors as long as the adapter continues to produce a valid contracted output.

- **Brigid** (`brigid.ts`, task 60.20.1) — Saraswati manufacturing ↔ Brigid
  industrial automation: factory-automation interface for CEM electronics lines,
  energy-system integration, robotics integration adapter, shared MES event bus,
  predictive-maintenance data sharing.
- **Asase** (`asase.ts`, task 60.20.2) — Saraswati EV fleet / drones / IoT /
  solar ↔ Asase agriculture: delivery EV fleet integration, agricultural drone
  connector, soil/weather/crop IoT network, cold-chain monitoring, solar-powered
  irrigation-pump controller.
- **Freya** (`freya.ts`, task 60.20.3) — Saraswati ↔ Freya e-commerce: last-mile
  delivery fleet, smart-retail IoT, packaging electronics (freshness and
  anti-tamper sensors), fintech-hardware deployment for Freya POS/kiosks,
  warehouse robotics.
- **Cybele** (`cybele.ts`, task 60.20.4) — Saraswati ↔ Cybele construction:
  construction robotics, smart-building IoT, EV-charging-infrastructure
  planning, rooftop/carport solar, data-center equipment provisioning,
  construction drone survey.
- **Maat** (`maat.ts`, task 60.20.5) — Saraswati R&D / IP / PLM ↔ Maat knowledge
  management. Five Zod schemas with adapters: `RDProjectSchema` +
  `RDProjectTrackerAdapter` (NASA TRL 1–9 progression, TRL-jump feasibility,
  monthly burn, IP recommendation); `IPAssetSchema` +
  `IPPortfolioManagerAdapter` (composite IP score from claims, territory, and
  BU-revenue weights; income-approach valuation; 20-year patent term);
  `PLMDataSchema` + `PLMDataSharingAdapter` (`PLMLifecycleStage` `concept` |
  `design` | `validate` | `launch` | `sustain` | `eol`; readiness score and A–F
  grade); `TechScoutingSchema` + `TechScoutingAdapter` (Gartner hype-cycle
  maturity, BU-relevance matrix, adoption recommendation `monitor` | `pilot` |
  `invest` | `divest`); `StandardsTrackingSchema` + `StandardsTrackingAdapter`
  (standards bodies including `IEC`, `ISO`, `IEEE`, `ETSI`, `DVLA_Ghana`,
  `Ghana_FDA`, `NCA_Ghana`, `GRA`, `ECOWAS`; urgency and compliance-gap
  scoring). The Maat-facing BU enum uses the longer-form values `ev_mobility`,
  `battery_storage`, `solar_energy`, `iot_agritech`, `contract_electronics`,
  `pharma`, `robotics`, `drone_services`, `telecom_infra`, `medical_devices`,
  `satellite_comms`, `additive_manufacturing`.

## Validation, Invariants, and Configuration

### Validation

Input validation in the gateway catches malformed requests before they reach
business logic. The constraints below are embedded in Zod schemas that run at
every create/update route. The pharmacy ATC-code regex and the solar-panel
efficiency cap are examples of domain knowledge encoded directly in the schema
layer rather than deferred to application code.

`@saraswati/core` ships Zod schemas for the primary domain objects
(`ChargingStationSchema`, `BatterySchema`, `SolarPanelSchema`,
`SensorReadingSchema`, `IoTDeviceSchema`, `PharmaceuticalSchema`, `DroneSchema`,
`WaypointSchema`, `FlightPlanSchema`, `TelecomTowerSchema`,
`VSATTerminalSchema`, `ManufacturingOrderSchema`, `QualityTestResultSchema`,
`MarketDataPointSchema`). Notable embedded constraints: latitude bounded to
[-90, 90] and longitude to [-180, 180]; `country` codes length 2 and `currency`
codes length 3; solar-panel `efficiencyPercent` capped at 30 and degradation
bounds enforced; the pharmaceutical ATC code matched against
`/^[A-Z]\d{2}[A-Z]{2}\d{2}$/`; `FlightPlanSchema` requires at least two
waypoints; `confidenceLevel` bounded to [0, 1]. Gateway routes validate request
bodies with route-local Zod schemas before mutating state.

### Domain invariants

These invariants are enforced in two places: as type-guard functions in
`@saraswati/core` (for business logic) and as gateway-route checks (for API
enforcement). The dual enforcement means a violation cannot slip in through
either path.

- Battery state of health below 80% is the end-of-life threshold
  (`isBatteryEndOfLife`); a healthy battery additionally has no fault codes
  (`isBatteryHealthy`).
- A pharmaceutical batch is release-ready only when QC has passed, the batch is
  in `qc_testing`, and every deviation is closed or minor
  (`isBatchReleaseReady`).
- A medical device of class IIb or III requires a notified body
  (`requiresNotifiedBody`); class III or surgically invasive devices require a
  clinical trial (`requiresClinicalTrial`).
- A flight plan is safe only when current wind is within the plan limit, maximum
  altitude is at or below 400 m (the Ghana GCAA general cap), and the minimum
  return-battery percentage is at least 15 (`isFlightPlanSafe`). The drones
  route additionally rejects flight-plan creation when the requested altitude
  exceeds 400 m AGL.
- A drone is airworthy only when active, with battery at or above 30% and fewer
  than 200 battery flight cycles (`isDroneAirworthy`).
- A robot is ready for a task only when active, with battery at or above 20% and
  no current task (`isRobotReadyForTask`); the robotics route enforces a single
  active task per robot.
- Telecom tower tenant provisioning is rejected when the requested equipment
  weight exceeds the tower's `upliftAvailableKg`.
- E-waste items in `high_hazard` or `restricted_export` classes are high-hazard
  (`isHighHazardEWaste`); `restricted_export` corresponds to Basel-Convention
  restriction, and recovered-material cross-border movement carries an
  `exportPermitNumber`.

### Configuration and environment inputs

- `SARASWATI_DATABASE_URL` — PostgreSQL connection string for the `saraswati`
  database; required by `getPool()` and `getTelemetryPool()`.
- `LOG_LEVEL` — when `debug`, the connection pool logs new client connections.
- `NODE_ENV` — when `production`, the gateway returns a generic error message on
  unhandled errors and the `POST /ws/inject` development endpoint is disabled.
- `@saraswati/db` pool sizing is tunable through `PoolConfig` (`maxConnections`,
  `minConnections`, `idleTimeoutMs`, `connectionTimeoutMs`,
  `statementTimeoutMs`).

## V2 Racing EV Consumption Surface

The V2 game project's racing ecosystem reuses Saraswati's electric-vehicle
powertrain engineering to model racing drivetrains. The
`@v2/racing-ecosystem-bridge` service (in the V2 monorepo, not in
`libs/saraswati/`) lists `@saraswati/ev` among its source-of-truth packages;
Saraswati itself has no dependency on V2.

The bridge instantiates Saraswati's real `PowertrainConfigurator` and feeds it
the racing vehicle's motor preference, building a `PowertrainConfig` for the
in-game car. `@saraswati/ev` already provides production-grade
`hybrid and electric vehicle modeling` — it models the full motor-to-wheel
drivetrain, including `hybrid` configurations alongside pure-electric ones, so
the racing surface inherits that engineering for free: the configurator computes
the drivetrain efficiency that the bridge reports back. When that efficiency or
configuration is unacceptable, the bridge raises
`saraswati-ev-powertrain-invalid` (hard) or
`saraswati-ev-powertrain-inefficient` (a warning), each tagged with
`sourcePackageName: '@saraswati/ev'`, so EV powertrain constraints surface to
the vehicle author rather than silently shipping.

This is an authoring / content-cook surface only: the bridge rejects live
race-frame RPCs and carries `mayInfluenceRollback: false`, so Saraswati's EV
model shapes vehicles offline and never runs inside the deterministic race
simulation. See `V2/docs/integration/racing-ecosystem-bridge.md`;
`V2/ue/Tools/check-v2-racing-ecosystem-bridge.py` enforces the wiring.

_Grounding: `apps/v2/racing-ecosystem-bridge/src/racing-ecosystem-bridge.ts`;
`libs/saraswati/ev/`._

## Verification Expectations

Per the architecture document, changes to Saraswati require package-level lint,
typecheck, and unit-test coverage. Each capability library carries its own
`vitest.config.ts` and a primary test file; several BUs additionally carry a
state-of-the-art (`*-sota.test.ts`) test file. Regulated BUs — pharma, medical
devices, fintech hardware, EVs, batteries, telecom, and e-waste — require
contract, compliance, audit, and data-retention tests in addition to the
package-level checks.
