IoT — worked graph designs
These designs describe intended system behavior and qualification requirements. Inspect the system atlas for named ports and relation participants.
IoT: constrained sensors, gateway buffering and ingestion
What can a gateway acknowledge during a cloud outage?
Inspect this design · Definition JSON · revision 120a91db6121
Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.
| Marker | Meaning in this diagram |
|---|---|
| ⊟ N30 | External system boundary |
| ▣ N05 | Actor / statechart |
| ≋ N24 | Queue / log / pubsub resource |
| ↗ N19 | Tool / external API adapter |
| ▰ N21 | Database / state authority |
| ⌕ N22 | Query / materialized view |
| ↦ P04 | Push event |
| ◎ P24 | Inspection / evidence |
| ⇆ P02 | Request / response |
| → H02 | Value flow |
| ⇆ H05 | Request / reply binding |
| ↦ H04 | Event subscription |
| ⊢ H27 | Derivation / provenance |
Logic and ownership
Kotlin common logic validates units and groups samples at the gateway; constrained firmware remains an explicit external system boundary when Kotlin/JS cannot execute there.
Failure and qualification
- Gateway durability and cloud commit have distinct acknowledgements.
- Retention limits and outage overflow behavior must be specified.
- Device identity, sample sequence and clock uncertainty travel with the batch.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| sample | H02 — Value flow {#h02--value-flow} | provider[0]: sensor.sample; consumer[0]: gateway.sample |
| buffer | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: buffer.append; consumer[0]: gateway.buffer |
| upload | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: ingest.batch; consumer[0]: gateway.upload |
| store | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: series.append; consumer[0]: ingest.storage |
| health | H04 — Event subscription {#h04--event-subscription} | provider[0]: gateway.health; consumer[0]: health.events |
| lineage | H27 — Derivation / provenance {#h27--derivation--provenance} | device[0]: sensor.identity; gateway[1]: buffer.position; cloud[2]: series.receipt |
Kotlin common declaration
Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.
graphDefinition("iot-sensor", "120a91db6121e8d92f1d0a4ceef8b562284679ecebac1ab725f1648b5f995a35") {
region("device", "Constrained devices")
region("gateway", "Gateway")
region("cloud", "Cloud authority")
node("sensor", "Sensor firmware boundary", "N30", "device", ports = listOf(
PortDefinition("sample", "P04", "SensorSampleV1", PortPolarity.Offer),
PortDefinition("identity", "P24", "DeviceRefV1", PortPolarity.Neutral)
))
node("gateway", "Gateway actor", "N05", "gateway", ports = listOf(
PortDefinition("sample", "P04", "SensorSampleV1", PortPolarity.Require),
PortDefinition("buffer", "P02", "GatewayAppendV1", PortPolarity.Require),
PortDefinition("upload", "P02", "SensorBatchV1", PortPolarity.Require),
PortDefinition("health", "P24", "DeviceHealthV1", PortPolarity.Offer)
))
node("buffer", "Gateway durable buffer", "N24", "gateway", ports = listOf(
PortDefinition("append", "P02", "GatewayAppendV1", PortPolarity.Offer),
PortDefinition("position", "P24", "LogPositionV1", PortPolarity.Neutral)
))
node("ingest", "Cloud ingestion", "N19", "cloud", ports = listOf(
PortDefinition("batch", "P02", "SensorBatchV1", PortPolarity.Offer),
PortDefinition("storage", "P02", "SeriesAppendV1", PortPolarity.Require)
))
node("series", "Time-series authority", "N21", "cloud", ports = listOf(
PortDefinition("append", "P02", "SeriesAppendV1", PortPolarity.Offer),
PortDefinition("receipt", "P24", "ReceiptRefV1", PortPolarity.Neutral)
))
node("health", "Fleet health view", "N22", "cloud", ports = listOf(
PortDefinition("events", "P24", "DeviceHealthV1", PortPolarity.Require)
))
relation("sample", "H02",
Incidence("sensor", "sample", "provider", 0),
Incidence("gateway", "sample", "consumer", 0)
)
relation("buffer", "H05",
Incidence("buffer", "append", "provider", 0),
Incidence("gateway", "buffer", "consumer", 0)
)
relation("upload", "H05",
Incidence("ingest", "batch", "provider", 0),
Incidence("gateway", "upload", "consumer", 0)
)
relation("store", "H05",
Incidence("series", "append", "provider", 0),
Incidence("ingest", "storage", "consumer", 0)
)
relation("health", "H04",
Incidence("gateway", "health", "provider", 0),
Incidence("health", "events", "consumer", 0)
)
relation("lineage", "H27",
Incidence("sensor", "identity", "device", 0),
Incidence("buffer", "position", "gateway", 1),
Incidence("series", "receipt", "cloud", 2)
)
}
IoT: commissioning, scoped commands and signed firmware
Which authority permits a physical command or firmware update?
Inspect this design · Definition JSON · revision 1a11b20d7611
Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.
| Marker | Meaning in this diagram |
|---|---|
| ✓ N20 | Human decision / approval |
| ⊂ N33 | Policy / principal boundary |
| ↗ N19 | Tool / external API adapter |
| ▸ N27 | Runtime host / executor |
| ⊟ N30 | External system boundary |
| ▧ N35 | Artifact / release manifest |
| ▣ P03 | Durable command / receipt |
| ◎ P24 | Inspection / evidence |
| ⊂ P20 | Authorization request / grant |
| ⇆ P02 | Request / response |
| ▧ P23 | Artifact / schema reference |
| → H02 | Value flow |
| ⊂ H23 | Scoped authorization |
| ⇆ H05 | Request / reply binding |
| ▧ H26 | Artifact dependency |
Logic and ownership
Kotlin evaluates commissioning identity and command scope at the gateway. Firmware admission remains in the target bootloader or update authority.
Failure and qualification
- A signed artifact authenticates its publisher, not the safety of every physical action.
- The device can reject an expired or unsafe command after cloud admission.
- Firmware and subgraph code push use different executable formats and recovery constraints; neither implies a universal Kotlin/JS device runtime.
Hyperedge roles
| Relation | Pattern | Participants, preserving role positions |
|---|---|---|
| proposal | H02 — Value flow {#h02--value-flow} | provider[0]: operator.intent; consumer[0]: command.intent |
| grant | H23 — Scoped authorization {#h23--scoped-authorization} | provider[0]: policy.grant; consumer[0]: command.authority |
| deliver | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: gateway.execute; consumer[0]: command.deliver |
| device | H05 — Request / reply binding {#h05--request--reply-binding} | provider[0]: device.command; consumer[0]: gateway.device |
| artifact | H26 — Artifact dependency {#h26--artifact-dependency} | provider[0]: bundle.artifact; consumer[0]: gateway.artifact |
| admission | H23 — Scoped authorization {#h23--scoped-authorization} | principal[0]: operator.principal; resource[1]: device.identity; policy[2]: policy.policy; executor[3]: gateway.executor |
Kotlin common declaration
Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.
graphDefinition("iot-command", "1a11b20d761148f7f75f75d0d955905cec811445932dd24149e5dc54500c9757") {
region("device", "Constrained devices")
region("gateway", "Gateway")
region("cloud", "Cloud authority")
node("operator", "Operator proposal", "N20", "cloud", ports = listOf(
PortDefinition("intent", "P03", "DeviceIntentV1", PortPolarity.Offer),
PortDefinition("principal", "P24", "PrincipalRefV1", PortPolarity.Neutral)
))
node("policy", "Device policy", "N33", "cloud", ports = listOf(
PortDefinition("grant", "P20", "DeviceGrantV1", PortPolarity.Offer),
PortDefinition("policy", "P24", "PolicyRefV1", PortPolarity.Neutral)
))
node("command", "Command service", "N19", "cloud", ports = listOf(
PortDefinition("intent", "P03", "DeviceIntentV1", PortPolarity.Require),
PortDefinition("authority", "P20", "DeviceGrantV1", PortPolarity.Require),
PortDefinition("deliver", "P02", "DeviceCommandV1", PortPolarity.Require)
))
node("gateway", "Gateway executor", "N27", "gateway", ports = listOf(
PortDefinition("execute", "P02", "DeviceCommandV1", PortPolarity.Offer),
PortDefinition("device", "P02", "DeviceProtocolV1", PortPolarity.Require),
PortDefinition("artifact", "P23", "FirmwareRefV1", PortPolarity.Require),
PortDefinition("executor", "P24", "HostRefV1", PortPolarity.Neutral)
))
node("device", "Device controller boundary", "N30", "device", ports = listOf(
PortDefinition("command", "P02", "DeviceProtocolV1", PortPolarity.Offer),
PortDefinition("identity", "P24", "DeviceRefV1", PortPolarity.Neutral)
))
node("bundle", "Signed firmware artifact", "N35", "cloud", ports = listOf(
PortDefinition("artifact", "P23", "FirmwareRefV1", PortPolarity.Offer)
))
relation("proposal", "H02",
Incidence("operator", "intent", "provider", 0),
Incidence("command", "intent", "consumer", 0)
)
relation("grant", "H23",
Incidence("policy", "grant", "provider", 0),
Incidence("command", "authority", "consumer", 0)
)
relation("deliver", "H05",
Incidence("gateway", "execute", "provider", 0),
Incidence("command", "deliver", "consumer", 0)
)
relation("device", "H05",
Incidence("device", "command", "provider", 0),
Incidence("gateway", "device", "consumer", 0)
)
relation("artifact", "H26",
Incidence("bundle", "artifact", "provider", 0),
Incidence("gateway", "artifact", "consumer", 0)
)
relation("admission", "H23",
Incidence("operator", "principal", "principal", 0),
Incidence("device", "identity", "resource", 1),
Incidence("policy", "policy", "policy", 2),
Incidence("gateway", "executor", "executor", 3)
)
}