Multi-Source β
New in 4.0 (expanded in 4.5+)
Multi-Source lets a single MeshMonitor deployment talk to multiple meshes at once β Meshtastic, MeshCore, and MQTT β side by side. Serial- and BLE-attached Meshtastic nodes reach MeshMonitor through the Serial Bridge or BLE Bridge sidecar (they present as a TCP endpoint). Everything that used to be a global setting (Virtual Node, auto-responder, auto-traceroute, scheduler, permissions) is now configured per source.

What is a Source? β
A source is one upstream connection MeshMonitor speaks to. Each source has:
- A type:
meshtastic_tcp,meshcore,meshcore_mqtt(reads a MeshCore region feed, no radio; see MeshCore MQTT Ingest),mqtt_broker(embedded MQTT broker hosting locally-connected clients),mqtt_bridge(MQTT client to one upstream broker, optionally attached to a siblingmqtt_brokerfor fan-out; see Embedded MQTT Broker & Bridge), orreticulum. Serial and BLE Meshtastic nodes connect through the Serial Bridge / BLE Bridge sidecars and appear asmeshtastic_tcpsources pointing at the bridge container. MeshCore connects directly β USB or TCP through the UI. No sidecar either way. - Its own connection settings (host, port, device path, credentials)
- Its own scheduler (auto-responder, auto-announce, auto-traceroute, auto-ack)
- Its own Virtual Node endpoint (TCP sources only)
- Its own permissions β which users can see/write to it
Sources are stored in the database and managed through the UI, not environment variables.
Where do sources come from? β
Bootstrap (first run) β
On first startup, MeshMonitor creates a meshtastic_tcp source named "Default" from these environment variables:
| Variable | Default | Notes |
|---|---|---|
MESHTASTIC_NODE_IP | unset | Hostname or IP of your node. Unset β the Default source is created disabled, pointing at the placeholder meshtastic.local |
MESHTASTIC_TCP_PORT | 4403 | TCP port |
For a MeshCore node, MESHCORE_TCP_HOST or MESHCORE_SERIAL_PORT creates a "Default MeshCore" source as well (see MeshCore Default Source). With only the MeshCore vars set, the disabled Meshtastic placeholder is skipped.
After that first boot, the env vars are informational only. Changing them won't rename or reconfigure the existing source β use the UI instead. This keeps your single-node docker-compose.yml familiar while unlocking multi-node.
Adding more sources β
Open the Sources sidebar on the dashboard (admin only) to:
- Add source β click the + button next to the Sources header, choose a type, fill in connection details, save
- Edit β rename, re-host, re-port, toggle Virtual Node, or change other per-source settings
- Enable / Disable β pause an upstream connection without deleting it
- Delete β remove the connection and its source-scoped data

