Louis King fc60cd201a feat: route badge reflects 7-day rolling average
The overall health badge on route cards now shows the rolling 7-day
average tier instead of the latest window-hours snapshot, so flapping
routes that are currently up still appear marginal/failing if the
week's mean warrants it. Same averaging drives the dashboard Route
Health widget's summary dot, the routes page summary strip counts,
and (already) the Route Trends chart line colors.

- compute_average_quality() in collector/routes.py (0/1/2 mean,
  thresholds 1.5/0.75, empty-history fallback) — kept in sync with
  the averageRouteTier JS helper in charts.js
- RouteRead / RouteDetail gain a 'quality_avg' field
- list/get/update handlers compute it per route; create skips (no
  meaningful history yet) and the frontend falls back to
  route_result.quality for brand-new routes
- diagnosis tooltip unchanged (still current-snapshot state text)
2026-07-19 17:23:54 +01:00
2026-05-05 17:32:29 +00:00
2025-12-03 17:02:57 +00:00
2025-12-03 01:24:42 +00:00
2026-06-14 20:27:52 +01:00
2025-12-02 21:52:34 +00:00
2026-02-11 11:22:03 +00:00
2026-06-14 20:27:52 +01:00

MeshCore Hub

CI Docker codecov BuyMeACoffee

Python 3.14+ platform for managing and orchestrating MeshCore mesh networks.

Warning

DEPRECATION NOTICE — v0.14 adds PostgreSQL support (DATABASE_BACKEND=postgres); SQLite remains the zero-config default. SQLite support will be maintained for at least the next few releases (~3 months), then removed in favour of PostgreSQL-only. See docs/database.md to switch backends and docs/upgrading.md to migrate.

MeshCore Hub Web Dashboard

Overview

MeshCore Hub provides a complete solution for monitoring, collecting, and interacting with MeshCore mesh networks. Data ingestion is handled by meshcore-packet-capture, which observes MeshCore RF traffic and publishes decoded packets to MQTT. It consists of multiple components that work together:

Component Description
Collector Subscribes to MQTT events and persists them to a database
API REST API for querying data
Web Dashboard Single Page Application (SPA) for visualizing network status

MeshCore Hub Networks

Local mesh communities that are using MeshCore Hub:

Architecture

flowchart LR
    subgraph Devices["MeshCore Devices"]
        D1["Device 1"]
        D2["Device 2"]
        D3["Device 3"]
    end

    PCAP["meshcore-packet-capture"]

    D1 -.->|RF| PCAP
    D2 -.->|RF| PCAP
    D3 -.->|RF| PCAP

    PCAP -->|Publish| MQTT["MQTT Broker"]

    subgraph Backend["Backend Services"]
        Collector --> Database --> API
    end

    MQTT --> Collector
    API --> Web["Web Dashboard"]

    style Devices fill:none,stroke:#0288d1,stroke-width:2px
    style PCAP fill:none,stroke:#f57c00,stroke-width:2px
    style Backend fill:none,stroke:#388e3c,stroke-width:2px
    style MQTT fill:none,stroke:#7b1fa2,stroke-width:3px
    style Collector fill:none,stroke:#388e3c,stroke-width:2px
    style Database fill:none,stroke:#c2185b,stroke-width:2px
    style API fill:none,stroke:#1976d2,stroke-width:2px
    style Web fill:none,stroke:#ffa000,stroke-width:2px

Features

  • Event Persistence: Store messages, advertisements, telemetry, and trace data
  • Raw Packet Inspection: Capture, browse, and search raw wire packets; a deduplicated packet view shows every observer reception and routing path, with clickable path-hash badges that look up the matching nodes
  • Route Health Monitoring: Define multi-hop mesh routes and track each link's health over time with traffic-light status, per-day history, and recent-match attribution
  • REST API: Query historical data with filtering and pagination
  • Node Tagging: Add custom metadata to nodes for organization
  • Web Dashboard: Visualize network status, node locations, and message history
  • Internationalization: Full i18n support with composable translation patterns
  • Docker Ready: Single image with all components, easy deployment

Getting Started

Docker Compose Profiles

Docker Compose uses profiles to select which services to run. The configuration is split across multiple files:

File Purpose
docker-compose.yml Base shared config (services, profiles, healthchecks, environment)
docker-compose.dev.yml Development overrides (port mappings for direct access)
docker-compose.prod.yml Production overrides (external proxy network, no exposed ports)
docker-compose.traefik.yml Optional Traefik auto-discovery labels

All docker compose commands require explicit file selection with -f:

# Development (default — exposes ports for local access)
docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile all up -d

# Production (generic reverse proxy — nginx, caddy, etc.)
docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile all up -d

