-
Notifications
You must be signed in to change notification settings - Fork 19
How MMRelay Works
MMRelay is a self-hosted bridge between a Meshtastic meshnet and Matrix. It runs continuously on a computer or server, connects to a Meshtastic node, and logs in to Matrix with a dedicated bot account.
The important idea is simple:
Meshtastic meshnet ⇄ Meshtastic node ⇄ MMRelay ⇄ Matrix rooms
Only traffic covered by MMRelay's configuration crosses that boundary.
sequenceDiagram
participant N as Meshtastic node
participant R as MMRelay
participant X as Matrix room
Note over N,X: Mesh to Matrix
N->>R: text packet on mapped channel
R->>X: bot posts message with sender prefix
Note over N,X: Matrix to Mesh
X->>R: message from a Matrix user
R->>N: transmit on the mapped channel
- Reach the mesh from anywhere. A meshnet only covers as far as its radios. Bridged into a Matrix room, the mesh is reachable from any Matrix client with Internet access — you can keep up with (and talk to) your mesh from your phone while you're away from the radio, or from anywhere with connectivity but no LoRa coverage.
- Let people without radios take part. Family, friends, or a community don't need LoRa hardware or a second app; from their side the mesh is just another Matrix room.
- Connect meshnets across regions. LoRa range is local. Multiple operators can each bridge their meshnet into a shared Matrix room, joining communities that could never reach each other over the air (see Multiple meshnets in one room).
- Ride Matrix's bridges. Matrix connects to many other chat platforms; a mesh bridged into a Matrix room can, through those bridges, reach users on other networks too.
A meshnet is the local Meshtastic network. Its radios (also commonly called nodes or devices) communicate over the mesh and may relay packets for one another.
MMRelay does not need a direct connection to every node. It communicates with the mesh through one Meshtastic node. That node should be dedicated to MMRelay: no other clients (phone app, other instances) may connect to it while MMRelay is running, or they will fight over the radio connection. This is a Meshtastic firmware design limitation — a node serves one client at a time — not an MMRelay restriction. The dedicated node is still a normal participant in the Meshtastic mesh.
MMRelay supports all three Meshtastic device connection interfaces:
- Serial — the node is attached directly to the MMRelay host.
- BLE — the MMRelay host connects to the node over Bluetooth Low Energy.
- TCP / network — the node is reachable by IP, commonly through Wi-Fi or Ethernet depending on its firmware and hardware.
MMRelay is the bridge: a Meshtastic client and a Matrix client in one process, with relay rules deciding which traffic crosses between them and how it is formatted on the way.
Both are client connections, so there is no bridge infrastructure to operate: the Meshtastic side needs only your node, and the Matrix side logs in to an existing homeserver like any other Matrix account — unlike many Matrix bridges, you do not need to self-host a homeserver.
MMRelay logs in with a dedicated Matrix bot account and joins the rooms listed in its configuration. Those rooms can be on matrix.org or another homeserver; the important part is that the bot account can access them.
The matrix_rooms section maps a Meshtastic channel to a Matrix room:
matrix_rooms:
- id: "#field-ops:example.org"
meshtastic_channel: 0
- id: "#family:example.org"
meshtastic_channel: 1Meshtastic firmware provides eight channels per node, numbered 0–7. MMRelay requires at least one mapped channel and supports mapping up to all eight. Within one MMRelay instance the mapping is 1:1: one Meshtastic channel maps to one Matrix room.
To bridge multiple meshnets into a shared Matrix room, run one MMRelay instance per meshnet and map each instance's channel to the same room. Each instance relays as its own bot account, and each message carries its origin — see Multiple meshnets in one room for how that round-trips.
Unmapped rooms and channels are not automatically bridged simply because the bot or Meshtastic node can see them.
For an eligible text packet on a mapped channel:
- The mesh delivers the packet to the node MMRelay is connected to. MMRelay decodes it and resolves the sender's long/short name and meshnet name from its node database.
- MMRelay prefixes the text with the sender and origin —
[Long name/Meshnet]:by default, configurable viamatrix.prefix_format— and posts it to the mapped room as the bot. The prefix is part of the message body, which is how room members see who and where the message came from. - The Matrix event also carries machine-readable fields
(
meshtastic_longname,meshtastic_shortname,meshtastic_meshnet, the Meshtastic packet ID, and the raw message text). These are not for display; they exist so other MMRelay instances sharing the room can relay the message onto their own meshnets with the original sender's identity intact (see below). - When message storage is enabled, MMRelay records the packet ID ↔ Matrix event ID mapping in its SQLite database so later replies and reactions can be correlated across the bridge.
MMRelay ignores events sent by its own bot. For other events in a mapped room:
-
Messages from Matrix users are prefixed with the sender's Matrix display
name —
{display5}[M]:by default, so the mesh seesAlex[M]: hello— truncated to the Meshtastic payload size, and transmitted on the channel mapped to that room. The[M]marker tells the mesh the sender is a Matrix user; the format is configurable viameshtastic.prefix_format. -
Messages relayed by another MMRelay instance are recognized by their
meshtastic_*fields. MMRelay reads the original sender's identity from the event metadata — not from the display text — and transmits the message on its own mesh with that identity preserved, so the receiving mesh sees the original sender (Alice/MeshnetA), not a Matrix user. If the event's meshnet matches the local one, the message is dropped, which prevents relay loops. -
Replies are sent as Meshtastic replies to the original packet, using the
stored ID mapping. Reactions become a short text notice on the mesh,
e.g.
Alex[M] reacted 😀 to "message preview".
Transmission to the mesh is gated by the broadcast_enabled setting, and bot
commands from plugins are handled before anything is relayed.
The multi-instance pattern relies on the event metadata described above:
- Alice sends a message on meshnet A. The meshnet A relay posts it to the
shared room as
[Alice/MeshnetA]: hello, with Alice's identity and the meshnet name in the event'smeshtastic_*fields. - The meshnet B relay sees the event from the meshnet A bot, reads the
metadata, and transmits the message on meshnet B with Alice's identity
preserved — nodes on meshnet B see the message as coming from
Alice/MeshnetA, not from a Matrix user or a foreign bot. - Replies and reactions flow back through the same path, so a node on meshnet B replying to Alice's message reaches meshnet A as a proper Meshtastic reply.
Every hop between meshnets is relay-to-relay over ordinary Matrix events; there is no separate inter-server channel.
sequenceDiagram
participant A as Meshnet A node
participant RA as MMRelay A
participant X as Shared Matrix room
participant RB as MMRelay B
participant B as Meshnet B node
A->>RA: text packet from Alice
RA->>X: bot A posts message, sender identity in event fields
X->>RB: event from bot A
RB->>B: transmit with Alice's identity preserved
Matrix and Meshtastic have different identity systems. MMRelay bridges messages; it does not turn a Meshtastic node identity into a verified Matrix identity.
That means anyone able to send relay-eligible traffic on a bridged Meshtastic channel can cause the MMRelay bot to post corresponding traffic into its mapped Matrix room. Operators should therefore bridge only channels and rooms whose participants they intend to connect.
In a multi-instance room, each relay forwards the attribution supplied by the other instances. That attribution is data carried in the event, not something Matrix verifies, so a misconfigured or untrusted instance can present forged sender names to your mesh.
Matrix end-to-end encryption protects Matrix traffic and the MMRelay bot's Matrix device identity. It does not authenticate Meshtastic sender names across the bridge.