Packet Source Detection
Every packet received by MeshDash goes through a source detection engine that determines whether it arrived over RF (physically received by the radio), via MQTT (relayed through an internet broker), from LOCAL (sent by this dashboard), or is UNKNOWN. The result is stored as the source and source_confidence fields on every packet.
Why It Matters
In a mixed mesh with MQTT bridges enabled, packets from distant nodes may arrive via the internet rather than RF. Without detection, you cannot tell the difference between a node 2 km away on RF and a node in a different country relayed through MQTT. The source field lets the dashboard show accurate signal analysis, MeshShark filtering, and correct distance/topology calculations.
The Evidence Scoring System
The engine accumulates weighted evidence for RF or MQTT from multiple independent signals. Evidence is never a single definitive test — each indicator contributes a score:
| Signal | Weight | Direction | Reasoning |
|---|---|---|---|
viaMqtt = true in packet | +10 | MQTT | Firmware explicitly marks MQTT-origin packets |
viaMqtt = false in packet | +8 | RF | Firmware explicitly clears MQTT flag |
| Real SNR + real RSSI present | +9 | RF | Physical air reception measured by radio hardware |
| Real SNR only | +6 | RF | SNR measured but RSSI unavailable |
| Real RSSI only | +6 | RF | RSSI measured but SNR unavailable |
| No SNR, no RSSI (and not viaMqtt=false) | +4 | MQTT | Packet not physically received by local radio |
| Hop delta impossible (hopStart < hopLimit) | +5 | MQTT | Packet was injected, not relayed naturally |
| Zero hops consumed, no SNR | +3 | MQTT | Arrived without traversing any hops physically |
| Multi-hop RF path (hops consumed > 0) | +2 | RF | Relay chain indicates physical RF propagation |
| Clock skew >45 s | +2 | MQTT | Possible MQTT relay delay or clock drift |
| Low clock skew + real SNR | +2 | RF | Fresh, physically received |
| TRACEROUTE_APP portnum | +3 | RF | Traceroutes are always RF-originated |
| Node historically RF-only | +3 | RF | Per-node evidence cache confirms RF history |
| Node historically MQTT-only | +3 | MQTT | Per-node evidence cache confirms MQTT history |
| SNR consistent with node's RF history | +2 | RF | Current SNR matches historical RF samples |
Resolution Logic
After all evidence is scored:
- If a definitive override was set (e.g.
_synthetic_outboundflag → LOCAL), it overrides all scoring - Otherwise: if RF score > MQTT score and RF confidence ≥ 65% →
RF - If MQTT score > RF score and MQTT confidence ≥ 65% →
MQTT - If neither side reaches 65% confidence →
UNKNOWN
Possible Source Values
RFMQTTviaMqtt=true or absence of physical reception indicators.UNKNOWNLOCALPer-Node Evidence Cache
The engine maintains a thread-safe in-memory cache of per-node evidence (_node_source_evidence). For each node it tracks:
- Whether it has ever been definitively seen over RF (
rf_confirmed) - Whether it has ever been definitively seen via MQTT (
mqtt_confirmed) - The last 20 RF SNR samples for pattern matching
- Running counts of RF, MQTT, and unknown packets
This cache is updated after every packet classification and consulted for the next packet from the same node — creating a feedback loop that improves detection accuracy for consistently-behaving nodes.
Where Source Data Appears in the UI
The source detection result is surfaced in several places across the dashboard:
source and source_confidence fields are visible in the Radio Layer detail pane, including the full evidence reasoning listsource and source_confidence fields on every packet object from /api/packets and /api/packets/historysource and source_confidence on the node object reflect the most recent packet classification for that nodeSource Detection for Synthetic Packets
Outbound packets generated by MeshDash itself (messages you send) are tagged with _synthetic_outbound: true before running through detection. The engine recognises this flag and immediately sets source to LOCAL with confidence 1.0 — no scoring needed.
Background sync packets (node database hydration, not real radio packets) are tagged with _from_sync: true and get source UNKNOWN with a note that they are database sync entries — no per-packet transport data is available for them.