# Production (Traefik)
docker compose -f docker-compose.yml -f docker-compose.prod.yml -f docker-compose.traefik.yml --profile all up -d

Service profiles:

Profile Services Use Case
all mqtt, observer, migrate, collector, api, web Everything on one host
core migrate, collector, api, web Central server infrastructure
mqtt meshcore-mqtt-broker Local MQTT broker (optional)
observer packet capture observer Observes RF traffic and publishes to MQTT
seed seed One-time seed data import
migrate migrate One-time database migration

Note: Most deployments connect to an external MQTT broker. Add --profile mqtt only if you need a local broker. The observer profile runs meshcore-packet-capture to observe MeshCore RF traffic and publish decoded packets to MQTT.

Simple Self-Hosted Setup

The quickest way to get started is running the entire stack on a single machine with a connected LoRa radio.

Prerequisites:

  1. A compatible LoRa radio (e.g., Heltec V3, T-Beam) connected via serial

Steps:

# Create a directory, download the Docker Compose files and
# example environment configuration file

mkdir meshcore-hub
cd meshcore-hub
wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/docker-compose.yml
wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/docker-compose.dev.yml
wget https://raw.githubusercontent.com/ipnet-mesh/meshcore-hub/refs/heads/main/.env.example

# Copy and configure environment
cp .env.example .env
# Edit .env: set PACKETCAPTURE_IATA to your 3-letter airport code
#            set SERIAL_PORT if not /dev/ttyUSB0

# Start the entire stack with local MQTT broker and packet capture
docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile mqtt --profile core --profile observer up -d

# View the web dashboard
open http://localhost:8080

This starts all services: MQTT broker, collector, API, web dashboard, and packet capture. The observer profile runs meshcore-packet-capture to observe MeshCore RF traffic and publish decoded packets to MQTT.

Deployment

For production deployments (reverse-proxy setup, Traefik overrides, multi-instance routing on a shared Docker host, and API scaling with API_WORKERS), see docs/deployment.md.

Adding Remote Observers

Other operators can run their own meshcore-packet-capture instance and publish decoded packets to your MeshCore Hub, with optional contribution to the LetsMesh and MeshRank networks. For the ready-made Docker Compose setup and the PACKETCAPTURE_* configuration reference, see docs/observer.md.

Backup & Restore

For backing up and restoring Docker volumes (Makefile and shell variants), see docs/maintenance.md.

Configuration

All components are configured via environment variables. Copy .env.example to .env and override what you need. The full variable reference — grouped by feature (Common, Database, Caching, Collector, Webhooks, Auth, Data Retention, API, Web Dashboard, Feature Flags, Traefik, Prometheus & Alertmanager) — lives in docs/configuration.md; each section there links to the relevant feature doc for setup and operational details.

Seed Data

The database can be seeded with node tags from YAML files. See docs/seeding.md for format details, directory structure, and running the seed process.

API Documentation

When running, the API provides interactive documentation at:

Health check endpoints are also available:

Authentication

The API supports optional bearer token authentication:

# Read-only access
curl -H "Authorization: Bearer <API_READ_KEY>" http://localhost:8000/api/v1/nodes

# Admin access
curl -H "Authorization: Bearer <API_ADMIN_KEY>" http://localhost:8000/api/v1/members

The web dashboard supports OIDC/OAuth2 authentication for admin access. When enabled, users must authenticate with an identity provider and have the admin role assigned. See docs/auth.md for setup instructions and IdP-specific guides.

Example Endpoints

Method Endpoint Description
GET /api/v1/nodes List all known nodes
GET /api/v1/nodes/{public_key} Get node details
GET /api/v1/nodes/prefix/{prefix} Get node by public key prefix
GET /api/v1/nodes/{public_key}/tags Get node tags
POST /api/v1/nodes/{public_key}/tags Create node tag
GET /api/v1/messages List messages with filters
GET /api/v1/advertisements List advertisements
GET /api/v1/telemetry List telemetry data
GET /api/v1/trace-paths List trace paths
GET /api/v1/members List network members
GET /api/v1/dashboard/stats Get network statistics
GET /api/v1/dashboard/activity Get daily advertisement activity
GET /api/v1/dashboard/message-activity Get daily message activity
GET /api/v1/dashboard/node-count Get cumulative node count history

Development

Setup

# Clone and setup
git clone https://github.com/ipnet-mesh/meshcore-hub.git
cd meshcore-hub

# Install frontend dependencies and build static assets (requires Node.js 22+ LTS)
npm install
npm run build

# Setup Python environment
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

# Install pre-commit hooks
pre-commit install

# Run database migrations
meshcore-hub db upgrade

# Start components (in separate terminals)
meshcore-hub collector
meshcore-hub api
meshcore-hub web

