MeshMonitor v4.17.2-rc5
Warning
This is a pre-release for testing. Don't run it in production unless you're ready to report issues.
Summary
This release is a performance pass: the app, and the frontend most of all, felt slow, and the cause was mostly data volume. The dashboard used to download its node data twice per visit, uncompressed, about 35 MB each time. It now downloads it once, compressed, at about 0.23 MB. Every tab's code used to load up front; now each tab loads its code when you open it. The server no longer loads every node to answer small questions like unread counts. Background polling sends only what changed since the last poll, and MQTT node updates now come over the live socket. Typing a message no longer redraws the whole app, and long MeshCore lists draw only the rows on screen. This release also fixes telemetry automation triggers, which never fired before. They now fire, with a per-node hourly limit and a guard against loops. Automation cooldowns and rate limits are now kept across restarts and saves.
Measured: rc4 vs rc5
Both builds were measured one after the other on the same server and database (8 sources, ~930 Meshtastic and ~300 MeshCore nodes). "Phone" means a phone-sized screen with the CPU slowed 4×. Page loads and the Info tab are the median of 3 runs; API times are the median of 5.
Network
| rc4 | rc5 | change | |
|---|---|---|---|
| Dashboard page, first load | 75.3 MB | 1.56 MB | −98% |
| Nodes page, first load | 7.7 MB | 1.5 MB | −80% |
| Messages page, first load | 7.6 MB | 1.1 MB | −85% |
| Info page, first load | 8.6 MB | 1.3 MB | −85% |
| JavaScript on first load (Nodes page) | 5.1 MB | 0.96 MB | −81% |
| Dashboard data on the wire | 35.6 MB ×2 | 0.23 MB ×1 | −99.7% |
| Idle page, data per minute | 1,867 KB | 12.9 KB | −99.3% |
/api/poll, steady state
| 924 KB | 0.2 KB | −99.9% |
Server
| rc4 | rc5 | change | |
|---|---|---|---|
| Dashboard response time (p50) | 611 ms | 209 ms | −66% |
| Dashboard requests served in 20 s | 32 | 96 | +200% |
| Health-check p95 while dashboard is busy | 630 ms | 158 ms | −75% |
| Unread counts (p50) | 88 ms | 6.6 ms | −93% |
| First unread (p50) | 87 ms | 6.0 ms | −93% |
| Unified messages (p50) | 255 ms | 31 ms | −88% |
| Aircraft trails (p50) | 320 ms | 96 ms | −70% |
Browser (desktop / phone)
| rc4 | rc5 | change | |
|---|---|---|---|
| Dashboard: total blocking time | 1,299 / 2,981 ms | 252 / 1,187 ms | −81% / −60% |
| Dashboard: first map marker | 3.7 / 4.8 s | 1.0 / 2.2 s | −72% / −54% |
| Nodes: total blocking time | 451 / 594 ms | 111 / 421 ms | −75% / −29% |
| Info tab: all charts drawn | 1.9 / 6.2 s | 0.7 / 2.0 s | −64% / −68% |
| Info tab: total blocking time | 1,535 / 5,786 ms | 446 / 2,310 ms | −71% / −60% |
| Typing 20 characters: components redrawn | 74,920 / 4,310 | 120 / 120 | −99.8% / −97% |
| Typing 20 characters: script time | 645 / 550 ms | 59 / 68 ms | −91% / −88% |
| Map pan ×5: components redrawn | 7,553 / 2,230 | 2,418 / 7 | −68% / −99.7% |
| MeshCore node list (297 nodes): page elements | 7,869 | 1,768 | −78% |
| MeshCore contacts list: page elements | 3,463 | 545 | −84% |
Trade-offs we know about:
- Opening a tab for the first time can take 15–75 ms longer, because that tab's code now downloads on first open. After that the browser serves it from cache.
- An idle page redraws a little more often (about 36 a minute, up from 27), because live socket updates now replace the heavy polling.
- The
/api/pollchange saves bytes, not server time: each poll still takes about 140 ms on the server.
What changed
Network and caching
- Compressed responses and long-lived caching of built files (#6025). API JSON is now gzip-compressed, and hashed JS/CSS files are cached for good.
- Dashboard loads its data once (#6026). It waits for login to finish instead of fetching once before and once after.
- Smaller dashboard data (#6028). The 37 node fields the dashboard never reads are left out.
- The dashboard skips nodes its map would hide by age (#6032, #6034). It applies the map's age settings on the server, inside the database query. 0 still means "no limit", and favourites, aircraft and your own nodes are always kept.
- Code splitting (#6029). Tabs, the MapLibre map engine, the chart library and the config search load only when used. If a deploy replaces a file while a page is open, the page reloads once instead of breaking.
- Polling sends only what changed (#6037). After the first poll, the browser sends a cursor and the server returns only the changes since then.
- A source that is down no longer triggers full polls (#6023). The browser checks it with the small
/api/connectioncall, backs off up to 30 s, and pauses while the tab is hidden. - MQTT node updates arrive over the live socket (#6035), grouped per source every 2 s, so pages don't have to poll for them.
Server
- Small questions no longer load every node (#6024). Unread counts, first unread, unified messages and aircraft trails now look up only the nodes they need.
Rendering
- Dashboard map markers keep their identity (#6026). The merge and marker lists are memoized, so markers aren't rebuilt when nothing changed.
- Telemetry charts are faster (#6027). Date formatters are cached, chart data is computed once per change, and large series drop point dots and animation; hovering still shows the tooltip.
- Typing and map panning stay local (#6030). The message draft and the map view moved out of app-wide state, so typing and panning no longer redraw the rest of the app.
- Message rows are memoized and long MeshCore lists are virtualized (#6036). Lists over 100 rows draw only what's on screen.
Bug fixes
- Telemetry automation triggers now fire (#6038). The "telemetry received" trigger never fired before this release. It now fires for Meshtastic TCP and MQTT sources, with these safeguards:
- Each node can fire a given telemetry automation at most once per hour, whatever cooldown the automation sets.
- Replies to MeshMonitor's own telemetry requests are ignored, so an automation that requests telemetry can't trigger itself.
- Local-node stats are exempt from that guard.
- Automation cooldowns and rate limits survive restarts and saves (#6042). They're stored in the database (new
automation_fire_statetable, migration 219), so restarting the container or saving an automation no longer resets them.
Upgrade notes
- Check your telemetry automations. Any automation with a "telemetry received" trigger has never fired before; from this release on, it will. A one-time banner on the Automations page points this out. Review those automations, especially any that send messages, before upgrading.
- Migration 219 adds one table. It runs automatically on SQLite, PostgreSQL and MySQL.
Pull requests
Performance: #6023, #6024, #6025, #6026, #6027, #6028, #6029, #6030, #6032, #6034, #6035, #6036, #6037
Bug fixes: #6038, #6042
Chores: #6043
Full changelog: v4.17.2-rc4...v4.17.2-rc5
🚀 MeshMonitor v4.17.2-rc5
📦 Installation
Docker (recommended):
docker run -d \
--name meshmonitor \
-p 8080:3001 \
-v meshmonitor-data:/data \
ghcr.io/Yeraze/meshmonitor:4.17.2-rc5🧪 Testing
✅ All tests passed
✅ TypeScript checks passed
✅ Docker images built for linux/amd64, linux/arm64, linux/arm/v7
📋 Changes
See commit history for detailed changes.