What Is This?
This is a self-hosted Docker stack that brings together everything you need to run a MeshCore bot on your local network — with a friendly web interface for monitoring, control, and configuration. No command-line experience required after the initial setup.
The stack consists of five services that work together:
- Serial Bridge — shares your USB radio with all other services simultaneously
- MeshCore Bot — listens on the mesh, responds to commands, and logs all activity
- Bot Web Viewer — live dashboard showing contacts, mesh graph, stats, and more
- RemoteTerm — interactive terminal for sending commands directly to the radio
- Config Editor — point-and-click interface for all bot settings (no INI file editing)
Architecture
All services run as Docker containers on the same host and communicate over an internal Docker network:
Your MeshCore Radio (USB)
│
[serial-bridge] — owns the USB port, broadcasts to TCP :5000
│ │
[meshcore-bot] [remoteterm]
:8080 web UI :8000 web UI
│
[config-editor]
:8090 web UI
[nginx dashboard] :80 — landing page + API proxy for dashboard widgets
Quick Start
- Plug your MeshCore radio into a USB port on the host machine.
- Make sure
/dev/ttyUSB0(or your device path) is visible:ls /dev/ttyUSB* - Clone or copy this project to your server.
- Open the Config Editor at
http://<your-ip>:8090and fill in at minimum:- Your Bot Name
- Your Timezone (e.g.
America/Chicago) - Your Latitude & Longitude (for weather and location features)
- The Channels you want to monitor
- Click Save Config then Restart Bot.
- Check the Bot Web Viewer at
:8080— the uptime counter should be running.
Dashboard :80
The page you're reading right now. It provides a single entry point to the whole stack — live stats pulled from the bot, quick links to all three service UIs, and this documentation. Stats refresh automatically every 30 seconds.
Use the moon/sun icon in the top-right to switch between dark and light mode. Your preference is saved in the browser.
Bot Web Viewer :8080
The main monitoring interface for the MeshCore bot. Key tabs:
- Dashboard — real-time uptime, component health, and recent activity
- Contacts — every node the bot has heard, with signal strength, hops, and location
- Realtime — live stream of incoming mesh packets
- Mesh Graph — visual map of how nodes are connected
- Stats — 24h/7d/30d activity charts and top users
- Radio — device info and channel list
docker logs serial-bridge
RemoteTerm :8000
A full-featured interactive terminal for MeshCore, built by jkingsman. Use it to:
- Send and receive direct messages
- Browse channel messages in real time
- Inspect raw packet data
- Manage contacts and channels on the device
- Run radio CLI commands interactively
Config Editor :8090
A point-and-click editor for the bot's config.ini file. Every setting has a
description pulled from the config file itself, so you always know what you're changing.
- Toggle switches for true/false settings
- Dropdowns for fixed-choice settings like connection type
- Number spinners for timeouts, rates, and coordinates
- Save Config — writes directly to disk without restarting
- Restart Bot — applies changes that require a full restart
!reload admin command on the mesh.
Serial Bridge internal :5000
A small Python service that owns /dev/ttyUSB0 and exposes it as a TCP server on
port 5000 — internal to the Docker network only, never exposed to the host.
It works as a broadcast multiplexer:
- Every byte received from the radio is sent to all connected TCP clients
- Writes from any client are queued and sent to the radio one at a time
To check which clients are connected and whether the port is open:
docker logs serial-bridge
Configuration Overview
The bot is configured via bot/data/config.ini — an INI-format file divided into
sections. You can edit it directly or use the Config Editor.
Connection
Controls how the bot physically connects to the MeshCore radio.
| Setting | What It Does | Default / Options |
|---|---|---|
connection_type |
Transport to use. In this Docker stack, always set to tcp — the serial bridge handles the USB connection. |
serial · tcp · ble |
hostname |
TCP host to connect to. In Docker, use the service name serial-bridge. |
serial-bridge |
tcp_port |
TCP port the serial bridge listens on. | 5000 |
serial_port |
USB device path. Only used when connection_type = serial. |
/dev/ttyUSB0 |
timeout |
Seconds to wait for a connection before giving up and retrying. | 30 |
Bot Behavior
The main section — controls the bot's identity, location, rate limiting, and how it handles messages.
Identity
| Setting | What It Does | Default |
|---|---|---|
bot_name | The name the bot uses to identify itself on the mesh. This is also set as the device name on startup if auto_update_device_name is enabled. | MeshCoreBot |
auto_update_device_name | Automatically rename the radio device to match bot_name when the bot starts. | true |
node_id | Leave empty for automatic assignment. Only set this if you need a fixed node ID. | empty |
command_prefix | Optional prefix required before every command. E.g. ! makes commands like !ping. Leave empty to allow commands without a prefix. | empty |
Location & Time
| Setting | What It Does | Example |
|---|---|---|
timezone | Timezone for scheduled messages and time-based features. Use standard tz names. | America/Chicago |
bot_latitude | Latitude of the bot's location. Used for weather lookups and proximity calculations. | 33.1788 |
bot_longitude | Longitude of the bot's location. | -96.90622 |
Rate Limiting
These settings prevent the bot from flooding the mesh network with too many messages.
| Setting | What It Does | Default |
|---|---|---|
rate_limit_seconds | Minimum seconds between any two bot transmissions globally. | 10 |
bot_tx_rate_limit_seconds | Minimum seconds between bot-initiated messages (adverts, scheduled messages). | 1.0 |
per_user_rate_limit_seconds | Minimum seconds between replies to the same user. Prevents one user from spamming the bot. | 5 |
per_user_rate_limit_enabled | Enable or disable per-user rate limiting. | true |
tx_delay_ms | Milliseconds to wait before sending a reply. Helps avoid collisions on busy networks. | 250 |
Startup & Advertising
| Setting | What It Does | Options |
|---|---|---|
startup_advert | Whether to announce the bot on startup. flood sends a network-wide advert; zero-hop is local only; false is silent. | false · zero-hop · flood |
advert_interval_hours | Send a periodic flood advert every N hours. Set to 0 to disable. | 0 |
Contact Management
| Setting | What It Does | Options |
|---|---|---|
auto_manage_contacts | How the bot handles new nodes it discovers. bot = bot automatically adds them; device = let the radio handle it; false = manual only. | false · bot · device |
max_channels | Maximum number of channels to fetch from the radio. MeshCore supports up to 40. | 12 |
dm_max_retries | How many times to retry a failed direct message. | 3 |
General
| Setting | What It Does | Default |
|---|---|---|
enabled | Master switch. When false the bot listens but never responds. | true |
passive_mode | When true the bot never transmits anything — pure listener mode. | false |
db_path | Path to the SQLite database file. Relative paths resolve from the config file's directory. | meshcore_bot.db |
Channels
| Setting | What It Does | Example |
|---|---|---|
monitor_channels | Comma-separated list of channel names the bot should watch and respond to. Must match the channel names on your radio exactly. | Public,test,emergency |
respond_to_dms | Whether the bot responds to direct messages sent to it. | true |
channel_keywords | Optional whitelist of commands that are allowed in channels. All commands are always available in DMs. Leave empty to allow all commands in channels. | help,ping,wx |
Banned Users
| Setting | What It Does | Example |
|---|---|---|
banned_users | Comma-separated list of sender names to completely ignore. Matching is prefix-based — BadUser also matches BadUser 🍆. Banned users get no responses in channels or DMs. | SpamBot,TrollUser |
Localization
| Setting | What It Does | Options |
|---|---|---|
language | Language code for bot responses. Falls back to English if a translation is missing. | en · es · fr · de · ja · es-MX |
translation_path | Directory containing translation JSON files. Only change this if you're using a custom translations folder. | translations/ |
Admin Access Control
Certain commands (like !reload and !repeater) are restricted to admin users only.
| Setting | What It Does | Format |
|---|---|---|
admin_pubkeys | Comma-separated list of 64-character hex public keys that have admin privileges. Leave blank to disable all admin commands. Find your public key in the Bot Web Viewer under Contacts. | 64-char hex, e.g. 957d4806… |
admin_commands | Which commands require admin access. | repeater,webviewer,reload |
Companion Purge
When the radio's contact list is nearly full, the bot can automatically remove contacts that haven't been heard from in a while.
| Setting | What It Does | Default |
|---|---|---|
companion_purge_enabled | Enable automatic purging of inactive contacts. Disabled by default for safety. | false |
companion_dm_threshold_days | Consider a companion inactive if they haven't sent a DM in this many days. | 30 |
Bot Commands
These commands can be sent to the bot over the mesh — in a monitored channel or via DM.
| Command | Description | Access |
|---|---|---|
ping | Bot replies with a pong and signal info. | Everyone |
help | Lists all available commands. | Everyone |
wx <location> | Current weather for a zip code or city (e.g. wx 75068 or wx dallas). | Everyone |
stats | Network statistics — node count, active users, top paths. | Everyone |
time | Current time in the bot's configured timezone. | Everyone |
path | Show the routing path between nodes. | Everyone |
reload | Reload config.ini without restarting the bot. Connection settings cannot be changed this way. | Admin only |
repeater | Manage the contact/repeater list. | Admin only |
command_prefix in config (e.g. !), all commands must be prefixed —
so ping becomes !ping.
Troubleshooting
Dashboard shows "Bot Down?" instead of uptime
The bot web viewer can't find the bot's start time in the database. Check the bot is actually running:
docker ps
docker logs meshcore-bot --tail 30
If it's crashing on startup, the most common causes are:
- Serial bridge not ready yet — wait 5 seconds and the bot will auto-retry
- Config syntax error — open the Config Editor and check for obvious mistakes
Bot connects but "Radio Disconnected" shows in dashboard
The serial bridge started but lost the USB device. Check:
docker logs serial-bridge
ls /dev/ttyUSB*
If /dev/ttyUSB0 is gone, the USB device was unplugged or the kernel dropped it. Re-plug the radio and restart the serial bridge:
docker restart serial-bridge
Config Editor says "Restart Bot" but uptime doesn't reset
The Docker socket permission may be wrong. Check:
docker logs config-editor --tail 20
You can manually restart the bot with:
docker restart meshcore-bot
RemoteTerm shows "Connecting…" forever
RemoteTerm connects via the serial bridge. If the bridge is healthy but RemoteTerm can't connect:
docker restart remoteterm
If that doesn't help, restart the whole stack:
cd ~/meshcore && docker compose down && docker compose up -d