Add public Meshtastic node layer - #15
Conversation
New optional layer showing nodes that opted in to Meshtastic's own public Map Reporting feature — the network's built-in "publish yourself to a public map" mechanism, over MQTT. Server: meshtasticProxy (vite.config.js) keeps one persistent MQTT connection to mqtt.meshtastic.org (the project's own public broker, published read-only credentials), subscribed only to msh/+/2/map/#. It decodes exclusively unencrypted MapReport packets (portnum 73) via the official @meshtastic/protobufs definitions and never inspects or attempts to decrypt anything else on the wire — encrypted channel packets are dropped without being touched. An in-memory cache (capped 8000 nodes, swept every 15 min, 24h eviction) is served as plain JSON from /api/meshtastic/nodes; validated live against the real broker end-to-end. Client: src/data/meshtasticNodes.js polls that snapshot like any other layer. A node's position_precision (bits retained) is honored rather than flattened into a false-precision pin — meaningful uncertainty renders as a translucent circle (positionUncertaintyRadiusM, calibrated against Meshtastic's own published precision reference points), a high-precision report as a plain point. Per-node LIVE/STALE opacity independent of the overall layer status. src/data/meshtasticMapReport.js holds the pure helpers (id formatting, uncertainty radius, freshness bucketing) shared by server and client; src/data/meshtasticDecode.js isolates the protobuf-dependent decode so it never reaches the browser bundle (confirmed via a production build — the client chunk grew ~4KB, not the hundreds of KB @meshtastic/protobufs would add). Registered in main.js + layerState.js (token 'n'), voice/text aliases, attribution in dataCredits.js and DATA_SOURCES.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZFzxaNKiGGkq9V64Y9omb
- meshtasticNodes.js loadNodes(): never resolve `false` from update() — manager.js's enable-lifecycle treats that as a fatal enable failure and bounces the toggle back off (src/data/manager.js:908-909), which a transient fetch failure right after enabling should never do. Also guard against a destroy() racing an in-flight fetch (dataSource null-deref). - vite.config.js meshtasticProxy: the node-cache cap was silently dropping brand-new nodes once full instead of evicting the oldest-seen one — permanently hiding new mesh participants in a dense region once the 8000-node cap was reached. Now a proper LRU: delete-then-set on every update (a Map's set() on an existing key doesn't reorder it) so the first key really is oldest-seen, then evict that on overflow. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GZFzxaNKiGGkq9V64Y9omb
Yep! |
Closes #6.
New optional layer showing nodes that opted in to Meshtastic's own public Map Reporting feature — the network's built-in "publish yourself to a public map" mechanism, over MQTT.
Server:
meshtasticProxy(vite.config.js) keeps one persistent MQTT connection tomqtt.meshtastic.org(the project's own public broker, published read-only credentials), subscribed only tomsh/+/2/map/#. It decodes exclusively unencrypted MapReport packets (portnum 73) via the official@meshtastic/protobufsdefinitions and never inspects or attempts to decrypt anything else on the wire — encrypted channel packets are dropped without being touched. An in-memory cache (capped 8000 nodes, swept every 15 min, 24h eviction, true LRU) is served as plain JSON from/api/meshtastic/nodes; validated live against the real broker end-to-end.Client:
src/data/meshtasticNodes.jspolls that snapshot like any other layer. A node'sposition_precision(bits retained) is honored rather than flattened into a false-precision pin — meaningful uncertainty renders as a translucent circle, a high-precision report as a plain point. Per-node LIVE/STALE opacity independent of overall layer status.src/data/meshtasticMapReport.jsholds the pure helpers (id formatting, uncertainty radius, freshness bucketing) shared by server and client;src/data/meshtasticDecode.jsisolates the protobuf-dependent decode so it never reaches the browser bundle (confirmed via a production build). Registered inmain.js+layerState.js, voice/text aliases, attribution indataCredits.jsandDATA_SOURCES.md.Includes a follow-up commit fixing two bugs a code-review pass found:
update()could resolve tofalseon a transient fetch failure, whichmanager.js's enable-lifecycle treats as a fatal enable failure and bounces the layer's toggle back off instead of leaving it enabled to report the failure viagetStats().🤖 Generated with Claude Code