{
  "id": "meshcore-mqtt-live-map",
  "name": "MeshCore MQTT Live Map",
  "kind": "tool",
  "status": "active",
  "maturity": "stable",
  "description": "Self-hosted real-time MeshCore traffic map that decodes MQTT packet feeds and visualizes nodes, routes, peers, activity heat, coverage, line-of-sight and propagation data in an installable web app.",
  "screenshots": [
    {
      "file": "live-map.png",
      "caption": "Live nodes, routes and mesh activity"
    },
    {
      "file": "route-history.png",
      "caption": "Route history and network analysis tools"
    }
  ],
  "maintainers": [
    {
      "name": "yellowcooln",
      "url": "https://github.com/yellowcooln"
    }
  ],
  "repository": "https://github.com/yellowcooln/meshcore-mqtt-live-map",
  "website": "https://live.bostonme.sh/",
  "documentation": "https://github.com/yellowcooln/meshcore-mqtt-live-map/blob/main/howto.md",
  "license": "GPL-3.0-only",
  "languages": [
    "python",
    "javascript"
  ],
  "platforms": [
    "docker",
    "web"
  ],
  "interfaces": [
    "web",
    "api",
    "headless"
  ],
  "connections": [
    "mqtt",
    "websocket",
    "http"
  ],
  "capabilities": [
    "monitoring",
    "telemetry",
    "packet-analysis",
    "mapping"
  ],
  "install": [
    {
      "type": "web",
      "url": "https://live.bostonme.sh/"
    },
    {
      "type": "docker-compose",
      "package": "yellowcooln/meshcore-mqtt-live-map",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map#quick-start",
      "command": "docker compose up -d --build"
    },
    {
      "type": "source",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map"
    }
  ],
  "popularity": {
    "githubStars": 74,
    "githubForks": 13,
    "githubWatchers": 1,
    "githubOpenIssues": 0,
    "githubContributors": 8,
    "latestReleaseDownloads": 0,
    "lastChecked": "2026-06-23"
  },
  "verification": {
    "sourceAvailable": true,
    "releasesAvailable": true,
    "signedReleases": false,
    "ciBuilds": true,
    "hasDocumentation": true,
    "lastChecked": "2026-06-23",
    "notes": [
      "Supports MQTT over TCP or WebSockets with optional TLS and production token protection.",
      "The Greater Boston deployment is the reference instance; several community meshes operate independent deployments."
    ]
  },
  "tags": [
    "live-map",
    "mqtt",
    "self-hosted",
    "pwa",
    "propagation"
  ],
  "last_reviewed": "2026-06-23",
  "source": {
    "path": "data/software/meshcore-mqtt-live-map/software.yaml",
    "updatedAt": "2026-06-24T01:55:49+02:00"
  },
  "latest_version": "1.9.3",
  "released": "2026-06-09",
  "releases": [
    {
      "version": "v1.9.3",
      "name": "v1.9.3 - XSS Hardening and Dependency Updates",
      "datetime": "2026-06-09T00:32:31Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.9.3",
      "prerelease": false,
      "notes": "This release focuses on security hardening and dependency maintenance.\r\n\r\n### Security\r\n\r\n- Fixed issue #74 by hardening frontend rendering against stored-XSS style payloads from untrusted MeshCore/MQTT fields.\r\n- Node names, peer names, route labels, search results, permanent labels, node popups, Peers rows, Route Details, History popups, and Coverage popups now escape displayed HTML before rendering.\r\n- Map behavior is unchanged: public keys, coordinates, QR payloads, copy actions, route handling, peer selection, and filters continue using the original data.\r\n\r\n### Maintenance\r\n\r\n- Updated backend dependencies:\r\n  - `fastapi==0.136.3`\r\n  - `uvicorn[standard]==0.49.0`\r\n  - `httpx==0.28.1`\r\n- Added `httpx2==2.3.0` for dev tests so FastAPI/Starlette `TestClient` runs without the deprecated-`httpx` warning.\r\n\r\n### Testing\r\n\r\n- `98 passed, 2 skipped`\r\n\r\n## What's Changed\r\n* Harden frontend rendering and update to release version 1.9.3 by @yellowcooln in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/75\r\n\r\n\r\n**Full Changelog**: https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.2...v.1.9.3",
      "notesHtml": "<p>This release focuses on security hardening and dependency maintenance.</p>\n<h3>Security</h3>\n<ul>\n<li>Fixed issue #74 by hardening frontend rendering against stored-XSS style payloads from untrusted MeshCore/MQTT fields.</li>\n<li>Node names, peer names, route labels, search results, permanent labels, node popups, Peers rows, Route Details, History popups, and Coverage popups now escape displayed HTML before rendering.</li>\n<li>Map behavior is unchanged: public keys, coordinates, QR payloads, copy actions, route handling, peer selection, and filters continue using the original data.</li>\n</ul>\n<h3>Maintenance</h3>\n<ul>\n<li>Updated backend dependencies:<ul>\n<li><code>fastapi==0.136.3</code></li>\n<li><code>uvicorn[standard]==0.49.0</code></li>\n<li><code>httpx==0.28.1</code></li>\n</ul>\n</li>\n<li>Added <code>httpx2==2.3.0</code> for dev tests so FastAPI/Starlette <code>TestClient</code> runs without the deprecated-<code>httpx</code> warning.</li>\n</ul>\n<h3>Testing</h3>\n<ul>\n<li><code>98 passed, 2 skipped</code></li>\n</ul>\n<h2>What's Changed</h2>\n<ul>\n<li>Harden frontend rendering and update to release version 1.9.3 by @yellowcooln in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/75\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/75</a></li>\n</ul>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.2...v.1.9.3\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.2...v.1.9.3</a></p>\n"
    },
    {
      "version": "v1.9.2",
      "name": "v1.9.2 - Route History Fixes, LOS Coordinates, and Peers Panel Cleanup",
      "datetime": "2026-05-28T02:14:45Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.9.2",
      "prerelease": false,
      "notes": "## v1.9.2 - Route History Fixes, LOS Coordinates, and Peers Panel Cleanup\r\n\r\n### Highlights\r\n\r\nThis release fixes Route History disable behavior, improves LOS point entry, and cleans up the Peers panel with feedback from the community.\r\n\r\n---\r\n\r\n### Added\r\n\r\n* Added direct latitude/longitude entry to the LOS panel\r\n  Related: #71\r\n\r\n* Added per-pin LOS height input for above-ground-level values\r\n  Related: #71\r\n\r\n* Added `PEERS_DEFAULT_OPEN=false` so admins can choose whether the Peers tool starts active on page load\r\n  Related: #72\r\n\r\n* Added a MQTT-only filter beside the MQTT online legend item\r\n\r\n  * Shows only MQTT-online nodes\r\n  * Hides:\r\n\r\n    * Non-MQTT markers\r\n    * Trails\r\n    * Routes\r\n    * Hop markers\r\n    * Route details\r\n    * Peer lines\r\n  * Does not save to browser storage\r\n  * Is not included in share links\r\n\r\n---\r\n\r\n### Changed\r\n\r\n* Improved LOS point editing so pins can be:\r\n\r\n  * Added\r\n  * Selected\r\n  * Dragged\r\n  * Updated directly from the coordinate editor\r\n    Related: #71\r\n\r\n* Kept LOS and Propagation as separate tools so obstruction checks and RF coverage planning remain independent workflows\r\n  Related: #71\r\n\r\n* Reworked the Peers panel layout based on ideas and testing from Stormlove / @beachmiles\r\n  Related: #72\r\n\r\n* Moved **Clear peers** into the Peers panel header beside **Minimize**\r\n  Related: #72\r\n\r\n* Updated the Peers panel title to use the selected node name, with the 24h window displayed underneath\r\n  Related: #72\r\n\r\n* Updated Incoming/Outgoing headings to display:\r\n\r\n  * Rx/Tx packet totals\r\n  * Unique peer counts\r\n  * Blue/purple line hints\r\n\r\n* Updated peer row stats to display:\r\n\r\n  * Count\r\n  * Percent\r\n  * Distance\r\n\r\n* Tuned Peers panel scrolling so long peer lists scroll inside capped Incoming/Outgoing sections instead of forcing the full panel height on mobile\r\n  Related: #72\r\n\r\n---\r\n\r\n### Fixed\r\n\r\n* Fixed Docker Compose deployments not passing Route History environment variables into the container\r\n  Related: #68\r\n\r\n* Fixed `ROUTE_HISTORY_ENABLED=false` so it now fully disables:\r\n\r\n  * History button\r\n  * History panel\r\n  * `/snapshot` history payloads\r\n  * WebSocket history payloads\r\n  * Route-history file growth from live traffic\r\n\r\n* Fixed Peers counts going empty when Route History was disabled and older history buckets expired\r\n  Related: #68\r\n\r\n* Peers now use dedicated rolling peer-history buckets, so peer counts continue updating from live routes even when Route History is disabled\r\n  Related: #68\r\n\r\n---\r\n\r\n### Credits\r\n\r\nThanks to Stormlove / @beachmiles for the Peers panel cleanup ideas, testing, and follow-up feedback in #72.\r\n\r\n## What's Changed\r\n* fix route history env passthrough by @yellowcooln in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/70\r\n* Enhance peers panel and fix related issues in v1.9.2 by @yellowcooln in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/73\r\n\r\n\r\n**Full Changelog**: https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.1...v1.9.2",
      "notesHtml": "<h2>v1.9.2 - Route History Fixes, LOS Coordinates, and Peers Panel Cleanup</h2>\n<h3>Highlights</h3>\n<p>This release fixes Route History disable behavior, improves LOS point entry, and cleans up the Peers panel with feedback from the community.</p>\n<hr />\n<h3>Added</h3>\n<ul>\n<li><p>Added direct latitude/longitude entry to the LOS panel\nRelated: #71</p>\n</li>\n<li><p>Added per-pin LOS height input for above-ground-level values\nRelated: #71</p>\n</li>\n<li><p>Added <code>PEERS_DEFAULT_OPEN=false</code> so admins can choose whether the Peers tool starts active on page load\nRelated: #72</p>\n</li>\n<li><p>Added a MQTT-only filter beside the MQTT online legend item</p>\n<ul>\n<li><p>Shows only MQTT-online nodes</p>\n</li>\n<li><p>Hides:</p>\n<ul>\n<li>Non-MQTT markers</li>\n<li>Trails</li>\n<li>Routes</li>\n<li>Hop markers</li>\n<li>Route details</li>\n<li>Peer lines</li>\n</ul>\n</li>\n<li><p>Does not save to browser storage</p>\n</li>\n<li><p>Is not included in share links</p>\n</li>\n</ul>\n</li>\n</ul>\n<hr />\n<h3>Changed</h3>\n<ul>\n<li><p>Improved LOS point editing so pins can be:</p>\n<ul>\n<li>Added</li>\n<li>Selected</li>\n<li>Dragged</li>\n<li>Updated directly from the coordinate editor\nRelated: #71</li>\n</ul>\n</li>\n<li><p>Kept LOS and Propagation as separate tools so obstruction checks and RF coverage planning remain independent workflows\nRelated: #71</p>\n</li>\n<li><p>Reworked the Peers panel layout based on ideas and testing from Stormlove / @beachmiles\nRelated: #72</p>\n</li>\n<li><p>Moved <strong>Clear peers</strong> into the Peers panel header beside <strong>Minimize</strong>\nRelated: #72</p>\n</li>\n<li><p>Updated the Peers panel title to use the selected node name, with the 24h window displayed underneath\nRelated: #72</p>\n</li>\n<li><p>Updated Incoming/Outgoing headings to display:</p>\n<ul>\n<li>Rx/Tx packet totals</li>\n<li>Unique peer counts</li>\n<li>Blue/purple line hints</li>\n</ul>\n</li>\n<li><p>Updated peer row stats to display:</p>\n<ul>\n<li>Count</li>\n<li>Percent</li>\n<li>Distance</li>\n</ul>\n</li>\n<li><p>Tuned Peers panel scrolling so long peer lists scroll inside capped Incoming/Outgoing sections instead of forcing the full panel height on mobile\nRelated: #72</p>\n</li>\n</ul>\n<hr />\n<h3>Fixed</h3>\n<ul>\n<li><p>Fixed Docker Compose deployments not passing Route History environment variables into the container\nRelated: #68</p>\n</li>\n<li><p>Fixed <code>ROUTE_HISTORY_ENABLED=false</code> so it now fully disables:</p>\n<ul>\n<li>History button</li>\n<li>History panel</li>\n<li><code>/snapshot</code> history payloads</li>\n<li>WebSocket history payloads</li>\n<li>Route-history file growth from live traffic</li>\n</ul>\n</li>\n<li><p>Fixed Peers counts going empty when Route History was disabled and older history buckets expired\nRelated: #68</p>\n</li>\n<li><p>Peers now use dedicated rolling peer-history buckets, so peer counts continue updating from live routes even when Route History is disabled\nRelated: #68</p>\n</li>\n</ul>\n<hr />\n<h3>Credits</h3>\n<p>Thanks to Stormlove / @beachmiles for the Peers panel cleanup ideas, testing, and follow-up feedback in #72.</p>\n<h2>What's Changed</h2>\n<ul>\n<li>fix route history env passthrough by @yellowcooln in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/70\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/70</a></li>\n<li>Enhance peers panel and fix related issues in v1.9.2 by @yellowcooln in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/73\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/73</a></li>\n</ul>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.1...v1.9.2\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.1...v1.9.2</a></p>\n"
    },
    {
      "version": "v1.9.1",
      "name": "v1.9.1 - Full Route History Disable Support",
      "datetime": "2026-05-08T19:15:07Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.9.1",
      "prerelease": false,
      "notes": "### Fixed\r\n\r\n* Fixed issue #68: `ROUTE_HISTORY_ENABLED=false` now disables Route History end-to-end instead of only stopping new history recording\r\n* When Route History is disabled:\r\n\r\n  * The History tool button is removed\r\n  * The History panel does not load\r\n  * `history=on` in the URL no longer re-enables the feature\r\n  * `/snapshot` no longer returns `history_edges` or a nonzero history window\r\n  * The realtime WebSocket snapshot no longer publishes history payloads\r\n\r\n---\r\n\r\n### Behavior Change\r\n\r\n`ROUTE_HISTORY_ENABLED` now behaves as a true feature flag for both:\r\n\r\n* Backend history payloads\r\n* Frontend History tool/UI\r\n\r\nThis makes it practical to disable Route History entirely on:\r\n\r\n* High-volume meshes\r\n* Lower-resource deployments\r\n\r\n---\r\n\r\n### Config\r\n\r\nUse:\r\n\r\n`ROUTE_HISTORY_ENABLED=false`\r\n\r\nThis now fully disables:\r\n\r\n* History tool UI\r\n* History snapshot payloads\r\n* History panel activation\r\n* Hidden frontend history state\r\n\r\n---\r\n\r\n### Validation\r\n\r\n* `pytest -q tests/test_api_auth.py tests/test_websocket_snapshot.py`\r\n\r\n  * `13 passed`\r\n* `node --check backend/static/app.js`\r\n* `python3 -m py_compile backend/*.py`\r\n\r\n## What's Changed\r\n* v1.9.1 disable route history end-to-end by @yellowcooln in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/69\r\n\r\n\r\n**Full Changelog**: https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.0...v1.9.1",
      "notesHtml": "<h3>Fixed</h3>\n<ul>\n<li><p>Fixed issue #68: <code>ROUTE_HISTORY_ENABLED=false</code> now disables Route History end-to-end instead of only stopping new history recording</p>\n</li>\n<li><p>When Route History is disabled:</p>\n<ul>\n<li>The History tool button is removed</li>\n<li>The History panel does not load</li>\n<li><code>history=on</code> in the URL no longer re-enables the feature</li>\n<li><code>/snapshot</code> no longer returns <code>history_edges</code> or a nonzero history window</li>\n<li>The realtime WebSocket snapshot no longer publishes history payloads</li>\n</ul>\n</li>\n</ul>\n<hr />\n<h3>Behavior Change</h3>\n<p><code>ROUTE_HISTORY_ENABLED</code> now behaves as a true feature flag for both:</p>\n<ul>\n<li>Backend history payloads</li>\n<li>Frontend History tool/UI</li>\n</ul>\n<p>This makes it practical to disable Route History entirely on:</p>\n<ul>\n<li>High-volume meshes</li>\n<li>Lower-resource deployments</li>\n</ul>\n<hr />\n<h3>Config</h3>\n<p>Use:</p>\n<p><code>ROUTE_HISTORY_ENABLED=false</code></p>\n<p>This now fully disables:</p>\n<ul>\n<li>History tool UI</li>\n<li>History snapshot payloads</li>\n<li>History panel activation</li>\n<li>Hidden frontend history state</li>\n</ul>\n<hr />\n<h3>Validation</h3>\n<ul>\n<li><p><code>pytest -q tests/test_api_auth.py tests/test_websocket_snapshot.py</code></p>\n<ul>\n<li><code>13 passed</code></li>\n</ul>\n</li>\n<li><p><code>node --check backend/static/app.js</code></p>\n</li>\n<li><p><code>python3 -m py_compile backend/*.py</code></p>\n</li>\n</ul>\n<h2>What's Changed</h2>\n<ul>\n<li>v1.9.1 disable route history end-to-end by @yellowcooln in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/69\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/69</a></li>\n</ul>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.0...v1.9.1\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.9.0...v1.9.1</a></p>\n"
    },
    {
      "version": "v1.9.0",
      "name": "v1.9.0 - Subpath Hosting, Direct Node Links, and Peer Distances",
      "datetime": "2026-05-06T18:15:56Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.9.0",
      "prerelease": false,
      "notes": "This release adds proper subpath hosting support and improves node sharing and peer visibility.\r\n\r\n---\r\n\r\n## Subpath Hosting\r\n\r\nAdded `APP_BASE_PATH`, allowing the map to be hosted from a subpath such as:\r\n\r\n`/livemap`\r\n\r\ninstead of only from `/`.\r\n\r\nThis work originated from:\r\n\r\n* PR #65 by @chrisdavis2110\r\n\r\n#### Covered Areas\r\n\r\n* App shell URLs\r\n* Static asset URLs\r\n* WebSocket path\r\n* Service worker registration and scope\r\n* Manifest URL, `start_url`, and scope\r\n* Turnstile verification flow\r\n* Auth cookie path\r\n* Preview/share image URLs\r\n\r\nThis release also includes follow-up fixes so preview and Open Graph image URLs work correctly when using both:\r\n\r\n* `APP_BASE_PATH`\r\n* Public site URLs\r\n\r\n---\r\n\r\n## Direct Node Links and Peer Distances\r\n\r\nThis work originated from:\r\n\r\n* PR #66 by @mitchellmoss\r\n\r\nNode popups now include direct map-link actions:\r\n\r\n* Copy node link\r\n* Copy repeater link\r\n\r\nThe generated links preserve current map state plus a target device.\r\n\r\nOpening a shared link now:\r\n\r\n* Automatically enables node visibility if hidden\r\n* Focuses the target device\r\n* Zooms to it\r\n* Opens its popup\r\n\r\n#### Supported Link Parameters\r\n\r\n* `node`\r\n* `repeater`\r\n* `device`\r\n* `device_id`\r\n* `public_key`\r\n* `pubkey`\r\n\r\n\r\nThe Peers tool now shows distance when both endpoints have coordinates.\r\n\r\n### Backend\r\n\r\n* `/peers/{device_id}` now includes:\r\n\r\n  * `distance_m`\r\n\r\n### Frontend\r\n\r\n* Renders peer distance using the currently selected unit system:\r\n\r\n  * Kilometers\r\n  * Miles\r\n\r\n\r\n---\r\n\r\n## Compatibility\r\n\r\nFor normal root-hosted installs:\r\n\r\n* No configuration changes required\r\n* Existing `/` deployments behave the same\r\n* `APP_BASE_PATH` remains dormant unless explicitly configured\r\n\r\n---\r\n\r\n## What's Changed\r\n* feat: APP_BASE_PATH config to set app as subpath by @chrisdavis2110 in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/65\r\n* [codex] Add direct node links and peer distances by @mitchellmoss in https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/66\r\n\r\n\r\n**Full Changelog**: https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.8.6...v1.9.0",
      "notesHtml": "<p>This release adds proper subpath hosting support and improves node sharing and peer visibility.</p>\n<hr />\n<h2>Subpath Hosting</h2>\n<p>Added <code>APP_BASE_PATH</code>, allowing the map to be hosted from a subpath such as:</p>\n<p><code>/livemap</code></p>\n<p>instead of only from <code>/</code>.</p>\n<p>This work originated from:</p>\n<ul>\n<li>PR #65 by @chrisdavis2110</li>\n</ul>\n<h4>Covered Areas</h4>\n<ul>\n<li>App shell URLs</li>\n<li>Static asset URLs</li>\n<li>WebSocket path</li>\n<li>Service worker registration and scope</li>\n<li>Manifest URL, <code>start_url</code>, and scope</li>\n<li>Turnstile verification flow</li>\n<li>Auth cookie path</li>\n<li>Preview/share image URLs</li>\n</ul>\n<p>This release also includes follow-up fixes so preview and Open Graph image URLs work correctly when using both:</p>\n<ul>\n<li><code>APP_BASE_PATH</code></li>\n<li>Public site URLs</li>\n</ul>\n<hr />\n<h2>Direct Node Links and Peer Distances</h2>\n<p>This work originated from:</p>\n<ul>\n<li>PR #66 by @mitchellmoss</li>\n</ul>\n<p>Node popups now include direct map-link actions:</p>\n<ul>\n<li>Copy node link</li>\n<li>Copy repeater link</li>\n</ul>\n<p>The generated links preserve current map state plus a target device.</p>\n<p>Opening a shared link now:</p>\n<ul>\n<li>Automatically enables node visibility if hidden</li>\n<li>Focuses the target device</li>\n<li>Zooms to it</li>\n<li>Opens its popup</li>\n</ul>\n<h4>Supported Link Parameters</h4>\n<ul>\n<li><code>node</code></li>\n<li><code>repeater</code></li>\n<li><code>device</code></li>\n<li><code>device_id</code></li>\n<li><code>public_key</code></li>\n<li><code>pubkey</code></li>\n</ul>\n<p>The Peers tool now shows distance when both endpoints have coordinates.</p>\n<h3>Backend</h3>\n<ul>\n<li><p><code>/peers/{device_id}</code> now includes:</p>\n<ul>\n<li><code>distance_m</code></li>\n</ul>\n</li>\n</ul>\n<h3>Frontend</h3>\n<ul>\n<li><p>Renders peer distance using the currently selected unit system:</p>\n<ul>\n<li>Kilometers</li>\n<li>Miles</li>\n</ul>\n</li>\n</ul>\n<hr />\n<h2>Compatibility</h2>\n<p>For normal root-hosted installs:</p>\n<ul>\n<li>No configuration changes required</li>\n<li>Existing <code>/</code> deployments behave the same</li>\n<li><code>APP_BASE_PATH</code> remains dormant unless explicitly configured</li>\n</ul>\n<hr />\n<h2>What's Changed</h2>\n<ul>\n<li>feat: APP_BASE_PATH config to set app as subpath by @chrisdavis2110 in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/65\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/65</a></li>\n<li>[codex] Add direct node links and peer distances by @mitchellmoss in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/66\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/pull/66</a></li>\n</ul>\n<p><strong>Full Changelog</strong>: <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.8.6...v1.9.0\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/compare/v1.8.6...v1.9.0</a></p>\n"
    },
    {
      "version": "v1.8.6",
      "name": "v1.8.6 - Route Filtering + Containerized Deployment (Multi-Arch)",
      "datetime": "2026-05-02T17:56:35Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.6",
      "prerelease": false,
      "notes": "This release combines the unreleased v1.8.5 container/deployment work with the new v1.8.6 route filtering feature.\r\n\r\n### Container and Deployment\r\n\r\n* Published image:\r\n\r\n  * `yellowcooln/meshcore-mqtt-live-map`\r\n* Multi-arch image support:\r\n\r\n  * `linux/amd64`\r\n  * `linux/arm64`\r\n* Added image-based deployment examples (no local build required):\r\n\r\n  * `deploy/docker-compose.image.yaml`\r\n  * `deploy/swarm-stack.yaml`\r\n  * `deploy/kubernetes-meshmap.yaml`\r\n* Adjusted Docker publish workflow:\r\n\r\n  * Docs-only changes no longer trigger rebuild/publish\r\n  * Image-relevant changes still trigger builds\r\n\r\n### New Route Filtering\r\n\r\n* Added issue #59: Path bytes filter in the HUD\r\n* Routes can now be filtered by decoded path-hash width:\r\n\r\n  * All\r\n  * 1-byte\r\n  * 2-byte\r\n  * 3-byte\r\n* Filter applies consistently to:\r\n\r\n  * Route lines\r\n  * Hop markers\r\n  * Route Details\r\n  * HUD route counts\r\n* Mixed-width paths remain visible if at least one hop matches the selected width\r\n* Share links now include:\r\n\r\n  * `route_bytes=all|1b|2b|3b`\r\n\r\n### Route Filter Stability Fixes\r\n\r\n* Fixed route redraw behavior so switching byte filters does not stack stale path lines\r\n* Default page loads now reset to **All** unless `route_bytes` is explicitly present in the URL\r\n\r\n  * Prevents stale browser state from hiding routes unexpectedly\r\n\r\n### Notes\r\n\r\n* Version metadata and documentation updated for v1.8.6\r\n* This release includes:\r\n\r\n  * v1.8.5 container/deployment work\r\n  * v1.8.6 path-byte route filtering",
      "notesHtml": "<p>This release combines the unreleased v1.8.5 container/deployment work with the new v1.8.6 route filtering feature.</p>\n<h3>Container and Deployment</h3>\n<ul>\n<li><p>Published image:</p>\n<ul>\n<li><code>yellowcooln/meshcore-mqtt-live-map</code></li>\n</ul>\n</li>\n<li><p>Multi-arch image support:</p>\n<ul>\n<li><code>linux/amd64</code></li>\n<li><code>linux/arm64</code></li>\n</ul>\n</li>\n<li><p>Added image-based deployment examples (no local build required):</p>\n<ul>\n<li><code>deploy/docker-compose.image.yaml</code></li>\n<li><code>deploy/swarm-stack.yaml</code></li>\n<li><code>deploy/kubernetes-meshmap.yaml</code></li>\n</ul>\n</li>\n<li><p>Adjusted Docker publish workflow:</p>\n<ul>\n<li>Docs-only changes no longer trigger rebuild/publish</li>\n<li>Image-relevant changes still trigger builds</li>\n</ul>\n</li>\n</ul>\n<h3>New Route Filtering</h3>\n<ul>\n<li><p>Added issue #59: Path bytes filter in the HUD</p>\n</li>\n<li><p>Routes can now be filtered by decoded path-hash width:</p>\n<ul>\n<li>All</li>\n<li>1-byte</li>\n<li>2-byte</li>\n<li>3-byte</li>\n</ul>\n</li>\n<li><p>Filter applies consistently to:</p>\n<ul>\n<li>Route lines</li>\n<li>Hop markers</li>\n<li>Route Details</li>\n<li>HUD route counts</li>\n</ul>\n</li>\n<li><p>Mixed-width paths remain visible if at least one hop matches the selected width</p>\n</li>\n<li><p>Share links now include:</p>\n<ul>\n<li><code>route_bytes=all|1b|2b|3b</code></li>\n</ul>\n</li>\n</ul>\n<h3>Route Filter Stability Fixes</h3>\n<ul>\n<li><p>Fixed route redraw behavior so switching byte filters does not stack stale path lines</p>\n</li>\n<li><p>Default page loads now reset to <strong>All</strong> unless <code>route_bytes</code> is explicitly present in the URL</p>\n<ul>\n<li>Prevents stale browser state from hiding routes unexpectedly</li>\n</ul>\n</li>\n</ul>\n<h3>Notes</h3>\n<ul>\n<li><p>Version metadata and documentation updated for v1.8.6</p>\n</li>\n<li><p>This release includes:</p>\n<ul>\n<li>v1.8.5 container/deployment work</li>\n<li>v1.8.6 path-byte route filtering</li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.8.4",
      "name": "v1.8.4 - Route Rendering Restore, Panel Minimization, and LOS Fresnel Guidance",
      "datetime": "2026-04-17T19:15:05Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.4",
      "prerelease": false,
      "notes": "This release restores route rendering on meshes affected by a regression introduced after `v1.7.0`, adds mobile-friendly panel minimization, and brings Fresnel-zone guidance into the LOS profile.\r\n\r\n### Routing Fix\r\n\r\n- Fixed the post-`v1.7.0` regression where some networks stopped showing routes that had previously rendered in `v1.6.6`\r\n- The regression came from stricter handling of ambiguous 1-byte hop prefixes in decoded path hashes\r\n- By default, the map still handles colliding 1-byte prefixes conservatively to avoid incorrect hop guesses on larger meshes\r\n- Added a new environment variable: `ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK`\r\n- Default: `false`\r\n- When set to `true`, this restores the older pre-`v1.7.0` closest/time-based fallback for ambiguous 1-byte hop prefixes\r\n- Use this if your mesh has valid routes that stopped rendering because multiple nodes share the same first-byte prefix and the conservative resolver is too strict for your network\r\n- This environment variable is documented in the docs, included in `.env.example`, and passed through `docker-compose.yaml`\r\n\r\n### New Features\r\n\r\n- Issue #55: LOS and Propagation panels can now be minimized without turning the tool off\r\n- Issue #55: the same minimize/expand control was extended to History, Peers, and Route Details so panels can get out of the way while the feature stays active\r\n- Issue #56: the LOS elevation profile now draws upper and lower Fresnel-zone lines, similar to the MeshCore app\r\n- Issue #56: LOS profile hover now shows Fresnel radius alongside terrain and LOS height\r\n- Fresnel rendering is segment-local for multi-pin LOS routes, so chained LOS paths do not get incorrect full-route Fresnel curves\r\n\r\n### Follow-up Fixes\r\n\r\n- Fixed the panel regression introduced during the new minimize-control work\r\n- History sliders continue to work\r\n- Peers opens correctly again\r\n- Route Details now clears when **Show hops** is turned off\r\n- LOS point selection works again\r\n- Propagation opens correctly again\r\n\r\n### Configuration\r\n\r\nNew environment variable in this release:\r\n\r\n- `ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK=false`\r\n\r\nPurpose:\r\n\r\n- restores legacy route fallback for colliding 1-byte hop prefixes when needed\r\n\r\nRecommended default:\r\n\r\n- keep it `false` unless your mesh specifically lost route rendering after the stricter prefix logic\r\n\r\n### Notes\r\n\r\n- Version metadata and release notes were updated for `v1.8.4`\r\n- The `dev` branch includes the route fix, the new panel controls, LOS Fresnel lines, and the follow-up regression fixes",
      "notesHtml": "<p>This release restores route rendering on meshes affected by a regression introduced after <code>v1.7.0</code>, adds mobile-friendly panel minimization, and brings Fresnel-zone guidance into the LOS profile.</p>\n<h3>Routing Fix</h3>\n<ul>\n<li>Fixed the post-<code>v1.7.0</code> regression where some networks stopped showing routes that had previously rendered in <code>v1.6.6</code></li>\n<li>The regression came from stricter handling of ambiguous 1-byte hop prefixes in decoded path hashes</li>\n<li>By default, the map still handles colliding 1-byte prefixes conservatively to avoid incorrect hop guesses on larger meshes</li>\n<li>Added a new environment variable: <code>ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK</code></li>\n<li>Default: <code>false</code></li>\n<li>When set to <code>true</code>, this restores the older pre-<code>v1.7.0</code> closest/time-based fallback for ambiguous 1-byte hop prefixes</li>\n<li>Use this if your mesh has valid routes that stopped rendering because multiple nodes share the same first-byte prefix and the conservative resolver is too strict for your network</li>\n<li>This environment variable is documented in the docs, included in <code>.env.example</code>, and passed through <code>docker-compose.yaml</code></li>\n</ul>\n<h3>New Features</h3>\n<ul>\n<li>Issue #55: LOS and Propagation panels can now be minimized without turning the tool off</li>\n<li>Issue #55: the same minimize/expand control was extended to History, Peers, and Route Details so panels can get out of the way while the feature stays active</li>\n<li>Issue #56: the LOS elevation profile now draws upper and lower Fresnel-zone lines, similar to the MeshCore app</li>\n<li>Issue #56: LOS profile hover now shows Fresnel radius alongside terrain and LOS height</li>\n<li>Fresnel rendering is segment-local for multi-pin LOS routes, so chained LOS paths do not get incorrect full-route Fresnel curves</li>\n</ul>\n<h3>Follow-up Fixes</h3>\n<ul>\n<li>Fixed the panel regression introduced during the new minimize-control work</li>\n<li>History sliders continue to work</li>\n<li>Peers opens correctly again</li>\n<li>Route Details now clears when <strong>Show hops</strong> is turned off</li>\n<li>LOS point selection works again</li>\n<li>Propagation opens correctly again</li>\n</ul>\n<h3>Configuration</h3>\n<p>New environment variable in this release:</p>\n<ul>\n<li><code>ROUTE_ALLOW_AMBIGUOUS_ONE_BYTE_FALLBACK=false</code></li>\n</ul>\n<p>Purpose:</p>\n<ul>\n<li>restores legacy route fallback for colliding 1-byte hop prefixes when needed</li>\n</ul>\n<p>Recommended default:</p>\n<ul>\n<li>keep it <code>false</code> unless your mesh specifically lost route rendering after the stricter prefix logic</li>\n</ul>\n<h3>Notes</h3>\n<ul>\n<li>Version metadata and release notes were updated for <code>v1.8.4</code></li>\n<li>The <code>dev</code> branch includes the route fix, the new panel controls, LOS Fresnel lines, and the follow-up regression fixes</li>\n</ul>\n"
    },
    {
      "version": "v1.8.3",
      "name": "v1.8.3 - Earth Curvature LOS Fixes and Config Cleanup",
      "datetime": "2026-04-16T18:21:42Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.3",
      "prerelease": false,
      "notes": "This release fixes the LOS tool so it no longer ignores Earth curvature, and it cleans up the related configuration and documentation.\r\n\r\n### LOS Fixes\r\n\r\n- Fixed issue #53: the LOS tool now accounts for Earth curvature instead of using a purely straight terrain-versus-line check\r\n- Applied the same curvature-aware LOS math to both the frontend realtime LOS path and the backend `/los` fallback so live interaction and server responses stay consistent\r\n- Updated LOS blockage and elevation-profile calculations to use curvature-adjusted terrain samples, which can now correctly mark longer paths as blocked where the previous LOS tool showed clear\r\n\r\n### New LOS Environment Controls\r\n\r\n- Added `LOS_CURVATURE_ENABLED`\r\n- Added `LOS_CURVATURE_FACTOR`\r\n\r\nDefaults when unset:\r\n\r\n- `LOS_CURVATURE_ENABLED=true`\r\n- `LOS_CURVATURE_FACTOR=1.333333`\r\n\r\n### Config and Runtime Fixes\r\n\r\n- Fixed `docker-compose.yaml` so all LOS-related environment variables are actually passed into the container:\r\n  - `LOS_ELEVATION_URL`\r\n  - `LOS_ELEVATION_PROXY_URL`\r\n  - `LOS_SAMPLE_MIN`\r\n  - `LOS_SAMPLE_MAX`\r\n  - `LOS_SAMPLE_STEP_METERS`\r\n  - `ELEVATION_CACHE_TTL`\r\n  - `LOS_CURVATURE_ENABLED`\r\n  - `LOS_CURVATURE_FACTOR`\r\n  - `LOS_PEAKS_MAX`\r\n\r\n### Docs\r\n\r\n- Updated `.env.example` with the new LOS curvature environment variables\r\n- Clarified MQTT authentication docs for `meshcore-mqtt-broker`: the live map normally uses a broker `SUBSCRIBER_N` username/password pair, not node-style signed publisher auth\r\n- Updated version, docs, and changelog for `v1.8.3`\r\n\r\n### Validation\r\n\r\n- `pytest -q` → `92 passed, 2 skipped`\r\n- `node --check backend/static/app.js`\r\n- `python3 -m py_compile backend/*.py`",
      "notesHtml": "<p>This release fixes the LOS tool so it no longer ignores Earth curvature, and it cleans up the related configuration and documentation.</p>\n<h3>LOS Fixes</h3>\n<ul>\n<li>Fixed issue #53: the LOS tool now accounts for Earth curvature instead of using a purely straight terrain-versus-line check</li>\n<li>Applied the same curvature-aware LOS math to both the frontend realtime LOS path and the backend <code>/los</code> fallback so live interaction and server responses stay consistent</li>\n<li>Updated LOS blockage and elevation-profile calculations to use curvature-adjusted terrain samples, which can now correctly mark longer paths as blocked where the previous LOS tool showed clear</li>\n</ul>\n<h3>New LOS Environment Controls</h3>\n<ul>\n<li>Added <code>LOS_CURVATURE_ENABLED</code></li>\n<li>Added <code>LOS_CURVATURE_FACTOR</code></li>\n</ul>\n<p>Defaults when unset:</p>\n<ul>\n<li><code>LOS_CURVATURE_ENABLED=true</code></li>\n<li><code>LOS_CURVATURE_FACTOR=1.333333</code></li>\n</ul>\n<h3>Config and Runtime Fixes</h3>\n<ul>\n<li>Fixed <code>docker-compose.yaml</code> so all LOS-related environment variables are actually passed into the container:<ul>\n<li><code>LOS_ELEVATION_URL</code></li>\n<li><code>LOS_ELEVATION_PROXY_URL</code></li>\n<li><code>LOS_SAMPLE_MIN</code></li>\n<li><code>LOS_SAMPLE_MAX</code></li>\n<li><code>LOS_SAMPLE_STEP_METERS</code></li>\n<li><code>ELEVATION_CACHE_TTL</code></li>\n<li><code>LOS_CURVATURE_ENABLED</code></li>\n<li><code>LOS_CURVATURE_FACTOR</code></li>\n<li><code>LOS_PEAKS_MAX</code></li>\n</ul>\n</li>\n</ul>\n<h3>Docs</h3>\n<ul>\n<li>Updated <code>.env.example</code> with the new LOS curvature environment variables</li>\n<li>Clarified MQTT authentication docs for <code>meshcore-mqtt-broker</code>: the live map normally uses a broker <code>SUBSCRIBER_N</code> username/password pair, not node-style signed publisher auth</li>\n<li>Updated version, docs, and changelog for <code>v1.8.3</code></li>\n</ul>\n<h3>Validation</h3>\n<ul>\n<li><code>pytest -q</code> → <code>92 passed, 2 skipped</code></li>\n<li><code>node --check backend/static/app.js</code></li>\n<li><code>python3 -m py_compile backend/*.py</code></li>\n</ul>\n"
    },
    {
      "version": "v1.8.2",
      "name": "v1.8.2 Node QR Sharing, Coverage Performance Fixes, and Map Behavior Updates",
      "datetime": "2026-04-10T03:47:09Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.2",
      "prerelease": false,
      "notes": "## Release Summary\r\n\r\nThis release adds node QR and contact sharing support, and fixes several map behavior issues.\r\n\r\n### New\r\n\r\n- Added optional MeshCore-compatible contact QR codes in node popups\r\n- Added `QR_CODE_BUTTON_ENABLED=true` to show a **Generate QR Code** action\r\n- The modal shows a clickable truncated public key while still copying the full key\r\n- Added local `/qr` PNG generation with existing prod-token protection\r\n\r\n<img width=\"802\" height=\"566\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7bfc6e9e-e6a7-4667-921d-75a4f7b1914e\" />\r\n\r\n### Fixes\r\n\r\n- Fixed the popup key flow so the short key shown under the node name is the copy target, without duplicating the full key elsewhere in the popup\r\n- Fixed MeshMapper coverage lag by reusing cached coverage rectangles during pan and zoom instead of rebuilding them on every viewport change\r\n- Fixed peer counts so `/peers/{device_id}` still reflects adjacency from route `point_id`s even when a hop cannot be drawn on the map\r\n- Fixed stale cleanup so nodes that are still MQTT-online keep their last known map position until MQTT presence expires",
      "notesHtml": "<h2>Release Summary</h2>\n<p>This release adds node QR and contact sharing support, and fixes several map behavior issues.</p>\n<h3>New</h3>\n<ul>\n<li>Added optional MeshCore-compatible contact QR codes in node popups</li>\n<li>Added <code>QR_CODE_BUTTON_ENABLED=true</code> to show a <strong>Generate QR Code</strong> action</li>\n<li>The modal shows a clickable truncated public key while still copying the full key</li>\n<li>Added local <code>/qr</code> PNG generation with existing prod-token protection</li>\n</ul>\n<img alt=\"image\" src=\"https://github.com/user-attachments/assets/7bfc6e9e-e6a7-4667-921d-75a4f7b1914e\" /><h3>Fixes</h3>\n<ul>\n<li>Fixed the popup key flow so the short key shown under the node name is the copy target, without duplicating the full key elsewhere in the popup</li>\n<li>Fixed MeshMapper coverage lag by reusing cached coverage rectangles during pan and zoom instead of rebuilding them on every viewport change</li>\n<li>Fixed peer counts so <code>/peers/{device_id}</code> still reflects adjacency from route <code>point_id</code>s even when a hop cannot be drawn on the map</li>\n<li>Fixed stale cleanup so nodes that are still MQTT-online keep their last known map position until MQTT presence expires</li>\n</ul>\n"
    },
    {
      "version": "v1.8.1",
      "name": "v1.8.1 - Issue Fixes and Arcade Route Visualization",
      "datetime": "2026-04-07T02:11:24Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.1",
      "prerelease": false,
      "notes": "This release resolves the current open issue set and improves live route visualization.\r\n\r\n### Fixes\r\n\r\n- Fixed ghost duplicate nodes by tightening device identity handling to use the advert owner public key for location updates\r\n- Added startup cleanup for persisted duplicate nodes, so stale same-name, same-location ghost entries are removed after restart\r\n- Fixed MeshMapper coverage tiles shifting incorrectly when zooming or panning by rebuilding visible coverage from cached source data on viewport updates\r\n- Removed the hard peer-list cap, so `/peers/{device_id}?limit=` now accepts any positive value\r\n\r\n### New\r\n\r\n- Added the upstream-style hidden arcade mode for live route flow visualization\r\n- Arcade mode can be activated by typing `/waka` or `/arcade` in the search box and pressing Enter",
      "notesHtml": "<p>This release resolves the current open issue set and improves live route visualization.</p>\n<h3>Fixes</h3>\n<ul>\n<li>Fixed ghost duplicate nodes by tightening device identity handling to use the advert owner public key for location updates</li>\n<li>Added startup cleanup for persisted duplicate nodes, so stale same-name, same-location ghost entries are removed after restart</li>\n<li>Fixed MeshMapper coverage tiles shifting incorrectly when zooming or panning by rebuilding visible coverage from cached source data on viewport updates</li>\n<li>Removed the hard peer-list cap, so <code>/peers/{device_id}?limit=</code> now accepts any positive value</li>\n</ul>\n<h3>New</h3>\n<ul>\n<li>Added the upstream-style hidden arcade mode for live route flow visualization</li>\n<li>Arcade mode can be activated by typing <code>/waka</code> or <code>/arcade</code> in the search box and pressing Enter</li>\n</ul>\n"
    },
    {
      "version": "v1.8.0",
      "name": "v1.8.0 - Automatic Backups, Chained LOS Paths, and Official Decoder Restore",
      "datetime": "2026-03-29T20:32:31Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.8.0",
      "prerelease": false,
      "notes": "This release adds automatic backups, expands the LOS tool into a chained path workflow, restores the official MeshCore decoder, and improves MeshMapper coverage behavior.\r\n\r\n### Highlights\r\n\r\n- Added automatic runtime backups as timestamped `.tar.gz` archives\r\n- Expanded the LOS tool to support multiple chained pins and full-path elevation profiles\r\n- Switched the runtime decoder back to the official `@michaelhart/meshcore-decoder`\r\n- Improved MeshMapper coverage rendering to better match the displayed coverage footprint\r\n- Added backup tests covering archive creation and retention pruning\r\n- Full test suite passes with `84 passed, 2 skipped`\r\n\r\n### Backups\r\n\r\nAutomatic runtime backups are now supported as timestamped `.tar.gz` archives. This release also adds backup-specific environment controls, changes the default backup location to `/backup`, adds retention pruning for older archives, and mounts `./backup:/backup` in `docker-compose.yaml`.\r\n\r\nBackups include live-state files such as:\r\n\r\n- `state.json`\r\n- `device_roles.json`\r\n- `device_coords.json`\r\n- `neighbor_overrides.json`\r\n- `channel_secrets.json`\r\n- `map_boundary.json`\r\n- `route_history.jsonl`\r\n- `coverage_cache.json`\r\n\r\nNew backup environment variables:\r\n\r\n- `BACKUP_ENABLED`\r\n- `BACKUP_INTERVAL_SECONDS`\r\n- `BACKUP_DIR`\r\n- `BACKUP_RETENTION_DAYS`\r\n\r\n### LOS Tool\r\n\r\nThe LOS tool now supports multiple chained pins as part of issue #41. Elevation profiles span the full chained path instead of only the active segment, height inputs are stored per pin, and intermediate pin movement now correctly recalculates both adjacent segments.\r\n\r\nThis release also removes the old `Keep A` LOS workflow, adds `Remove last pin`, and updates helper text and labels to match the chained-pin model.\r\n\r\n### Decoder\r\n\r\nThe runtime decoder has been switched back to the official `@michaelhart/meshcore-decoder`, which now supports the multibyte path decoding needed by the map.\r\n\r\n### MeshMapper Coverage\r\n\r\nMeshMapper coverage rendering now expands returned squares to better match MeshMapper’s displayed footprint. Styling was also tuned to keep the fuller footprint while making coverage easier to read.",
      "notesHtml": "<p>This release adds automatic backups, expands the LOS tool into a chained path workflow, restores the official MeshCore decoder, and improves MeshMapper coverage behavior.</p>\n<h3>Highlights</h3>\n<ul>\n<li>Added automatic runtime backups as timestamped <code>.tar.gz</code> archives</li>\n<li>Expanded the LOS tool to support multiple chained pins and full-path elevation profiles</li>\n<li>Switched the runtime decoder back to the official <code>@michaelhart/meshcore-decoder</code></li>\n<li>Improved MeshMapper coverage rendering to better match the displayed coverage footprint</li>\n<li>Added backup tests covering archive creation and retention pruning</li>\n<li>Full test suite passes with <code>84 passed, 2 skipped</code></li>\n</ul>\n<h3>Backups</h3>\n<p>Automatic runtime backups are now supported as timestamped <code>.tar.gz</code> archives. This release also adds backup-specific environment controls, changes the default backup location to <code>/backup</code>, adds retention pruning for older archives, and mounts <code>./backup:/backup</code> in <code>docker-compose.yaml</code>.</p>\n<p>Backups include live-state files such as:</p>\n<ul>\n<li><code>state.json</code></li>\n<li><code>device_roles.json</code></li>\n<li><code>device_coords.json</code></li>\n<li><code>neighbor_overrides.json</code></li>\n<li><code>channel_secrets.json</code></li>\n<li><code>map_boundary.json</code></li>\n<li><code>route_history.jsonl</code></li>\n<li><code>coverage_cache.json</code></li>\n</ul>\n<p>New backup environment variables:</p>\n<ul>\n<li><code>BACKUP_ENABLED</code></li>\n<li><code>BACKUP_INTERVAL_SECONDS</code></li>\n<li><code>BACKUP_DIR</code></li>\n<li><code>BACKUP_RETENTION_DAYS</code></li>\n</ul>\n<h3>LOS Tool</h3>\n<p>The LOS tool now supports multiple chained pins as part of issue #41. Elevation profiles span the full chained path instead of only the active segment, height inputs are stored per pin, and intermediate pin movement now correctly recalculates both adjacent segments.</p>\n<p>This release also removes the old <code>Keep A</code> LOS workflow, adds <code>Remove last pin</code>, and updates helper text and labels to match the chained-pin model.</p>\n<h3>Decoder</h3>\n<p>The runtime decoder has been switched back to the official <code>@michaelhart/meshcore-decoder</code>, which now supports the multibyte path decoding needed by the map.</p>\n<h3>MeshMapper Coverage</h3>\n<p>MeshMapper coverage rendering now expands returned squares to better match MeshMapper’s displayed footprint. Styling was also tuned to keep the fuller footprint while making coverage easier to read.</p>\n"
    },
    {
      "version": "v1.7.8.1",
      "name": "v1.7.8.1 - Peers Clear Fix",
      "datetime": "2026-03-25T00:11:46Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.7.8.1",
      "prerelease": false,
      "notes": "## v1.7.8.1\r\n\r\n  This release fixes issue #38 in the Peers tool.\r\n\r\n### Changes\r\n- Fixed issue #38: the `Clear peers` action now removes both incoming and outgoing peer lines even when the same peer appears in both directions\r\n\r\n### Why this release\r\nThe Peers tool tracked lines by peer ID only. When the same peer appeared in both incoming and outgoing lists, one line could overwrite the other in the internal line map, so clearing peers did not remove everything.",
      "notesHtml": "<h2>v1.7.8.1</h2>\n<p>  This release fixes issue #38 in the Peers tool.</p>\n<h3>Changes</h3>\n<ul>\n<li>Fixed issue #38: the <code>Clear peers</code> action now removes both incoming and outgoing peer lines even when the same peer appears in both directions</li>\n</ul>\n<h3>Why this release</h3>\n<p>The Peers tool tracked lines by peer ID only. When the same peer appeared in both incoming and outgoing lists, one line could overwrite the other in the internal line map, so clearing peers did not remove everything.</p>\n"
    },
    {
      "version": "v1.7.8",
      "name": "v1.7.8 - MeshMapper Coverage Legend and Browser Performance Improvements",
      "datetime": "2026-03-24T19:42:25Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.7.8",
      "prerelease": false,
      "notes": "## v1.7.8\r\n\r\nThis release improves MeshMapper coverage presentation and reduces browser rendering overhead, especially on larger maps and mobile devices.\r\n\r\n### Changes\r\n\r\n- Adjusted MeshMapper coverage rendering to better match the native MeshMapper look:\r\n  - Removed visible square borders\r\n  - Increased fill density for a more solid coverage blob\r\n- Added a MeshMapper coverage legend in the HUD:\r\n  - Shown only when Coverage is enabled and the active provider is MeshMapper\r\n  - Includes:\r\n    - `BIDIR`\r\n    - `DISC / TRACE`\r\n    - `TX`\r\n    - `RX`\r\n    - `DEAD`\r\n    - `DROP`\r\n- Improved browser-side rendering performance:\r\n  - Enabled Leaflet canvas-backed rendering where it does not change visible behavior\r\n  - Kept animated route and trail lines on SVG so existing visual motion remains unchanged\r\n  - Added viewport culling for off-screen nodes and coverage tiles\r\n  - Batched realtime WebSocket updates using `requestAnimationFrame`\r\n  - Batched stats updates to avoid repeated recalculation during burst traffic\r\n\r\n### Notes\r\n\r\n- These performance changes are browser-side only and do not increase server load\r\n- MeshMapper rendering changes apply only when the Coverage provider is MeshMapper\r\n- Legacy coverage rendering remains unchanged\r\n\r\n### Docs\r\n\r\n- Clarified that `MQTT_TOPIC` supports comma-separated values for subscribing to multiple topic trees, for example:\r\n  - `meshcore/BOS/#,meshcore/CON/#`",
      "notesHtml": "<h2>v1.7.8</h2>\n<p>This release improves MeshMapper coverage presentation and reduces browser rendering overhead, especially on larger maps and mobile devices.</p>\n<h3>Changes</h3>\n<ul>\n<li>Adjusted MeshMapper coverage rendering to better match the native MeshMapper look:<ul>\n<li>Removed visible square borders</li>\n<li>Increased fill density for a more solid coverage blob</li>\n</ul>\n</li>\n<li>Added a MeshMapper coverage legend in the HUD:<ul>\n<li>Shown only when Coverage is enabled and the active provider is MeshMapper</li>\n<li>Includes:<ul>\n<li><code>BIDIR</code></li>\n<li><code>DISC / TRACE</code></li>\n<li><code>TX</code></li>\n<li><code>RX</code></li>\n<li><code>DEAD</code></li>\n<li><code>DROP</code></li>\n</ul>\n</li>\n</ul>\n</li>\n<li>Improved browser-side rendering performance:<ul>\n<li>Enabled Leaflet canvas-backed rendering where it does not change visible behavior</li>\n<li>Kept animated route and trail lines on SVG so existing visual motion remains unchanged</li>\n<li>Added viewport culling for off-screen nodes and coverage tiles</li>\n<li>Batched realtime WebSocket updates using <code>requestAnimationFrame</code></li>\n<li>Batched stats updates to avoid repeated recalculation during burst traffic</li>\n</ul>\n</li>\n</ul>\n<h3>Notes</h3>\n<ul>\n<li>These performance changes are browser-side only and do not increase server load</li>\n<li>MeshMapper rendering changes apply only when the Coverage provider is MeshMapper</li>\n<li>Legacy coverage rendering remains unchanged</li>\n</ul>\n<h3>Docs</h3>\n<ul>\n<li>Clarified that <code>MQTT_TOPIC</code> supports comma-separated values for subscribing to multiple topic trees, for example:<ul>\n<li><code>meshcore/BOS/#,meshcore/CON/#</code></li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.7.7",
      "name": "v1.7.7 - Optional Polygon Boundary Mode and Boundary Builder",
      "datetime": "2026-03-23T01:38:07Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.7.7",
      "prerelease": false,
      "notes": "## v1.7.7\r\n\r\nThis release adds optional polygon-based map boundaries while keeping the existing radius filter as the default.\r\n\r\n### Changes\r\n\r\n* Added `MAP_BOUNDARY_MODE` with support for:\r\n\r\n  * `radius` (default)\r\n  * `polygon`\r\n* Added `MAP_BOUNDARY_FILE` for loading polygon boundary JSON\r\n* Added `MAP_BOUNDARY_SHOW` to control boundary overlay visibility\r\n* Polygon boundaries now apply consistently to:\r\n\r\n  * devices\r\n  * routes\r\n  * history\r\n* Added frontend polygon overlay rendering from backend-injected boundary JSON\r\n* Added `map_boundary.example.json` as the reference schema\r\n* Added a standalone boundary builder:\r\n\r\n  * `tools/map-boundary-builder.html`\r\n* Added hosted boundary builder:\r\n\r\n  * [https://yellowcooln.com/map-boundary-builder/](https://yellowcooln.com/map-boundary-builder/)\r\n\r\n---\r\n\r\n### Boundary JSON Format\r\n\r\n```json\r\n{\r\n  \"name\": \"Example Boundary\",\r\n  \"points\": [\r\n    [42.4705, -71.2902],\r\n    [42.4112, -71.4046],\r\n    [42.2987, -71.3651]\r\n  ]\r\n}\r\n```\r\n\r\n---\r\n\r\n### Example Config\r\n\r\n```env\r\nMAP_BOUNDARY_MODE=polygon\r\nMAP_BOUNDARY_FILE=/data/map_boundary.json\r\nMAP_BOUNDARY_SHOW=true\r\n```\r\n\r\n---\r\n\r\n### Notes\r\n\r\n* Default behavior is unchanged unless `MAP_BOUNDARY_MODE=polygon` is set\r\n* Radius mode remains the fallback/default\r\n* The standalone builder is not part of the live app UI; it is a separate utility for generating boundary JSON files\r\n\r\n---\r\n\r\n### Testing\r\n\r\n* Added boundary module tests\r\n* Added frontend config injection coverage for boundary mode\r\n* Test suite passing:\r\n\r\n  * `82 passed, 2 skipped`",
      "notesHtml": "<h2>v1.7.7</h2>\n<p>This release adds optional polygon-based map boundaries while keeping the existing radius filter as the default.</p>\n<h3>Changes</h3>\n<ul>\n<li><p>Added <code>MAP_BOUNDARY_MODE</code> with support for:</p>\n<ul>\n<li><code>radius</code> (default)</li>\n<li><code>polygon</code></li>\n</ul>\n</li>\n<li><p>Added <code>MAP_BOUNDARY_FILE</code> for loading polygon boundary JSON</p>\n</li>\n<li><p>Added <code>MAP_BOUNDARY_SHOW</code> to control boundary overlay visibility</p>\n</li>\n<li><p>Polygon boundaries now apply consistently to:</p>\n<ul>\n<li>devices</li>\n<li>routes</li>\n<li>history</li>\n</ul>\n</li>\n<li><p>Added frontend polygon overlay rendering from backend-injected boundary JSON</p>\n</li>\n<li><p>Added <code>map_boundary.example.json</code> as the reference schema</p>\n</li>\n<li><p>Added a standalone boundary builder:</p>\n<ul>\n<li><code>tools/map-boundary-builder.html</code></li>\n</ul>\n</li>\n<li><p>Added hosted boundary builder:</p>\n<ul>\n<li><a href=\"https://yellowcooln.com/map-boundary-builder/\" target=\"_blank\" rel=\"noopener noreferrer\">https://yellowcooln.com/map-boundary-builder/</a></li>\n</ul>\n</li>\n</ul>\n<hr />\n<h3>Boundary JSON Format</h3>\n<pre><code>{\n  \"name\": \"Example Boundary\",\n  \"points\": [\n    [42.4705, -71.2902],\n    [42.4112, -71.4046],\n    [42.2987, -71.3651]\n  ]\n}\n</code></pre>\n<hr />\n<h3>Example Config</h3>\n<pre><code>MAP_BOUNDARY_MODE=polygon\nMAP_BOUNDARY_FILE=/data/map_boundary.json\nMAP_BOUNDARY_SHOW=true\n</code></pre>\n<hr />\n<h3>Notes</h3>\n<ul>\n<li>Default behavior is unchanged unless <code>MAP_BOUNDARY_MODE=polygon</code> is set</li>\n<li>Radius mode remains the fallback/default</li>\n<li>The standalone builder is not part of the live app UI; it is a separate utility for generating boundary JSON files</li>\n</ul>\n<hr />\n<h3>Testing</h3>\n<ul>\n<li><p>Added boundary module tests</p>\n</li>\n<li><p>Added frontend config injection coverage for boundary mode</p>\n</li>\n<li><p>Test suite passing:</p>\n<ul>\n<li><code>82 passed, 2 skipped</code></li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.7.6",
      "name": "v1.7.6 - Configurable Peers Default Limit",
      "datetime": "2026-03-22T23:54:34Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.7.6",
      "prerelease": false,
      "notes": "## v1.7.6\r\n\r\nThis release makes the Peers tool default list size configurable via environment variable. #31 \r\n\r\n### Changes\r\n\r\n- Added `PEERS_DEFAULT_LIMIT`\r\n- `/peers/{device_id}` now uses `PEERS_DEFAULT_LIMIT` when no `?limit=` query is provided\r\n- Explicit `?limit=` still overrides the environment default\r\n- Both the environment default and query override are clamped to a maximum of `50`\r\n- Updated example env, compose, and docs to include the new setting\r\n\r\n### Example\r\n\r\n```env\r\nPEERS_DEFAULT_LIMIT=25\r\n```\r\n\r\n### Notes\r\nDefault remains 8 if the environment variable is not set\r\nThis change only affects the default number of peers returned and does not impact peer counting logic",
      "notesHtml": "<h2>v1.7.6</h2>\n<p>This release makes the Peers tool default list size configurable via environment variable. #31 </p>\n<h3>Changes</h3>\n<ul>\n<li>Added <code>PEERS_DEFAULT_LIMIT</code></li>\n<li><code>/peers/{device_id}</code> now uses <code>PEERS_DEFAULT_LIMIT</code> when no <code>?limit=</code> query is provided</li>\n<li>Explicit <code>?limit=</code> still overrides the environment default</li>\n<li>Both the environment default and query override are clamped to a maximum of <code>50</code></li>\n<li>Updated example env, compose, and docs to include the new setting</li>\n</ul>\n<h3>Example</h3>\n<pre><code>PEERS_DEFAULT_LIMIT=25\n</code></pre>\n<h3>Notes</h3>\n<p>Default remains 8 if the environment variable is not set\nThis change only affects the default number of peers returned and does not impact peer counting logic</p>\n"
    },
    {
      "version": "v.1.7.5",
      "name": "v1.7.5 - Route Details, Channel Secrets, and Packet Analyzer Links",
      "datetime": "2026-03-22T22:31:42Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v.1.7.5",
      "prerelease": false,
      "notes": "This release improves Route Details, adds channel secret file support for decrypting sender names, and introduces optional packet analyzer links.\r\n\r\n### Changes\r\n\r\n- Added `CHANNEL_SECRETS_FILE` support for loading channel secrets from a file instead of a long environment variable\r\n- Added a shipped `channel_secrets.example.json` with example channel entries\r\n- Replaced per-packet Node decoder process spawning with a persistent decoder worker\r\n- Reused the loaded channel key store across packets to reduce decode overhead\r\n- Route Details can now show a display-only sender-name row as hop `0` when the packet includes a decrypted sender name but not a stable sender identity\r\n- Route Details now preserves companion endpoints more accurately, including non-spatial endpoint rows for companions without GPS coordinates\r\n- Route Details hop counts now match the visible rows when sender companion rows are present\r\n- Route Details now shows the full packet hash in the header instead of truncating it\r\n- Route Details now prefers the MQTT packet hash over a shorter decoder `messageHash`\r\n- Added optional `PACKET_ANALYZER_URL` so packet hashes in Route Details can link to an external analyzer\r\n- Fixed the production route payload so Route Details keeps the packet hash and sender name instead of falling back to the internal `hash:receiver` route ID\r\n- Made the node popup `Location:` line clickable to copy `Location: <lat>, <lon>` to the clipboard\r\n\r\n### New ENVs\r\n\r\n\r\nOptional channel secrets file (for decrypting sender names from supported group-text packets):\r\n\r\n```env\r\nCHANNEL_SECRETS_FILE=/data/channel_secrets.json\r\n```\r\n\r\nCopy `channel_secrets.example.json` to your chosen path and keep only the channels you want to ship.\r\n\r\nOptional packet analyzer link base (used for Route Details hashes):\r\n\r\n```env\r\nPACKET_ANALYZER_URL=https://analyzer.letsmesh.net/packets?packet_hash=\r\n```\r\n\r\n\r\n### Testing\r\n\r\n- Added channel secret decoder coverage\r\n- Added route identity regression coverage for packet hash preference and production payload behavior\r\n- Test suite passing:\r\n  - `76 passed, 2 skipped`",
      "notesHtml": "<p>This release improves Route Details, adds channel secret file support for decrypting sender names, and introduces optional packet analyzer links.</p>\n<h3>Changes</h3>\n<ul>\n<li>Added <code>CHANNEL_SECRETS_FILE</code> support for loading channel secrets from a file instead of a long environment variable</li>\n<li>Added a shipped <code>channel_secrets.example.json</code> with example channel entries</li>\n<li>Replaced per-packet Node decoder process spawning with a persistent decoder worker</li>\n<li>Reused the loaded channel key store across packets to reduce decode overhead</li>\n<li>Route Details can now show a display-only sender-name row as hop <code>0</code> when the packet includes a decrypted sender name but not a stable sender identity</li>\n<li>Route Details now preserves companion endpoints more accurately, including non-spatial endpoint rows for companions without GPS coordinates</li>\n<li>Route Details hop counts now match the visible rows when sender companion rows are present</li>\n<li>Route Details now shows the full packet hash in the header instead of truncating it</li>\n<li>Route Details now prefers the MQTT packet hash over a shorter decoder <code>messageHash</code></li>\n<li>Added optional <code>PACKET_ANALYZER_URL</code> so packet hashes in Route Details can link to an external analyzer</li>\n<li>Fixed the production route payload so Route Details keeps the packet hash and sender name instead of falling back to the internal <code>hash:receiver</code> route ID</li>\n<li>Made the node popup <code>Location:</code> line clickable to copy <code>Location: &lt;lat&gt;, &lt;lon&gt;</code> to the clipboard</li>\n</ul>\n<h3>New ENVs</h3>\n<p>Optional channel secrets file (for decrypting sender names from supported group-text packets):</p>\n<pre><code>CHANNEL_SECRETS_FILE=/data/channel_secrets.json\n</code></pre>\n<p>Copy <code>channel_secrets.example.json</code> to your chosen path and keep only the channels you want to ship.</p>\n<p>Optional packet analyzer link base (used for Route Details hashes):</p>\n<pre><code>PACKET_ANALYZER_URL=https://analyzer.letsmesh.net/packets?packet_hash=\n</code></pre>\n<h3>Testing</h3>\n<ul>\n<li>Added channel secret decoder coverage</li>\n<li>Added route identity regression coverage for packet hash preference and production payload behavior</li>\n<li>Test suite passing:<ul>\n<li><code>76 passed, 2 skipped</code></li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.7.0",
      "name": "v1.7.0 - MeshMapper Coverage Support and Safer Legacy Routing",
      "datetime": "2026-03-20T17:28:30Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.7.0",
      "prerelease": false,
      "notes": "## v1.7.0\r\n\r\nThis release adds MeshMapper coverage support, maintains legacy coverage map compatibility, and improves route accuracy on older 1-byte networks.\r\n\r\n### New in v1.7.0\r\n\r\nAll new ENV's in https://github.com/yellowcooln/meshcore-mqtt-live-map/blob/main/VERSIONS.md#v170-03-20-2026\r\n\r\n#### Coverage API support\r\n\r\n- Added support for two coverage providers:\r\n  - Legacy coverage maps using `/get-samples`\r\n  - MeshMapper using `https://meshmapper.net/coverage.php`\r\n- Legacy coverage map support remains unchanged and fully compatible\r\n- MeshMapper API documentation:\r\n  - https://wiki.meshmapper.net/coverage-api/\r\n  - Access requires a Coverage API key provisioned by a Master administrator\r\n  - Each key is scoped to a specific region or multiregion group and includes a daily rate limit\r\n- To avoid exhausting API limits:\r\n  - The server caches MeshMapper data hourly\r\n  - All user requests are served from the cached dataset instead of hitting the API directly\r\n\r\n#### MeshMapper server-side caching\r\n\r\n- Added server-side MeshMapper coverage sync\r\n- Coverage data is downloaded and stored locally on the server\r\n- Map requests now use the local cache instead of querying MeshMapper directly\r\n- Added cooldown handling for MeshMapper API rate limits\r\n- Added MeshMapper-specific configuration options:\r\n  - API key\r\n  - Cache file path\r\n  - Sync interval\r\n  - Rate-limit cooldown\r\n\r\n#### Coverage age filtering\r\n\r\n- Added `COVERAGE_MAX_AGE_DAYS`\r\n- Full MeshMapper dataset is cached locally\r\n- Only the most recent `N` days are served to the map\r\n- Default coverage window is `30` days\r\n\r\n#### Safer 1-byte route resolution\r\n\r\n- Tightened handling of ambiguous 1-byte route prefixes\r\n- Removed weak matching when multiple nodes share the same first byte\r\n- Ambiguous 1-byte hops now require stronger validation before rendering\r\n- Reduces false long-distance links on large 1-byte meshes\r\n\r\n#### Timing consistency fixes\r\n\r\n- Improved route expiry behavior after page load\r\n- Improved MQTT online indicator timing\r\n- Route and MQTT timing now align with server time instead of relying solely on the browser clock\r\n\r\n### Testing\r\n\r\n- Added coverage tests for:\r\n  - Legacy coverage maps\r\n  - MeshMapper integration\r\n  - Cache behavior\r\n  - Rate-limit cooldown handling\r\n  - Coverage age filtering\r\n- Added regression tests for ambiguous 1-byte route handling",
      "notesHtml": "<h2>v1.7.0</h2>\n<p>This release adds MeshMapper coverage support, maintains legacy coverage map compatibility, and improves route accuracy on older 1-byte networks.</p>\n<h3>New in v1.7.0</h3>\n<p>All new ENV's in <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/blob/main/VERSIONS.md#v170-03-20-2026\" target=\"_blank\" rel=\"noopener noreferrer\">https://github.com/yellowcooln/meshcore-mqtt-live-map/blob/main/VERSIONS.md#v170-03-20-2026</a></p>\n<h4>Coverage API support</h4>\n<ul>\n<li>Added support for two coverage providers:<ul>\n<li>Legacy coverage maps using <code>/get-samples</code></li>\n<li>MeshMapper using <code>https://meshmapper.net/coverage.php</code></li>\n</ul>\n</li>\n<li>Legacy coverage map support remains unchanged and fully compatible</li>\n<li>MeshMapper API documentation:<ul>\n<li><a href=\"https://wiki.meshmapper.net/coverage-api/\" target=\"_blank\" rel=\"noopener noreferrer\">https://wiki.meshmapper.net/coverage-api/</a></li>\n<li>Access requires a Coverage API key provisioned by a Master administrator</li>\n<li>Each key is scoped to a specific region or multiregion group and includes a daily rate limit</li>\n</ul>\n</li>\n<li>To avoid exhausting API limits:<ul>\n<li>The server caches MeshMapper data hourly</li>\n<li>All user requests are served from the cached dataset instead of hitting the API directly</li>\n</ul>\n</li>\n</ul>\n<h4>MeshMapper server-side caching</h4>\n<ul>\n<li>Added server-side MeshMapper coverage sync</li>\n<li>Coverage data is downloaded and stored locally on the server</li>\n<li>Map requests now use the local cache instead of querying MeshMapper directly</li>\n<li>Added cooldown handling for MeshMapper API rate limits</li>\n<li>Added MeshMapper-specific configuration options:<ul>\n<li>API key</li>\n<li>Cache file path</li>\n<li>Sync interval</li>\n<li>Rate-limit cooldown</li>\n</ul>\n</li>\n</ul>\n<h4>Coverage age filtering</h4>\n<ul>\n<li>Added <code>COVERAGE_MAX_AGE_DAYS</code></li>\n<li>Full MeshMapper dataset is cached locally</li>\n<li>Only the most recent <code>N</code> days are served to the map</li>\n<li>Default coverage window is <code>30</code> days</li>\n</ul>\n<h4>Safer 1-byte route resolution</h4>\n<ul>\n<li>Tightened handling of ambiguous 1-byte route prefixes</li>\n<li>Removed weak matching when multiple nodes share the same first byte</li>\n<li>Ambiguous 1-byte hops now require stronger validation before rendering</li>\n<li>Reduces false long-distance links on large 1-byte meshes</li>\n</ul>\n<h4>Timing consistency fixes</h4>\n<ul>\n<li>Improved route expiry behavior after page load</li>\n<li>Improved MQTT online indicator timing</li>\n<li>Route and MQTT timing now align with server time instead of relying solely on the browser clock</li>\n</ul>\n<h3>Testing</h3>\n<ul>\n<li>Added coverage tests for:<ul>\n<li>Legacy coverage maps</li>\n<li>MeshMapper integration</li>\n<li>Cache behavior</li>\n<li>Rate-limit cooldown handling</li>\n<li>Coverage age filtering</li>\n</ul>\n</li>\n<li>Added regression tests for ambiguous 1-byte route handling</li>\n</ul>\n"
    },
    {
      "version": "v1.6.6",
      "name": "v1.6.6 - Peer History Retention Fix",
      "datetime": "2026-03-19T17:37:26Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.6.6",
      "prerelease": false,
      "notes": "## v1.6.6\r\n\r\nThis release fixes a long-standing peer history retention bug that caused the **Peers** tool to lose older data prematurely on high-volume meshes.\r\n\r\n### Changes\r\n\r\n- Moved `/peers` statistics off raw `route_history_segments`\r\n- Added dedicated rolling peer-history buckets for directional peer counts\r\n- Made peer counts independent of `ROUTE_HISTORY_MAX_SEGMENTS`\r\n- Added pruning logic so peer-history buckets remain bounded by time\r\n- Added state persistence so peer-history survives restarts\r\n- Added rebuild logic to reconstruct peer buckets from saved route history when needed\r\n\r\n### Why this release\r\n\r\nOn larger meshes, the Peers tool could report a 24-hour window while actually only retaining a few hours of data due to early truncation by `ROUTE_HISTORY_MAX_SEGMENTS`.  \r\nThis release separates peer statistics from route segment limits, ensuring accurate long-term visibility.\r\n\r\n### Testing\r\n\r\n- Added regression tests to ensure peer counts remain accurate when route history reaches segment limits\r\n- Added regression tests for peer-history persistence across restarts\r\n- Full test suite passing:\r\n  - `57 passed`",
      "notesHtml": "<h2>v1.6.6</h2>\n<p>This release fixes a long-standing peer history retention bug that caused the <strong>Peers</strong> tool to lose older data prematurely on high-volume meshes.</p>\n<h3>Changes</h3>\n<ul>\n<li>Moved <code>/peers</code> statistics off raw <code>route_history_segments</code></li>\n<li>Added dedicated rolling peer-history buckets for directional peer counts</li>\n<li>Made peer counts independent of <code>ROUTE_HISTORY_MAX_SEGMENTS</code></li>\n<li>Added pruning logic so peer-history buckets remain bounded by time</li>\n<li>Added state persistence so peer-history survives restarts</li>\n<li>Added rebuild logic to reconstruct peer buckets from saved route history when needed</li>\n</ul>\n<h3>Why this release</h3>\n<p>On larger meshes, the Peers tool could report a 24-hour window while actually only retaining a few hours of data due to early truncation by <code>ROUTE_HISTORY_MAX_SEGMENTS</code>.<br />This release separates peer statistics from route segment limits, ensuring accurate long-term visibility.</p>\n<h3>Testing</h3>\n<ul>\n<li>Added regression tests to ensure peer counts remain accurate when route history reaches segment limits</li>\n<li>Added regression tests for peer-history persistence across restarts</li>\n<li>Full test suite passing:<ul>\n<li><code>57 passed</code></li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.6.5",
      "name": "v1.6.5 - Role Accuracy, Version Logging, and Multibyte Hash Fixes",
      "datetime": "2026-03-17T01:30:40Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.6.5",
      "prerelease": false,
      "notes": "This release improves device role detection accuracy, makes it easier to verify the running version, and fixes low-range multibyte route hash handling.\r\n\r\n### Changes\r\n\r\n- Made MQTT role detection strict for accuracy\r\n  - Roles are now assigned only from explicit role fields and numeric MeshCore role codes\r\n  - Removed weak role guessing from names, model strings, origin text, and descriptions\r\n- Added runtime version logging\r\n  - Server startup logs now print the running map version\r\n  - Browser console now prints the running map version\r\n  - Version is sourced from `VERSION.txt`\r\n- Fixed backend route hash normalization to respect decoder `pathLength`\r\n  - Preserves low-range 2-byte IDs like `00AB`\r\n  - Preserves low-range 3-byte IDs like `0000AB`\r\n  - Prevents these values from collapsing to shorter prefixes and failing route lookup\r\n\r\n### Why this release\r\n\r\nSome nodes were being mislabeled due to weak MQTT metadata, and low-range multibyte repeater IDs could silently fail route resolution #26 . This release makes role detection more reliable and fixes width-aware route hash handling for multibyte paths.\r\n\r\n### Testing\r\n\r\n- Added regression tests for:\r\n  - Strict role detection\r\n  - Version injection into the frontend\r\n  - Low-range 2-byte and 3-byte route hash padding\r\n- Full test suite passing:\r\n  - `55 passed`",
      "notesHtml": "<p>This release improves device role detection accuracy, makes it easier to verify the running version, and fixes low-range multibyte route hash handling.</p>\n<h3>Changes</h3>\n<ul>\n<li>Made MQTT role detection strict for accuracy<ul>\n<li>Roles are now assigned only from explicit role fields and numeric MeshCore role codes</li>\n<li>Removed weak role guessing from names, model strings, origin text, and descriptions</li>\n</ul>\n</li>\n<li>Added runtime version logging<ul>\n<li>Server startup logs now print the running map version</li>\n<li>Browser console now prints the running map version</li>\n<li>Version is sourced from <code>VERSION.txt</code></li>\n</ul>\n</li>\n<li>Fixed backend route hash normalization to respect decoder <code>pathLength</code><ul>\n<li>Preserves low-range 2-byte IDs like <code>00AB</code></li>\n<li>Preserves low-range 3-byte IDs like <code>0000AB</code></li>\n<li>Prevents these values from collapsing to shorter prefixes and failing route lookup</li>\n</ul>\n</li>\n</ul>\n<h3>Why this release</h3>\n<p>Some nodes were being mislabeled due to weak MQTT metadata, and low-range multibyte repeater IDs could silently fail route resolution #26 . This release makes role detection more reliable and fixes width-aware route hash handling for multibyte paths.</p>\n<h3>Testing</h3>\n<ul>\n<li>Added regression tests for:<ul>\n<li>Strict role detection</li>\n<li>Version injection into the frontend</li>\n<li>Low-range 2-byte and 3-byte route hash padding</li>\n</ul>\n</li>\n<li>Full test suite passing:<ul>\n<li><code>55 passed</code></li>\n</ul>\n</li>\n</ul>\n"
    },
    {
      "version": "v1.6.2",
      "name": "v1.6.2 - Route Details Accuracy Fixes and Better MQTT Role Detection",
      "datetime": "2026-03-11T19:37:43Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.6.2",
      "prerelease": false,
      "notes": "## v1.6.2\r\n\r\nThis release improves route-details accuracy and strengthens device role detection from MQTT data.\r\n\r\n### Fixed\r\n\r\n- Fixed route-details hop ordering in the UI\r\n- Removed frontend route reversal and name-guessing heuristics\r\n- Route details now use backend `point_ids` directly for hop attribution\r\n- Fixed **Show Hops** so each row displays the matched node’s true prefix\r\n- Fixed hop-prefix display for:\r\n  - 1-byte prefixes: `AB`\r\n  - 2-byte prefixes: `ABCD`\r\n  - 3-byte prefixes: `ABCDEF`\r\n\r\n### Improved\r\n\r\n- Improved device role detection from MQTT payloads by accepting:\r\n  - nested role fields\r\n  - numeric role codes\r\n  - model/client hints from status and decoded packet data\r\n\r\n### Testing / Debug\r\n\r\n- Added decoder role tests for nested MQTT role hints and numeric-string role codes\r\n- Dev route debug output now includes resolved `point_id` / `point_label` data for easier hop verification\r\n\r\n### Notes\r\n\r\nThis release focuses on making route-details popups more accurate and reducing prefix/name mismatches in the hop list.",
      "notesHtml": "<h2>v1.6.2</h2>\n<p>This release improves route-details accuracy and strengthens device role detection from MQTT data.</p>\n<h3>Fixed</h3>\n<ul>\n<li>Fixed route-details hop ordering in the UI</li>\n<li>Removed frontend route reversal and name-guessing heuristics</li>\n<li>Route details now use backend <code>point_ids</code> directly for hop attribution</li>\n<li>Fixed <strong>Show Hops</strong> so each row displays the matched node’s true prefix</li>\n<li>Fixed hop-prefix display for:<ul>\n<li>1-byte prefixes: <code>AB</code></li>\n<li>2-byte prefixes: <code>ABCD</code></li>\n<li>3-byte prefixes: <code>ABCDEF</code></li>\n</ul>\n</li>\n</ul>\n<h3>Improved</h3>\n<ul>\n<li>Improved device role detection from MQTT payloads by accepting:<ul>\n<li>nested role fields</li>\n<li>numeric role codes</li>\n<li>model/client hints from status and decoded packet data</li>\n</ul>\n</li>\n</ul>\n<h3>Testing / Debug</h3>\n<ul>\n<li>Added decoder role tests for nested MQTT role hints and numeric-string role codes</li>\n<li>Dev route debug output now includes resolved <code>point_id</code> / <code>point_label</code> data for easier hop verification</li>\n</ul>\n<h3>Notes</h3>\n<p>This release focuses on making route-details popups more accurate and reducing prefix/name mismatches in the hop list.</p>\n"
    },
    {
      "version": "v1.6.1",
      "name": "v1.6.1 - Multibyte Decoder Migration and Routing Compatibility",
      "datetime": "2026-03-11T15:32:45Z",
      "url": "https://github.com/yellowcooln/meshcore-mqtt-live-map/releases/tag/v1.6.1",
      "prerelease": false,
      "notes": "## v1.6.1\r\n\r\nThis release updates the map to use a patched MeshCore decoder with multibyte repeater-prefix support, preparing the app for upcoming 2-byte and 3-byte routing changes.\r\n\r\n### Highlights\r\n\r\n- Replaced the official `@michaelhart/meshcore-decoder` package with [`meshcore-decoder-multibyte-patch`](https://www.npmjs.com/package/meshcore-decoder-multibyte-patch)\r\n- Added route prefix support for:\r\n  - 1-byte prefixes: `AB`\r\n  - 2-byte prefixes: `ABCD`\r\n  - 3-byte prefixes: `ABCDEF`\r\n- Updated route normalization and matching so mixed-width paths can be resolved in the same network\r\n- Expanded route tests to cover mixed 1/2/3-byte path handling and exact-prefix matching\r\n\r\n### Why this release\r\n\r\nMeshCore is moving toward multibyte repeater prefixes. The previous official decoder was a blocker for that rollout.  \r\nThis release switches the map to a patched decoder that can ingest and decode those path formats now.\r\n\r\n### Current support status\r\n\r\n- Multibyte path ingest is working in development\r\n- Live path strings with 1-byte, 2-byte, and 3-byte segments have been observed during testing\r\n- The app is intended to be ready for upcoming multibyte repeater rollouts\r\n- Not tested in a mixed-network environment. I am currently unable to test this in my mesh. Please open an [Issue](https://github.com/yellowcooln/meshcore-mqtt-live-map/issues) if you encounter any problems related to this feature.\r\n\r\n### Notes\r\n\r\n- **Show Hops** now documents and expects plain prefix display such as `AB`, `ABCD`, and `ABCDEF`\r\n- This release focuses on decoder and routing compatibility and does not introduce UI features",
      "notesHtml": "<h2>v1.6.1</h2>\n<p>This release updates the map to use a patched MeshCore decoder with multibyte repeater-prefix support, preparing the app for upcoming 2-byte and 3-byte routing changes.</p>\n<h3>Highlights</h3>\n<ul>\n<li>Replaced the official <code>@michaelhart/meshcore-decoder</code> package with <a href=\"https://www.npmjs.com/package/meshcore-decoder-multibyte-patch\" target=\"_blank\" rel=\"noopener noreferrer\"><code>meshcore-decoder-multibyte-patch</code></a></li>\n<li>Added route prefix support for:<ul>\n<li>1-byte prefixes: <code>AB</code></li>\n<li>2-byte prefixes: <code>ABCD</code></li>\n<li>3-byte prefixes: <code>ABCDEF</code></li>\n</ul>\n</li>\n<li>Updated route normalization and matching so mixed-width paths can be resolved in the same network</li>\n<li>Expanded route tests to cover mixed 1/2/3-byte path handling and exact-prefix matching</li>\n</ul>\n<h3>Why this release</h3>\n<p>MeshCore is moving toward multibyte repeater prefixes. The previous official decoder was a blocker for that rollout.<br />This release switches the map to a patched decoder that can ingest and decode those path formats now.</p>\n<h3>Current support status</h3>\n<ul>\n<li>Multibyte path ingest is working in development</li>\n<li>Live path strings with 1-byte, 2-byte, and 3-byte segments have been observed during testing</li>\n<li>The app is intended to be ready for upcoming multibyte repeater rollouts</li>\n<li>Not tested in a mixed-network environment. I am currently unable to test this in my mesh. Please open an <a href=\"https://github.com/yellowcooln/meshcore-mqtt-live-map/issues\" target=\"_blank\" rel=\"noopener noreferrer\">Issue</a> if you encounter any problems related to this feature.</li>\n</ul>\n<h3>Notes</h3>\n<ul>\n<li><strong>Show Hops</strong> now documents and expects plain prefix display such as <code>AB</code>, <code>ABCD</code>, and <code>ABCDEF</code></li>\n<li>This release focuses on decoder and routing compatibility and does not introduce UI features</li>\n</ul>\n"
    }
  ],
  "changelogSource": "github",
  "changelogUpdatedAt": "2026-06-23T20:38:34.106Z"
}