Changes that alter the upstream target (host, port, heartbeat) automatically restart the connection β no separate restart action is needed. The Sources sidebar is where all post-bootstrap connection changes happen.
Arranging the sidebar β
New in 4.9.3
- Reorder (Edit mode) β click Edit next to + Add (requires
sources:write) to reveal drag handles, then drag source cards into the order you want. Click Done to hide the handles again. The order is saved server-side and shared across users. - Resize β drag the sidebar's right edge to widen or narrow it; the chosen width is remembered per browser (200β480px). Useful for long MQTT source names.
- The per-source node count badge reflects each source's own nodes consistently, regardless of which source is currently selected.
Unread direct-message badge β
New in 4.16.0
Each source card shows how many unread direct messages it holds, so you can see which source needs you without opening them one by one. Counts over 99 show as 99+.
To switch it off, click Unread DM indicator in the Sources list header. That stops the polling as well as the badges, and MeshMonitor saves the choice to your account, not your browser. Run five sources that all hear the same traffic and you may want this β the badge will stay lit.
The badge obeys the permissions you already hold:
- MeshMonitor counts a source only if you hold
messages:readon that source. A grant on one source never leaks another source's count. - It counts a DM only if you can see the node that sent it β the same rule the per-conversation badges use.
- Muted conversations never light the badge.
- Anonymous visitors see no badges, because unread state belongs to a user.
MeshCore and Reticulum sources show no badge. Their messages live in other tables, and MeshMonitor leaves them out rather than showing a zero it cannot stand behind.
Source picker β
Nearly every top-level view has a source picker in the header. It controls which source's data you're looking at:
- Single-source mode β pick one source; the view shows only that source's nodes, messages, telemetry, and traceroutes
- Unified mode (where available) β combines messages/telemetry/traceroutes across all sources while still tagging each row with its originating source
- On Unified Messages, pick All Channels in the channel list to read every channel you can open in one feed. Each message carries a tag with its channel and a pill for each source that heard it. Direct messages are left out; read those on the source's own Messages tab.
- A MeshCore message heard by several of your sources shows as one row, with a pill for each source that heard it. Only sources you can read are counted.
- The left border of each message shows its mesh protocol: green for Meshtastic (MQTT sources included), orange for MeshCore, the accent colour for Reticulum. Hover the card to read the protocol name. The source pills keep one colour per source, so you can still tell which source heard a message. Channel and DM views on a single source draw the same protocol stripe on each message.
Your picker choice persists per view and per user.
Virtual Node β
Virtual Node is a MeshMonitor feature that lets mobile Meshtastic apps connect through MeshMonitor instead of directly to the node. In 4.0 it is per-source.
- Only
meshtastic_tcpsources support Virtual Node β MeshCore and MQTT sources ignore VN settings - Each source can expose its own VN endpoint on its own port
- Ports must be unique across sources β the API rejects collisions with HTTP 409
Enabling on a source β
- Open Dashboard β Edit Source on the TCP source you want to expose
- Expand the Virtual Node section
- Toggle it on
- Choose a port (the previous global default was
4404) - Decide whether to allow admin commands through the VN (reduces security β leave off unless you know you need it)
- Save

Virtual Node config changes are hot-swapped without reconnecting the upstream TCP socket. Changing host/port still triggers a full reconnect.
Docker port mapping β
When you add a VN, publish the in-container port from your docker-compose.yml:
services:
meshmonitor:
ports:
- "8080:3001" # Web UI
- "4404:4404" # Virtual Node for source A
- "4405:4405" # Virtual Node for source B (if you expose a second)The Docker Compose Configurator publishes one VN host port by default (4404). Match the VN port you choose in the UI to one of the published host ports.
Breaking change from 3.x
The old global env vars no longer drive the Virtual Node at runtime. VIRTUAL_NODE_ALLOW_ADMIN_COMMANDS was removed. ENABLE_VIRTUAL_NODE and VIRTUAL_NODE_PORT only seed the Default source when MeshMonitor first creates it (with MESHTASTIC_NODE_IP set); after that they do nothing. See Headless installs. Re-enable VN in the UI on any other source you want to expose.
Passive Mode β
Passive Mode is a per-source option for Meshtastic TCP sources, in Dashboard β Edit Source. Use it for a large or fragile node, such as a router with a big NodeDB, that closes the TCP link while MeshMonitor syncs.
With Passive Mode on, MeshMonitor:
- Keeps its cached node list and config across a disconnect.
- Skips the full NodeDB download on reconnect. It asks the node for its config only, so packets keep flowing without the full NodeDB stream. It does this only after one full sync has finished; until then, every connect does a full sync.
- Sends no follow-up requests to the node after a sync: no LoRa or module config requests, no time sync, no remote admin scanner, and no remote LocalStats polling. The config-only request already returns the LoRa and module config.
The Resync staleness window (default 4 hours) sets how long the cache stays valid after a disconnect. A reconnect after a longer gap does a full sync again. Resync in the source's menu forces one full sync at any time.
One TCP client per node β
A Meshtastic node serves one TCP client at a time. When a second client connects, the node closes the first. Two clients pointed at one node take turns knocking each other off, so neither gets a stable link.
MeshMonitor logs a warning when:
- two enabled Meshtastic TCP sources point at the same host and port (at startup, and when you save a source), or
- the node closes the link within seconds of connecting, several times in a row.
See the FAQ for how to find the other client.
Default Meshtastic source β
Some requests name no source: an API call to POST /api/messages/send without a sourceId, the v1 default source alias, and device pages opened without a source. They go to one Meshtastic TCP source, the default.
By default this is Automatic: the first Meshtastic TCP source started at boot (sidebar order). To pick one yourself, an admin opens Global Settings β Default Meshtastic source and chooses a source. The change applies at once, without a restart, and survives restarts.
- If the chosen source is disabled, not connected at boot, or set to not auto-connect, Automatic applies until it is running again. Then it takes over again on its own.
- Deleting the chosen source resets the setting to Automatic.
- Anything that names a source (a source page, an automation, an API call with
sourceId) keeps using that source. - The section shows which source a source-less request uses right now.

