A comprehensive web application for monitoring Meshtastic mesh networks over IP. Built with React, TypeScript, and Node.js, featuring a beautiful Catppuccin Mocha dark theme and persistent SQLite database storage.
Get MeshMonitor running in minutes with these simple copy-paste commands:
Get MeshMonitor running in 60 seconds with just 3 steps:
1. Create docker-compose.yml
cat > docker-compose.yml << 'EOF'
services:
meshmonitor:
image: ghcr.io/yeraze/meshmonitor:latest
container_name: meshmonitor
ports:
- "8080:3001"
volumes:
- meshmonitor-data:/data
environment:
- MESHTASTIC_NODE_IP=192.168.1.100 # Change to your node's IP
restart: unless-stopped
volumes:
meshmonitor-data:
EOF
2. Start MeshMonitor
docker compose up -d
3. Open your browser
http://localhost:8080
Default login: Username admin
/ Password changeme
(change this after first login!)
That's it! MeshMonitor is now running with sensible defaults optimized for local HTTP access.
For production deployments with HTTPS, reverse proxies, or advanced security, see Production Deployment Guide
- A Meshtastic node with WiFi/Ethernet connectivity
- TCP port 4403 accessible from your MeshMonitor host
- Docker (or Node.js 20+ for manual deployment)
1. Remote Node Connection
# Connect to a node on your network
export MESHTASTIC_NODE_IP=192.168.5.25
docker compose up -d
2. Subfolder Deployment (e.g., /meshmonitor)
# Run at https://example.com/meshmonitor/
export BASE_URL=/meshmonitor
export MESHTASTIC_NODE_IP=192.168.1.100
docker compose up -d
3. Kubernetes/Helm Deployment
helm install meshmonitor ./helm/meshmonitor \
--set env.meshtasticNodeIp=192.168.1.100 \
--set env.meshtasticTcpPort=4403
4. Using meshtasticd for BLE/Serial Nodes
If your Meshtastic device uses Bluetooth or Serial (not WiFi/Ethernet), you can use meshtasticd as a TCP proxy:
# Install meshtasticd
pip install meshtasticd
# Run meshtasticd to bridge BLE -> TCP
meshtasticd --ble-device "Meshtastic_1234"
# Or for Serial devices
meshtasticd --serial-port /dev/ttyUSB0
# Point MeshMonitor to meshtasticd (default: localhost:4403)
export MESHTASTIC_NODE_IP=localhost
docker compose up -d
This allows MeshMonitor to connect to any Meshtastic device (BLE, Serial, or TCP) through meshtasticd's TCP interface.
- Connect to Meshtastic nodes via direct TCP connection (port 4403)
- Real-time node discovery and status updates via event-driven architecture
- Signal strength monitoring (SNR, RSSI)
- GPS position tracking
- Battery and voltage telemetry
- Node role display (Client, Router, Repeater, etc.)
- Hops Away tracking for network distance
- Beautiful message bubbles with proper alignment
- Interactive reply button - Reply to any message with proper threading
- Instant emoji reactions - Quick tapback with 👍 👎 ❓ ❗ 😂 😢 💩
- Hover-based action menu on each message
- Clickable existing reactions to send the same emoji
- Sender identification dots with tooltips
- Real-time message delivery status
- Send and receive text messages
- Direct messaging and channel broadcasts
- Message persistence across restarts
- Optimistic UI updates for instant feedback
- SQLite database for messages, nodes, and traceroutes
- Automatic data deduplication
- Export/import functionality
- Data cleanup utilities
- Cross-restart persistence
- Traceroute history storage with SNR data
- Catppuccin Mocha dark theme
- Responsive design for mobile/desktop
- Real-time connection status
- Interactive node cards
- Smooth animations and transitions
- Interactive map with node positions
- Automatic traceroute scheduler (runs every 3 minutes)
- Weighted route visualization (thickness based on usage)
- Route display with "Show Routes" toggle
- Network topology discovery
- Traceroute history with SNR tracking
- Dual Notification System: Choose Web Push (browser), Apprise (100+ services), or both
- Web Push Notifications: Browser-based push notifications with PWA support
- Apprise Integration: Send notifications to Discord, Slack, Telegram, Email, SMS, and 100+ other services
- No HTTPS Required: Apprise works over HTTP, removing SSL certificate barrier
- Per-User Preferences: Individual control over notification methods and filtering
- Smart Filtering: Whitelist/blacklist keywords, channel selection, and DM toggles
- Shared Filter Logic: Consistent filtering across both notification methods
- Service Worker: Background push support with custom icons and notification tags
- Notification Sounds: Audio alerts for new messages (configurable per-user)
- Unread Indicators: Visual badges showing unread message counts
- Comprehensive device configuration UI with toast notifications
- Node Settings: Configure long name, short name, device role, and node info broadcast interval
- LoRa Configuration: Set modem preset, region, and hop limit (1-7 hops)
- Position Configuration: Configure fixed position with GPS coordinates, position broadcast interval, and smart positioning
- MQTT Configuration: Set up MQTT server, credentials, encryption, JSON mode, and root topic
- GPS Coordinates Helper: Quick link to find your exact GPS coordinates
- Real-time device synchronization with automatic reboot handling
- Input validation and helpful error messages
- Session-based authentication with secure cookie management
- Local authentication (username/password) with bcrypt password hashing
- OpenID Connect (OIDC) support for enterprise identity providers
- Role-based access control (RBAC) with granular permissions
- Connection control permission - manage who can disconnect/reconnect from nodes
- Traceroute permission - control who can initiate traceroute requests
- Anonymous user support for read-only public access
- Default password warning banner for security best practices
- Audit logging for all authentication and administrative actions
- User management interface for admins (create, deactivate, reset passwords)
- Manual connection control - disconnect/reconnect from nodes with permission control
- Cached data remains accessible while disconnected (read-only mode)
- Connection state preserved through page refreshes
- Header displays connected node info with IP tooltip
- Automatic update notifications - banner alerts when new versions are available
- Container restart functionality from the UI (Docker deployments)
- Version check API - integrates with GitHub releases
- Database-backed settings for automation and configuration persistence
- System status endpoint with uptime, memory usage, and version info
- Pre-built images on GitHub Container Registry
- Full containerization with Docker and Docker Compose
- Persistent data volumes
- Production-ready deployment
- Environment-based configuration
- Multi-architecture support
- Node.js 20+ or 22+ (Node.js 18 is deprecated and will lose support April 2025)
- Docker (recommended) or local Node.js environment
- A Meshtastic device with WiFi/Ethernet connectivity
- Network access to your Meshtastic node
The easiest way to deploy MeshMonitor is using the pre-built Docker images published to GitHub Container Registry:
# Set environment variables
export MESHTASTIC_NODE_IP=192.168.1.100
export MESHTASTIC_TCP_PORT=4403
# Pull and start the application
docker-compose up -d
The default docker-compose.yml
is configured to use ghcr.io/yeraze/meshmonitor:latest
.
You can also specify a specific version:
docker pull ghcr.io/yeraze/meshmonitor:1.10.0
docker run -d \
-p 8080:3001 \
-v meshmonitor-data:/data \
-e MESHTASTIC_NODE_IP=192.168.1.100 \
-e MESHTASTIC_TCP_PORT=4403 \
ghcr.io/yeraze/meshmonitor:1.10.0
If you want to build from source:
-
Edit docker-compose.yml - Comment out the
image
line and uncomment thebuild
section:# image: ghcr.io/yeraze/meshmonitor:latest build: context: . dockerfile: Dockerfile
-
Build and start:
docker-compose up -d --build
- Open http://localhost:8080 in your browser
- The application will automatically attempt to connect to your Meshtastic node
latest
- Latest build from main branch1.0.0
,1.0
,1
- Specific version tagsmain
- Development builds from main branch
MeshMonitor includes a Helm chart for easy deployment to Kubernetes clusters. The chart supports all Docker environment variables, persistent storage, ingress, and subfolder deployments.
Quick Install:
helm install meshmonitor ./helm/meshmonitor \
--set env.meshtasticNodeIp=192.168.1.100 \
--set env.meshtasticTcpPort=4403
For complete Kubernetes documentation, configuration options, and examples, see the Helm Chart README.
-
Clone the repository with submodules
git clone --recurse-submodules https://github.com/Yeraze/meshmonitor.git cd meshmonitor
Important: If you already cloned without
--recurse-submodules
:git submodule update --init --recursive
-
Install dependencies
npm install
-
Configure environment
cp .env.example .env # Edit .env with your Meshtastic node's IP address
-
Start development servers
npm run dev:full
This starts both the React dev server (port 5173) and the Express API server (port 3001).
Variable | Default | Description |
---|---|---|
MESHTASTIC_NODE_IP |
192.168.1.100 |
IP address of your Meshtastic node |
MESHTASTIC_TCP_PORT |
4403 |
TCP port for Meshtastic node connection |
NODE_ENV |
development |
Environment mode |
PORT |
3001 |
Server port (production) |
BASE_URL |
(empty) | Runtime base URL path for subfolder deployment (e.g., /meshmonitor ) |
TZ |
America/New_York |
Timezone for auto-acknowledge message timestamps (see TZ database) |
SESSION_SECRET |
(auto-generated) | Secret key for session encryption. Required for production deployments |
SESSION_MAX_AGE |
86400000 |
Session cookie lifetime in milliseconds (default: 24 hours) |
COOKIE_SECURE |
true (production)false (development) |
Require HTTPS for session cookies. Set to false if accessing over HTTP in production (not recommended) |
COOKIE_SAMESITE |
strict (production)lax (development) |
SameSite cookie policy (strict , lax , or none ) |
TRUST_PROXY |
1 (production)unset (development) |
Trust reverse proxy headers (true , false , number of hops, or IP/CIDR). Required for HTTPS reverse proxy setups |
Your Meshtastic device must have:
- WiFi or Ethernet connectivity
- TCP port 4403 accessible (standard Meshtastic port)
- Network accessibility from MeshMonitor
MeshMonitor v2.0+ includes a comprehensive authentication and authorization system with:
- Local Authentication: Username/password with secure bcrypt hashing
- OpenID Connect (OIDC): Integration with enterprise identity providers
- Granular Permissions: Resource-based access control (dashboard, nodes, channels, messages, settings, connection control, traceroute initiation, etc.)
- User Management: Admin interface for creating and managing users
- Session Security: Secure session storage with SQLite backend
On first startup, MeshMonitor creates a default admin account:
- Username:
admin
- Password:
changeme
MeshMonitor supports Single Sign-On (SSO) with any OpenID Connect provider:
- Authentik - Open-source Identity Provider
- Keycloak - Red Hat's open-source IAM
- Auth0, Okta, Azure AD, Google Workspace
- Any OIDC-compliant provider
Enable OIDC:
environment:
- OIDC_ENABLED=true
- OIDC_ISSUER=https://auth.example.com/application/o/meshmonitor/
- OIDC_CLIENT_ID=your-client-id
- OIDC_CLIENT_SECRET=your-client-secret
- OIDC_REDIRECT_URI=https://meshmonitor.example.com/api/auth/oidc/callback
For complete authentication setup, OIDC configuration with step-by-step provider examples, permission management, and security best practices, see:
📖 Authentication & Authorization Guide
MeshMonitor supports being served from a subfolder using the BASE_URL
environment variable. This allows you to host MeshMonitor at a path like https://example.com/meshmonitor/
instead of the root.
Important: BASE_URL is now a runtime-only configuration. You can use the same Docker image for any base path - just set the BASE_URL environment variable when running the container.
When deploying MeshMonitor behind a reverse proxy with HTTPS (recommended), you need to configure MeshMonitor to trust the proxy headers. This is critical for proper session cookie handling.
Why this matters: When a reverse proxy terminates HTTPS, MeshMonitor sees the connection as HTTP. Without trusting proxy headers, secure cookies won't work correctly, causing login failures.
Solution: Set TRUST_PROXY=true
(or TRUST_PROXY=1
for single proxy):
environment:
- NODE_ENV=production
- TRUST_PROXY=true # Required for HTTPS reverse proxy
- SESSION_SECRET=your-secret-here
Advanced trust proxy configurations:
TRUST_PROXY=true
- Trust all proxies (use with caution)TRUST_PROXY=1
- Trust first proxy only (recommended, default in production)TRUST_PROXY=2
- Trust first 2 proxies (for CDN + reverse proxy)TRUST_PROXY=false
- Don't trust any proxiesTRUST_PROXY=192.168.1.0/24
- Trust specific subnet
server {
listen 443 ssl;
server_name meshmonitor.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # Required for TRUST_PROXY
}
}
MeshMonitor Configuration:
environment:
- NODE_ENV=production
- TRUST_PROXY=true
- SESSION_SECRET=your-secret-here
location ^~ /meshmonitor {
# Strip the /meshmonitor prefix when proxying
rewrite /meshmonitor(.*) /$1 break;
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme; # Required for TRUST_PROXY
}
MeshMonitor Configuration:
environment:
- NODE_ENV=production
- BASE_URL=/meshmonitor
- TRUST_PROXY=true
- SESSION_SECRET=your-secret-here
services:
meshmonitor:
image: ghcr.io/yeraze/meshmonitor:latest
labels:
- "traefik.enable=true"
- "traefik.http.routers.meshmonitor.rule=Host(`meshmonitor.example.com`)"
- "traefik.http.routers.meshmonitor.entrypoints=websecure"
- "traefik.http.routers.meshmonitor.tls.certresolver=letsencrypt"
- "traefik.http.services.meshmonitor.loadbalancer.server.port=3001"
environment:
- NODE_ENV=production
- TRUST_PROXY=true # Traefik automatically sets X-Forwarded-* headers
- SESSION_SECRET=your-secret-here
- MESHTASTIC_NODE_IP=192.168.1.100
meshmonitor.example.com {
reverse_proxy localhost:8080
}
MeshMonitor Configuration:
environment:
- NODE_ENV=production
- TRUST_PROXY=true # Caddy automatically sets X-Forwarded-* headers
- SESSION_SECRET=your-secret-here
Simply set the BASE_URL
environment variable at runtime:
services:
meshmonitor:
image: ghcr.io/yeraze/meshmonitor:latest
environment:
- BASE_URL=/meshmonitor # Set at runtime, no rebuild needed!
- MESHTASTIC_NODE_IP=192.168.1.100
- MESHTASTIC_TCP_PORT=4403
# ... rest of configuration
Or with docker run:
docker run -d \
-p 8080:3001 \
-e BASE_URL=/meshmonitor \
-e MESHTASTIC_NODE_IP=192.168.1.100 \
-e MESHTASTIC_TCP_PORT=4403 \
ghcr.io/yeraze/meshmonitor:latest
The HTML is dynamically rewritten at runtime to include the correct base path for all assets and API calls. This means:
- ✅ No need to rebuild the image for different base paths
- ✅ The same image works for root (
/
) or any subfolder path - ✅ Can be changed by simply restarting the container with a different BASE_URL
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ React App │────│ Express API │────│ SQLite Database│
│ (Frontend) │ │ (Backend) │ │ (Persistence) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ │ │
└────────────────────────┼────────────────────────┘
│
│ TCP Connection
│ (Event-Driven)
↓
┌─────────────────────────┐
│ Meshtastic Node │
│ TCP Port 4403 │
│ (WiFi/Ethernet) │
└─────────────────────────┘
TCP Streaming Protocol (New in v1.10.0)
MeshMonitor connects directly to Meshtastic devices using their native TCP streaming protocol on port 4403. This provides:
- Real-time event delivery - Messages arrive instantly (no polling delays)
- Efficient bandwidth usage - ~90% reduction vs HTTP polling
- Automatic reconnection - Exponential backoff recovery from connection drops
- Binary protocol - 4-byte framed packets (0x94 0xc3 + length MSB/LSB + protobuf payload)
Connection Flow:
- TCP socket connects to node on port 4403
- Backend sends
want_config_id
message to request configuration - Node streams configuration (myInfo, channels, nodes) as individual frames
- Event-driven message processing begins
- All messages, position updates, and telemetry arrive as TCP frames
Frame Protocol:
[0x94][0x93][LENGTH_MSB][LENGTH_LSB][PROTOBUF_PAYLOAD]
│ │ │ │ │
│ │ │ │ └─ Encoded FromRadio/ToRadio message
│ │ │ └─ Payload length low byte (0-255)
│ │ └─ Payload length high byte (0-512)
│ └─ Frame marker byte 2
└─ Frame marker byte 1 (Meshtastic protocol identifier)
MeshMonitor's TCP-based architecture is compatible with multiple Meshtastic connection methods:
1. Direct TCP Connection (WiFi/Ethernet nodes)
MeshMonitor → TCP:4403 → Meshtastic Node
Standard deployment for nodes with network connectivity.
2. meshtasticd Proxy (BLE/Serial nodes)
MeshMonitor → TCP:4403 → meshtasticd → BLE/Serial → Meshtastic Node
meshtasticd bridges Bluetooth or Serial connections to TCP, allowing MeshMonitor to work with any Meshtastic device.
3. HomeAssistant Integration
MeshMonitor → TCP:4403 → HomeAssistant MQTT Bridge → Meshtastic
Connect through HomeAssistant's Meshtastic integration for unified smart home monitoring.
4. Other TCP Implementations
MeshMonitor → TCP:4403 → Custom Proxy → Meshtastic Network
Any proxy implementing the Meshtastic TCP protocol can bridge to MeshMonitor.
- Frontend (React): User interface with Catppuccin theme and real-time updates
- Backend (Express): REST API, static file serving, and TCP transport layer
- Database (SQLite): Message, node, and telemetry data persistence
- TCP Transport: Event-driven connection to Meshtastic with automatic reconnection
- Frame Parser: Robust buffer management and protobuf message extraction
GET /api/nodes
- Get all nodesGET /api/nodes/active
- Get recently active nodes
GET /api/messages
- Get messages with paginationPOST /api/messages/send
- Send message to channel (supportstext
,channel
,destination
,replyId
,emoji
)GET /api/messages/channel/:channel
- Channel-specific messagesGET /api/messages/direct/:nodeId1/:nodeId2
- Direct messages
GET /api/stats
- Database statisticsGET /api/health
- Health check
GET /api/traceroutes/recent
- Get recent traceroutesPOST /api/traceroutes/send
- Send traceroute request
POST /api/export
- Export all dataPOST /api/import
- Import dataPOST /api/cleanup/messages
- Cleanup old messagesPOST /api/cleanup/nodes
- Cleanup inactive nodesPOST /api/cleanup/channels
- Cleanup invalid channels
GET /api/channels
- Get all channelsGET /api/config
- Get configurationGET /api/device-config
- Get device configuration
GET /api/connection
- Get connection status
interface DeviceInfo {
nodeNum: number;
user?: {
id: string;
longName: string;
shortName: string;
hwModel: number;
};
position?: {
latitude: number;
longitude: number;
altitude?: number;
};
deviceMetrics?: {
batteryLevel?: number;
voltage?: number;
snr?: number;
rssi?: number;
};
lastHeard?: number;
}
interface MeshMessage {
id: string;
from: string;
to: string;
fromNodeId: string;
toNodeId: string;
text: string;
timestamp: Date;
channel: number;
portnum?: number;
acknowledged?: boolean;
ackFailed?: boolean;
isLocalMessage?: boolean;
hopStart?: number;
hopLimit?: number;
replyId?: number; // Message ID being replied to
emoji?: number; // 0=normal message, 1=tapback reaction
}
interface Channel {
id: number;
name: string;
psk?: string;
uplinkEnabled: boolean;
downlinkEnabled: boolean;
createdAt: number;
updatedAt: number;
}
npm run dev
- Start React development servernpm run dev:server
- Start Express API servernpm run dev:full
- Start both development serversnpm run build
- Build React app for productionnpm run build:server
- Build Express server for productionnpm start
- Start production server
npm run test
- Run tests in watch modenpm run test:run
- Run all tests oncenpm run test:ui
- Run tests with interactive UInpm run test:coverage
- Generate coverage reportnpm run lint
- Run ESLintnpm run typecheck
- Run TypeScript compiler checks
MeshMonitor includes a comprehensive test suite using Vitest and Testing Library:
- Unit Tests: Database operations, API endpoints, and utility functions
- Component Tests: React components with user interaction testing
- Integration Tests: API integration and data flow validation
- 100% passing tests maintained through CI/CD
All pull requests must pass automated testing before merge. See CONTRIBUTING.md for testing guidelines.
Our GitHub Actions workflows ensure code quality:
- PR Tests: Quick validation on every pull request
- Full CI: Comprehensive testing across Node.js versions
- Docker Builds: Multi-platform image creation
- Security Scanning: Vulnerability detection with Trivy
- Release Automation: Automated versioning and publishing
meshmonitor/
├── src/
│ ├── components/ # React components
│ ├── services/ # Services (Meshtastic, Database)
│ ├── server/ # Express server
│ └── App.tsx # Main React application
├── docs/ # Documentation
├── data/ # SQLite database (development)
├── docker-compose.yml # Docker Compose configuration
├── Dockerfile # Docker build configuration
└── package.json # Dependencies and scripts
Frontend:
- React 18 with TypeScript
- Vite (build tool)
- CSS3 with Catppuccin theme
- Modern ES modules
Backend:
- Node.js with Express
- TypeScript
- better-sqlite3 (SQLite driver)
- CORS enabled
DevOps:
- Docker with multi-stage builds
- Docker Compose for orchestration
- Volume mounting for data persistence
- Environment-based configuration
-
Login succeeds but immediately logs out / Session not maintained
This is a cookie security issue. The solution depends on your deployment:
Scenario A: HTTPS Reverse Proxy (Recommended)
Browser ←HTTPS→ Reverse Proxy ←HTTP→ MeshMonitor
✅ Solution: Set
TRUST_PROXY=true
to trust the proxy's headersenvironment: - NODE_ENV=production - TRUST_PROXY=true - SESSION_SECRET=your-secret-here
This allows MeshMonitor to detect the HTTPS connection via
X-Forwarded-Proto
header.Scenario B: Direct HTTP Access (Not Recommended for Production)
Browser ←HTTP→ MeshMonitor
⚠️ Solution: SetCOOKIE_SECURE=false
to allow cookies over HTTPenvironment: - NODE_ENV=production - COOKIE_SECURE=false - SESSION_SECRET=your-secret-here
Warning: This reduces security. Use HTTPS if possible.
Scenario C: Direct HTTPS Access
Browser ←HTTPS→ MeshMonitor (with TLS cert)
✅ No configuration needed - secure cookies work automatically.
How to diagnose:
- Check browser DevTools → Application → Cookies
- If
meshmonitor.sid
cookie is missing, it's a cookie security issue - Check container logs for warnings about SESSION_SECRET or COOKIE_SECURE
-
Cannot connect to Meshtastic node
- Check IP address in
.env
file - Ensure TCP port 4403 is accessible
- Verify network connectivity
- Check firewall settings (allow TCP port 4403)
- Check IP address in
-
Database errors
- Ensure
/data
directory is writable - Check disk space
- Verify SQLite permissions
- Ensure
-
Build failures
- Run
npm install
to update dependencies - Check Node.js version (20+ required)
- Clear
node_modules
and reinstall
- Run
View logs in development:
npm run dev:full
View Docker logs:
docker-compose logs -f meshmonitor
We welcome contributions! Please see our Contributing Guide for details on:
- Development setup
- Testing requirements
- Code style guidelines
- Pull request process
- CI/CD workflows
Quick start:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Make your changes and add tests
- Run tests locally (
npm run test:run
) - Commit with conventional commits (
feat: add amazing feature
) - Push and create a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Meshtastic - Open source mesh networking
- Catppuccin - Soothing pastel theme
- React - Frontend framework
- better-sqlite3 - SQLite driver
MeshMonitor - Monitor your mesh, beautifully. 🌐✨
This application was entirely vibe coded with Claude Code.