Skip to content

Meshtastic vs MeshCore feature parity

This document summarizes which client features are Meshtastic-only, MeshCore-only, or shared, and whether gaps are app wiring, post-MQTT, or blocked by protocol.

See also CONTRIBUTING.md (dual-protocol architecture).

Capability flags

Shared UI gates use ProtocolCapabilities in src/renderer/lib/radio/BaseRadioProvider.ts. Prefer new gates there instead of protocol === 'meshcore' string checks.

Feature matrix

Area Meshtastic MeshCore Gap type
Transports BLE, Serial, HTTP (@meshtastic/core) BLE, Web Serial, TCP bridge (5000) Different stacks
Tab “Modules” / “Repeaters” ModulePanel (protobuf modules) RepeatersPanel (trace, status, neighbors) Product split
MQTT broker UI Full (with transport selection) Same broker fields; transport protocol selected when connecting; MeshCore-only LetsMesh / Ripple / Custom presets fill known public brokers Post-MQTT codec on broker path
MQTT wire format ServiceEnvelope / MeshPacket (mqtt-manager.ts) JSON v1 chat on {topicPrefix}/meshcore/chat (non-LetsMesh / private brokers); LetsMesh: optional meshcoretomqtt-style packet JSON on {topicPrefix}/meshcore/packets (meshcore-mqtt-adapter.ts); chat parser in meshcoreMqttEnvelope.ts Adapter vs protobuf
Node list hops / MQTT columns hops_away, via_mqtt from device Contact model; hops via outPathLen from device trace App (implemented)
RF diagnostics (LocalStats) From protobuf Not available Blocked
Routing diagnostics (hop-based) RoutingDiagnosticEngine with hop count Skipped when hasHopCount === false Blocked until hop metric exists
Neighbor UI neighborInfo protobuf getNeighbours (repeaters) Different primitive
Radio config Full protobuf (role, presets, WiFi, etc.) setRadioParams, channels, advert name/position Blocked for Meshtastic-only admin
Position Full GPS protobuf + request position Advert lat/lon + setAdvertLatLong Partial
Waypoints Supported Not in protocol surface Blocked
Favorites nodes table meshcore_contacts.favorited + db:updateMeshcoreContactFavorited App (implemented)
Environment telemetry charts Device telemetry module Cayenne LPP via getTelemetryenvironmentTelemetry App (implemented)
Chat transport badges / history received_via on Meshtastic messages meshcore_messages.received_via (rf / mqtt / both) App (implemented)
Chat search searchMessages searchMeshcoreMessages; UI search modal supports user: / channel: filters for cross-channel lookup Parallel DB tables
Chat @[Display Name] tokens Same on-wire pattern for replies / reactions / path-style lines Same App — chat body renders tokens as inline labels (see below)
Repeater CLI Not applicable Per-repeater expandable CLI in RepeatersPanel; prefix-token correlation, retry, flood/auto routing toggle (RepeaterCommandService); Flood Advert and Sync Clock buttons moved to Radio panel (Device Actions section); auto flood advert scheduling available in App Settings (disabled / 12h / 24h) App (MeshCore-only)
Security / PKI admin SecurityPanel when hasSecurityPanel Tab omitted — no Meshtastic-style PKI surface on MeshCore firmware Blocked (protocol)
Contact groups Built-in groups (GPS, RF+MQTT) via meshtasticContactGroupUtils; user-managed via ContactGroupsModal SQLite-backed groups + Nodes toolbar (useContactGroups, ContactGroupsModal); built-in Room filter App — protocol-neutral with Meshtastic built-ins
Log analyzer LogPanelAnalyze (logAnalyzer.ts, protocol-aware) Same shared UI App (implemented)

Windows: MeshCore over BLE

Pair the radio in Settings → Bluetooth & devices before connecting from the app; WinRT is much more reliable with a bonded device. The client may retry once after transient GATT discovery failures, and canceling mid-connect should not surface a misleading long-running channel timeout. User-facing copy lives in the Connection tab on Windows; contributor details are in CONTRIBUTING.md (MeshCore internals, BLE) and README.md (MeshCore Transport Notes).

