A small Node.js web server that subscribes to events from the
tr-plugin-mqtt trunk-recorder
plugin and serves a live status dashboard in your browser.
Why not the old status server? The original
trunk-recorder-status-server(and its companiontr-plugin-websocket-server) relied on Boost.Beast WebSockets built into trunk-recorder. Starting with trunk-recorder v4.7+/v5, changes to the Boost dependencies broke that WebSocket interface (issue #4, issue #1). This dashboard replaces that approach entirely — it uses the standalone MQTT status plugin (which uses the Paho MQTT library instead of Boost) and requires no WebSocket support in trunk-recorder itself.
- Live decode rates per system
- Total calls and captured audio time per system
- Active calls table (talkgroup, tag, unit, length, flags)
- Recorders table, live graph split by SDR device, missed-calls table, and conspicuous no-recorder alerts
- Systems cards (sys_num, type, sysid, wacn, nac, rfss, site)
- Sources cards (driver, device, gain, ranges) from the retained
configmessage - Audio playback of completed calls (when
mqtt_audio: true) - Recent unit activity log
- Recently ended calls
- Multi-instance support via
instance_id - MQTT + WebSocket connection indicators
git clone https://github.com/theficus/trunk-recorder-mqtt-status-server.git
cd trunk-recorder-mqtt-status-server
npm install
./setup.shThe script prompts for your broker URL, credentials, topic prefixes, and HTTP
port, installs dependencies, writes a .env, and starts the server.
Non-interactive:
MQTT_URL=mqtt://broker.local:1883 \
MQTT_USERNAME=user MQTT_PASSWORD=pass \
MQTT_TOPIC=trunk-recorder \
MQTT_UNIT_TOPIC=trunk-recorder/units \
./setup.sh --no-promptInstall only (no auto-start):
./setup.sh --no-startAfter install:
./run.sh # loads .env and starts the server
npm start # same thing, without .env auto-loadThen open http://localhost:3080.
Demo mode with mock data is available at http://localhost:3080/demo
or by adding ?demo=1 to the dashboard URL. It renders synthetic values only and
does not use live MQTT data.
setup.sh writes a .env (chmod 600) that run.sh sources at start.
Set mqtt_audio: true in the trunk-recorder plugin config and
MQTT_AUDIO_ENABLED=true in the dashboard. The dashboard subscribes to
<MQTT_TOPIC>/audio, decodes audio_wav_base64 / audio_m4a_base64 to files
under RECORDINGS_DIR, and serves them with an <audio> player in the
"Recent Audio" section.
⚠️ Audio messages are large (a 30s WAV is ~700 KB base64). Make sure your broker'smessage_size_limitis high enough. Mosquitto: setmessage_size_limit 10485760(or larger) inmosquitto.conf.
Add this to your trunk-recorder config.json (broker URL uses tcp://,
the dashboard uses mqtt://; both go to the same broker):
{
"plugins": [
{
"name": "MQTT Status",
"library": "libmqtt_status_plugin.so",
"broker": "tcp://your-broker:1883",
"topic": "trunk-recorder",
"unit_topic": "trunk-recorder/units",
"username": "",
"password": "",
"console_logs": true,
"mqtt_audio": false,
"mqtt_qos": 0
}
]
}Status (MQTT_TOPIC/...): rates, config, systems, system,
calls_active, recorders, recorder, call_start, call_end,
plugin_status, console.
Unit (MQTT_UNIT_TOPIC/...): call, end, on, off, ackresp, join,
data, ans_req, location.
Trunking (MQTT_MESSAGE_TOPIC/...): messages.
See the official example messages for full payload schemas.
server.jsconnects to MQTT and subscribes to the three topic prefixes.- Each message is parsed and merged into an in-memory per-
instance_idstate. - State is broadcast to browsers over Socket.IO.
public/app.jsre-renders on every update.
No database, no audio handling — just status.
- Node.js 18 or newer
- An MQTT broker (e.g. Mosquitto) reachable from wherever you run the dashboard
- A trunk-recorder instance with
libmqtt_status_plugin.soenabled
MIT — see LICENSE.

