Skip to content

v4.14.0

Latest

Choose a tag to compare

@Yeraze Yeraze released this 03 Aug 18:26
96b3106

MeshMonitor v4.14.0

The first stable release since v4.13.2 — the 4.13.3 release candidates were folded into this minor instead, because the work that landed is a minor's worth, not a patch's. The headline is the MeshCore Analyzer Observer: publish what your Companion hears to a regional analyzer without a second app fighting for the serial port, observation-only by design. Meshtastic and MeshCore sources now share one navigation system, which on phones means a bottom bar that scrolls sideways with readable labels instead of eleven crushed icons. Remote admin gained distinct ACK outcomes — rejected reads differently from timed out — plus opt-in auto-retry, and commands no longer die when the browser navigates away. MeshCore messages carry SNR and RSSI when heard directly, "Discover Nodes" now lists who answered with signal in both directions, and per-channel permissions finally do what they say. Node Details shows where a position came from and how precise it is, and Null Island estimates are no longer passed off as real fixes. The Packet Monitor's node filters became searchable comboboxes, which matters most on a phone and on a large mesh. Under the hood, the database driver moved to better-sqlite3 13 (N-API), verified at runtime on amd64, arm64 and armv7 before shipping.

⚠️ Breaking Change: v1 API root paths removed

4.13 moved the v1 mesh-data endpoints under /api/v1/sources/{sourceId}/… and kept the old root paths alive for one release behind a Warning: 299 header. That grace period ends here — these paths now return 404:

/api/v1/nodes      /api/v1/telemetry     /api/v1/network
/api/v1/messages   /api/v1/traceroutes   /api/v1/status
/api/v1/channels   /api/v1/packets       /api/v1/position-history

Migrate by moving the source into the path:

# Before (4.13, deprecated)
curl -H "Authorization: Bearer mm_v1_…" "https://host/api/v1/nodes?sourceId=abc123"

# After (4.14)
curl -H "Authorization: Bearer mm_v1_…" "https://host/api/v1/sources/abc123/nodes"

