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

204 lines
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:
```text
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:
```text
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
```text
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
```json
{
"event": "server.started",
"server_id": "2345",
"version": "2.0-dev",
"time": "2026-05-24T12:00:00Z"
}
```
```json
{
"event": "client.connected",
"server_id": "2345",
"client_id": 2345001,
"listener": "hbp-public",
"endpoint": "198.51.100.10:62031",
"time": "2026-05-24T12:00:01Z"
}
```
```json
{
"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"
}
```
```json
{
"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"
}
```
```json
{
"event": "call.ended",
"server_id": "2345",
"stream_id": 12345678,
"reason": "terminator",
"duration_ms": 18420,
"packets": 614,
"time": "2026-05-24T12:00:21Z"
}
```
```json
{
"event": "call.lost",
"server_id": "2345",
"stream_id": 12345678,
"reason": "timeout",
"last_seen_ms_ago": 7000,
"time": "2026-05-24T12:00:28Z"
}
```
```json
{
"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"
}
```
```json
{
"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.