Telemetry is the whole point of connecting a device. Whatever transport you use —
HTTP, MQTT, CoAP, LwM2M, SNMP — every reading is normalized into one internal
shape: the TelemetryPoint. This page is the definitive reference for that
shape, the decoders that turn your wire format into it, the validation a point
must pass, and what CORE-M does with a point once it is accepted.
On the wire the timestamp is RFC 3339 (2026-05-29T14:30:00Z). But when a point
is hashed for blockchain anchoring, that timestamp is taken as Unix seconds —
nanoseconds are discarded. The canonical hash is:
where jcs_payload_utf8 is the payload canonicalized per RFC 8785 (JCS).
This matters because a third-party verifier may only have second precision. By
truncating to whole seconds, CORE-M guarantees the verifier can reproduce the
exact same hash from the same logical reading. If you send two points for the same
device in the same second, they hash against the same timestamp — give them
distinct values or space them at least a second apart if each must anchor
independently. See Anchoring and
Merkle proofs for how that hash becomes an on-chain
proof.
Devices rarely speak protobuf natively. CORE-M’s ingest adapters decode common
wire formats into the TelemetryPoint shape. The canonical HTTP path
(POST /api/v1/telemetry, port 8080) takes JSON; MQTT and CoAP devices can use
JSON, CBOR, binary protobuf, CSV, or a custom field mapping configured on the
device profile.
CBOR is JSON’s compact binary cousin — same data model, far fewer bytes, ideal for
constrained CoAP/DTLS devices. Encode the identical structure as a CBOR map and
send it with Content-Type: application/cbor. The adapter decodes it into the
same TelemetryPoint.
Content-Type: application/cbor
A3 # map(3)
69 6465766963655F6964 # "device_id"
68 6465765F38663361 # "dev_8f3a"
6E 6E756D657269635F76616C756573 # "numeric_values"
A2 # map(2): temperature=22.5, humidity=65
...
A CoAP device typically POSTs CBOR to the telemetry resource; the field semantics
are byte-for-byte the JSON model above.
Devices that can link the generated client send the wire-format TelemetryPoint
(or an IngestTelemetryRequest of many) directly — no decode step, smallest and
fastest path. Send with Content-Type: application/x-protobuf.
# Using the generated corem.v1 client
from corem.v1 import telemetry_pb2
from google.protobuf.timestamp_pb2 import Timestamp
body = req.SerializeToString() # POST body, application/x-protobuf
Gateways and legacy data loggers often emit CSV rows. Configure the column→metric
mapping on the device profile; the adapter parses each row into a point. A header
row names the columns:
device_id,timestamp,temperature,humidity,status
dev_8f3a,2026-05-29T14:30:00Z,22.5,65,ok
dev_8f3a,2026-05-29T14:31:00Z,22.6,64,ok
Numeric columns land in numeric_values, text columns in string_values,
according to the profile mapping. Each row becomes one TelemetryPoint.
When a device emits a fixed shape you can’t change — a vendor JSON blob, a packed
binary frame, or SNMP OIDs polled off network gear — attach a custom field
mapping to the device profile. The mapping declares, per source field, the
target metric name and whether it is numeric or string.
A decoded point is not yet accepted. It must pass validation first.
Authentication. The credential must be valid and authorized to ingest for
the claimed tenant. An unauthenticated point is rejected and never published;
metric telemetry_rejected_total{reason="auth"} increments.
Known device. The device_id must exist in the tenant’s registry. If it
doesn’t, the point is dropped silently — not published to the validated
subject — and telemetry_dropped_total{reason="unknown_device"} increments,
with a warning log carrying trace context. This is the single most common cause
of “my data isn’t showing up”: the device wasn’t provisioned, or you’re sending
the wrong device_id.
Schema validation (if the profile defines one). If the device’s profile
carries a telemetry_schema, the point is checked against it. A profile that
requires numeric key temperature rejects a point that omits it with
INVALID_ARGUMENT, and corem_telemetry_schema_rejections_total{profile="…"}
increments. Profiles with no schema accept any well-formed point.
Once a point passes validation, a lot happens — and the device doesn’t wait for
any of it. The edge acknowledges immediately and the point propagates
asynchronously:
Updates last_seen on the device record, and if the device was offline,
flips it to online and publishes a status-change event.
Enriches the point with the device’s name, groups, and tags.
Dual-writes it: appended to the hot Aerospike CDT list (keyed
{tenant}:{device}:{metric}, ~900-second TTL window — a fast cache, not the
system of record) and inserted into the cold TimescaleDB hypertable
(batched for throughput) as the durable historical record.
Fans out to telemetry.live.{tenant}.{device} so dashboards and WebSocket
subscribers see it in real time.
Queues it for anchoring — its hash joins a Merkle batch that is later
committed to the blockchain.
The hot write failing does not block the cold write; the cold path always
proceeds. See Telemetry query & retention
for how the hot and cold stores are queried and how long data is kept.