Skip to content

Granit.IoT — IoT Platform for .NET

Granit.IoT is the IoT and Industry 4.0 module family of the Granit framework. It ships 22 focused NuGet packages plus two meta-bundles that turn any multi-tenant Granit application into a production-grade IoT platform: device inventory, telemetry ingestion, threshold alerting, audit trail, AI agent access, MQTT and AWS IoT Core transports, and PostgreSQL- or TimescaleDB-backed time-series storage.

It is the open-source, modular answer to platforms like ThingsBoard, AWS IoT Core, and Azure IoT Hub — wired into the same DDD aggregates, CQRS readers/writers, Wolverine outbox, and multi-tenant query filters as the rest of the Granit stack.

Teams adding IoT features on top of any .NET SaaS hit the same wall every time. Granit.IoT closes the gap with opinionated, framework-aligned defaults.

ProblemWithout Granit.IoTWith Granit.IoT
No standard device modelEvery app reinvents Device + lifecycleDDD aggregate with state machine + FullAuditedAggregateRoot
Blocking webhooksProvider retries collide with slow DB writes202 Accepted in < 1 s + Wolverine transactional outbox
Forgotten signature validationA webhook becomes a public DB insertHMAC-SHA256 / SigV4 validated before any DB hit
Duplicate messagesSame payload fires alerts N timesRedis SETNX dedup on transport message-id (TTL 5 min)
Multi-tenancy leaksA missing WHERE tenant_id = … ships another customer’s dataNamed query filter on TenantId, enforced by arch tests
Exploding telemetry tablesA year of data → multi-minute queriesBRIN + GIN + monthly partitioning, opt-in TimescaleDB hypertables
Provider lock-inEach cloud is a re-integrationPluggable IIngestionParser / IPayloadSignatureValidator
Silos for alerts & auditThreshold alerts live in their own UIBridge to Granit.Notifications + Granit.Timeline
No AI surfaceOperators chase dashboards by hand[McpServerTool] wrappers, tenant-scoped, read-only

The 22 packages are organised in three cohesion rings. Each ring depends only on itself and inner rings — Ring 2 cannot reach Ring 3, and Ring 3 never duplicates infrastructure that already exists in Ring 1 or Ring 2. The discipline is enforced by Granit.IoT.ArchitectureTests and fails CI on violation.

flowchart TB
  subgraph META["Meta-bundles (transverse)"]
    B["Granit.Bundle.IoT"]
    BAWS["Granit.Bundle.IoT.Aws"]
  end
  subgraph R3["Ring 3 — Cross-cutting bridges"]
    N["Granit.IoT.Notifications"]
    T["Granit.IoT.Timeline"]
    MCP["Granit.IoT.Mcp"]
    AWS["Granit.IoT.Aws<br/>(+ EFCore, Provisioning,<br/>Shadow, Jobs, FleetProvisioning)"]
  end
  subgraph R2["Ring 2 — Ingestion &amp; transport"]
    I["Granit.IoT.Ingestion"]
    IE["Granit.IoT.Ingestion.Endpoints"]
    IS["Granit.IoT.Ingestion.Scaleway"]
    IA["Granit.IoT.Ingestion.Aws"]
    W["Granit.IoT.Wolverine"]
    M["Granit.IoT.Mqtt"]
    MM["Granit.IoT.Mqtt.Mqttnet"]
  end
  subgraph R1["Ring 1 — Device management"]
    IOT["Granit.IoT"]
    EP["Granit.IoT.Endpoints"]
    EF["Granit.IoT.EntityFrameworkCore"]
    EFP["Granit.IoT.EntityFrameworkCore.Postgres"]
    EFT["Granit.IoT.EntityFrameworkCore.Timescale"]
    BJ["Granit.IoT.BackgroundJobs"]
  end
  R2 --> R1
  R3 --> R2
  R3 --> R1
  META -. "bundles" .-> R1
  META -. "bundles" .-> R2
  META -. "bundles" .-> R3

The DDD core. Aggregates, value objects, persistence, recurring jobs. No network I/O, no external providers.

