you 1bae39963f revert: aggressive coverage interactions dropped score from 42% to 39%
The page.evaluate() calls corrupting localStorage and firing fake events
caused page error-reloads, losing accumulated coverage. Reverting to
the 42% version which was the actual high water mark.
2026-03-24 05:48:06 +00:00
2026-03-24 05:45:29 +00:00
2026-03-20 05:38:23 +00:00

MeshCore Analyzer

Backend Tests Backend Coverage Frontend Tests Frontend Coverage Deploy

Self-hosted, open-source MeshCore packet analyzer — a community alternative to the closed-source analyzer.letsmesh.net.

Collects MeshCore packets via MQTT, decodes them, and presents a full web UI with live packet feed, node map, channel chat, packet tracing, per-node analytics, and more.

Features

📡 Live Trace Map

Real-time animated map with packet route visualization, VCR-style playback controls, and a retro LCD clock. Replay the last 24 hours of mesh activity, scrub through the timeline, or watch packets flow live at up to 4× speed.

Live VCR playback — watch packets flow across the Bay Area mesh

📦 Packet Feed

Filterable real-time packet stream with byte-level breakdown, Excel-like resizable columns, and a detail pane. Toggle "My Nodes" to focus on your mesh.

Packets view

🗺️ Network Overview

At-a-glance mesh stats — node counts, packet volume, observer coverage.

Network overview

🔀 Route Patterns

Visualize how packets traverse the mesh — see which repeaters carry the most traffic and identify routing patterns.

Route patterns

📊 Node Analytics

Per-node deep dive with 6 interactive charts: activity timeline, packet type breakdown, SNR distribution, hop count analysis, peer network graph, and hourly heatmap.

Node analytics

💬 Channel Chat

Decoded group messages with sender names, @mentions, timestamps — like reading a Discord channel for your mesh.

Channels

📱 Mobile Ready

Full experience on your phone — proper touch controls, iOS safe area support, and a compact VCR bar that doesn't fight your thumb.

Live view on iOS

