Skip to content

How MMRelay Works

Jeremiah K edited this page Aug 18, 2026 · 6 revisions

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
Loading

Why run it

  • 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.

The pieces

Meshtastic meshnet

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.

The Meshtastic connection

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

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.

Matrix

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.

Mapping Matrix rooms to Meshtastic channels

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: 1

Meshtastic 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.

Meshtastic → Matrix

For an eligible text packet on a mapped channel:

  1. 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.
  2. MMRelay prefixes the text with the sender and origin — [Long name/Meshnet]: by default, configurable via matrix.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.
  3. 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).
  4. 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.

Matrix → Meshtastic

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 sees Alex[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 via meshtastic.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.

Multiple meshnets in one room

The multi-instance pattern relies on the event metadata described above:

  1. 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's meshtastic_* fields.
  2. 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.
  3. 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
Loading

Identity and trust boundary

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.

Where to go next

Clone this wiki locally