MQTT Topic Structure and Packet Format
Understanding Meshtastic's MQTT topic structure and packet encoding lets you build integrations, parse data, and troubleshoot gateway connectivity issues.
Topic Structure
Meshtastic publishes to MQTT topics using this hierarchy:
msh/{region}/2/{protocol_version}/{channel_type}e|json}/{channel_name}/{node_id}user_id}
Examples:
msh/US/2/e/LongFast/!ab12cd34 # Encrypted packetpacket, frompublished nodeby gateway !ab12cd34
msh/US/2/json/LongFast/!ab12cd34 # JSON-decoded packet (only if node has JSON enabled)enabled on the gateway)
Where:
msh/ - Meshtastic prefix
US/ - Region code (US, EU, etc.)
2/ - Protocol version
e/ - Encrypted protobuf (default)
json/ - JSON decoded (optional, only if JSON output is enabled on the gateway)
LongFast/ - Channel name (this segment is always present)
!ab12cd34 - SourceUser/gateway node ID (the node that published this packet to MQTT, not necessarily the originator)
The 4th segment is e (encrypted protobuf) or json; the 5th segment is the channel name; the last segment is the publishing (gateway) node's hex ID. The packet's type (text, telemetry, position, etc.) is a field inside the payload, not a topic segment - there are no .../json/text/ or .../json/telemetry/ topics.
Subscribing to All Traffic
# Subscribe to all Meshtastic packets from all US nodes:
mosquitto_sub -h mqtt.meshtastic.org -u meshdev -P large4cats -t "msh/US/2/e/#" -v
# Subscribe to a specific channel only:
mosquitto_sub -h mqtt.meshtastic.org -t "msh/US/2/e/CommunityMesh/#" -v
# Subscribe to JSON-decoded packets (if gateway has JSON enabled):
mosquitto_sub -h localhost -t "msh/US/2/json/#" -v
Packet Format
Each MQTT message payload is a protobuf-encoded ServiceEnvelope (defined in meshtastic.protobuf.mqtt_pb2) - the wrapper a gateway publishes - containing:
- packet - The MeshPacket (encrypted payload + routing info)
- channel_id - Channel name (e.g., "LongFast")
- gateway_id - Node ID of the gateway that published to MQTT
Enabling JSON Output on a Gateway Node
Security warning: JSON output publishes fully decoded (plaintext) packet content to MQTT, bypassing channel encryption entirely. The gateway can only emit JSON for packets it can decrypt (channels whose key it holds). Never enable json_enabled on a gateway that uplinks to a public or shared broker if any channel content is sensitive - anyone subscribed to that broker will read the cleartext.
# Enable JSON output (in addition to protobuf) on the GATEWAY node that publishes to MQTT.
# Only packets the gateway can decode (notknown insteadchannel of)key) protobuf:are emitted as JSON:
meshtastic --set mqtt.json_enabled true
# JSON packets are published to: msh/US/REGION/2/json/{channel}/{node_id}CHANNELNAME/USERID
# CHANNELNAME = the channel's name (e.g. LongFast)
# USERID = the gateway node's hex ID (e.g. !7efeee00)
# JSON payload example:
{
"from": 2881537332,
"to": 4294967295,
"channel": 0,
"type": "text",
"id": 123456789,
"rx_time": 1712000000,
"hop_limit": 3,
"payload": {
"text": "Hello mesh!"
}
}
Decoding Protobuf Packets in Python
import paho.mqtt.client as mqtt
from meshtastic.mesh_pb2protobuf.mqtt_pb2 import ServiceEnvelope
from meshtastic.protobuf.portnums_pb2 import PortNum
import base64
def on_message(client, userdata, msg):
try:
se = ServiceEnvelope()
se.ParseFromString(msg.payload)
packet = se.packet
print(f"From: !{packet.from_:08x}")
print(f"To: !{packet.to:08x}")
print(f"Channel: {se.channel_id}")
# Decrypt and decode based on portnum for full payload
except Exception as e:
print(f"Parse error: {e}")
client = mqtt.Client()
client.username_pw_set("meshdev", "large4cats")
client.on_message = on_message
client.connect("mqtt.meshtastic.org", 1883)
client.subscribe("msh/US/2/e/#")
client.loop_forever()