And More

  • Node Directory — searchable list with role tabs, detail panel, QR codes, advert timeline, "Heard By" observer table
  • Packet Tracing — follow individual packets across observers with SNR/RSSI timeline
  • Observer Status — health monitoring, packet counts, uptime
  • Hash Collision Matrix — detect address collisions across the mesh
  • Claimed Nodes — star your nodes, always sorted to top, visual distinction
  • Dark / Light Mode — auto-detects system preference, instant toggle, map tiles swap too
  • Multi-Broker MQTT — connect to multiple MQTT brokers simultaneously with per-source IATA filtering
  • Observer Detail Pages — click any observer for analytics, charts, status, radio info, recent packets
  • Channel Key Auto-Derivation — hashtag channels (#channel) keys derived automatically via SHA256
  • Global Search — search packets, nodes, and channels (Ctrl+K)
  • Shareable URLs — deep links to individual packets, channels, and observer detail pages
  • Mobile Responsive — proper two-row VCR bar, iOS safe area support, touch-friendly
  • Accessible — ARIA patterns, keyboard navigation, screen reader support, distinct marker shapes

Performance (v2.1.1)

Two-layer caching architecture: in-memory packet store + TTL response cache. All packet reads served from RAM — SQLite is write-only. Heavy endpoints pre-warmed on startup.

Endpoint Before After Speedup
Bulk Health 7,059 ms 1 ms 7,059×
Node Analytics 381 ms 1 ms 381×
Topology 685 ms 2 ms 342×
Node Health 195 ms 1 ms 195×
Node Detail 133 ms 1 ms 133×

See PERFORMANCE.md for the full benchmark.

Quick Start

The easiest way to run MeshCore Analyzer. Includes Mosquitto MQTT broker — everything in one container.

docker build -t meshcore-analyzer .
docker run -d \
  --name meshcore-analyzer \
  -p 80:80 \
  -p 443:443 \
  -p 1883:1883 \
  -v meshcore-data:/app/data \
  -v caddy-certs:/data/caddy \
  meshcore-analyzer

Open http://localhost. Point your MeshCore gateway's MQTT to <host-ip>:1883.

With a domain (automatic HTTPS):

# Create a Caddyfile with your domain
echo 'analyzer.example.com { reverse_proxy localhost:3000 }' > Caddyfile

docker run -d \
  --name meshcore-analyzer \
  -p 80:80 \
  -p 443:443 \
  -p 1883:1883 \
  -v meshcore-data:/app/data \
  -v caddy-certs:/data/caddy \
  -v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
  meshcore-analyzer

Caddy automatically provisions Let's Encrypt TLS certificates.

Custom config:

# Copy and edit the example config
cp config.example.json config.json
# Edit config.json with your channel keys, regions, etc.

docker run -d \
  --name meshcore-analyzer \
  -p 3000:3000 \
  -p 1883:1883 \
  -v meshcore-data:/app/data \
  meshcore-analyzer

Config lives in the data volume at /app/data/config.json — a default is created on first run. To edit it:

docker exec -it meshcore-analyzer vi /app/data/config.json

Or use a bind mount for the data directory:

docker run -d \
  --name meshcore-analyzer \
  -p 3000:3000 \
  -p 1883:1883 \
  -v ./data:/app/data \
  meshcore-analyzer
# Now edit ./data/config.json directly on the host

Theme customization: Put theme.json next to config.json — wherever your config lives, that's where the theme goes. Use the built-in customizer (Tools → Customize) to design your theme, download the file, and drop it in. Changes are picked up on page refresh — no restart needed. The server logs where it's looking on startup.

Manual Install

Prerequisites

  • Node.js 18+ (tested with 22.x)
  • MQTT broker (Mosquitto recommended) — optional, can inject packets via API

Install

git clone https://github.com/Kpa-clawbot/meshcore-analyzer.git
cd meshcore-analyzer
npm install

Configure

Edit config.json:

{
  "port": 3000,
  "https": {
    "cert": "/path/to/cert.pem",
    "key": "/path/to/key.pem"
  },
  "mqtt": {
    "broker": "mqtt://localhost:1883",
    "topic": "meshcore/+/+/packets"
  },
  "mqttSources": [
    {
      "name": "remote-feed",
      "broker": "mqtts://remote-broker:8883",
      "topics": ["meshcore/+/+/packets", "meshcore/+/+/status"],
      "username": "user",
      "password": "pass",
      "rejectUnauthorized": false,
      "iataFilter": ["SJC", "SFO", "OAK"]
    }
  ],
  "channelKeys": {
    "public": "8b3387e9c5cdea6ac9e5edbaa115cd72"
  },
  "defaultRegion": "SJC",
  "regions": {
    "SJC": "San Jose, US",
    "SFO": "San Francisco, US",
    "OAK": "Oakland, US"
  }
}
Field Description
port HTTP server port (default: 3000)
https.cert / https.key Optional PEM cert/key paths to enable native HTTPS (falls back to HTTP if omitted or unreadable)
mqtt.broker Local MQTT broker URL. Set to "" to disable
mqtt.topic MQTT topic pattern for packet ingestion
mqttSources Array of external MQTT broker connections (optional)
mqttSources[].name Friendly name for logging
mqttSources[].broker Broker URL (mqtt:// or mqtts:// for TLS)
mqttSources[].topics Array of MQTT topic patterns to subscribe to
mqttSources[].username / password Broker credentials
mqttSources[].rejectUnauthorized Set false for self-signed TLS certs
mqttSources[].iataFilter Only accept packets from these IATA regions
channelKeys Named channel decryption keys (hex). Hashtag channels auto-derived via SHA256
defaultRegion Default IATA region code for the UI
regions Map of IATA codes to human-readable region names

Run

node server.js

Open http://localhost:3000 in your browser.

Environment Variables

Variable Description
PORT Override config.json port
DB_PATH Override SQLite database path (default: data/meshcore.db)

Generate Test Data

# Generate and inject 200 packets via API
node tools/generate-packets.js --api --count 200

# Or output as JSON
node tools/generate-packets.js --json --count 50

Run Tests

# End-to-end test
DB_PATH=/tmp/test-e2e.db PORT=13590 node tools/e2e-test.js

# Frontend smoke test
DB_PATH=/tmp/test-fe.db PORT=13591 node tools/frontend-test.js

MQTT Setup

MeshCore packets flow into the analyzer via MQTT:

  1. Flash an observer node with MESH_PACKET_LOGGING=1 build flag
  2. Connect via USB to a host running meshcoretomqtt
  3. Configure meshcoretomqtt with your IATA region code and MQTT broker address
  4. Packets appear on topic meshcore/{IATA}/{PUBKEY}/packets

Alternatively, POST raw hex packets to POST /api/packets for manual injection.

Architecture

Observer Node → USB → meshcoretomqtt → MQTT Broker → Analyzer Server → WebSocket → Browser
                                                    → SQLite DB
                                                    → REST API

Project Structure

meshcore-analyzer/
├── Dockerfile           # Single-container build (Node + Mosquitto + Caddy)
├── .dockerignore
├── config.example.json  # Example config (copy to config.json)
├── config.json          # MQTT, channel keys, regions (gitignored)
├── server.js            # Express + WebSocket + MQTT + REST API
├── decoder.js           # Custom MeshCore packet decoder
├── db.js                # SQLite schema + queries
├── packet-store.js      # In-memory packet store (ring buffer, indexed)
├── docker/
│   ├── supervisord.conf # Process manager config
│   ├── mosquitto.conf   # MQTT broker config
│   ├── Caddyfile        # Default Caddy config (localhost)
│   └── entrypoint.sh    # Container entrypoint
├── data/
│   └── meshcore.db      # Packet database (auto-created)
├── public/
│   ├── index.html       # SPA shell
│   ├── style.css        # Theme (light/dark)
│   ├── app.js           # Router, WebSocket, utilities
│   ├── packets.js       # Packet feed + byte breakdown + detail page
│   ├── map.js           # Leaflet map with route visualization
│   ├── live.js          # Live trace page with VCR playback
│   ├── channels.js      # Channel chat
│   ├── nodes.js         # Node directory + detail views
│   ├── analytics.js     # Global analytics dashboard
│   ├── node-analytics.js # Per-node analytics with charts
│   ├── traces.js        # Packet tracing
│   ├── observers.js     # Observer status
│   ├── observer-detail.js # Observer detail with analytics
│   ├── home.js          # Dashboard home page
│   └── perf.js          # Performance monitoring dashboard
└── tools/
    ├── generate-packets.js  # Synthetic packet generator
    ├── e2e-test.js          # End-to-end API tests
    └── frontend-test.js     # Frontend smoke tests

License

MIT

Description
No description provided
Readme 102 MiB
Languages
JavaScript 59.6%
Go 30.6%
CSS 5.2%
Shell 2.9%
Python 1.1%
Other 0.5%