You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FreeDMR/docs/freedmr-2/07-reporting-model.md

5.4 KiB

Reporting Model

Decision: FreeDMR 2 replaces the legacy dashboard/report socket model with a new structured reporting event model.

The existing dashboard is not a compatibility constraint for the FreeDMR 2 core. It must be updated separately or supported by an optional out-of-process adapter.

Reporting is observational only. Packet routing must not depend on dashboard/report consumers.

The v2 event schema is the compatibility contract. Raw BRIDGES/SYSTEM state should not be exposed as the primary v2 API. Old dashboard/report socket event names should not shape the FreeDMR 2 core.

Any old dashboard compatibility must live in a sidecar/adapter, not inside packet routing. FreeDMR 1.x/current code remains live until FreeDMR 2 is ready, so FreeDMR 2 can make a clean reporting break.

Preferred Transport

MQTT is the preferred external live reporting transport.

Architecture:

packet path -> non-blocking bounded local event queue -> MQTT publisher worker -> local broker/feed

Constraints:

  • MQTT publishing must be asynchronous from the packet worker.
  • Use a bounded queue.
  • The bounded local event queue is the only coupling from packet path to reporting worker.
  • Drop or coalesce low-priority events under pressure.
  • Emit a later reporting-health event rather than blocking packet handling.
  • Voice stability takes precedence over reporting completeness.
  • Reconnect with exponential backoff.
  • Refresh retained state after reconnect.
  • Reporting backpressure must be visible through reporting-health events but must not delay DMR packets.

Reporting is the first major candidate for out-of-process execution. The MQTT publisher should be an independent worker or sidecar where practical. The global lastheard exporter should be a separate process. Dashboard aggregation should not run in the packet hot path.

Reporting worker crash must not affect packet routing. Reporting worker restart should refresh retained state after reconnect.

Local Dashboard and Global Lastheard

Local dashboard:

  • Consumes local MQTT live state/events.
  • Displays live client/repeater/server traffic.
  • Recovers from retained state after reconnect.

Global lastheard:

  • Central/non-real-time.
  • Consumes summaries, not packet-plane traffic.
  • Should preferably be fed by a separate exporter process.
  • Central outage must not affect local packet handling or local dashboard.

Preferred flow:

FreeDMR core -> local MQTT feed -> local dashboard
                              -> global-exporter process -> network MQTT/collector

Initial Event Families

  • server.started
  • server.stopping
  • client.connected
  • client.disconnected
  • client.options_changed
  • subscription.activated
  • subscription.deactivated
  • subscription.expired
  • call.started
  • call.ended
  • call.lost
  • mesh.peer_up
  • mesh.peer_down
  • mesh.source_quench
  • mesh.stun
  • loop.detected
  • packet.rate_limited
  • reporting.queue_overflow
  • reporting.publisher_disconnected
  • reporting.publisher_reconnected
  • reporting.events_dropped

Suggested MQTT Topics

freedmr/v2/{server_id}/state
freedmr/v2/{server_id}/client/{client_id}/state
freedmr/v2/{server_id}/client/{client_id}/slot/{slot}/activity
freedmr/v2/{server_id}/subscription/{subscription_id}/state
freedmr/v2/{server_id}/call/{stream_id}/start
freedmr/v2/{server_id}/call/{stream_id}/end
freedmr/v2/{server_id}/mesh/{peer_id}/state
freedmr/v2/{server_id}/event

Use retained messages for current state and non-retained messages for transient events.

Example Events

{
  "event": "server.started",
  "server_id": "2345",
  "version": "2.0-dev",
  "time": "2026-05-24T12:00:00Z"
}
{
  "event": "client.connected",
  "server_id": "2345",
  "client_id": 2345001,
  "listener": "hbp-public",
  "endpoint": "198.51.100.10:62031",
  "time": "2026-05-24T12:00:01Z"
}
{
  "event": "subscription.activated",
  "server_id": "2345",
  "subscription_id": "2345001-2-9-4400",
  "client_id": 2345001,
  "slot": 2,
  "rf_tg": 9,
  "conference_tg": 4400,
  "mode": "dial",
  "source": "dial-a-tg",
  "time": "2026-05-24T12:00:02Z"
}
{
  "event": "call.started",
  "server_id": "2345",
  "stream_id": 12345678,
  "client_id": 2345001,
  "slot": 2,
  "source_id": 2345678,
  "rf_tg": 9,
  "conference_tg": 4400,
  "source": "hbp",
  "time": "2026-05-24T12:00:03Z"
}
{
  "event": "call.ended",
  "server_id": "2345",
  "stream_id": 12345678,
  "reason": "terminator",
  "duration_ms": 18420,
  "packets": 614,
  "time": "2026-05-24T12:00:21Z"
}
{
  "event": "call.lost",
  "server_id": "2345",
  "stream_id": 12345678,
  "reason": "timeout",
  "last_seen_ms_ago": 7000,
  "time": "2026-05-24T12:00:28Z"
}
{
  "event": "mesh.source_quench",
  "server_id": "2345",
  "peer_id": "2350",
  "stream_id": 12345678,
  "conference_tg": 4400,
  "reason": "duplicate-source",
  "time": "2026-05-24T12:00:04Z"
}
{
  "event": "reporting.queue_overflow",
  "server_id": "2345",
  "dropped_events": 42,
  "queue_limit": 2048,
  "policy": "drop-low-priority",
  "time": "2026-05-24T12:00:05Z"
}

Open Questions

  • Broker packaging and defaults: Mosquitto, embedded broker, external broker, or optional dependency.
  • Exact retained-state expiry policy.
  • Authentication model for MQTT clients.
  • Whether legacy dashboard compatibility is a supplied sidecar or a separate dashboard migration task.

Powered by TurnKey Linux.