Per-Source Permissions β
MeshMonitor's role-based access control treats sources as first-class resources. Admins can:
- Grant a user access to one or more sources
- Restrict read/write/admin actions per source
- Hide entire sources from users who shouldn't see them
Unauthorized endpoints return HTTP 403 β URL-hash route bypasses are blocked.
See Per-Source Permissions for the full model.
What else is per-source? β
Everything that used to be a single global setting is now per-source:
| Feature | Per-source in 4.0 |
|---|---|
| Virtual Node | β |
| Auto-Responder | β |
| Auto-Announce | β |
| Auto-Traceroute filter | β |
| Auto-Ack | β |
| Auto-Delete by Distance | β |
| Auto-Favorite | β |
| Scheduled Messages | β |
| Permissions | β |
| Notifications / Apprise | β (admins and subscribers receive per-source events) |
Two display choices are remembered too:
- Show MQTT/Bridge Messages in the Channels view is saved per user and per source. Turning it on for one source leaves the others as they were. MeshMonitor saves it to your account, so it follows you to another browser. Anonymous users keep it in the browser only.
- Unified Messages remembers the channel you last picked in its selector, per browser. It restores that channel on your next visit when the channel still exists, or when you chose All channels. If you never pick one, the page chooses as before.
For global behavior that isn't source-specific (theme, language, map center, push VAPID keys, session policy), use the new Global Settings page β the gear icon in the dashboard sidebar footer.
Migration from 3.x β
If you're upgrading from 3.x:
- Keep your env vars β
MESHTASTIC_NODE_IP/MESHTASTIC_TCP_PORTstill seed the first source on a fresh install - Check your VN env vars β
VIRTUAL_NODE_ALLOW_ADMIN_COMMANDSdoes nothing in 4.0.ENABLE_VIRTUAL_NODE/VIRTUAL_NODE_PORTapply only when MeshMonitor creates the Default source, and never turn on admin commands - Re-enable VN per source β Dashboard β Edit Source β Virtual Node on each TCP source you want to expose
- Review permissions β the per-source permission matrix may need admin review for non-admin users
- Back up first β use System Backup from the Settings page before upgrading. The sources table is included.
REST API β
The v1 API reflects the per-source model directly. All mesh-data resources are scoped under /api/v1/sources/{sourceId}/...:
GET /api/v1/sources # list sources
GET /api/v1/sources/{sourceId}/nodes
GET /api/v1/sources/{sourceId}/messages
GET /api/v1/sources/{sourceId}/channels
GET /api/v1/sources/{sourceId}/telemetry
GET /api/v1/sources/{sourceId}/traceroutes
GET /api/v1/sources/{sourceId}/network
GET /api/v1/sources/{sourceId}/packets
GET /api/v1/sources/{sourceId}/statusUse "default" as {sourceId} to target the default Meshtastic source when one is set and your token can read it, else the oldest enabled source your token can read. All v1 requests require Authorization: Bearer mm_v1_.... See the API Reference for complete documentation.
Related β
- Per-Source Permissions
- Store & Forward
- Auto-Responder
- Packet Monitor β now source-aware