docs: refresh README for dual-mesh (Meshtastic + MeshCore) + dashboard (#24)

Rewrite the README to reflect the current project: dual-transport
(Meshtastic base + MeshCore auto-on via meshcore_host), the web dashboard
(with live screenshots), per-mesh routing, the conversational bot with
per-mesh scoped context + three privacy lanes, environmental/hazard
broadcasts, mesh-health scoring, and the RAG knowledge base. Removes the
retired subscription backend/commands, updates the LLM model + architecture,
and adds live dashboard screenshots under docs/images/.

For transparency, documents that the project was vibecoded (built with LLM
coding assistants).

Co-authored-by: Matt Johnson <mj@k7zvx.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
malice 2026-07-04 00:47:11 -06:00 committed by GitHub
commit fbb4fa0e94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 316 additions and 990 deletions

699
README.md
View file

@ -1,34 +1,62 @@
# MeshAI # MeshAI
LLM-powered mesh intelligence assistant for Meshtastic networks. MeshAI connects to your mesh as a physical node, monitors network health in real-time, and answers questions about your infrastructure over LoRa. **An LLM-powered assistant for LoRa mesh networks — on Meshtastic *and* MeshCore, at the same time.**
## What It Does MeshAI connects to your mesh, watches network health and the world around it in real time, answers questions over the air, and broadcasts the alerts that matter — weather, wildfire, road, seismic, RF, and mesh-health — with a full web dashboard to drive it all.
MeshAI runs on your Meshtastic node and provides: > ### 🤖 Built with AI ("vibecoded")
> In the interest of transparency: **MeshAI was vibecoded** — designed, built, debugged, and documented in close collaboration with LLM coding assistants. The architecture, most of the implementation, and this README were produced that way. It's a real, running project on a live mesh, but expect the pragmatic style, opinionated shortcuts, and occasional rough edges that come with the territory. Issues and PRs are welcome.
- **Mesh Intelligence** — 5-pillar health scoring, per-region breakdowns, infrastructure monitoring, coverage gap analysis, and environmental sensing ![MeshAI dashboard](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/dashboard.png)
- **Conversational Queries** — ask "how's the mesh?" or "tell me about MHR" and get data-driven answers over LoRa
- **Node Distance** — GPS-based distance calculations between any two nodes on the mesh
- **Multi-Source Awareness** — aggregates data from multiple Meshview instances and MeshMonitor with staggered polling
- **Feeder Gateway Tracking** — identifies which physical MQTT gateways hear each node and signal quality
- **Subscriptions** — scheduled daily/weekly health reports and instant alerts delivered via DM
- **LLM Chat** — general conversation, knowledge base lookups, and weather queries
- **Multi-Backend** — supports Google Gemini, OpenAI, Anthropic Claude, and local LLMs via LiteLLM
## Quick Start ---
## Highlights
- **Dual-transport** — runs on **Meshtastic** and **MeshCore** simultaneously. Each mesh is first-class: independent connection, routing, and behavior, one shared brain.
- **Conversational bot** — DM it "how's the mesh?" or ask about weather, fires, roads, or a specific node, and get a data-driven answer over LoRa. The reply goes back on whichever mesh you asked from.
- **Per-mesh awareness** — it watches chat on each mesh separately (rolling short-term memory), so "what's happening on the mesh?" answers about *your* mesh. Private DMs stay private; curated knowledge stays separate.
- **Broadcast intelligence** — weather alerts, wildfire updates, road/traffic, seismic, RF/band conditions, and mesh-health notifications, formatted to fit LoRa and routed per-mesh, per-family.
- **Mesh health** — a 5-pillar health score with per-region breakdowns, infrastructure monitoring, coverage-gap analysis, and battery/solar tracking (Meshtastic).
- **Web dashboard** — a clean React UI to configure every transport, route every message type, watch a live activity feed, browse contacts, and tune the bot — no config-file spelunking required.
- **Knowledge base (RAG)** — optional hybrid retrieval over a large curated vector store for survival/comms/technical Q&A.
- **Multi-backend LLM** — Google Gemini, OpenAI, Anthropic, or any OpenAI-compatible local model (Ollama, LiteLLM, etc.).
---
## The dashboard
Everything is driven from the web UI, organized into **General**, **Meshtastic**, and **MeshCore** sections — each mesh mirrors the other so there's nothing to relearn when you add the second transport.
**Live activity log** — every broadcast, on both meshes, with per-mesh badges and Sent/Skip status:
![Activity Log](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/activity.png)
**Per-family routing** — decide exactly where each message type goes: broadcast vs. DM, which channel, which recipients — independently for each mesh:
![Routing](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mt-routing.png)
**MeshCore contacts & companion** — the live roster from your MeshCore companion node, with names, types, last-heard, position, and optional telemetry polling:
![MeshCore Contacts](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mc-contacts.png)
**Data feeds** — turn environmental sources on/off and tune thresholds in one place:
![Data Feeds](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/datafeeds.png)
**Nodes & health** — per-node infrastructure detail: battery, utilization, coverage, neighbors, hardware:
![Nodes & Health](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/nodes.png)
---
## Quick start
```bash ```bash
# Clone
git clone https://github.com/zvx-echo6/meshai.git git clone https://github.com/zvx-echo6/meshai.git
cd meshai cd meshai
# Install
pip install -e . pip install -e .
meshai --config # interactive setup TUI
# Configure (interactive TUI)
meshai --config
# Run
meshai meshai
``` ```
@ -38,272 +66,89 @@ Or with Docker:
mkdir -p meshai/data && cd meshai mkdir -p meshai/data && cd meshai
curl -O https://raw.githubusercontent.com/zvx-echo6/meshai/main/docker-compose.yml curl -O https://raw.githubusercontent.com/zvx-echo6/meshai/main/docker-compose.yml
curl -o data/config.yaml https://raw.githubusercontent.com/zvx-echo6/meshai/main/config.example.yaml curl -o data/config.yaml https://raw.githubusercontent.com/zvx-echo6/meshai/main/config.example.yaml
# Edit data/config.yaml # edit data/config.yaml, then:
docker compose up -d docker compose up -d
``` ```
## Commands The dashboard comes up on `http://localhost:8080`.
| Command | Description |
|---------|-------------|
| `!health` | Mesh health overview with colored status dots |
| `!region` | List all regions with health status |
| `!region [name]` | Detailed region breakdown |
| `!neighbors [node]` | Top infrastructure neighbors with signal quality |
| `!sub daily 6pm` | Subscribe to daily health reports |
| `!sub weekly 8am sun` | Subscribe to weekly digest |
| `!sub alerts` | Subscribe to instant alerts on issues |
| `!unsub [type]` | Remove a subscription |
| `!mysubs` | List your active subscriptions |
| `!clear` | Clear conversation history |
| `!help` | Show available commands |
| `!help [cmd]` | Detailed help for a command |
| `!quakes` | Recent earthquakes in monitored area |
| `!fires` | Active wildfires from NIFC |
| `!hotspots` | NASA FIRMS satellite fire detections |
| `!hotspots --new` | Only hotspots not matching known fires |
| `!traffic` | Traffic incidents from TomTom |
| `!space` | Space weather conditions (solar/geomagnetic) |
| `!water` | USGS stream gauge readings |
| `!air` | Air quality index |
Commands can be disabled in config if another service (like MeshMonitor) handles them.
## Mesh Intelligence
MeshAI continuously polls mesh data sources and computes a 5-pillar health score:
| Pillar | Weight | What It Measures |
|--------|--------|------------------|
| Infrastructure | 30% | Router uptime — how many infra nodes are online |
| Utilization | 25% | Channel busyness — RF congestion across the mesh |
| Coverage | 20% | Gateway reach — how many monitoring sources see each node |
| Behavior | 15% | Traffic patterns — detecting noisy or misconfigured nodes |
| Power | 10% | Battery health — infrastructure nodes only |
### Health Display
`!health` shows a compact overview with personality:
```
📡 Mesh 🟢 healthy
🏗️ 15/16 routers up
❌ Down: TVM Tablerock Relay
📶 152 full coverage, 94 on thin ice
🔥 Hayden Peak Router at 21% util
🔋 All infra powered ✅
🌡️ 29-34°C across 2 sensors
Treasure Valley 🟢 | Magic Valley 🟢
```
Status dots: 🔵 perfect (100) · 🟢 healthy (75+) · 🟠 warning (50+) · 🔴 critical (<50)
### Monitoring Rules
Infrastructure nodes (routers, repeaters) are monitored individually with full detail — battery, offline alerts, coverage, neighbors, hardware. Client nodes dying is normal and not tracked. Channel utilization and environmental sensors are monitored for all nodes.
### Conversational Queries
Ask questions naturally over LoRa:
- "how's the mesh?" → health overview with top issues
- "tell me about MHR" → full node detail with neighbors, coverage, feeders
- "where do we need more coverage?" → named gaps with specific nodes
- "how far is MHR from AIDA?" → GPS distance calculation
- "which nodes only reach one gateway?" → named nodes with their gateway
- "which gateway has the best signal?" → feeder comparison
### Geographic Regions
Regions are configurable with local names, descriptions, aliases, and cities — all manageable through the TUI. No hardcoded geography in the code.
```yaml
mesh_intelligence:
regions:
- name: "South Central ID"
local_name: "Magic Valley"
description: "Twin Falls area"
aliases: ["southern Idaho", "magic valley"]
cities: ["Twin Falls", "Burley", "Jerome"]
lat: 42.5
lon: -114.5
radius_km: 80
```
## Environmental Feeds
MeshAI integrates real-time environmental data for situational awareness beyond mesh network health.
### USGS Earthquake Monitoring
```yaml
env:
usgs:
enabled: true
min_magnitude: 2.5
radius_km: 500
center_lat: 43.6150
center_lon: -116.2023
```
No API key required. Data from [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/fdsnws/event/1/).
### NWS Weather Alerts
```yaml
env:
nws:
enabled: true
zone: IDZ025 # NWS zone ID
point: "43.6150,-116.2023"
```
No API key required. Find your zone at [NWS Zone Lookup](https://alerts.weather.gov/).
### NOAA Space Weather
```yaml
env:
noaa_space:
enabled: true
```
No API key required. Data from [NOAA SWPC](https://services.swpc.noaa.gov/).
### NIFC Wildfire Perimeters
```yaml
env:
nifc:
enabled: true
radius_km: 200
center_lat: 43.6150
center_lon: -116.2023
```
No API key required. Data from [NIFC Open Data](https://data-nifc.opendata.arcgis.com/).
### NASA FIRMS Satellite Fire Detection
```yaml
env:
firms:
enabled: true
map_key: "your-map-key" # Required
radius_km: 200
center_lat: 43.6150
center_lon: -116.2023
source: VIIRS_SNPP # VIIRS_SNPP, VIIRS_NOAA20, MODIS_NRT
day_range: 1 # 1, 2, or 10 days
```
**API Key Required**: Register at [NASA FIRMS](https://firms.modaps.eosdis.nasa.gov/api/area/). Free MAP_KEY provides access to near real-time satellite fire detections. Hotspots are cross-referenced against NIFC perimeters to identify potential new ignitions.
### TomTom Traffic
```yaml
env:
tomtom:
enabled: true
api_key: "your-api-key" # Required
bbox: "-117.5,42.5,-115.0,44.5" # lon1,lat1,lon2,lat2
```
**API Key Required**: Register at [TomTom Developer Portal](https://developer.tomtom.com/). Free tier includes 2,500 requests/day.
### 511 Road Conditions
```yaml
env:
fiveonone:
enabled: true
state: ID # State code
api_key: "your-api-key" # If required by state
bbox: [-117.5, 42.5, -115.0, 44.5]
```
API key requirements vary by state. Check your state's 511 developer portal.
### USGS Water Services
```yaml
env:
usgs_water:
enabled: true
sites: ["13206000", "13202000"] # USGS site numbers
```
No API key required. Find sites at [USGS Water Services](https://waterservices.usgs.gov/).
### AirNow Air Quality
```yaml
env:
airnow:
enabled: true
api_key: "your-api-key" # Required
zipcode: "83702"
```
**API Key Required**: Register at [AirNow API](https://docs.airnowapi.org/).
### Dashboard Configuration
```yaml
dashboard:
enabled: true
host: 0.0.0.0
port: 8080
```
The web dashboard provides real-time visualization of mesh nodes, environmental conditions, and alerts with WebSocket push notifications.
--- ---
## Data Sources ## Transports
MeshAI aggregates from multiple sources using staggered tick-based polling (one API call per 30-second tick): MeshAI speaks two mesh protocols. **Meshtastic is always the base transport.** **MeshCore turns on automatically the moment you set a MeshCore host** — there's no separate on/off toggle to forget.
### Meshview ### Meshtastic
Unauthenticated REST API. Supports multiple instances. Connect over TCP (recommended) or serial:
| Endpoint | Interval | Data | ```yaml
|----------|----------|------| connection:
| `/api/packets` | 30s | Near real-time packet feed | type: "tcp" # or "serial"
| `/api/nodes` | 2 min | Node list with metadata | tcp_host: "192.168.1.100"
| `/api/stats` | 3 min | Traffic statistics | tcp_port: 4403
| `/api/edges` | 3 min | Node-to-node connections | # serial_port: "/dev/ttyUSB0"
| `/api/traceroutes` | 5 min | Route data | ```
| `/api/packets_seen` | 10 min | Per-gateway RSSI/SNR (sampled) |
### MeshMonitor ### MeshCore
Authenticated (Bearer token). Single instance. MeshAI attaches to a MeshCore **companion** (the pyMC / MeshCore companion frame server) over TCP and acts as a node on the MeshCore mesh:
| Endpoint | Interval | Data | ```yaml
|----------|----------|------| connection:
| `/api/v1/packets` | 60s | Packet feed | meshcore_host: "192.168.1.253" # blank = MeshCore off
| `/api/v1/nodes` | 2 min | Nodes with battery, utilization, hardware | meshcore_port: 5050
| `/api/v1/telemetry` | 2 min | Environmental sensors, device metrics | ```
| `/api/v1/traceroutes` | 5 min | Route data |
| `/api/v1/channels` | 5 min | Channel configuration |
| `/api/v1/network` | 5 min | Network statistics |
| `/api/v1/solar` | 10 min | Solar estimates |
### Rate Limiting Once connected, MeshCore gets its own **Connection**, **Routing**, **Scheduled Broadcasts**, **Contacts & Companion**, and **Danger Zones** pages in the dashboard — the same capabilities as Meshtastic, using MeshCore's own idioms (channels by name, contacts by pubkey). Messages are sized to fit whichever mesh they go out on.
Built-in protection for all sources: HTTP 429 backoff with Retry-After, exponential backoff on consecutive errors, slow response warnings, and optional polite mode for shared instances. ---
### Source Configuration ## The conversational bot
DM MeshAI on either mesh and it answers with the LLM, using live mesh data, environmental feeds, and (optionally) a knowledge base. A few things it's careful about:
- **Answers on the mesh you asked from.** A MeshCore DM gets a MeshCore reply; a Meshtastic DM gets a Meshtastic reply. Each mesh's "answer DMs" switch is independent.
- **Per-mesh chat memory.** It keeps a short rolling window of recent channel chatter *per mesh* (configurable retention, default 14 days) so "what's happening on the mesh?" reflects the mesh you're on. Ask about the other mesh by name to cross over.
- **Three separate lanes.** Shared channel context, your private DM history, and the curated knowledge base never bleed into each other.
- **LoRa-fit replies.** Responses are chunked to a per-mesh character budget with sentence-aware splitting and continuation prompts.
### Commands
Alongside natural-language questions, a set of `!` commands are available (all toggleable, so they can defer to another service like MeshMonitor):
| Category | Commands |
|----------|----------|
| Mesh | `!health` · `!mesh` · `!status` · `!region [name]` · `!neighbors [node]` |
| Weather / RF | `!wx-alerts` · `!solar` · `!hf` · `!satpass` |
| Fire | `!fire` · `!hotspots` · `!ignitions` |
| Hazards | `!avalanche` · `!roads` / `!traffic` · `!rivers` / `!gauges` |
| Utility | `!help` · `!clear` |
---
## Mesh intelligence (Meshtastic)
MeshAI continuously aggregates mesh data and computes a **5-pillar health score**:
| Pillar | Weight | Measures |
|--------|--------|----------|
| Infrastructure | 30% | Router/repeater uptime |
| Utilization | 25% | Channel busyness / RF congestion |
| Coverage | 20% | How many monitoring sources see each node |
| Behavior | 15% | Traffic patterns (noisy/misconfigured nodes) |
| Power | 10% | Battery health of infrastructure nodes |
Infrastructure nodes are tracked individually (battery, offline alerts, coverage, neighbors, hardware); client nodes coming and going is normal and ignored. Regions are fully configurable — local names, aliases, cities, and radius — with no hardcoded geography.
Data comes from one or more **Meshview** instances and a **MeshMonitor** instance, polled on a staggered schedule with built-in rate-limiting:
```yaml ```yaml
mesh_sources: mesh_sources:
- name: "local-meshview" - name: "meshview"
type: meshview type: meshview
url: "http://192.168.1.100:8080" url: "http://192.168.1.100:8080"
enabled: true enabled: true
- name: "meshmonitor" - name: "meshmonitor"
type: meshmonitor type: meshmonitor
url: "http://192.168.1.100:3333" url: "http://192.168.1.100:3333"
@ -311,285 +156,91 @@ mesh_sources:
enabled: true enabled: true
``` ```
## Knowledge Base (RAG) ---
MeshAI uses a hybrid knowledge retrieval system with two backends: ## Environmental & hazard feeds
### Primary: RECON Qdrant Backend MeshAI pulls real-time situational data and turns it into LoRa broadcasts and query answers. Sources include **NWS weather alerts**, **NIFC wildfire perimeters**, **NASA FIRMS satellite fire detections**, **USGS earthquakes**, **USGS stream gauges**, **road/traffic (511 / TomTom)**, **NOAA space weather**, and **avalanche/RF-propagation** feeds.
Queries [RECON](https://forge.echo6.co/matt/recon)'s knowledge extraction pipeline — 2.8M+ vectors covering survival skills, communications, medical, technical documentation, Meshtastic docs, and more. Uses the same embedding infrastructure as RECON: Everything is switched on/off and tuned from the dashboard's **Data Feeds** page — enable a source, set thresholds and geography, and route its output per-mesh on the **Routing** page. Broadcast wording is tightened to fit a single LoRa packet without dropping the important details (e.g. affected towns on a weather alert).
- **Dense embeddings**: TEI service with BAAI/bge-m3 (1024-dim) ---
- **Sparse embeddings**: bge-m3-sparse with IDF modifier
- **Search**: Qdrant hybrid with Reciprocal Rank Fusion (dense + sparse)
No data is copied — MeshAI queries RECON's Qdrant and TEI services over the network. ## Knowledge base (RAG)
Optional hybrid retrieval for survival, comms, medical, and technical Q&A.
- **Primary** — queries a **Qdrant** hybrid store (dense `bge-m3` + sparse, Reciprocal Rank Fusion) over a large curated vector set, via a networked TEI embedding service. Nothing is copied locally.
- **Fallback** — a local **SQLite** knowledge base (FTS5 keyword + `bge-small-en-v1.5` vectors) if the vector service is unreachable.
```yaml ```yaml
knowledge: knowledge:
enabled: true enabled: true
backend: auto # "qdrant", "sqlite", or "auto" (try qdrant, fall back) backend: auto # qdrant | sqlite | auto
qdrant_host: "192.168.1.150" qdrant_host: "192.168.1.150"
qdrant_port: 6333 qdrant_port: 6333
qdrant_collection: "recon_knowledge_hybrid" qdrant_collection: "recon_knowledge_hybrid"
tei_host: "192.168.1.150" tei_host: "192.168.1.150"
tei_port: 8090 tei_port: 8090
sparse_host: "192.168.1.150"
sparse_port: 8091
use_sparse: true
top_k: 5 top_k: 5
``` ```
### Fallback: Local SQLite The curated channel chatter your bot observes is used only as short-term *context* — it is never written into the knowledge base.
If the Qdrant backend is unreachable, MeshAI falls back to a local SQLite knowledge base using FTS5 keyword search and `bge-small-en-v1.5` vector embeddings (384-dim). ---
```bash ## LLM configuration
# Build from Meshtastic ZIM file
python scripts/zim_to_knowledge.py meshtastic.zim --output knowledge.db
```
```yaml ```yaml
knowledge: llm:
enabled: true backend: "google" # google | openai | anthropic
backend: sqlite api_key: "your-api-key"
db_path: /data/meshai_knowledge.db model: "gemini-2.5-flash"
top_k: 5
``` ```
Requires `sqlite-vec` and `fastembed` for the SQLite backend. Any OpenAI-compatible endpoint works for local models — point `base_url` at Ollama (`http://localhost:11434/v1`), LiteLLM (`http://localhost:4000/v1`), or Open WebUI.
---
## Architecture ## Architecture
``` ```
┌──────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────┐
│ MeshAI │ Meshtastic ────────▶│ │◀──────── MeshCore
├──────────────────────────────────────────────────────────────────────┤ (TCP / serial) │ CompositeTransport │ (companion / pyMC TCP)
│ │ │ per-mesh routing + sizing │
│ DATA SOURCES INTELLIGENCE DELIVERY │ └──────────────┬──────────────┘
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Meshview ×N │─────┐ │ Health Engine │────────▶│ Reporter │ │ ┌─────────────────────────────┼─────────────────────────────┐
│ │ (staggered) │ │ │ 5-pillar │ │ Tier 1/2 │ │ ▼ ▼ ▼
│ └─────────────┘ ▼ │ scoring │ └─────┬──────┘ │ ┌───────────┐ ┌─────────────────┐ ┌──────────────┐
│ ┌─────────────┐ ┌──────┴──┐ │ │ │ │ Router │ │ Notification │ │ Mesh Data │
│ │ MeshMonitor │─▶│ Data │─┘ │ ┌─────▼──────┐ │ │ LLM / cmd │ │ Pipeline │ │ Store + │
│ │ (staggered) │ │ Store │ │ │ Router │ │ │ DM gating │ │ weather · fire │ │ Health │
│ └─────────────┘ │ SQLite │ │ │ scope/dist │ │ │ per-mesh │ │ road · seismic │ │ Engine │
│ └─────────┘ │ └─────┬──────┘ │ │ context │ │ RF · mesh-health│ │ 5-pillar │
│ │ │ │ │ └─────┬─────┘ └────────┬─────────┘ └──────┬───────┘
│ ┌────▼────┐ ┌─────▼──────┐ ┌────▼────┐ │ │ │ │
│ │ Feeder │ │ LLM │ │ Chunker │ │ ┌────▼─────┐ ┌──────────────┐ │ ┌──────────────┐ │
│ │ Sampling│ │ Backend │ │LoRa-fit │ │ │ LLM │ │ Knowledge │ │ │ Env / Central │◀─────┘
│ └─────────┘ └────────────┘ └────┬────┘ │ │ backend │ │ Qdrant/FTS5 │ │ │ feed adapters │
│ │ │ └──────────┘ └──────────────┘ ▼ └──────────────┘
│ KNOWLEDGE ALERTS DELIVERY │ │ ┌──────────┐
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │ │ Responder│ ACK-paced, LoRa-fit,
│ │ RECON/Qdrant│ │ Alert │ │ Subscription │ │ │ │ + Chunker│ routed per mesh
│ │ 2.8M vectors│ │ Engine │ │ Manager │ │ │ └──────────┘
│ │ (network) │ │ 17 triggers │ │ daily/weekly │ │ │
│ ├─────────────┤ │ scaling │ │ alerts │ │ │ Web Dashboard (React) ── configure everything
│ │ SQLite FTS5 │ │ cooldown │ └──────┬───────┘ │ │
│ │ (fallback) │ └──────┬──────┘ │ │ │
│ └─────────────┘ │ ┌─────▼────────┐ │ │
│ └─────────▶│ Responder │◀──┘ │
│ ┌─────────────┐ │ ACK-paced DM │ │
│ │ Conversation│ │ Channel alert│ │
│ │ History │ └──────────────┘ │
│ └─────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘
│ │
┌────▼────┐ ┌────▼────┐
│ TEI │ │ Qdrant │
│ bge-m3 │ │ hybrid │
│ cortex │ │ cortex │
└─────────┘ └─────────┘
``` ```
## Dashboard API Reference ---
The dashboard exposes a REST API (default port 8080): ## Running as a service
### Core Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/health` | GET | System health check |
| `/api/status` | GET | Full system status with health scores |
| `/api/nodes` | GET | Connected mesh nodes |
| `/api/messages` | GET | Recent mesh messages |
### Environmental Data
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/env/earthquakes` | GET | Recent earthquakes |
| `/api/env/weather` | GET | Weather conditions and alerts |
| `/api/env/fires` | GET | Active wildfires from NIFC |
| `/api/env/hotspots` | GET | NASA FIRMS satellite detections |
| `/api/env/traffic` | GET | Traffic incidents |
| `/api/env/water` | GET | Stream gauge readings |
| `/api/env/space` | GET | Space weather data |
| `/api/env/air` | GET | Air quality readings |
### Alerts
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/alerts/active` | GET | Currently active alerts |
| `/api/alerts/history` | GET | Historical alerts (`?severity=`, `?source=`, `?limit=`, `?offset=`) |
| `/api/alerts/{id}/ack` | POST | Acknowledge an alert |
| `/api/subscriptions` | GET | Alert subscriptions |
### WebSocket
Connect to `/ws` for real-time updates:
```javascript
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// data.type: 'message', 'alert', 'node_update', 'health_update'
};
```
## Message Chunking
Long responses are split into mesh-friendly chunks with sentence-aware splitting, configurable limits, and continuation prompts. Command output (like `!health`) packs multiple lines into 2-3 messages using newlines within each message to minimize airtime usage.
```yaml
response:
max_length: 200 # Max chars per message
max_messages: 3 # Messages before continuation prompt
```
## Alerting
Real-time alerts when mesh conditions change, with scaling cooldowns to prevent spam.
### Alert Conditions (17 total, each toggleable)
| Pillar | Condition | Default Threshold |
|--------|-----------|-------------------|
| Infrastructure | Router goes offline | — |
| Infrastructure | Router recovery | — |
| Infrastructure | New router appears | — |
| Power | Battery warning | <50% |
| Power | Battery critical | <25% |
| Power | Battery emergency | <10% |
| Power | 7-day declining trend | >10% drop with rate |
| Power | USB → battery (power outage) | — |
| Power | Solar not charging during day | — |
| Utilization | Sustained high utilization | >20% for 6h |
| Utilization | Packet flood | >500 pkts/24h |
| Coverage | Infra drops to single gateway | — |
| Coverage | Feeder gateway stops responding | — |
| Coverage | Region total blackout | All infra offline |
| Scores | Mesh health score drop | <70/100 |
| Scores | Region health score drop | <60/100 |
### Scaling Cooldown
Alerts don't spam. When a condition triggers:
1. **Alert 1**: fires immediately
2. **Alert 2**: 12 hours later (if still in condition)
3. **Alert 3**: 24 hours after that
4. **Alert 4**: 48 hours after that
5. **Stops** until condition resolves
When the condition clears, one recovery notification fires and the tracker resets.
### Delivery
Alerts are delivered two ways:
- **Channel broadcast**: configurable channel index for mesh-wide visibility
- **DM to subscribers**: users who ran `!sub alerts` receive DMs matching their scope
### Critical Nodes
Designate important infrastructure (e.g., MHR, HPR) as critical. When a critical node goes offline, alerts use priority formatting.
```yaml
mesh_intelligence:
critical_nodes: ["MHR", "HPR"]
alert_channel: 0 # Channel for broadcast alerts (-1 = disabled)
```
All conditions and thresholds are configurable via the TUI under Mesh Intelligence → Alert Rules.
## LLM Configuration
```yaml
llm:
backend: "google" # openai, anthropic, google
api_key: "your-api-key"
model: "gemini-2.0-flash"
```
### Local LLMs
MeshAI works with any OpenAI-compatible API:
- **LiteLLM**: `base_url: "http://localhost:4000/v1"`
- **Open WebUI**: `base_url: "http://localhost:3000/api"`
- **Ollama**: `base_url: "http://localhost:11434/v1"`
## Docker
### TCP Connection (recommended)
```yaml
connection:
type: "tcp"
tcp_host: "192.168.1.100"
tcp_port: 4403
```
### Serial Connection
```yaml
connection:
type: "serial"
serial_port: "/dev/ttyUSB0"
```
Edit `docker-compose.serial.yml` to match your device path.
### Environment Variables
```bash
LLM_API_KEY=your-key-here docker compose up -d
```
## Running Alongside Other Services
### advBBS
MeshAI coexists with [advBBS](https://github.com/zvx-echo6/advbbs) on the same node. BBS protocol messages (sync, RAP, mail notifications) are automatically filtered. No configuration needed.
```yaml
bot:
filter_bbs_protocols: true
```
### MeshMonitor
MeshAI integrates with [MeshMonitor](https://github.com/Yeraze/meshmonitor) at two levels: it fetches MeshMonitor's auto-responder patterns to avoid duplicate responses, and it uses MeshMonitor's API as a data source for mesh intelligence (battery, telemetry, traceroutes, solar).
```yaml
meshmonitor:
enabled: true
url: "http://192.168.1.100:8080"
inject_into_prompt: true
refresh_interval: 300
```
## Running as a Service
```ini ```ini
# /etc/systemd/system/meshai.service # /etc/systemd/system/meshai.service
[Unit] [Unit]
Description=MeshAI - Meshtastic Mesh Intelligence Description=MeshAI
After=network.target After=network.target
[Service] [Service]
@ -606,22 +257,34 @@ WantedBy=multi-user.target
```bash ```bash
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable meshai sudo systemctl enable --now meshai
sudo systemctl start meshai
``` ```
Every deployment is designed to survive a reboot; the dashboard's connection settings drive both transports.
---
## Playing nice with other services
- **advBBS** — MeshAI coexists on the same Meshtastic node; BBS protocol traffic (sync, RAP, mail) is auto-filtered (`bot.filter_bbs_protocols: true`).
- **MeshMonitor** — MeshAI reads MeshMonitor's auto-responder patterns to avoid duplicate replies, and uses its API as a mesh-intelligence data source.
- **MeshCore companion** — MeshAI attaches as its own companion identity so it can share the radio without evicting other companion clients.
---
## Acknowledgments ## Acknowledgments
- [Meshtastic](https://meshtastic.org/) — the mesh networking platform - [Meshtastic](https://meshtastic.org/) — the mesh platform it started on
- [MeshMonitor](https://github.com/Yeraze/meshmonitor) by Yeraze — monitoring integration and data source - [MeshCore](https://meshcore.io/) & [pyMC](https://github.com/rightup/pyMC_core) — the second transport
- [advBBS](https://github.com/zvx-echo6/advbbs) — BBS coexistence design - [MeshMonitor](https://github.com/Yeraze/meshmonitor) by Yeraze — monitoring integration & data source
- [sqlite-vec](https://github.com/asg017/sqlite-vec) by Alex Garcia — vector search in SQLite - [advBBS](https://github.com/zvx-echo6/advbbs) — coexistence design
- [fastembed](https://github.com/qdrant/fastembed) by Qdrant — fast local embeddings - [Qdrant](https://github.com/qdrant/qdrant) · [sqlite-vec](https://github.com/asg017/sqlite-vec) · [fastembed](https://github.com/qdrant/fastembed) — retrieval stack
- The LLM coding assistants that vibecoded most of this
## License ## License
MIT License MIT
## Author ## Author
K7ZVX - matt@echo6.co K7ZVX matt@echo6.co

BIN
docs/images/activity.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

BIN
docs/images/dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

BIN
docs/images/datafeeds.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
docs/images/mc-contacts.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

BIN
docs/images/mt-routing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

BIN
docs/images/nodes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

View file

@ -1,34 +1,62 @@
# MeshAI # MeshAI
LLM-powered mesh intelligence assistant for Meshtastic networks. MeshAI connects to your mesh as a physical node, monitors network health in real-time, and answers questions about your infrastructure over LoRa. **An LLM-powered assistant for LoRa mesh networks — on Meshtastic *and* MeshCore, at the same time.**
## What It Does MeshAI connects to your mesh, watches network health and the world around it in real time, answers questions over the air, and broadcasts the alerts that matter — weather, wildfire, road, seismic, RF, and mesh-health — with a full web dashboard to drive it all.
MeshAI runs on your Meshtastic node and provides: > ### 🤖 Built with AI ("vibecoded")
> In the interest of transparency: **MeshAI was vibecoded** — designed, built, debugged, and documented in close collaboration with LLM coding assistants. The architecture, most of the implementation, and this README were produced that way. It's a real, running project on a live mesh, but expect the pragmatic style, opinionated shortcuts, and occasional rough edges that come with the territory. Issues and PRs are welcome.
- **Mesh Intelligence** — 5-pillar health scoring, per-region breakdowns, infrastructure monitoring, coverage gap analysis, and environmental sensing ![MeshAI dashboard](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/dashboard.png)
- **Conversational Queries** — ask "how's the mesh?" or "tell me about MHR" and get data-driven answers over LoRa
- **Node Distance** — GPS-based distance calculations between any two nodes on the mesh
- **Multi-Source Awareness** — aggregates data from multiple Meshview instances and MeshMonitor with staggered polling
- **Feeder Gateway Tracking** — identifies which physical MQTT gateways hear each node and signal quality
- **Subscriptions** — scheduled daily/weekly health reports and instant alerts delivered via DM
- **LLM Chat** — general conversation, knowledge base lookups, and weather queries
- **Multi-Backend** — supports Google Gemini, OpenAI, Anthropic Claude, and local LLMs via LiteLLM
## Quick Start ---
## Highlights
- **Dual-transport** — runs on **Meshtastic** and **MeshCore** simultaneously. Each mesh is first-class: independent connection, routing, and behavior, one shared brain.
- **Conversational bot** — DM it "how's the mesh?" or ask about weather, fires, roads, or a specific node, and get a data-driven answer over LoRa. The reply goes back on whichever mesh you asked from.
- **Per-mesh awareness** — it watches chat on each mesh separately (rolling short-term memory), so "what's happening on the mesh?" answers about *your* mesh. Private DMs stay private; curated knowledge stays separate.
- **Broadcast intelligence** — weather alerts, wildfire updates, road/traffic, seismic, RF/band conditions, and mesh-health notifications, formatted to fit LoRa and routed per-mesh, per-family.
- **Mesh health** — a 5-pillar health score with per-region breakdowns, infrastructure monitoring, coverage-gap analysis, and battery/solar tracking (Meshtastic).
- **Web dashboard** — a clean React UI to configure every transport, route every message type, watch a live activity feed, browse contacts, and tune the bot — no config-file spelunking required.
- **Knowledge base (RAG)** — optional hybrid retrieval over a large curated vector store for survival/comms/technical Q&A.
- **Multi-backend LLM** — Google Gemini, OpenAI, Anthropic, or any OpenAI-compatible local model (Ollama, LiteLLM, etc.).
---
## The dashboard
Everything is driven from the web UI, organized into **General**, **Meshtastic**, and **MeshCore** sections — each mesh mirrors the other so there's nothing to relearn when you add the second transport.
**Live activity log** — every broadcast, on both meshes, with per-mesh badges and Sent/Skip status:
![Activity Log](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/activity.png)
**Per-family routing** — decide exactly where each message type goes: broadcast vs. DM, which channel, which recipients — independently for each mesh:
![Routing](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mt-routing.png)
**MeshCore contacts & companion** — the live roster from your MeshCore companion node, with names, types, last-heard, position, and optional telemetry polling:
![MeshCore Contacts](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/mc-contacts.png)
**Data feeds** — turn environmental sources on/off and tune thresholds in one place:
![Data Feeds](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/datafeeds.png)
**Nodes & health** — per-node infrastructure detail: battery, utilization, coverage, neighbors, hardware:
![Nodes & Health](https://raw.githubusercontent.com/zvx-echo6/meshai/main/docs/images/nodes.png)
---
## Quick start
```bash ```bash
# Clone
git clone https://github.com/zvx-echo6/meshai.git git clone https://github.com/zvx-echo6/meshai.git
cd meshai cd meshai
# Install
pip install -e . pip install -e .
meshai --config # interactive setup TUI
# Configure (interactive TUI)
meshai --config
# Run
meshai meshai
``` ```
@ -38,272 +66,89 @@ Or with Docker:
mkdir -p meshai/data && cd meshai mkdir -p meshai/data && cd meshai
curl -O https://raw.githubusercontent.com/zvx-echo6/meshai/main/docker-compose.yml curl -O https://raw.githubusercontent.com/zvx-echo6/meshai/main/docker-compose.yml
curl -o data/config.yaml https://raw.githubusercontent.com/zvx-echo6/meshai/main/config.example.yaml curl -o data/config.yaml https://raw.githubusercontent.com/zvx-echo6/meshai/main/config.example.yaml
# Edit data/config.yaml # edit data/config.yaml, then:
docker compose up -d docker compose up -d
``` ```
## Commands The dashboard comes up on `http://localhost:8080`.
| Command | Description |
|---------|-------------|
| `!health` | Mesh health overview with colored status dots |
| `!region` | List all regions with health status |
| `!region [name]` | Detailed region breakdown |
| `!neighbors [node]` | Top infrastructure neighbors with signal quality |
| `!sub daily 6pm` | Subscribe to daily health reports |
| `!sub weekly 8am sun` | Subscribe to weekly digest |
| `!sub alerts` | Subscribe to instant alerts on issues |
| `!unsub [type]` | Remove a subscription |
| `!mysubs` | List your active subscriptions |
| `!clear` | Clear conversation history |
| `!help` | Show available commands |
| `!help [cmd]` | Detailed help for a command |
| `!quakes` | Recent earthquakes in monitored area |
| `!fires` | Active wildfires from NIFC |
| `!hotspots` | NASA FIRMS satellite fire detections |
| `!hotspots --new` | Only hotspots not matching known fires |
| `!traffic` | Traffic incidents from TomTom |
| `!space` | Space weather conditions (solar/geomagnetic) |
| `!water` | USGS stream gauge readings |
| `!air` | Air quality index |
Commands can be disabled in config if another service (like MeshMonitor) handles them.
## Mesh Intelligence
MeshAI continuously polls mesh data sources and computes a 5-pillar health score:
| Pillar | Weight | What It Measures |
|--------|--------|------------------|
| Infrastructure | 30% | Router uptime — how many infra nodes are online |
| Utilization | 25% | Channel busyness — RF congestion across the mesh |
| Coverage | 20% | Gateway reach — how many monitoring sources see each node |
| Behavior | 15% | Traffic patterns — detecting noisy or misconfigured nodes |
| Power | 10% | Battery health — infrastructure nodes only |
### Health Display
`!health` shows a compact overview with personality:
```
📡 Mesh 🟢 healthy
🏗️ 15/16 routers up
❌ Down: TVM Tablerock Relay
📶 152 full coverage, 94 on thin ice
🔥 Hayden Peak Router at 21% util
🔋 All infra powered ✅
🌡️ 29-34°C across 2 sensors
Treasure Valley 🟢 | Magic Valley 🟢
```
Status dots: 🔵 perfect (100) · 🟢 healthy (75+) · 🟠 warning (50+) · 🔴 critical (<50)
### Monitoring Rules
Infrastructure nodes (routers, repeaters) are monitored individually with full detail — battery, offline alerts, coverage, neighbors, hardware. Client nodes dying is normal and not tracked. Channel utilization and environmental sensors are monitored for all nodes.
### Conversational Queries
Ask questions naturally over LoRa:
- "how's the mesh?" → health overview with top issues
- "tell me about MHR" → full node detail with neighbors, coverage, feeders
- "where do we need more coverage?" → named gaps with specific nodes
- "how far is MHR from AIDA?" → GPS distance calculation
- "which nodes only reach one gateway?" → named nodes with their gateway
- "which gateway has the best signal?" → feeder comparison
### Geographic Regions
Regions are configurable with local names, descriptions, aliases, and cities — all manageable through the TUI. No hardcoded geography in the code.
```yaml
mesh_intelligence:
regions:
- name: "South Central ID"
local_name: "Magic Valley"
description: "Twin Falls area"
aliases: ["southern Idaho", "magic valley"]
cities: ["Twin Falls", "Burley", "Jerome"]
lat: 42.5
lon: -114.5
radius_km: 80
```
## Environmental Feeds
MeshAI integrates real-time environmental data for situational awareness beyond mesh network health.
### USGS Earthquake Monitoring
```yaml
env:
usgs:
enabled: true
min_magnitude: 2.5
radius_km: 500
center_lat: 43.6150
center_lon: -116.2023
```
No API key required. Data from [USGS Earthquake Hazards Program](https://earthquake.usgs.gov/fdsnws/event/1/).
### NWS Weather Alerts
```yaml
env:
nws:
enabled: true
zone: IDZ025 # NWS zone ID
point: "43.6150,-116.2023"
```
No API key required. Find your zone at [NWS Zone Lookup](https://alerts.weather.gov/).
### NOAA Space Weather
```yaml
env:
noaa_space:
enabled: true
```
No API key required. Data from [NOAA SWPC](https://services.swpc.noaa.gov/).
### NIFC Wildfire Perimeters
```yaml
env:
nifc:
enabled: true
radius_km: 200
center_lat: 43.6150
center_lon: -116.2023
```
No API key required. Data from [NIFC Open Data](https://data-nifc.opendata.arcgis.com/).
### NASA FIRMS Satellite Fire Detection
```yaml
env:
firms:
enabled: true
map_key: "your-map-key" # Required
radius_km: 200
center_lat: 43.6150
center_lon: -116.2023
source: VIIRS_SNPP # VIIRS_SNPP, VIIRS_NOAA20, MODIS_NRT
day_range: 1 # 1, 2, or 10 days
```
**API Key Required**: Register at [NASA FIRMS](https://firms.modaps.eosdis.nasa.gov/api/area/). Free MAP_KEY provides access to near real-time satellite fire detections. Hotspots are cross-referenced against NIFC perimeters to identify potential new ignitions.
### TomTom Traffic
```yaml
env:
tomtom:
enabled: true
api_key: "your-api-key" # Required
bbox: "-117.5,42.5,-115.0,44.5" # lon1,lat1,lon2,lat2
```
**API Key Required**: Register at [TomTom Developer Portal](https://developer.tomtom.com/). Free tier includes 2,500 requests/day.
### 511 Road Conditions
```yaml
env:
fiveonone:
enabled: true
state: ID # State code
api_key: "your-api-key" # If required by state
bbox: [-117.5, 42.5, -115.0, 44.5]
```
API key requirements vary by state. Check your state's 511 developer portal.
### USGS Water Services
```yaml
env:
usgs_water:
enabled: true
sites: ["13206000", "13202000"] # USGS site numbers
```
No API key required. Find sites at [USGS Water Services](https://waterservices.usgs.gov/).
### AirNow Air Quality
```yaml
env:
airnow:
enabled: true
api_key: "your-api-key" # Required
zipcode: "83702"
```
**API Key Required**: Register at [AirNow API](https://docs.airnowapi.org/).
### Dashboard Configuration
```yaml
dashboard:
enabled: true
host: 0.0.0.0
port: 8080
```
The web dashboard provides real-time visualization of mesh nodes, environmental conditions, and alerts with WebSocket push notifications.
--- ---
## Data Sources ## Transports
MeshAI aggregates from multiple sources using staggered tick-based polling (one API call per 30-second tick): MeshAI speaks two mesh protocols. **Meshtastic is always the base transport.** **MeshCore turns on automatically the moment you set a MeshCore host** — there's no separate on/off toggle to forget.
### Meshview ### Meshtastic
Unauthenticated REST API. Supports multiple instances. Connect over TCP (recommended) or serial:
| Endpoint | Interval | Data | ```yaml
|----------|----------|------| connection:
| `/api/packets` | 30s | Near real-time packet feed | type: "tcp" # or "serial"
| `/api/nodes` | 2 min | Node list with metadata | tcp_host: "192.168.1.100"
| `/api/stats` | 3 min | Traffic statistics | tcp_port: 4403
| `/api/edges` | 3 min | Node-to-node connections | # serial_port: "/dev/ttyUSB0"
| `/api/traceroutes` | 5 min | Route data | ```
| `/api/packets_seen` | 10 min | Per-gateway RSSI/SNR (sampled) |
### MeshMonitor ### MeshCore
Authenticated (Bearer token). Single instance. MeshAI attaches to a MeshCore **companion** (the pyMC / MeshCore companion frame server) over TCP and acts as a node on the MeshCore mesh:
| Endpoint | Interval | Data | ```yaml
|----------|----------|------| connection:
| `/api/v1/packets` | 60s | Packet feed | meshcore_host: "192.168.1.253" # blank = MeshCore off
| `/api/v1/nodes` | 2 min | Nodes with battery, utilization, hardware | meshcore_port: 5050
| `/api/v1/telemetry` | 2 min | Environmental sensors, device metrics | ```
| `/api/v1/traceroutes` | 5 min | Route data |
| `/api/v1/channels` | 5 min | Channel configuration |
| `/api/v1/network` | 5 min | Network statistics |
| `/api/v1/solar` | 10 min | Solar estimates |
### Rate Limiting Once connected, MeshCore gets its own **Connection**, **Routing**, **Scheduled Broadcasts**, **Contacts & Companion**, and **Danger Zones** pages in the dashboard — the same capabilities as Meshtastic, using MeshCore's own idioms (channels by name, contacts by pubkey). Messages are sized to fit whichever mesh they go out on.
Built-in protection for all sources: HTTP 429 backoff with Retry-After, exponential backoff on consecutive errors, slow response warnings, and optional polite mode for shared instances. ---
### Source Configuration ## The conversational bot
DM MeshAI on either mesh and it answers with the LLM, using live mesh data, environmental feeds, and (optionally) a knowledge base. A few things it's careful about:
- **Answers on the mesh you asked from.** A MeshCore DM gets a MeshCore reply; a Meshtastic DM gets a Meshtastic reply. Each mesh's "answer DMs" switch is independent.
- **Per-mesh chat memory.** It keeps a short rolling window of recent channel chatter *per mesh* (configurable retention, default 14 days) so "what's happening on the mesh?" reflects the mesh you're on. Ask about the other mesh by name to cross over.
- **Three separate lanes.** Shared channel context, your private DM history, and the curated knowledge base never bleed into each other.
- **LoRa-fit replies.** Responses are chunked to a per-mesh character budget with sentence-aware splitting and continuation prompts.
### Commands
Alongside natural-language questions, a set of `!` commands are available (all toggleable, so they can defer to another service like MeshMonitor):
| Category | Commands |
|----------|----------|
| Mesh | `!health` · `!mesh` · `!status` · `!region [name]` · `!neighbors [node]` |
| Weather / RF | `!wx-alerts` · `!solar` · `!hf` · `!satpass` |
| Fire | `!fire` · `!hotspots` · `!ignitions` |
| Hazards | `!avalanche` · `!roads` / `!traffic` · `!rivers` / `!gauges` |
| Utility | `!help` · `!clear` |
---
## Mesh intelligence (Meshtastic)
MeshAI continuously aggregates mesh data and computes a **5-pillar health score**:
| Pillar | Weight | Measures |
|--------|--------|----------|
| Infrastructure | 30% | Router/repeater uptime |
| Utilization | 25% | Channel busyness / RF congestion |
| Coverage | 20% | How many monitoring sources see each node |
| Behavior | 15% | Traffic patterns (noisy/misconfigured nodes) |
| Power | 10% | Battery health of infrastructure nodes |
Infrastructure nodes are tracked individually (battery, offline alerts, coverage, neighbors, hardware); client nodes coming and going is normal and ignored. Regions are fully configurable — local names, aliases, cities, and radius — with no hardcoded geography.
Data comes from one or more **Meshview** instances and a **MeshMonitor** instance, polled on a staggered schedule with built-in rate-limiting:
```yaml ```yaml
mesh_sources: mesh_sources:
- name: "local-meshview" - name: "meshview"
type: meshview type: meshview
url: "http://192.168.1.100:8080" url: "http://192.168.1.100:8080"
enabled: true enabled: true
- name: "meshmonitor" - name: "meshmonitor"
type: meshmonitor type: meshmonitor
url: "http://192.168.1.100:3333" url: "http://192.168.1.100:3333"
@ -311,285 +156,91 @@ mesh_sources:
enabled: true enabled: true
``` ```
## Knowledge Base (RAG) ---
MeshAI uses a hybrid knowledge retrieval system with two backends: ## Environmental & hazard feeds
### Primary: RECON Qdrant Backend MeshAI pulls real-time situational data and turns it into LoRa broadcasts and query answers. Sources include **NWS weather alerts**, **NIFC wildfire perimeters**, **NASA FIRMS satellite fire detections**, **USGS earthquakes**, **USGS stream gauges**, **road/traffic (511 / TomTom)**, **NOAA space weather**, and **avalanche/RF-propagation** feeds.
Queries [RECON](https://forge.echo6.co/matt/recon)'s knowledge extraction pipeline — 2.8M+ vectors covering survival skills, communications, medical, technical documentation, Meshtastic docs, and more. Uses the same embedding infrastructure as RECON: Everything is switched on/off and tuned from the dashboard's **Data Feeds** page — enable a source, set thresholds and geography, and route its output per-mesh on the **Routing** page. Broadcast wording is tightened to fit a single LoRa packet without dropping the important details (e.g. affected towns on a weather alert).
- **Dense embeddings**: TEI service with BAAI/bge-m3 (1024-dim) ---
- **Sparse embeddings**: bge-m3-sparse with IDF modifier
- **Search**: Qdrant hybrid with Reciprocal Rank Fusion (dense + sparse)
No data is copied — MeshAI queries RECON's Qdrant and TEI services over the network. ## Knowledge base (RAG)
Optional hybrid retrieval for survival, comms, medical, and technical Q&A.
- **Primary** — queries a **Qdrant** hybrid store (dense `bge-m3` + sparse, Reciprocal Rank Fusion) over a large curated vector set, via a networked TEI embedding service. Nothing is copied locally.
- **Fallback** — a local **SQLite** knowledge base (FTS5 keyword + `bge-small-en-v1.5` vectors) if the vector service is unreachable.
```yaml ```yaml
knowledge: knowledge:
enabled: true enabled: true
backend: auto # "qdrant", "sqlite", or "auto" (try qdrant, fall back) backend: auto # qdrant | sqlite | auto
qdrant_host: "192.168.1.150" qdrant_host: "192.168.1.150"
qdrant_port: 6333 qdrant_port: 6333
qdrant_collection: "recon_knowledge_hybrid" qdrant_collection: "recon_knowledge_hybrid"
tei_host: "192.168.1.150" tei_host: "192.168.1.150"
tei_port: 8090 tei_port: 8090
sparse_host: "192.168.1.150"
sparse_port: 8091
use_sparse: true
top_k: 5 top_k: 5
``` ```
### Fallback: Local SQLite The curated channel chatter your bot observes is used only as short-term *context* — it is never written into the knowledge base.
If the Qdrant backend is unreachable, MeshAI falls back to a local SQLite knowledge base using FTS5 keyword search and `bge-small-en-v1.5` vector embeddings (384-dim). ---
```bash ## LLM configuration
# Build from Meshtastic ZIM file
python scripts/zim_to_knowledge.py meshtastic.zim --output knowledge.db
```
```yaml ```yaml
knowledge: llm:
enabled: true backend: "google" # google | openai | anthropic
backend: sqlite api_key: "your-api-key"
db_path: /data/meshai_knowledge.db model: "gemini-2.5-flash"
top_k: 5
``` ```
Requires `sqlite-vec` and `fastembed` for the SQLite backend. Any OpenAI-compatible endpoint works for local models — point `base_url` at Ollama (`http://localhost:11434/v1`), LiteLLM (`http://localhost:4000/v1`), or Open WebUI.
---
## Architecture ## Architecture
``` ```
┌──────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────┐
│ MeshAI │ Meshtastic ────────▶│ │◀──────── MeshCore
├──────────────────────────────────────────────────────────────────────┤ (TCP / serial) │ CompositeTransport │ (companion / pyMC TCP)
│ │ │ per-mesh routing + sizing │
│ DATA SOURCES INTELLIGENCE DELIVERY │ └──────────────┬──────────────┘
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Meshview ×N │─────┐ │ Health Engine │────────▶│ Reporter │ │ ┌─────────────────────────────┼─────────────────────────────┐
│ │ (staggered) │ │ │ 5-pillar │ │ Tier 1/2 │ │ ▼ ▼ ▼
│ └─────────────┘ ▼ │ scoring │ └─────┬──────┘ │ ┌───────────┐ ┌─────────────────┐ ┌──────────────┐
│ ┌─────────────┐ ┌──────┴──┐ │ │ │ │ Router │ │ Notification │ │ Mesh Data │
│ │ MeshMonitor │─▶│ Data │─┘ │ ┌─────▼──────┐ │ │ LLM / cmd │ │ Pipeline │ │ Store + │
│ │ (staggered) │ │ Store │ │ │ Router │ │ │ DM gating │ │ weather · fire │ │ Health │
│ └─────────────┘ │ SQLite │ │ │ scope/dist │ │ │ per-mesh │ │ road · seismic │ │ Engine │
│ └─────────┘ │ └─────┬──────┘ │ │ context │ │ RF · mesh-health│ │ 5-pillar │
│ │ │ │ │ └─────┬─────┘ └────────┬─────────┘ └──────┬───────┘
│ ┌────▼────┐ ┌─────▼──────┐ ┌────▼────┐ │ │ │ │
│ │ Feeder │ │ LLM │ │ Chunker │ │ ┌────▼─────┐ ┌──────────────┐ │ ┌──────────────┐ │
│ │ Sampling│ │ Backend │ │LoRa-fit │ │ │ LLM │ │ Knowledge │ │ │ Env / Central │◀─────┘
│ └─────────┘ └────────────┘ └────┬────┘ │ │ backend │ │ Qdrant/FTS5 │ │ │ feed adapters │
│ │ │ └──────────┘ └──────────────┘ ▼ └──────────────┘
│ KNOWLEDGE ALERTS DELIVERY │ │ ┌──────────┐
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │ │ Responder│ ACK-paced, LoRa-fit,
│ │ RECON/Qdrant│ │ Alert │ │ Subscription │ │ │ │ + Chunker│ routed per mesh
│ │ 2.8M vectors│ │ Engine │ │ Manager │ │ │ └──────────┘
│ │ (network) │ │ 17 triggers │ │ daily/weekly │ │ │
│ ├─────────────┤ │ scaling │ │ alerts │ │ │ Web Dashboard (React) ── configure everything
│ │ SQLite FTS5 │ │ cooldown │ └──────┬───────┘ │ │
│ │ (fallback) │ └──────┬──────┘ │ │ │
│ └─────────────┘ │ ┌─────▼────────┐ │ │
│ └─────────▶│ Responder │◀──┘ │
│ ┌─────────────┐ │ ACK-paced DM │ │
│ │ Conversation│ │ Channel alert│ │
│ │ History │ └──────────────┘ │
│ └─────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘
│ │
┌────▼────┐ ┌────▼────┐
│ TEI │ │ Qdrant │
│ bge-m3 │ │ hybrid │
│ cortex │ │ cortex │
└─────────┘ └─────────┘
``` ```
## Dashboard API Reference ---
The dashboard exposes a REST API (default port 8080): ## Running as a service
### Core Endpoints
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/health` | GET | System health check |
| `/api/status` | GET | Full system status with health scores |
| `/api/nodes` | GET | Connected mesh nodes |
| `/api/messages` | GET | Recent mesh messages |
### Environmental Data
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/env/earthquakes` | GET | Recent earthquakes |
| `/api/env/weather` | GET | Weather conditions and alerts |
| `/api/env/fires` | GET | Active wildfires from NIFC |
| `/api/env/hotspots` | GET | NASA FIRMS satellite detections |
| `/api/env/traffic` | GET | Traffic incidents |
| `/api/env/water` | GET | Stream gauge readings |
| `/api/env/space` | GET | Space weather data |
| `/api/env/air` | GET | Air quality readings |
### Alerts
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/alerts/active` | GET | Currently active alerts |
| `/api/alerts/history` | GET | Historical alerts (`?severity=`, `?source=`, `?limit=`, `?offset=`) |
| `/api/alerts/{id}/ack` | POST | Acknowledge an alert |
| `/api/subscriptions` | GET | Alert subscriptions |
### WebSocket
Connect to `/ws` for real-time updates:
```javascript
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// data.type: 'message', 'alert', 'node_update', 'health_update'
};
```
## Message Chunking
Long responses are split into mesh-friendly chunks with sentence-aware splitting, configurable limits, and continuation prompts. Command output (like `!health`) packs multiple lines into 2-3 messages using newlines within each message to minimize airtime usage.
```yaml
response:
max_length: 200 # Max chars per message
max_messages: 3 # Messages before continuation prompt
```
## Alerting
Real-time alerts when mesh conditions change, with scaling cooldowns to prevent spam.
### Alert Conditions (17 total, each toggleable)
| Pillar | Condition | Default Threshold |
|--------|-----------|-------------------|
| Infrastructure | Router goes offline | — |
| Infrastructure | Router recovery | — |
| Infrastructure | New router appears | — |
| Power | Battery warning | <50% |
| Power | Battery critical | <25% |
| Power | Battery emergency | <10% |
| Power | 7-day declining trend | >10% drop with rate |
| Power | USB → battery (power outage) | — |
| Power | Solar not charging during day | — |
| Utilization | Sustained high utilization | >20% for 6h |
| Utilization | Packet flood | >500 pkts/24h |
| Coverage | Infra drops to single gateway | — |
| Coverage | Feeder gateway stops responding | — |
| Coverage | Region total blackout | All infra offline |
| Scores | Mesh health score drop | <70/100 |
| Scores | Region health score drop | <60/100 |
### Scaling Cooldown
Alerts don't spam. When a condition triggers:
1. **Alert 1**: fires immediately
2. **Alert 2**: 12 hours later (if still in condition)
3. **Alert 3**: 24 hours after that
4. **Alert 4**: 48 hours after that
5. **Stops** until condition resolves
When the condition clears, one recovery notification fires and the tracker resets.
### Delivery
Alerts are delivered two ways:
- **Channel broadcast**: configurable channel index for mesh-wide visibility
- **DM to subscribers**: users who ran `!sub alerts` receive DMs matching their scope
### Critical Nodes
Designate important infrastructure (e.g., MHR, HPR) as critical. When a critical node goes offline, alerts use priority formatting.
```yaml
mesh_intelligence:
critical_nodes: ["MHR", "HPR"]
alert_channel: 0 # Channel for broadcast alerts (-1 = disabled)
```
All conditions and thresholds are configurable via the TUI under Mesh Intelligence → Alert Rules.
## LLM Configuration
```yaml
llm:
backend: "google" # openai, anthropic, google
api_key: "your-api-key"
model: "gemini-2.0-flash"
```
### Local LLMs
MeshAI works with any OpenAI-compatible API:
- **LiteLLM**: `base_url: "http://localhost:4000/v1"`
- **Open WebUI**: `base_url: "http://localhost:3000/api"`
- **Ollama**: `base_url: "http://localhost:11434/v1"`
## Docker
### TCP Connection (recommended)
```yaml
connection:
type: "tcp"
tcp_host: "192.168.1.100"
tcp_port: 4403
```
### Serial Connection
```yaml
connection:
type: "serial"
serial_port: "/dev/ttyUSB0"
```
Edit `docker-compose.serial.yml` to match your device path.
### Environment Variables
```bash
LLM_API_KEY=your-key-here docker compose up -d
```
## Running Alongside Other Services
### advBBS
MeshAI coexists with [advBBS](https://github.com/zvx-echo6/advbbs) on the same node. BBS protocol messages (sync, RAP, mail notifications) are automatically filtered. No configuration needed.
```yaml
bot:
filter_bbs_protocols: true
```
### MeshMonitor
MeshAI integrates with [MeshMonitor](https://github.com/Yeraze/meshmonitor) at two levels: it fetches MeshMonitor's auto-responder patterns to avoid duplicate responses, and it uses MeshMonitor's API as a data source for mesh intelligence (battery, telemetry, traceroutes, solar).
```yaml
meshmonitor:
enabled: true
url: "http://192.168.1.100:8080"
inject_into_prompt: true
refresh_interval: 300
```
## Running as a Service
```ini ```ini
# /etc/systemd/system/meshai.service # /etc/systemd/system/meshai.service
[Unit] [Unit]
Description=MeshAI - Meshtastic Mesh Intelligence Description=MeshAI
After=network.target After=network.target
[Service] [Service]
@ -606,22 +257,34 @@ WantedBy=multi-user.target
```bash ```bash
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable meshai sudo systemctl enable --now meshai
sudo systemctl start meshai
``` ```
Every deployment is designed to survive a reboot; the dashboard's connection settings drive both transports.
---
## Playing nice with other services
- **advBBS** — MeshAI coexists on the same Meshtastic node; BBS protocol traffic (sync, RAP, mail) is auto-filtered (`bot.filter_bbs_protocols: true`).
- **MeshMonitor** — MeshAI reads MeshMonitor's auto-responder patterns to avoid duplicate replies, and uses its API as a mesh-intelligence data source.
- **MeshCore companion** — MeshAI attaches as its own companion identity so it can share the radio without evicting other companion clients.
---
## Acknowledgments ## Acknowledgments
- [Meshtastic](https://meshtastic.org/) — the mesh networking platform - [Meshtastic](https://meshtastic.org/) — the mesh platform it started on
- [MeshMonitor](https://github.com/Yeraze/meshmonitor) by Yeraze — monitoring integration and data source - [MeshCore](https://meshcore.io/) & [pyMC](https://github.com/rightup/pyMC_core) — the second transport
- [advBBS](https://github.com/zvx-echo6/advbbs) — BBS coexistence design - [MeshMonitor](https://github.com/Yeraze/meshmonitor) by Yeraze — monitoring integration & data source
- [sqlite-vec](https://github.com/asg017/sqlite-vec) by Alex Garcia — vector search in SQLite - [advBBS](https://github.com/zvx-echo6/advbbs) — coexistence design
- [fastembed](https://github.com/qdrant/fastembed) by Qdrant — fast local embeddings - [Qdrant](https://github.com/qdrant/qdrant) · [sqlite-vec](https://github.com/asg017/sqlite-vec) · [fastembed](https://github.com/qdrant/fastembed) — retrieval stack
- The LLM coding assistants that vibecoded most of this
## License ## License
MIT License MIT
## Author ## Author
K7ZVX - matt@echo6.co K7ZVX matt@echo6.co