Domain · Specifications

Saraswati Domain — Technical Specifications

There is no apps/saraswati/* or services/saraswati/* package.

10sections41 minread

On this page

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_lifedecommissioned.

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/54/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 AG, 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 Maps 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.