Linux: MeshCore over BLE

Linux uses Web Bluetooth in the renderer (not Noble). After you pick a device, the client reads bluetoothctl info <MAC>. If the radio is not paired in BlueZ, the UI asks for the PIN shown on the device and runs bluetooth-pair before resolving the pending Web Bluetooth requestDevice() selection. If a handshake times out, a single retry reuses the granted device via getDevices() so requestDevice() is not called again without a click. See development-environment.md and troubleshooting.md.

Chat mention tokens

Meshtastic and MeshCore use the literal form @[Display Name] in channel payloads for thread replies, emoji tapbacks, path / hop summaries, and inline references. The client may keep the raw string in storage when a reply parent cannot be matched; the Chat tab still parses these segments for display only: brackets are hidden and the name is shown as a compact inline label (ChatPayloadText in ChatPanel.tsx, parser in chatMentionSegments.ts). Threading / replyId behavior is unchanged — this is purely presentational.

MeshCore MQTT JSON envelope (v1)

Interim broker format until a binary/official MeshCore MQTT layout ships:

{
  "v": 1,
  "text": "message body",
  "channelIdx": 0,
  "senderName": "optional",
  "senderNodeId": 305419896,
  "timestamp": 1700000000000
}

Subscribes under {topicPrefix}/#. Outbound optional publish uses mqtt:publishMeshcore{topicPrefix}/meshcore/chat (JSON same shape). LetsMesh public brokers do not use that path for MQTT-only chat without a radio; optional mqtt:publishMeshcorePacketLog{topicPrefix}/meshcore/packets for Analyzer (see letsmesh-mqtt-auth.md § Packet logger).

Meshtastic MQTT network presets

In Meshtastic mode, ConnectionPanel.tsx shows MQTT :1883, Liam's, and Custom preset buttons. They populate MQTTSettings used by mqtt-manager.ts.

Preset Broker host Port Notes
MQTT :1883 mqtt.meshtastic.org 1883 Plaintext; may be blocked on some networks
Liam's mqtt.meshtastic.liamcottle.net 1883 Uplink-only (puts your node on Liam Cottle's map; no downlink). uplink / uplink credentials, no TLS. Useful when mqtt.meshtastic.org is unreachable
Custom (user) No automatic changes — use for private brokers

Topic prefix defaults to msh/US/; users can edit fields after choosing a preset. Defined in meshtasticMqttTlsMigration.ts.

MeshCore MQTT network presets

In MeshCore mode only, ConnectionPanel.tsx shows LetsMesh, Ripple Networks, and Custom preset buttons. They populate the same MQTTSettings the main process uses for meshcore-mqtt-adapter.ts (with mqttTransportProtocol: 'meshcore').

Preset Broker host Port Notes
LetsMesh mqtt-us-v1.letsmesh.net or mqtt-eu-v1.letsmesh.net 443 WebSocket (useWebSocket / wss). Auth matches meshcore-mqtt-broker: MQTT username v1_<64-hex public key> (uppercase); password is a token from @michaelhart/meshcore-decoder createAuthToken with publicKey, iat, exp, and JWT aud equal to the regional broker hostname (same as the Server field; aligns with common token tooling). Optional Packet logger publishes RX summaries to meshcore/packets under the topic prefix (meshcoretomqtt-shaped JSON). See docs/letsmesh-mqtt-auth.md. Implemented in letsMeshJwt.ts. Import MeshCore config JSON (Radio tab) so public_key and private_key are cached.
Ripple Networks mqtt.ripplenetworks.com.au 8883 TLS; preset fills default shared credentials and insecure TLS for self-signed / non–public CA chains
Custom (user) No automatic changes — use for private brokers

Topic prefix is set to meshcore for both public presets; users can still edit fields after choosing a preset.

Maintenance

When MeshCore firmware/SDK defines official MQTT topics and payloads, replace or extend MeshcoreMqttAdapter and update this document.