Live Map of Meshcore MQTT feed https://live.bostonme.sh/
Find a file
2026-01-30 03:46:53 +00:00
.github Update ko_fi funding username 2026-01-13 14:17:31 -05:00
backend Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
data Initial commit 2025-12-24 04:32:48 +00:00
.env.example Bump to v1.2.0 and document Turnstile config 2026-01-27 16:42:36 +00:00
.gitignore Remove __pycache__ artifacts and ignore bytecode 2026-01-27 14:52:39 +00:00
AGENTS.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
ARCHITECTURE.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
CONTRIBUTING.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
docker-compose.yaml Bump version to 1.2.2 and sync Turnstile/route path fixes 2026-01-29 17:17:55 +00:00
docs.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
example.gif Add README preview gif 2025-12-27 02:01:56 +00:00
example2.gif Move example2 gif to repo root 2026-01-04 03:45:29 +00:00
howto.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
LICENSE Initial commit 2025-12-24 04:32:48 +00:00
README.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
VERSION.txt Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00
VERSIONS.md Turnstile auth cookie for API/WS and bump version to 1.2.4 2026-01-30 03:46:53 +00:00

Mesh Live Map

Version: 1.2.4 (see VERSIONS.md)

Live MeshCore traffic map that renders nodes, routes, and activity in real time on a Leaflet map. The backend subscribes to MQTT over WebSockets+TLS or TCP, decodes MeshCore packets with @michaelhart/meshcore-decoder, and streams updates to the browser via WebSockets.

Live example sites:

Live map preview

Live map preview