PackageRole
Granit.IoTDevice aggregate, TelemetryPoint, value objects, CQRS readers / writers, domain events, diagnostics
Granit.IoT.EndpointsMinimal API route groups (/iot/devices, /iot/telemetry)
Granit.IoT.EntityFrameworkCoreIsolated IoTDbContext, named query filters, EF Core configurations
Granit.IoT.EntityFrameworkCore.PostgresBRIN / GIN / JSONB indexes, RANGE partitioning helpers
Granit.IoT.EntityFrameworkCore.TimescaleOpt-in TimescaleDB hypertable + continuous aggregates
Granit.IoT.BackgroundJobsPurge, heartbeat timeout, partition maintenance

→ Read Device management and Time-series storage.

Everything that turns raw provider payloads into normalised domain events.

PackageRole
Granit.IoT.IngestionProvider-agnostic pipeline (validate → parse → dedup → outbox)
Granit.IoT.Ingestion.EndpointsPOST /iot/ingest/{source} (202 Accepted)
Granit.IoT.Ingestion.ScalewayScaleway IoT Hub — HMAC-SHA256 + JSON envelope
Granit.IoT.Ingestion.AwsAWS IoT Core — SNS / Direct / API Gateway (RSA-SHA256, SigV4)
Granit.IoT.WolverineWolverine handlers + threshold evaluation
Granit.IoT.Mqtt + Granit.IoT.Mqtt.MqttnetMQTT transport (any 3.1.1 / 5.0 broker)

→ Read Telemetry ingestion and MQTT transport.

Thin adapters that connect IoT events to other Granit modules. Every bridge is opt-in — the core pipeline works without them.

PackageRole
Granit.IoT.NotificationsThreshold + offline alerts → INotificationPublisher
Granit.IoT.TimelineDevice lifecycle → ITimelineWriter audit chatter
Granit.IoT.Mcp[McpServerTool] wrappers for AI assistants (read-only, tenant-scoped)
Granit.IoT.Aws family (6 packages)AWS IoT Core bridge: companion AwsThingBinding, provisioning saga, Device Shadow sync, IoT Jobs, JITP

→ Read Notifications bridge, Timeline bridge, MCP bridge, and AWS IoT Core bridge.

BundleIncludesWhen to use
Granit.Bundle.IoTFull Ring 1 + 2 + 3 cloud-agnostic stack (12 packages, MCP included)New projects, full IoT functionality
Granit.Bundle.IoT.AwsAWS IoT Core device-binding family (6 packages)Add on top of Granit.Bundle.IoT when targeting AWS

MQTT (Granit.IoT.Mqtt[.Mqttnet]), TimescaleDB (Granit.IoT.EntityFrameworkCore.Timescale), and AWS-native ingestion (Granit.IoT.Ingestion.Aws) ship as opt-in extensions outside the bundles.

→ Read Bundle reference.

Cold-chain monitoring

Ingest temperature + humidity from Scaleway IoT Hub, alert when threshold is breached, drill into the timeline of each sensor — all per tenant.

Industrial fleet management

Provision thousands of devices on AWS IoT Core via fleet provisioning (JITP), push lifecycle into Device Shadows, dispatch firmware updates as IoT Jobs.

Smart-building sensors

Subscribe to MQTT topics (Mosquitto, EMQX, HiveMQ), persist telemetry in PostgreSQL, expose live state to Claude / Copilot through MCP.

Predictive maintenance

Aggregate billions of rows in a TimescaleDB hypertable, surface anomalies in a Granit.Notifications channel, audit every state transition.

Designed for compliance — GDPR + ISO 27001

Section titled “Designed for compliance — GDPR + ISO 27001”
RequirementBuilt-in mechanism
GDPR Art. 17 — right to erasureStaleTelemetryPurgeJob bulk-deletes per tenant; partition drops are O(1) at month boundaries
GDPR Art. 30 — records of processingEvery device transition mirrored in Granit.Timeline with CreatedBy / ModifiedBy
ISO 27001 A.8.1.1 — asset inventoryDevice is FullAuditedAggregateRoot with full provenance trail
ISO 27001 A.12.4.1 — event loggingTimeline entries are tenant-scoped, tamper-resistant, durable past log rotation
Data residencyNo telemetry ever leaves the host’s database; webhooks terminate locally
Secrets at restDeviceCredential.ProtectedSecret is encrypted via Granit.Encryption and stripped from logs, OpenAPI, and validation errors
Multi-tenant isolationNamed query filter on TenantId, enforced by EF Core + arch tests