JingleManSweep 9df7bf2f92 Merge pull request #277 from ipnet-mesh/renovate/actions-cache-6.x
chore(deps): update actions/cache action to v6
2026-06-24 09:10:39 +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
  • 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
│   ├── 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 82.8%
JavaScript 15.1%
HTML 1%
CSS 0.7%
Dockerfile 0.2%