A gateway is a device that connects to CORE-M on behalf of other devices that
cannot reach the platform themselves. The classic case is an industrial bus: a
dozen Modbus RTU sensors hang off a single RS-485 segment, and one edge gateway
speaks Modbus to them and CORE-M to the cloud. The gateway authenticates once and
fans in telemetry for all its children, each of which appears in CORE-M as its own
first-class device.
Children behind a gateway have no direct credential and no direct route to
CORE-M. The gateway provides both: it is the authenticated uplink, and CORE-M
maps each child’s local name to a real device_id so the child’s telemetry is
stored, ruled on, and anchored exactly as if it had connected directly.
Register the gateway. Provision the edge device normally. It receives a
device_id and an API key; this is the only credential the children need.
Connect each child. The gateway calls GatewayConnectDevice with a stable
child_name. CORE-M creates (or looks up) the child device and returns its
child_device_id. This establishes the route for that child.
Submit telemetry. The gateway calls GatewaySubmitTelemetry with a batch
of points, each addressed by child_name. CORE-M resolves each name to a child
device and publishes one raw telemetry point per child.
GatewayConnectDevice is idempotent: calling it again with the same child_name
returns the same child. Internally the child is stored with a hardware ID of the
form gw:{gateway_device_id}:{child_name}, which is what makes the lookup stable.
GatewaySubmitTelemetry fans a batch in. Each point carries the child_name,
an optional timestamp (the server uses now if absent), and the numeric/string
readings. CORE-M resolves each name and publishes one raw point per child,
attributed to the child’s device_id for storage, rules, and anchoring.
The response counts points across the batch, the same accepted / rejected
shape as direct ingest:
{
"accepted": 2,
"rejected": 0
}
sequenceDiagram
participant GW as Gateway G1
participant CM as CORE-M gateway :8080
participant Reg as Device registry
participant RP as Redpanda telemetry.raw.{tenant}
GW->>CM: POST /api/v1/gateway/G1/telemetry (batch by child_name)
CM->>CM: Verify caller device_id == G1
loop each point
CM->>Reg: Resolve {tenant}:G1:child_name → device_id
alt route exists (or auto_create)
CM->>RP: Publish point as child device_id
else unknown child, auto_create=false
CM->>CM: Reject (corem_gateway_child_rejections_total +1)
end
end
CM-->>GW: { accepted, rejected }
What happens when telemetry arrives for a child_name that has no route yet
depends on the auto_create flag:
auto_create = true — CORE-M creates the child device on the fly from the
first telemetry, so you can skip the explicit GatewayConnectDevice step.
auto_create = false — telemetry for an unknown child is rejected, and
corem_gateway_child_rejections_total{reason="unknown_child"} is incremented.
Use this when you want children to exist only after a deliberate connect.
A gateway’s children share its fate on the uplink. If the profile policy says
child status follows the gateway, then when the gateway goes offline, the
offline checker marks every child routed through it offline too, and publishes one
device.status event per affected child. This keeps the dashboard honest — a bus
of sensors does not show as online when the only thing that can reach them has
dropped off.
Independently, each child still flips to online on its own first telemetry,
and to offline after the offline threshold (default 120 seconds) of silence.