Appearance
Routing: TTL, flood, strict on-wire routes, duplicate suppression
Where path details live on the wire (header vs routing block vs encrypted tail): Routing on wire.
LowMesh repeaters are stateless dumb-pipes at the mesh layer: they do not maintain distance-vector tables, predictive nextHop RAM, or MeshCore-style learned routes for third-party traffic. Forwarding is driven by:
- Flood routing — Controlled duplication when
FLOODis set; path state is carried in the packet (routing block), not in repeater RAM. - Strict source routing — Optional explicit path hashes in the routing block for unicast legs where the originator supplies the path.
- RAM duplicate suppression — Recent
(channelHash, srcId, sessionId, messageId, fragmentWord)signatures discard echoes and loops.
Hop limit (hopControl / ttlRaw)
- Each forwarded frame carries
hopControl(1 byte on wire; firmware namettlRaw). Normative layout: bit 7 =HAS_ROUTE_BLOCK(0x80); bit 6 reserved (MUST be 0); bits 5–0 =HOP_LIMIT(hopLimit = hopControl & 0x3F). - Each relay decrements only the hop field while preserving bits 7–6, e.g.
hopControl = (hopControl & 0xC0) | ((hopLimit - 1) & 0x3F)after readinghopLimit; whenhopLimitreaches 0, the frame is not forwarded.
TTL ceilings vs HASH_SIZE (normative policy)
Breadcrumbs in the routing block consume pathCount × hashSize bytes, where hashSize = ((verFlags >> 9) & 3) + 1 (1–4). To keep worst-case growth within the 255-byte LoRa PHY payload and to stabilize application tail budgets, originators and gateways MUST clamp the initial hop limit to the maximum for the selected HASH_SIZE:
HASH_SIZE encoding (verFlags bits 10–9) | hashSize | MAX TTL (hop limit) |
|---|---|---|
00 | 1 | 63 |
01 | 2 | 32 |
10 | 3 | 21 |
11 | 4 | 16 |
Note: The on-wire hop field is 6 bits (absolute max 63). Originators MUST clamp to the table above for the selected HASH_SIZE.
Repeaters: if a received frame’s hop limit exceeds the ceiling for its embedded HASH_SIZE, the frame is invalid — drop (do not append breadcrumbs, do not retransmit).
HASH_SIZE and collision risk
HASH_SIZE is flexible (1–4 bytes per path entry). The spec does not mandate a minimum hash width.
Smaller hashSize values reduce airtime but increase collision risk among path fingerprints. Product profiles MAY choose hashSize based on network size, payload budget, and acceptable ambiguity. The Base profile recommends 2 bytes while requiring parsers to accept 1–4.
Route fingerprint (normative)
Each relay’s breadcrumb entry is a truncated digest of the local nodeId32:
digest[0..31] = SHA256( UTF8("LowMesh/route-fingerprint/1") || uint32_le(nodeId32) )
pathHash = digest[0 .. hashSize-1]pathHash is appended to pathHashes in the routing block. hashSize comes from verFlags bits 10–9 — see Routing on wire.
Flood routing
Flood mode is used when FLOOD is set (application policy) and/or when discovering neighbors or delivering group traffic.
Breadcrumbs (on-wire, variable width)
When FLOOD is set and a relay forwards a frame with (ttlRaw & 0x80) == 0, no routing block exists on the wire — the relay MUST insert a new routing block (pathCount = 1 + local fingerprint) before the encrypted application tail, set ttlRaw |= 0x80, decrement hops, and verify ≤ 255 bytes (see Routing on wire). Subsequent relays with ttlRaw bit 7 set append their fingerprint (hashSize bytes) to pathHashes, increment pathCount, and grow the PDU only if the result still fits PHY policy.
Flood rebroadcast timing (randomized backoff)
When a node rebroadcasts an eligible flood frame (hop budget still valid, not suppressed by duplicate cache, role policy, or strong-signal suppression), it MUST NOT retransmit immediately. It MUST schedule using a randomized backoff window to reduce collisions.
Normative default (recommended for interop):
- Compute a deterministic per-relay jitter seed from
channelHash,srcId,sessionId,messageId, and fragment word. - Pick
delayMsuniformly in a role-specific window, then apply listen-before-talk (see LoRa parameters and hardware).
Duplicate suppression (normative)
Each relay maintains a recently seen cache keyed by:
(channelHash, srcId, sessionId, messageId, fragmentWord)channelHash: 2 bytes (LE).srcId: 4 bytes (LE).sessionId: 4 bytes (LE) — from the base header.messageId: 2 bytes (LE).fragmentWord: 2 bytes — packed mesh fragment block whenHAS_FRAGMENTis set; otherwise0.
Entries expire after tDup (e.g. 2–5 minutes, scaled by airtime class).
Strong-signal suppression (flood relays)
A relay MAY suppress a flood rebroadcast when local RF heuristics indicate that sufficient nearby peers likely received the original transmission. Thresholds and scoring are radio-profile / product policy and are not part of Base Profile interoperability.
Normative (interop): duplicate suppression, TTL/hop limits, and randomized flood backoff remain required. Strong-signal suppression is not required for interoperability. Different vendors MAY choose different RSSI/SNR/margin/peer-density thresholds.
Informative example inputs (not universal dBm thresholds): inbound RSSI, SNR, calculated link margin, recently heard peer density, CAD/LBT state.
Interaction with device roles
| Role | Strict source route relay | Flood relay |
|---|---|---|
| Gateway | Yes (policy) | Yes |
| Repeater | Yes (policy) | Yes |
| Client (not in temporary relay) | No third-party relay | No |
Client (Standard; TempRelay) | Policy-defined | Yes — Dynamic Repeater Mode fallback only |
| Client (Sensor Profile) | No third-party relay | No |
Clients in temporary relay state MUST respect tempRelayFloodTtlCap / tempRelayMaxForwardsPerMin — see Device roles — Dynamic Repeater Mode (Standard Client Profile only by default).
Strict source routes and authorization
The routing block is outside CCM AAD so relays can insert or extend flood breadcrumbs without re-encrypting. That is intentional for flood forwarding.
Strict source-route instructions that affect security, authorization, or delivery guarantees MUST be authenticated inside the encrypted application body or by a separate routeAuth mechanism. Strict source routes carried only in the plaintext routing block are advisory unless authenticated by a higher layer — MUST NOT be treated as trusted authorization by themselves.
See Routing on wire and Channels & security.
Receiver drop reasons (implementation / logging)
These names are for firmware logs, USB/BLE diagnostics, and test harnesses. They are not normative on-wire status codes (except where an application payload like ACK_BASIC defines its own status byte).
| Reason | Typical cause |
|---|---|
DROP_BAD_VERSION | verFlags VERSION not supported |
DROP_RESERVED_BITS | Reserved verFlags bits 1–0 non-zero, or MIC_MODE reserved values 10/11 |
DROP_UNSUPPORTED_MIC_MODE | MIC_MODE = 01 (CCM-8) not supported locally |
DROP_TOO_SHORT | PDU shorter than minimum for declared flags |
DROP_TOO_LARGE | Mesh PDU > 255 bytes |
DROP_BAD_REGION_BLOCK | HAS_REGION set but region bytes invalid |
DROP_BAD_FRAGMENT_BLOCK | HAS_FRAGMENT set but fragment word invalid |
DROP_BAD_TOTAL_COUNT | totalCount == 0 or totalCount > 1024 (or local nFragMax) |
DROP_FRAGMENT_INDEX_MISMATCH | Header fragmentIndex ≠ currentIndex |
DROP_BAD_MORE_FRAGMENTS | Header moreFragments disagrees with (currentIndex + 1 < totalCount) |
DROP_IDENTITY_COLLISION | Same nodeId32 with a different pkEd on the active domain (contact/identity handling; not an on-wire NAK) |
DROP_BAD_ROUTE_BLOCK | HAS_ROUTE_BLOCK set but pathCount / length inconsistent |
DROP_ROUTE_TOO_LARGE | Growing routing block would exceed PHY limit |
DROP_TTL_ZERO | Hop limit exhausted |
DROP_TTL_EXCEEDS_HASH_POLICY | Hop limit above ceiling for HASH_SIZE |
DROP_UNKNOWN_KEY | No local psk32 candidate for channelHash |
DROP_MIC_FAIL | AES-CCM verification failed for all candidates |
DROP_ADMIN_BAD_FORMAT | ADMIN_CMD inner layout invalid |
DROP_ADMIN_REPLAY | adminCounter replay |
DROP_ADMIN_SIGNATURE_REQUIRED | Policy requires signature; none present |
DROP_ADMIN_SIGNATURE_BAD | Ed25519 signature verify failed |
DROP_ADMIN_POLICY_REQUIRES_CCM8 | Policy requires CCM-8; frame used CCM-4 or peer lacks CCM-8 |
DROP_MIC_AMBIGUOUS | More than one candidate key verified the same frame |
DROP_REPLAY | CCM nonce / replay window rejection |
DROP_DUPLICATE | (channelHash, srcId, sessionId, messageId, fragmentWord) cache hit |
DROP_REGION_POLICY | FORCE_REGION or spatial policy drop |
DROP_QUEUE_FULL | TX queue congested; policy drop |
DROP_UNSUPPORTED_PAYLOAD | Unknown payloadType after decrypt (or policy reject before decrypt) |
DROP_ADMIN_AUTH_REQUIRED | Admin frame failed operator / key policy |
DROP_UNAUTHORIZED_GPS_SCHEDULE | GPS_SCHEDULE not from authorized source — see GPS |
Security note
AES-CCM-4 on the inner tail prevents forgery of payloads by outsiders who lack the psk32. Metadata in the plaintext prefix (base, region, fragment, routing) is still visible on the air; deployments MUST NOT treat admin traffic as authenticated unless decrypted with adminChannelPsk — see Mesh admin.
Spatial Region policy (HAS_REGION) and FORCE_REGION
The Spatial Region block (when verFlags & HAS_REGION) is a plaintext routing hint that may be used by repeaters to restrict forwarding scope (deployment policy).
- Packets without
HAS_REGION: repeaters default to forwarding behavior based onttlRawhop semantics andFLOODrules (normal operation). FORCE_REGIONenforcement: if a repeater hasallowedRegionCount > 0andforceRegionEnforcement == 1(NVMDeviceRole), it MUST drop any packet that does not haveHAS_REGIONset (no implicit “global” fallback in this mode).
Transmit queue priority (congestion)
When the outbound queue is congested, implementations SHOULD preserve control traffic and defer bulk transfers. Recommended priority order (highest first):
- Emergency / critical admin
ACK_BASIC/FRAG_NACK- Direct user messages (unicast / high-priority chat)
- Telemetry / position
- Bulletin sync
- File / voice bulk transfer (
PAYLOAD_BODY_V2)
When queues are full, implementations SHOULD drop or defer lower-priority bulk traffic first while keeping ACK/control slots available.
Flood rebroadcasts MUST use randomized backoff and SHOULD use listen-before-talk where supported — see LoRa parameters.