Features

  • Live node markers with roles (Repeater, Companion, Room Server, Unknown)
  • MQTT online indicator (green outline + popup status)
  • Animated route/trace lines
  • Dev route inspection: click a route line in dev (PROD_MODE=false) to log hop-by-hop details in the browser console (PR #14, credit: https://github.com/sefator)
  • Heat map for the last 10 minutes of message activity (includes adverts)
  • Persistent device state and optional trails (disable with TRAIL_LEN=0)
  • 24-hour route history tool with volume-based coloring, click-to-view packet details, a heat-band slider, and a link-size slider
  • History panel can be dismissed with an X without hiding history lines (re-open via History tool)
  • Peers tool showing incoming/outgoing neighbors with on-map lines (blue = incoming, purple = outgoing)
  • Coverage layer from a coverage map API (button hidden when not configured)
  • Update available banner (git local vs upstream) with dismiss
  • UI controls: legend toggle, dark map, topo map, units toggle (km/mi), labels toggle, hide nodes, heat toggle
  • Share button that copies a URL with current view + settings
  • URL parameters to open the map at a specific view (center, zoom, toggles)
  • Node search by name or public key
  • Adjustable node size slider (defaults from env, saves locally)
  • LOS tool with elevation profile + peak markers and hover sync (Shift+click or longpress nodes)
  • Embeddable metadata (Open Graph/Twitter tags) driven by env vars
  • Preview image renders in-bounds device dots for shared links
  • Route pruning via neighbor-aware closest-hop selection + max hop distance (configurable)
  • Route lines are derived from decoded packet paths only (no MQTT observer/receiver fallback)
  • First-hop collision fix prefers the closest repeater/room to the sender (Issue #11)
  • Propagation panel lives on the right and keeps the last render until you generate a new one (click an origin marker to remove it)
  • Installable PWA (manifest + service worker) for Add to Home Screen
  • Click the logo to hide/show the left HUD panel while tools stay open

Project Structure

  • backend/app.py: FastAPI server wiring, MQTT lifecycle, WS broadcast
  • backend/config.py: environment configuration
  • backend/state.py: shared in-memory state + dataclasses
  • backend/decoder.py: payload parsing + meshcore-decoder integration
  • backend/los.py: LOS math + elevation helpers
  • backend/history.py: route history persistence + pruning
  • backend/static/index.html: HTML shell + template placeholders
  • backend/static/styles.css: UI styles
  • backend/static/app.js: map logic + UI controls
  • backend/static/sw.js: PWA service worker
  • docker-compose.yaml: runtime configuration (reads from .env)
  • data/: runtime state (created at first run)

Quick Start

  1. Clone the repo and enter it:
git clone https://github.com/yellowcooln/meshcore-mqtt-live-map
cd meshcore-mqtt-live-map
  1. Copy env template:
cp .env.example .env
  1. Edit .env with your MQTT broker and site metadata.
    • See howto.md for a step-by-step guide to setting up the MQTT server and this live map.
  2. Build and run:
docker compose up -d --build
  1. Open: http://localhost:8080/ (or your WEB_PORT)

Configuration (.env)

Debugging:

  • DEBUG_PAYLOAD (verbose decode logs)
  • DEBUG_PAYLOAD_MAX / PAYLOAD_PREVIEW_MAX (log truncation limits)
  • DEBUG_LAST_MAX / DEBUG_STATUS_MAX (debug endpoint entry caps)

Storage + server:

  • STATE_DIR (persisted state path)
  • STATE_FILE (full state file path override)
  • DEVICE_ROLES_FILE (optional role override JSON file)
  • NEIGHBOR_OVERRIDES_FILE (optional JSON mapping for neighbor overrides)
  • STATE_SAVE_INTERVAL (seconds between state saves)
  • WEB_PORT (host port for the web UI)
  • PROD_MODE (true to require a token for API + WS)
  • PROD_TOKEN (required token; send via ?token= or Authorization: Bearer)

Turnstile protection (prod-only):

  • TURNSTILE_ENABLED (requires PROD_MODE=true)
  • TURNSTILE_SITE_KEY
  • TURNSTILE_SECRET_KEY
  • TURNSTILE_API_URL
  • TURNSTILE_TOKEN_TTL_SECONDS
  • TURNSTILE_BOT_BYPASS (allowlist embed bots like Discord)
  • TURNSTILE_BOT_ALLOWLIST (comma-separated user-agent tokens; default: discordbot,twitterbot,slackbot,facebookexternalhit,linkedinbot,telegrambot,whatsapp,skypeuripreview,redditbot)

Site metadata (page title + embeds):

  • SITE_TITLE
  • SITE_DESCRIPTION
  • SITE_OG_IMAGE (optional; leave blank to omit embed image)
  • SITE_URL (public URL)
  • SITE_ICON
  • SITE_FEED_NOTE
  • CUSTOM_LINK_URL (optional extra HUD link; hidden when blank)
  • DISTANCE_UNITS (km or mi, default display units)
  • NODE_MARKER_RADIUS (default node marker size in pixels)

MQTT:

  • MQTT_HOST
  • MQTT_PORT
  • MQTT_USERNAME
  • MQTT_PASSWORD
  • MQTT_TRANSPORT (tcp or websockets)
  • MQTT_WS_PATH (usually / or /mqtt)
  • MQTT_TLS (true)
  • MQTT_TLS_INSECURE (allow invalid TLS certs)
  • MQTT_CA_CERT (custom CA bundle path)
  • MQTT_CLIENT_ID (optional client id override)
  • MQTT_TOPIC (e.g. meshcore/# or meshcore/#,other/topic/+ for multiple topics)

Coverage layer:

  • COVERAGE_API_URL (URL to coverage map API; button hidden when blank)

Device + route tuning:

  • DEVICE_TTL_SECONDS (node expiry)
  • TRAIL_LEN (points per device trail; 0 disables trails)
  • ROUTE_TTL_SECONDS
  • ROUTE_PATH_MAX_LEN (skip oversized path-hash lists)
  • ROUTE_PAYLOAD_TYPES (packet types used for live routes)
  • ROUTE_MAX_HOP_DISTANCE (km; prunes unrealistic hops)
  • ROUTE_INFRA_ONLY (true = only repeaters/rooms in route lines)
  • MESSAGE_ORIGIN_TTL_SECONDS

History overlay:

  • ROUTE_HISTORY_ENABLED
  • ROUTE_HISTORY_HOURS
  • ROUTE_HISTORY_MAX_SEGMENTS
  • ROUTE_HISTORY_COMPACT_INTERVAL
  • ROUTE_HISTORY_FILE
  • ROUTE_HISTORY_PAYLOAD_TYPES
  • ROUTE_HISTORY_ALLOWED_MODES (comma-separated route modes; default path)
  • HISTORY_LINK_SCALE (default history line weight multiplier)

Heat + online status:

  • HEAT_TTL_SECONDS
  • MQTT_ONLINE_SECONDS (online window for status ring)
  • MQTT_ONLINE_TOPIC_SUFFIXES (comma-separated topics that count as “online”)
  • MQTT_SEEN_BROADCAST_MIN_SECONDS
  • MQTT_ONLINE_FORCE_NAMES (comma-separated names to force as MQTT online; also excluded from peers)

Update checks:

  • GIT_CHECK_ENABLED (show update banner if repo is behind)
  • GIT_CHECK_FETCH (fetch before comparing)
  • GIT_CHECK_PATH (path to git repo in the container)
  • GIT_CHECK_INTERVAL_SECONDS (defaults to 43200 = 12h)

Map + LOS:

  • MAP_START_LAT / MAP_START_LON / MAP_START_ZOOM (default map view)
  • MAP_DEFAULT_LAYER (light, dark, or topo; localStorage overrides)
  • MAP_RADIUS_KM (0 disables radius filtering; .env.example uses 241.4 km ≈ 150mi)
  • MAP_RADIUS_SHOW (true draws the radius debug circle)
  • LOS_ELEVATION_URL (elevation API for LOS tool)
  • LOS_SAMPLE_MIN / LOS_SAMPLE_MAX / LOS_SAMPLE_STEP_METERS
  • ELEVATION_CACHE_TTL (seconds)
  • LOS_PEAKS_MAX (max peaks shown on LOS profile)

Decoder helpers:

  • DECODE_WITH_NODE (toggle meshcore-decoder usage)
  • NODE_DECODE_TIMEOUT_SECONDS
  • DIRECT_COORDS_MODE (topic or payload)
  • DIRECT_COORDS_TOPIC_REGEX (topic matcher for direct coords)
  • DIRECT_COORDS_ALLOW_ZERO (allow 0,0 coords if true)

Common Commands

  • Rebuild/restart: docker compose up -d --build
  • Logs: docker compose logs -f meshmap-live
  • Snapshot: curl -s http://localhost:8080/snapshot
  • Stats: curl -s http://localhost:8080/stats

Production Token

Enable protection by setting:

PROD_MODE=true
PROD_TOKEN=<random-string>

Turnstile protection is also gated by PROD_MODE=true. If PROD_MODE=false, Turnstile stays off even when TURNSTILE_ENABLED=true. When Turnstile is enabled, its auth cookie now grants access to /snapshot, /stats, /peers, and the WebSocket without requiring a PROD token (prevents reconnect spam). Ensure PROD_MODE/PROD_TOKEN are set in .env (docker-compose passes them through).

Generate a token:

openssl rand -hex 32

Use it:

  • HTTP: http://host:8080/snapshot?token=YOUR_TOKEN
  • WS: ws://host:8080/ws?token=YOUR_TOKEN
  • Or send Authorization: Bearer YOUR_TOKEN

Notes

  • The map can only draw routes for hops that appear in your MQTT feed.
  • To see full paths, the feed must include Path/Trace packets (payload types 8/9).
  • Runtime state is persisted to data/state.json.
  • MQTT disconnects are handled; the client will reconnect when the broker returns.
  • Line-of-sight tool: click LOS tool and pick two points, or Shift+click two nodes to measure LOS between them.
  • On mobile, longpress a node to select it for LOS.
  • LOS runs server-side via /los (no client-side elevation fetch).
  • History tool always loads off (use the button or history=on in the URL).
  • Peers tool uses route history segments; forced MQTT listeners are excluded from peer lists.
  • URL params override stored settings: lat, lon/lng/long, zoom, layer, history, heat, labels, nodes, legend, menu, units, history_filter.
  • Dark map also darkens node popups for readability.
  • Route styling uses payload type: 2/5 = Message (blue), 8/9 = Trace (orange), 4 = Advert (green).
  • If hop hashes collide, the backend prefers known neighbors (or overrides) before picking the closest hop and pruning beyond ROUTE_MAX_HOP_DISTANCE.
  • Coordinates at 0,0 (including string values) are filtered from devices, trails, and routes.
  • With Turnstile enabled, common embed bots (Discord, Slack, etc.) can be allowlisted via TURNSTILE_BOT_BYPASS and TURNSTILE_BOT_ALLOWLIST.

API

The backend exposes a nodes API for external tools (e.g. MeshBuddy):

  • GET /api/nodes?token=YOUR_TOKEN
    • Default response: {"data":{"nodes":[...]}}
    • Optional: format=flat returns {"data":[...]}
    • Optional: mode=delta applies updated_since filtering

Example:

https://your-host/api/nodes?token=YOUR_TOKEN
https://your-host/api/nodes?token=YOUR_TOKEN&mode=delta&updated_since=2025-01-01T12:00:00Z
https://your-host/api/nodes?token=YOUR_TOKEN&format=flat

Each node includes: public_key, name, device_role (1/2/3), last_seen (ISO), timestamp (epoch), and location with latitude/longitude.

Peer summary:

  • GET /peers/{device_id}?token=YOUR_TOKEN
    • Returns incoming/outgoing neighbors with counts/percentages from route history.

License

GPL-3.0.


This project was vibe-coded with Codex—please expect rough edges and the occasional bug.

Star History

Star History Chart