Skip to content

Latest commit

 

History

History
167 lines (122 loc) · 6.07 KB

File metadata and controls

167 lines (122 loc) · 6.07 KB

Heartbeat

The heartbeat subsystem tracks liveness of both the agent itself and services running on the same host.

Overview

Two heartbeat paths exist:

Path Transport Purpose
Self-heartbeat MQTT → Atom Agent publishes its own status periodically
Service heartbeat FluxMQ (AMQP) → local Co-located services register liveness via the local message bus

The agent also accepts an MQTT ping command that publishes an immediate heartbeat without waiting for the next interval.

On a graceful reset, the agent publishes a goodbye heartbeat with heartbeat: false before disconnecting, so downstream consumers can detect the agent going offline without waiting for a timeout.

Self-Heartbeat

The agent publishes a SenML heartbeat to the telemetry channel on startup and at every interval:

Topic: m/<tenant-id>/c/<telemetry-channel-id>/gateway/heartbeat

Payload schema:

[
  { "bn": "agent:", "n": "service_type", "vs": "agent" },
  { "n": "heartbeat", "vb": true },
  { "n": "fw_version", "vs": "0.0.0" },
  { "n": "uptime", "u": "s", "v": 123.4 },
  { "n": "heap_free", "u": "By", "v": 1048576 },
  { "n": "devices", "u": "count", "v": 3 },
  { "n": "connected", "vb": true }
]
Field Type Unit Description
service_type string Always "agent"
heartbeat bool Always true
fw_version string Agent binary version (set via -ldflags)
uptime float s Seconds since agent started
heap_free float By Go runtime free heap bytes
devices float count Number of registered downstream devices
connected bool MQTT connection state

Service Heartbeat

Co-located services publish a heartbeat message to the local FluxMQ broker. The agent subscribes to m/<tenant-id>/c/<commands-channel-id>/services/# and extracts the service name and type from the topic path.

Topic format: heartbeat.<service-name>.<service-type>

When a heartbeat is received, the agent:

  1. Creates a tracker entry if the service is new
  2. Resets the service's last_seen timestamp
  3. Marks the service as online

If no heartbeat arrives within the configured interval, the service is marked offline.

Service info schema:

{
  "name": "myservice",
  "last_seen": "2026-06-10T12:00:00Z",
  "status": "online",
  "type": "sensor",
  "terminal": 0
}

Configuration

Environment Variables

Variable Default Description
MG_AGENT_HEARTBEAT_INTERVAL 10s Period between self-heartbeat publishes and the timeout for marking services offline
MG_AGENT_BROKER_URL amqp://guest:guest@localhost:5682/ FluxMQ (AMQP) broker URL for local service heartbeats

Runtime Config (MQTT set)

The heartbeat interval can be changed at runtime via the config subsystem:

config set heartbeat_interval <duration>

See control.md for the full config set recipe.

Topic Map

Direction Topic QoS Description
Agent → Cloud m/<tenant-id>/c/<data-chan>/gateway/heartbeat Configurable Periodic self-heartbeat
Service → Agent heartbeat.<name>.<type> (via AMQP) Local service registration
Cloud → Agent m/<tenant-id>/c/<ctrl-chan>/req 1 ping command (on-demand heartbeat)

MQTT Test Recipes

Subscribe to self-heartbeat

mosquitto_sub \
    -h <mqtt-host> -p 1883 \
    -u <gateway-id> -P <gateway-secret> \
    -t "m/<tenant-id>/c/<telemetry-channel-id>/gateway/heartbeat" \
    -v

Expected output (repeats every interval):

m/<tenant-id>/c/<telemetry-channel-id>/gateway/heartbeat [{"bn":"agent:","n":"service_type","vs":"agent"},{"n":"heartbeat","vb":true},{"n":"fw_version","vs":"unknown"},{"n":"uptime","u":"s","v":4580.244433353},{"n":"heap_free","u":"By","v":417792},{"n":"devices","u":"count","v":0},{"n":"connected","vb":true}]

Trigger an on-demand ping

mosquitto_pub \
    -h <mqtt-host> -p 1883 \
    -u <gateway-id> -P <gateway-secret> --id "ping-$(date +%s)" \
    -t "m/<tenant-id>/c/<commands-channel-id>/req" \
    -m '[{"bn":"req-1:", "n":"ping", "vs":""}]'

Check registered services via HTTP

curl -s http://localhost:9999/services | jq .

Expected output:

[
  {
    "name": "nodered",
    "last_seen": "2026-06-11T13:53:18.692168817Z",
    "status": "online",
    "type": "nodered",
    "terminal": 0
  }
]

Publish a local service heartbeat (Go)

go run ./examples/publish/main.go \
    -s amqp://guest:guest@localhost:5682/ \
    heartbeat.myservice.sensor ""

Change heartbeat interval at runtime

mosquitto_pub \
    -h <mqtt-host> -p 1883 \
    -u <gateway-id> -P <gateway-secret> --id "cfg-$(date +%s)" \
    -t "m/<tenant-id>/c/<commands-channel-id>/req" \
    -m '[{"bn":"req-1:", "n":"config", "vs":"set,heartbeat_interval,30s"}]'

The agent responds on the control response topic with "ok" and the next heartbeat uses the new interval.