- Python 46.5%
- JavaScript 41.8%
- TypeScript 11.5%
Third family of the v0.5.7 NATS-and-categories campaign. Fire is the heaviest of the campaign -- four distinct fixes plus a category audit. Two of the four were broken in production: FIRMS subscribed to a syntactically invalid pattern, and WFIGS tombstones were silently dropped.
FIX 1 -- FIRMS NATS pattern (the canonical bug). Pre-v0.5.7-fire `_subjects_for("firms","us.id")` returned `["central.fire.hotspot.>.us.id"]`, which is INVALID NATS (the `>` multi-level wildcard is only legal at the tail token). It also wouldn't have matched anything Central publishes: per the Central v0.10.0 consumer integration guide §firms, the actual published pattern is `central.fire.hotspot.<satellite>.<confidence>` (5 tokens, no us.<state> suffix). The two slots after "hotspot" are satellite name and confidence band -- NOT tile coordinates or region tokens.
Note on prompt vs. guide discrepancy: the v0.5.7-fire task spec described a tile-coord/state pattern `central.fire.hotspot.*.*.us.id` (7 tokens with us.<state> tail). That's neither what Central v0.10.0 publishes nor what its guide documents. We follow the guide. Subscribing to the prompt's 7-token pattern would silently match zero messages in production (token-count mismatch). State filtering for FIRMS happens client-side via data.latitude / data.longitude against the configured region bbox.
New subscription: `central.fire.hotspot.>` -- tail-only `>`, NATS-legal, matches all <satellite>.<confidence> combinations.
FIX 2 -- WFIGS tombstone subjects. Per guide §wfigs_incidents and §wfigs_perimeters, WFIGS publishes:
active: central.fire.incident.<state>.<county> (Convention A, depth-3 state)
active: central.fire.perimeter.<state>.<county>
tombstone: central.fire.incident.removed.<state> (5 tokens, "removed" at depth-3)
tombstone: central.fire.perimeter.removed.<state>
Pre-v0.5.7-fire `_subjects_for("fires","us.id")` subscribed only to the active subjects (`central.fire.incident.id.>` and `central.fire.perimeter.id.>`). The tombstone subjects have "removed" at depth-3 instead of the state token, so the active-subject `>` filters silently dropped EVERY tombstone. Fall-off signals never reached meshai's inhibitor, so old incidents stayed "live" in the pipeline indefinitely.
Added the two tombstone subjects to the subscription list. Both are 5-token literals with no wildcards -- trivially NATS-legal.
FIX 3 -- WFIGS tombstone dedup. Per guide §wfigs_incidents removal semantics, the tombstone env_id has the shape `<IrwinID>:removed:<iso_now>` -- the `:removed:` is sandwiched in the middle, with a timestamp tail. Pre-v0.5.7-fire the consumer.py group_key recovery was `re.sub(r":removed$", "", group_key)` -- a literal trailing `:removed` match -- which DID NOT FIRE on the WFIGS form (the regex required `:removed` at the very end of the string, but the WFIGS form has `:<iso>` after it).
Consequence: WFIGS tombstones' group_key was the full `<IrwinID>:removed:<iso>` string instead of the bare `<IrwinID>`. The pipeline grouper/inhibitor never matched tombstones to their original incidents, so the lapse signal was lost.
Fixed by switching the regex to `re.sub(r":removed(:.*)?$", "", group_key)` -- handles both the WFIGS `<IrwinID>:removed:<iso>` form AND the legacy GDACS `<id>:removed` form. The `is_tombstone` detection also gained an explicit `":removed:" in env_id` check for the WFIGS shape.
Per the guide: "the same incident can have one or more removal tombstones over its lifecycle" (it can re-enter and re-fall-off). To preserve per-tombstone distinctness for downstream lifecycle accounting, the full env_id is stashed on `Event.data["_central_tombstone_id"]` (the group_key collapses to the IrwinID by design, but the original env_id with the :<iso> tail survives on data).
FIX 4 -- ALERT_CATEGORIES fire-family audit + removed parametric entries. Per Matt's direct feedback ("fire near mesh has its own set of parameters that I don't even know what they could be. like how far is near mesh? I don't know I can't set that."), the parametric `fire_proximity` and the duplicate-named `wildfire_proximity` (both labeled "Fire Near Mesh" with parametric radius-based descriptions) were unselectable in the new Advanced Rules UI. Removed both.
Cross-referenced what FIRMS and WFIGS actually emit (per the guide and the native adapter code) and audited the registry:
Native emit:
firms.py -> new_ignition (when adapter flags new_ignition)
or wildfire_hotspot (otherwise) [v0.5.7-fire: was wildfire_proximity]
fires.py -> wildfire_incident
Central path emit (via map_category):
fire.hotspot.* -> wildfire_hotspot
fire.incident.* -> wildfire_incident
fire.perimeter.* -> wildfire_incident (perimeters merge to the incident)
fire.<other> -> wildfire_incident (catchall)
Registry after v0.5.7-fire:
{new_ignition, wildfire_hotspot, wildfire_incident}
Parity confirmed. No orphans, no missing.
Aligning firms.py to emit `wildfire_hotspot` (matching the central FIRMS map) means native + central FIRMS produce identical categories regardless of which feed path is enabled.
Composer (`_CATEGORY_EMOJI`, `_CATEGORY_LABEL`) and router (three source-attribution tables) updated to drop the removed categories and add the new ones.
Deferred to v0.5.8: distance_max_km field on rules for actual proximity filtering. Replaces the parametric fire_proximity registry entry with a parameterized rule field that the user CAN configure ("alert me about wildfire_incident within 30 km" instead of an opaque "Fire Near Mesh" toggle).
Tests
-----
PYTHONPATH=. pytest -q: 380 passed (was 366; +14 net).
- tests/test_fire_v057.py (new): FIRMS subject is tail-only `>` with no mid-subject placement; WFIGS subjects cover active + four tombstones; WFIGS tombstone strips `:removed(:.*)?$` for group_key; two same-IrwinID tombstones both propagate through _handle and share group_key, with the original env_id preserved on data["_central_tombstone_id"]; legacy GDACS `:removed` shape still strips cleanly; fire_proximity / wildfire_proximity absent from ALERT_CATEGORIES; no "Fire Near Mesh" name duplicates; fire-family parity (native + central emit == registry); required-fields check on the three fire entries.
- tests/test_central_region_routing.py: updated FIRMS test (tail-only `>`) and WFIGS test (includes tombstone subjects).
- tests/test_pipeline_toggle_filter.py, tests/test_adapter_firms.py, tests/test_v052_dispatcher.py, tests/test_pipeline_digest.py: bulk-migrated obsolete category references (wildfire_proximity -> wildfire_hotspot, fire_proximity -> wildfire_incident) so the existing test suites continue to exercise the same routing/digest/dispatch paths with the new category names.
Safe-mode preserved (master off, all family toggles off, all adapters native, central disabled). No live toggle flipped. Not tagging yet -- v0.5.7 tag waits until all families ship.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|---|---|---|
| .github/workflows | ||
| config | ||
| dashboard-frontend | ||
| meshai | ||
| tests | ||
| .dockerignore | ||
| .gitattributes | ||
| .gitignore | ||
| config.example.yaml | ||
| docker-compose.yml | ||
| docker-entrypoint.sh | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| requirements.txt | ||
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.
What It Does
MeshAI runs on your Meshtastic node and provides:
- Mesh Intelligence — 5-pillar health scoring, per-region breakdowns, infrastructure monitoring, coverage gap analysis, and environmental sensing
- 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
# Clone
git clone https://github.com/zvx-echo6/meshai.git
cd meshai
# Install
pip install -e .
# Configure (interactive TUI)
meshai --config
# Run
meshai
Or with Docker:
mkdir -p meshai/data && cd meshai
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
# Edit data/config.yaml
docker compose up -d
Commands
| 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.
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
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.
NWS Weather Alerts
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.
NOAA Space Weather
env:
noaa_space:
enabled: true
No API key required. Data from NOAA SWPC.
NIFC Wildfire Perimeters
env:
nifc:
enabled: true
radius_km: 200
center_lat: 43.6150
center_lon: -116.2023
No API key required. Data from NIFC Open Data.
NASA FIRMS Satellite Fire Detection
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. 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
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. Free tier includes 2,500 requests/day.
511 Road Conditions
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
env:
usgs_water:
enabled: true
sites: ["13206000", "13202000"] # USGS site numbers
No API key required. Find sites at USGS Water Services.
AirNow Air Quality
env:
airnow:
enabled: true
api_key: "your-api-key" # Required
zipcode: "83702"
API Key Required: Register at AirNow API.
Dashboard Configuration
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
MeshAI aggregates from multiple sources using staggered tick-based polling (one API call per 30-second tick):
Meshview
Unauthenticated REST API. Supports multiple instances.
| Endpoint | Interval | Data |
|---|---|---|
/api/packets |
30s | Near real-time packet feed |
/api/nodes |
2 min | Node list with metadata |
/api/stats |
3 min | Traffic statistics |
/api/edges |
3 min | Node-to-node connections |
/api/traceroutes |
5 min | Route data |
/api/packets_seen |
10 min | Per-gateway RSSI/SNR (sampled) |
MeshMonitor
Authenticated (Bearer token). Single instance.
| Endpoint | Interval | Data |
|---|---|---|
/api/v1/packets |
60s | Packet feed |
/api/v1/nodes |
2 min | Nodes with battery, utilization, hardware |
/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
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
mesh_sources:
- name: "local-meshview"
type: meshview
url: "http://192.168.1.100:8080"
enabled: true
- name: "meshmonitor"
type: meshmonitor
url: "http://192.168.1.100:3333"
api_token: "your-bearer-token"
enabled: true
Knowledge Base (RAG)
MeshAI uses a hybrid knowledge retrieval system with two backends:
Primary: RECON Qdrant Backend
Queries 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:
- 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:
enabled: true
backend: auto # "qdrant", "sqlite", or "auto" (try qdrant, fall back)
qdrant_host: "192.168.1.150"
qdrant_port: 6333
qdrant_collection: "recon_knowledge_hybrid"
tei_host: "192.168.1.150"
tei_port: 8090
sparse_host: "192.168.1.150"
sparse_port: 8091
use_sparse: true
top_k: 5
Fallback: Local SQLite
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).
# Build from Meshtastic ZIM file
python scripts/zim_to_knowledge.py meshtastic.zim --output knowledge.db
knowledge:
enabled: true
backend: sqlite
db_path: /data/meshai_knowledge.db
top_k: 5
Requires sqlite-vec and fastembed for the SQLite backend.
Architecture
┌──────────────────────────────────────────────────────────────────────┐
│ MeshAI │
├──────────────────────────────────────────────────────────────────────┤
│ │
│ DATA SOURCES INTELLIGENCE DELIVERY │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ Meshview ×N │─────┐ │ Health Engine │────────▶│ Reporter │ │
│ │ (staggered) │ │ │ 5-pillar │ │ Tier 1/2 │ │
│ └─────────────┘ ▼ │ scoring │ └─────┬──────┘ │
│ ┌─────────────┐ ┌──────┴──┐ │ │ │
│ │ MeshMonitor │─▶│ Data │─┘ │ ┌─────▼──────┐ │
│ │ (staggered) │ │ Store │ │ │ Router │ │
│ └─────────────┘ │ SQLite │ │ │ scope/dist │ │
│ └─────────┘ │ └─────┬──────┘ │
│ │ │ │ │
│ ┌────▼────┐ ┌─────▼──────┐ ┌────▼────┐ │
│ │ Feeder │ │ LLM │ │ Chunker │ │
│ │ Sampling│ │ Backend │ │LoRa-fit │ │
│ └─────────┘ └────────────┘ └────┬────┘ │
│ │ │
│ KNOWLEDGE ALERTS DELIVERY │ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │
│ │ RECON/Qdrant│ │ Alert │ │ Subscription │ │ │
│ │ 2.8M vectors│ │ Engine │ │ Manager │ │ │
│ │ (network) │ │ 17 triggers │ │ daily/weekly │ │ │
│ ├─────────────┤ │ scaling │ │ alerts │ │ │
│ │ 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):
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:
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.
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:
- Alert 1: fires immediately
- Alert 2: 12 hours later (if still in condition)
- Alert 3: 24 hours after that
- Alert 4: 48 hours after that
- 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 alertsreceive 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.
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
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)
connection:
type: "tcp"
tcp_host: "192.168.1.100"
tcp_port: 4403
Serial Connection
connection:
type: "serial"
serial_port: "/dev/ttyUSB0"
Edit docker-compose.serial.yml to match your device path.
Environment Variables
LLM_API_KEY=your-key-here docker compose up -d
Running Alongside Other Services
advBBS
MeshAI coexists with advBBS on the same node. BBS protocol messages (sync, RAP, mail notifications) are automatically filtered. No configuration needed.
bot:
filter_bbs_protocols: true
MeshMonitor
MeshAI integrates with 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).
meshmonitor:
enabled: true
url: "http://192.168.1.100:8080"
inject_into_prompt: true
refresh_interval: 300
Running as a Service
# /etc/systemd/system/meshai.service
[Unit]
Description=MeshAI - Meshtastic Mesh Intelligence
After=network.target
[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/meshai
ExecStart=/usr/bin/python3 -m meshai
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable meshai
sudo systemctl start meshai
Acknowledgments
- Meshtastic — the mesh networking platform
- MeshMonitor by Yeraze — monitoring integration and data source
- advBBS — BBS coexistence design
- sqlite-vec by Alex Garcia — vector search in SQLite
- fastembed by Qdrant — fast local embeddings
License
MIT License
Author
K7ZVX - matt@echo6.co