Pattern: IoT metering¶
Shape. A bounded session per device fleet. A gateway collects sensor readings, aggregates them into chunks, and submits each chunk as a CellTx. Settlement is the total at session close.
Why this fits Myelin¶
Industrial IoT metering has a very specific shape:
- bounded session per fleet (one metering period, e.g. a day)
- many off-chain updates (sensor readings every N seconds)
- few on-chain settlements (one settlement CellTx at session close)
- clear dispute chunks (an aggregated hour, a day, a week)
That shape matches Myelin's design:
- Bounded session — Myelin sessions have an open and a close.
- Many off-chain updates — the fast path is exactly designed for this throughput profile.
- Few on-chain settlements — only the open and close touch L1.
- Clear dispute chunks — the gateway's aggregation is deterministic; the chunks are verifiable.
It does not fit:
- Raw sensor firehose as individual Cells. The capacity overhead per Cell would be prohibitive. Aggregation is the answer.
The session shape¶
session_id -> fleet_id || metering_period_start_ms
participants -> [gateway, customer]
escrow -> pre-paid capacity Cells from the customer
max_chunk_bytes -> ~256 KB (one hour of aggregated readings)
max_cycles -> VM budget per aggregation script
A typical session:
open at T0 (customer locks, e.g., 1000 CKB worth of capacity)
chunk 0..N-1 between T0 and T1 (gateway submits aggregated readings)
close at T1 (settlement CellTx transfers capacity per the rule)
What a chunk looks like¶
Each chunk represents one aggregation window. Inside the chunk:
witness[0] -> signature from the gateway
witness[1] -> aggregated reading batch
witness[2] -> prior chunk's state root (chained)
witness[3] -> per-device nonce list (anti-replay)
The aggregation script:
- Verifies the gateway signature.
- Replays the aggregated reading batch through the metering rule.
- Updates the session state root.
- Emits a
MyelinExecutionReportwith the per-device totals.
Conflict domain keying¶
The conflict key for an IoT session looks like:
This guarantees that two gateways cannot double-write the same chunk, and that the customer cannot submit conflicting settlement intents for the same chunk.
The dispute path¶
If the customer disputes a chunk:
dispute -> court-bundle for chunk K
replay -> Myelin VM probe runs the same aggregation script
compare -> chunk_K.state_root_after matches the disputed state root?
verdict -> accept (gateway was right) or slash (gateway was wrong)
The aggregation script is deterministic — given the same witness batch, it always produces the same state root. This is what makes single-chunk verification possible.
A reference implementation sketch¶
use myelin_exec::celltx::{CellTx, CellTxBuilder};
use myelin_exec::witness::{Witness, WitnessLayout};
// The gateway builds a chunk CellTx
fn build_chunk(fleet_id: [u8; 32], index: u64, batch: Vec<u8>, signature: [u8; 64]) -> CellTx {
CellTxBuilder::new()
.witnesses(vec![
Witness::signature(signature),
Witness::data(batch), // aggregated readings
Witness::data(prev_chunk_state_root), // chain
Witness::data(per_device_nonces), // anti-replay
])
.cell_deps(vec![metering_script_dep()])
.build()
.expect("iot metering chunk CellTx build")
}
The metering_script_dep() returns the cell_dep reference for
the aggregation script — a CKB type script that runs in Myelin's
verifier with vm_profile = "ckb-strict-basic".
Anti-replay per device¶
Each device has a nonce that's incremented on every accepted reading. The aggregation script checks:
for each (device_id, reading) in batch:
if reading.nonce != device_state[device_id].last_nonce + 1:
reject("stale reading")
device_state[device_id].last_nonce = reading.nonce
device_state[device_id].total += reading.value
This prevents a malicious gateway from re-submitting an old reading batch.
Where the boundary is honest¶
Myelin can produce:
- ✅ A gateway aggregation script that runs deterministically.
- ✅ Per-chunk CellTx reports with projection status.
- ✅ A court bundle for any disputed chunk.
Myelin does not ship:
- The gateway runtime — that's a separate component.
- The metering standard — Myelin supports whatever rule you encode in the aggregation script.
- The device attestation — that's a separate identity concern.
If you have a specific metering standard in mind, encode it in the aggregation script and the per-chunk CellTx will follow.
What this looks like in production evidence¶
For a typical 1000-device fleet metering over a one-day session:
session duration : 24 hours
chunks per session : 24 (one per hour)
cells per chunk : ~1000 device updates + 1 settlement delta
chunks to L1 (DA) : 24 (one per hour)
settlement to L1 : 1 (at close)
The L1 footprint is small — one open, 24 DA anchors, one close. The off-chain workload is large. That's the Myelin sweet spot.
Where to go next¶
- Pattern: streaming payments — similar shape, different content.
- Session lifecycle — the session primitives in detail.
- What is Myelin? — the broader positioning.