Note: npm run build compiles Tailwind CSS and copies vendor libraries (lit-html, Leaflet, Chart.js, QRCode.js) into src/meshcore_hub/web/static/vendor/. This step is required before the web dashboard will render correctly. In Docker, this happens automatically during the build.

Running Tests

# Run all tests
pytest

# Run with coverage
pytest --cov=meshcore_hub --cov-report=html

# Run specific test file
pytest tests/test_api/test_nodes.py

# Run tests matching pattern
pytest -k "test_list"

Code Quality

# Run all code quality checks (formatting, linting, type checking)
pre-commit run --all-files

Creating Database Migrations

# Auto-generate migration from model changes
meshcore-hub db revision --autogenerate -m "Add new field to nodes"

# Create empty migration
meshcore-hub db revision -m "Custom migration"

# Apply migrations
meshcore-hub db upgrade

Project Structure

meshcore-hub/
├── src/meshcore_hub/       # Main package
│   ├── common/             # Shared code (models, schemas, config)
│   ├── collector/          # MQTT event collector
│   ├── api/                # REST API
│   └── web/                # Web dashboard
│       ├── templates/      # Jinja2 templates (SPA shell)
│       └── static/
│           ├── css/         # Stylesheets (app.css, input.css, built tailwind.css)
│           ├── vendor/      # Vendored JS/CSS libraries (built by npm run build)
│           ├── js/spa/      # SPA frontend (ES modules, lit-html)
│           └── locales/     # Translation files (en.json)
├── tests/                  # Test suite
├── alembic/                # Database migrations
├── etc/                    # Configuration files (MQTT, Prometheus, Alertmanager)
├── example/                # Example files for reference
│   ├── seed/               # Example seed data files
│   │   └── node_tags.yaml  # Example node tags
│   └── content/            # Example custom content
│       ├── pages/          # Example custom pages
│       │   └── join.md     # Example join page
│       └── media/          # Example media files
│           └── images/     # Custom images
├── seed/                   # Seed data directory (SEED_HOME, copy from example/seed/)
├── content/                # Custom content directory (CONTENT_HOME, optional)
│   ├── pages/              # Custom markdown pages
│   └── media/              # Custom media files
│       └── images/         # Custom images (logo.svg/png/jpg/jpeg/webp replace default logo)
├── data/                   # Runtime data directory (DATA_HOME, created at runtime)
├── Dockerfile              # Docker build configuration (multi-stage: Node.js frontend + Python)
├── package.json            # Frontend build dependencies (Tailwind, DaisyUI, lit-html, etc.)
├── build.js                # Frontend build script (Tailwind CLI + vendor copy)
├── docker-compose.yml      # Docker Compose base config
├── docker-compose.dev.yml  # Development overrides (port mappings)
├── docker-compose.prod.yml # Production overrides (proxy network)
├── docker-compose.traefik.yml # Optional Traefik labels
├── docs/                    # Documentation
│   ├── images/              # Screenshots and images
│   ├── hosting/             # Reverse proxy hosting guides
│   ├── configuration.md     # Single source of truth for environment variables
│   ├── content.md           # Custom content setup guide
│   ├── database.md          # Database backends (SQLite/PostgreSQL) reference
│   ├── deployment.md        # Production setup, scaling, Redis, multi-instance
│   ├── i18n.md              # Translation reference guide
│   ├── letsmesh.md          # LetsMesh packet decoding details
│   ├── maintenance.md       # Backup and restore procedures
│   ├── observer.md          # Remote observers and PACKETCAPTURE_* reference
│   ├── routes.md            # Route health monitoring
│   ├── seeding.md           # Seed data format and import guide
│   ├── upgrading.md         # Upgrade guide for breaking changes
│   └── webhooks.md          # Webhook configuration reference
├── SCHEMAS.md               # Event schema documentation
└── AGENTS.md                # AI assistant guidelines

Documentation

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Run tests and quality checks (pytest && pre-commit run --all-files)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Important

Help Translate MeshCore Hub 🌍

We need volunteers to translate the web dashboard! Currently only English is available. Check out the Translation Guide to contribute a language pack. Partial translations welcome!

License

This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See LICENSE for details.

Acknowledgments

  • MeshCore - The mesh networking protocol
  • meshcore - Python library for MeshCore devices
  • meshcore-packet-capture - RF packet capture and MQTT publisher for data ingestion
  • meshcore-mqtt-broker - WebSocket MQTT broker with MeshCore public key authentication. The Docker image (ghcr.io/ipnet-mesh/meshcore-mqtt-broker) is built and published by a GitHub Action in this repository that clones the upstream source, as the upstream project does not currently provide a public Docker image (although a PR has been submitted to add this).
Languages
Python 76.1%
TypeScript 22.5%
CSS 0.7%
HTML 0.2%
Dockerfile 0.2%
Other 0.1%