GET /api/v1/sources lists your source ids, or use the literal default to target the primary source. The /api/upgrade/* endpoints, returning 410 Gone since 4.13, are also removed.

If you script against the REST API, check for these paths before upgrading. Everything else is drop-in; the database schema migrates automatically on first boot.

Full details in the release announcement.

Features

  • feat(analysis): show node names in the ok_to_mqtt violations report (#4343)
  • feat(i18n): localize the MapStyleManager settings component (#4363)
  • feat(meshcore): serve ExportPrivateKey over the Virtual Node behind an "Allow PKI export" gate (#4366)
  • feat(automations): hop-count tapback + {{ trigger.hopEmoji }} (#4340 phase 1) (#4390)
  • feat(traceroute): visual node strip on the Node Details panel (#4392)
  • feat(automations): per-node cooldown scope (#4340 phase 2) (#4396)
  • feat(meshcore): add per-node ignore list to the auto-responder (#4402)
  • feat(map): per-overlay toggle to disable the GeoJSON click popup (#4403)
  • feat(meshcore): add zero-hop ping action (#4404)
  • feat(tx-guard): allow sends when UDP Broadcast can relay for a TX-disabled node (#4405)
  • feat(waypoints): let the user pick the broadcast channel (#4406)
  • feat(automations): Auto-Ack parity conditions (#4340 phase 3) (#4407)
  • feat(automations): Auto-Acknowledge → Automation converter (#4340 phase 4) (#4408)
  • feat(settings): per-source settings foundation and guardrails (#4412 Phase 1) (#4417)
  • feat(traceroute): interactive strip — node popup More Details action + link tooltips (epic phase 1) (#4424)
  • feat(settings): read Node Display settings per-source (#4412 Phase 2) (#4425)
  • feat(traceroute): participation picker on all sources — brings the strip to MQTT (epic phase 2) (#4427)
  • feat(traceroute): Copy Forward/Return/Both text links with insecure-context fallback (#4429)
  • feat(settings): per-source Node Display frontend (#4412 Phase 3) (#4431)
  • feat(meshcore): per-source node-age filtering and Node Display settings (closes #4412) (#4433)
  • feat(traceroute): statistical route aggregation + union layout engine (epic phase 1) (#4444)
  • feat(traceroute): Statistical route view in the Node Details strip (epic phase 2) (#4451)
  • feat(meshcore): Analyzer Observer backend foundation — Phase 1 (#4464)
  • feat(nodes): show position provenance and accuracy in Node Details (#4467)
  • feat(meshcore): Analyzer Observer publisher service — Phase 2 (#4468)
  • feat(meshcore): Analyzer Observer UI + docs — Phase 3, final (#4471)
  • feat(nav): unify Meshtastic/MeshCore per-source navigation (#4481)
  • feat(nav): move the Meshtastic phone nav to the shared bottom bar (#4484)
  • feat(nodes): name the position source on the Node Details badge (#4501)
  • feat(admin): distinct ACK outcomes + opt-in auto-retry (#4492, #4487) (#4502)
  • feat(admin): expose the retry-attempt controls in the UI (#4487 follow-up) (#4503)
  • feat(meshcore): show SNR on directly-received messages (#4505)
  • feat(meshcore): carry RSSI onto received messages (#4504 follow-up) (#4506)
  • feat(packets): type-to-filter the Packet Monitor node filters (#4513)
  • feat(meshcore): show which nodes answered a discovery sweep (#4532)

Bug Fixes

  • fix(map): let waypoint placement clicks win over overlay popups (#4364)
  • fix(meshtastic): only push add_contact when the radio lacks the peer's key (#4369)
  • fix(map-analysis): give the 3D view the view-state, follow, and layer plumbing 2D has (#4373)
  • fix(map): wire activeStyleJson into MapAnalysis and MeshCore BaseMap consumers (#4376)
  • fix(news): absolutize meshmonitor.org links in the news feed (#4377)
  • fix(ci): publish stable releases to the :dev tag as well as :latest (#4382)
  • fix(map): give the tile selector a mobile bottom sheet (#4383)
  • fix(map): persist the "Show ATAK Contacts" toggle (#4384)
  • fix(nodes): give every node row a distinct Node Details button (#4385)
  • fix(traceroute): portal the strip hover popup and show the Map node card (#4397)
  • fix(dashboard): give every sidebar link an icon (#4398)
  • fix(map): give the legend a usable mobile presentation (#4401)
  • fix(meshcore): sign-extend traceContactPath per-hop SNR bytes (#4409)
  • fix: bound geofenceState and autoAckCooldowns growth (#4413)
  • fix(mqtt): guard handleUplink against a client nulled mid-flight; harden the ok_to_mqtt assertion (#4414)
  • fix(virtual-node): fall back to persisted modem preset for slot-0 channel name (#4415)
  • fix(virtual-node): stop duplicate MQTT client-proxy publishing (#4037 follow-up) (#4418)
  • fix(automation): disable Convert-to-Automation button for MeshCore sources (#4421)
  • fix(meshcore): scope /snapshot messages field to messages:read permission (#4423)
  • fix(traceroute): route strip edges and labels around unrelated node glyphs (#4430)
  • fix(meshcore): guard startAutoPathfinding() against overlapping calls (#4435)
  • fix(telemetry): humanize Dashboard uptime widget (#3261) + align DM chart padding (#4439)
  • fix(ui): unify sidebar footer across dashboard and per-source views (#4440)
  • fix(settings): close the defects the per-source epic uncovered (#4441)
  • fix(auth): make settings a per-source permission resource (closes #4416) (#4443)
  • fix(nodes): guard lastHeard against replayed position/telemetry packets (#4446)
  • fix: close the five defects the per-source epic uncovered (#4442, #4437, #4438, #4449, #4448) (#4453)
  • fix(nav): pass showList from every back-to-dashboard button (#4454)
  • fix(position-estimation): exclude single-anchor estimates by default (closes #4450) (#4455)
  • fix(telemetry,sidebar): humanize per-node uptime graphs (#3261), stop sidebar controls overlapping the shared footer (#4458)
  • fix(traceroute): relayed participation in the node picker is MQTT-only (#4459)
  • fix(meshcore): infinite-scroll pagination for channel message history (#4461)
  • fix(pwa): deep-link notification clicks to the source route (#4465)
  • fix(positions): stop Null Island estimates being shown as real positions (#4469)
  • fix(api): GET /stats returns 500 whenever sourceId is omitted (#4470)
  • fix(meshcore): preserve hop/route/scope info in channel message history (#4475)
  • fix(messaging): stop older-message loads from force-scrolling to bottom (#4477)
  • fix(admin): send admin messages at the node's configured hop limit (#4479)
  • fix(ui): restore the send-message button icon (#4480)
  • fix(admin): decouple remote-admin commands from the HTTP request (#4485)
  • fix(admin): decouple session-passkey and config-import from the HTTP request (#4486)
  • fix(meshcore): channel view opens at the top and Delete appears to do nothing (#4488)
  • fix(meshcore): anon auth banner, channel-sync data loss, and reply/trigger ordering (#4491)
  • fix(nav,map): iOS PWA bottom-nav safe area + map controls clearance and zoom-to-fit (#4499)
  • fix(automation): match the Primary/unnamed channel in "On channels" (#4508)
  • fix(meshcore): clear zero-hop ping result when the selected contact changes (#4515)
  • fix(meshcore): don't paint a stale reply onto the wrong contact (#4530)
  • fix(meshcore): honour per-channel permissions on channel messages (#4537)

Performance

  • perf(test): parallelize the unit suite (~17.5 min → ~6.6 min) and retire duplicate CI workflow (#4372)

Documentation

  • docs(release): document #4366 and #4368 for the 4.13.2 notes (#4370)
  • docs(admin): document asynchronous remote-admin execution (#4490)
  • docs(automations): say which text operators are case-sensitive (#4509)
  • docs(automations): say the TRIGGER regex is case-sensitive too (#4538)
  • docs(blog): v4.14.0 release announcement (#4540)

Tests

  • test(packet-log): de-flake the since-timestamp filter test (#4386)

CI/DevOps

  • ci: wait on CodeQL and ClamAV before auto-merging Dependabot PRs (#4536)

Chores

  • chore(api): remove 4.13 grace-period shims — v1 root paths + /api/upgrade 410s (closes #4117) (#4189)
  • chore(ci): drop Node 20 from the test matrix (#4361)
  • chore(map): route DefaultCenterController logs through the logger (#4365)
  • chore(scripts): retry transient GitHub API errors in the CI/release watchers (#4462)
  • chore(protobufs): refresh pin to develop@6ceceae and honor rx_rssi explicit presence (#4466)
  • chore(release): 4.13.3-rc4 (#4472)
  • chore(release): 4.13.3-rc5 (#4494)
  • chore(dev): allow https://mm-dev.yeraze.online as a CORS origin (#4500)
  • chore(release): 4.13.3-rc6 (#4510)
  • chore(release): open 4.14.0 (#4539)

Other

  • Fix oversized apple-touch-icon and add standards-track PWA meta tag (#4359)
  • Fix Map Style activation UI, label collision, and Dashboard styling (#4360)
  • Fix: purge position history also clears the global position estimate (#4452)

Dependencies

23 dependency updates, including better-sqlite3 12 → 13 (N-API rework of the database driver, verified at runtime on amd64/arm64/armv7), jsdom 30, and vite 8.2.

Issues Resolved

  • #3261 [FEAT] Telemetri Meshcore
  • #4037 [BUG] Virtual Node issues (MQTT conn rejected, channel info lost)
  • #4117 4.14: remove the two 4.13 grace-period shims (v1 root-path API + /api/upgrade/* 410s)
  • #4192 [BUG] Meshtastic 2.8 periodic rebroadcast of cached position/telemetry updates node "Last Heard"
  • #4340 [FEAT] Per-channel Auto-Acknowledge message body (redirect test traffic without misdirecting correct users)
  • #4341 Feature request: allow selecting channel when creating a Waypoint
  • #4342 Bug: GeoJSON overlay tooltip blocks Waypoint creation on map click
  • #4344 Feature request: per-overlay setting to disable GeoJSON popup on click
  • #4348 [BUG] Custom map styles never actually apply, and the controls to try are confusingly duplicated/mislabeled.
  • #4358 [BUG] Oversized apple-touch-icon (132KB) and deprecated PWA meta tag
  • #4362 [FEAT] Surface observed ADMIN_APP (portnum 6) node pairs as a best-effort "likely administered by" indicator
  • #4368 [BUG] add_contact is re-sent before every PKI DM, so the firmware re-favorites every DM recipient
  • #4371 Map Analysis 3D view missing view-state, follow, and layer-switching plumbing that 2D has
  • #4374 [BUG] Map Style fix (#4360) missed two more BaseMap consumers: Map Analysis and MeshCore maps
  • #4375 [BUG] Favorite star icon renders black instead of gold (v4.13.2, icon-system migration regression)
  • #4378 [BUG] "Show ATAK Contacts" map toggle never persists — silently dropped by /map-preferences
  • #4379 [FEAT] Replace icon-click affordance for Node Details with a distinct labeled button
  • #4380 Tile selector popover hidden on mobile with no fallback UI
  • #4381 [FEAT] Color-code node roles in traceroute display
  • #4389 Map legend hidden on mobile with no fallback UI
  • #4391 [FEAT] MeshCore: Auto-Ack needs a sender skip-list to prevent bot-echo loops
  • #4393 [FEAT] Meshcore - add PING [zero hops] in ACTIONS
  • #4394 [FEAT] Receive-only mode send guard doesn't account for UDP Broadcast relay path
  • #4395 [BUG] Dashboard sidebar: Automation Engine has an icon, its five sibling links don't
  • #4399 Automation engine: geofenceState and autoAckCooldowns are unbounded maps
  • #4400 Flaky test: ok_to_mqtt fail-closed assertion fails open on the Node 25.x CI leg
  • #4411 [BUG] 4.13.2-rc4 - MAP issues
  • #4412 [FEAT] Meschore - last advert time filtering
  • #4416 [BUG] Two divergent SOURCEY_RESOURCES lists — per-source permission scoping silently ignored for settings, dashboard, info, audit, security
  • #4419 [BUG] Per-source settings POST compares against global current-values; getSourceSettings does a full-table scan
  • #4420 [SUPPORT] MeshCore: Cannot find "Convert to automation" button in Auto-Acknowledge Settings
  • #4422 [BUG] Meshore: Anonymous users can see Direct Messages even if they should not be allowed to
  • #4426 [BUG] Telemetry chart padding doesn't line up with rest of DM conversation view (mobile)
  • #4428 [BUG] Traceroute strip: edges can visually overlap unrelated nodes/labels
  • #4432 [BUG] Estimated positions are indistinguishable from real GPS positions client-side
  • #4434 [BUG] Meshcore - Time intervals on Auto-Pathfinding and Telemetry retrieval not being respected
  • #4436 Unify sidebar footer across unified dashboard and per-source views
  • #4437 [QUESTION] externalUrl is read by the security digest and written nowhere — wire a writer or drop the link?
  • #4438 [BUG] MeshCore local-node detection matches on the name string "(local)"
  • #4442 [BUG] Security digest ignores appriseApiServerUrl — hardcodes http://localhost:8000
  • #4445 [BUG] offline device seen as active
  • #4447 [BUG] Back-to-Dashboard buttons on 5 pages ignore skipDefaultLanding, redirect to default landing page instead
  • #4448 [BUG] New-user default grants are written globally, but 4 of 6 are per-source resources — silently inert
  • #4449 [BUG] MeshCore "Refresh Contacts" drops the local node — POST /contacts/refresh omits the synthetic row GET /contacts adds
  • #4450 [BUG] Nodes without location incorrectly show up on map
  • #4457 [FEAT] Add MeshCore Analyzer Observer MQTT Output
  • #4460 [BUG] Meshcore scrolling up on channel doesn't load older messages
  • #4463 iOS PWA: push notification click doesn't deep-link when app is fully closed (blocked on WebKit bug)
  • #4473 [FEAT] Unify Meshtastic/MeshCore per-source navigation (sidebar vs bottom bar)
  • #4474 [BUG] Meshcore path and hops information disappears on older messages
  • #4476 [BUG] Meshtastic older message loading causes scroll to bottom
  • #4478 [BUG] Send message button renders with no icon (Lucide style, High Contrast Dark theme)
  • #4482 [BUG] Remote admin HTTP requests can block up to 75s (passkey + ACK waits), causing upstream 502s under RF retransmission
  • #4487 [FEAT] Auto-retry admin commands until verified successful
  • #4492 Surface remote-admin ACK outcomes as operation states (timed_out / rejected)
  • #4495 [FEAT] Reflow map controls to avoid overlapping attribution on mobile
  • #4496 [FEAT] Add "zoom to fit all nodes" button to map controls
  • #4497 [BUG] iOS PWA bottom nav: safe-area padding overridden, scroll fights home-indicator/app-switcher gesture, no scroll cue
  • #4498 Node Details: Position badge shows only generic "GPS", not source (Internal/External/Manual)
  • #4504 [FEAT] Meshcore show SNR RSSI on direct messages
  • #4507 [BUG] "On channels (Primary)" in automation engine isn't checked
  • #4512 [FEAT] Packet Monitor node filters should support free-text search instead of native scroll dropdown
  • #4514 [BUG] Meshcore zero-hop ping results follow contact change
  • #4516 [FEAT] Meshcore - show "Discover nodes" results
  • #4517 MeshCore Trace Path result can be painted onto the wrong contact (same race as #4514)

Contributors

Thanks to @rancur for #4470, and to Dependabot for keeping the dependency tree current.

Full Changelog: v4.13.2...v4.14.0

🚀 MeshMonitor v4.14.0

📦 Installation

Docker (recommended):

docker run -d \
  --name meshmonitor \
  -p 8080:3001 \
  -v meshmonitor-data:/data \
  ghcr.io/Yeraze/meshmonitor:4.14.0

🧪 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.