An Empirically Calibrated Evaluation of Flood, Source-Path, Next-Hop and Gradient-Corridor Forwarding on Real Meshtastic Networks

Alen Zubic · RelayMesh
July 4, 2026 · Data window: 2026-06-27 to 2026-07-04 (UTC) · Produced with AI assistance (see disclosure before References)
Abstract. Meshtastic carries broadcast traffic — and, before firmware 2.6, all traffic — by managed flooding; 2.6 added cached next hops for direct messages, with flood as the fallback; MeshCore source-routes. This paper proposes a fourth strategy — gradient-corridor anycast (GradCor): one byte of state per active destination, receiver-side anycast forwarding and no hop limit — strict gradient descent gives termination and loop freedom while the corridor is tight, per-packet duplicate suppression bounds it once widened, so a packet travels as far as real progress exists and stops at delivery or a locally-verified dead end. Replayed on the real topology of the Norwegian Meshtastic mesh (499 nodes, 1,423 directed links reconstructed from 18,919 production traceroutes, SNR-calibrated link quality, real duty-cycle presence), the final clock-free specification GradCor-R delivers 50.7% of unicast packets at 21 transmissions per delivered packet vs flooding's 25.3% at 71 tx. Because end-to-end retries lift every protocol, that pair is restated at matched budgets, one axis at a time (§6.12). Auditing our own workload then showed the replay's assumed conversational density was ~90× production, so §6.13 measures the real addressed-traffic process and the real broadcast cadence and re-runs the comparison on both: at the airtime managed flooding already spends, GradCor-R delivers 2.3× as much; to reach flooding's best delivery it spends 3.1× less airtime. That correction also overturns the paper's own explanation of why the protocol works. Gradients are kept fresh not by conversation — most real pairs exchange one isolated packet, and with conversation alone GradCor-R merely ties flooding — but by the destination's routine broadcasts, which production data shows arrive about hourly. The protocol adds no announce mechanism; it inherits Meshtastic's, and its advantage tracks that cadence. A MeshCore-inspired source-routing model (deliberately not a MeshCore emulation; §7) collapses to 4.0% under link asymmetry and next-hop caching reaches 30.4%. The ranking reproduces on four of five further production networks; the fifth, with too little traceroute evidence to plant gradients on, degrades to flood level and defines a measurable deployment precondition. We also identify and fix a production extraction defect (traceroute responses parsed as requests) that had inflated the topology graph 2.5× and understated link reciprocity (36% → 57%).

1  Introduction

LoRa mesh networks operate under one binding constraint: airtime on a shared, duty-cycled, ~kilobit-per-second channel is the only currency that matters. Meshtastic's managed flood is robust and stateless but spends the whole neighbourhood's airtime on every packet it carries; deterministic schemes promise cheaper unicast but import state that rots as nodes sleep, move and reboot. A preliminary design exploration of ours argued from first principles that a protocol should move continuously along the knowledge spectrum between flood (zero knowledge) and source routing (total assumed knowledge) and proposed gradient-corridor anycast: per-destination hop-distance gradients planted by a single flood, anycast forwarding down the gradient and an eligibility corridor that widens as the gradient ages — degrading gracefully toward flood instead of failing. Its supporting evidence was a synthetic simulation: 120 uniformly scattered nodes, invented link qualities and synthetic mobility churn.

Synthetic topologies flatter every protocol differently, so the natural objection is: does the result survive a real network? This paper answers that question using production data from RelayMesh's ingestion of the Norwegian national Meshtastic mesh, then cross-validates on five further production networks (§6.1) — to our knowledge the first evaluation of these four strategies on fully empirical LoRa mesh topologies at this scale. Along the way the real data pushes back on the design itself: an ablation (§3.6) shows the original wall-clock staleness thresholds are unnecessary and the paper's final specification is the simpler, clock-free form that survives it.

2  The four strategies

3  The routing method: gradient-corridor anycast

This section specifies GradCor as a protocol, independently of how we evaluate it. The design answers one question: what should a router do when its knowledge of the network is partially wrong — and it cannot know which part? Classical answers pick an endpoint: flooding assumes nothing and pays full price every time; source routing assumes everything and breaks when any assumption fails. GradCor's answer is to make the degree of trust in its own state an explicit, continuously-acting protocol parameter. Every mechanism below is an instance of that one idea and Figure 1 compresses the whole method into one picture: where the design sits (a), what any single node actually does (b) and why removing the hop limit is safe (c).

We present the method as it evolved rather than only its end state, because the simplification is itself a finding. §3.1–3.5 specify the design as originally drafted (“v1”), in which trust decays with a wall-clock age per gradient and two fixed thresholds govern corridor widening and replanting. §3.6 then ablates those clocks on the real Norwegian network and finds them redundant: the per-hop escalation already measures staleness directly. The result is the final specification, GradCor-R (“reactive”) — identical machinery with every age test deleted and one byte of state per destination — which is what we recommend implementing. Readers should take v1 as the pedagogical and historical form and GradCor-R as the protocol.

Figure 1. GradCor in one picture (shown in its final, clock-free form). (a) Flood and source routing are the two ends of a knowledge spectrum; GradCor is not a point on it but a slider, moved per-hop by direct failure evidence. (b) A node's complete view: one byte of state per active destination and five local rules evaluated on every decoded frame — eligibility is decided at the receivers, so one broadcast simultaneously probes every potential relay. (c) In the strict corridor, forwarding requires the gradient to strictly decrease at every hop, so a packet may legitimately travel 12, 20, or 50 hops — a finite integer cannot descend forever, making termination and loop freedom arithmetic facts rather than counter side-effects; in the widened corridors the walk stays bounded by the same per-packet dedup the flood relies on (each node forwards a given packet at most once). The flood's hop limit, by contrast, amputates at a fixed radius regardless of progress. A packet stops only at delivery or at a locally-verified dead end (deterministic drop + route error).

3.1  State: two numbers per destination (one, in the final spec)

Each node keeps, per active destination only, a single pair: the estimated hop distance to that destination (the gradient, g) and, in v1, the age of that estimate. Nothing else — no next-hop pointer, no path, no per-neighbour table. This is deliberate: a next-hop pointer names one specific relay and fails when that relay sleeps; a gradient only claims “I am about N hops away,” a far weaker statement that many different neighbours can satisfy. Weak claims rot slower than strong ones. Forwarding is also independent of the node database: a relay never identifies, addresses, or decrypts for the destination — it only compares its own g against the g in the packet header — so a relay can carry traffic toward destinations it has never heard of as nodes and routing state grows with the node's active conversations, not with mesh size. Only the sending endpoint needs the destination's identity (to address the packet and encrypt), exactly as it does today.

FieldTypeMeaningFate in final spec
guint8estimated hops from this node to the destinationkept — the entire routing state
ageuint8 (hours)time since g was planted or last refreshedremoved by the §3.6 ablation; optional replant hint only
Table 1. Per-destination state, ≈2 bytes in v1 and a single byte in GradCor-R, plus the node-ID key. Thirty active conversations cost a few hundred bytes — comfortably inside the RAM budget of an nRF52 target, where full link-state routing is categorically out of reach.

On the wire, a data packet carries the destination, the normal packet ID (reused for duplicate suppression), the holder's own gradient gh and a 2-bit corridor level. That is 1–2 bytes of overhead — against a source route's full node list, this is the difference between constant and linear per-packet cost in path length.

3.2  Planting: one flood buys a whole gradient field

Gradients are created by a single flood from the destination (or equivalently, by the reverse view of any flood the destination originates — in Meshtastic practice, a NodeInfo, position, or ordinary channel-message broadcast can double as a planting flood at zero extra cost). There is no dedicated announce packet type and no transmission the mesh would not otherwise carry: planting is a side effect of whatever the destination already sends. Every node the flood reaches records the hop count at which it first heard it: that is the gradient, age zero. One broadcast per node, once, amortised over every subsequent packet toward that destination. There is no periodic beaconing; the protocol never spends airtime maintaining state it is not using. This side-channel turns out to be the load-bearing one. Production broadcast cadence puts a typical Norwegian node's own broadcasts about an hour apart (§6.13), so gradients toward it are re-planted hourly by traffic the mesh sends regardless — and §6.13 shows the protocol's advantage tracks that cadence closely. GradCor adds no announce mechanism, but it is not independent of the mesh having one: it inherits Meshtastic's. Nor does a gradient-less source need to solicit a plant: it can managed-flood the packet exactly as Meshtastic does today and if that flood delivers, the refresh of §3.5 plants the gradient along the proven path for every packet after it — the replay's on-demand planting shortcut and this deployable cold start are compared quantitatively in §8. Figure 2 traces the two numbers through their whole life: stamped by the planting wave (a), sitting as a two-byte table row (b) and re-stamped for free whenever a delivery proves what the true working distance is (c).

Figure 2. How each node builds its two numbers. (a) The planting flood expands outward from the destination; every node records the hop count at which it first decoded it — late copies are ignored by packet-ID dedup, so g is the shortest observed distance. Nothing is requested and nothing is acknowledged. (b) The result at one node: a table row per active destination; the age column and its lifecycle notes are v1 — the final spec keeps only g (§3.6). (c) When a packet is delivered, every node on the path it actually took is re-stamped with its distance along that proven path and age 0 — riding the existing delivery confirmation, so active conversations keep their own gradients sharp at zero airtime cost. On production traffic this refresh is real but secondary to (a): most pairs never send a second packet (§6.13).

One honest subtlety: the planting flood travels outward from the destination, while data flows toward it — and §4 shows 43% of real links have no observed reverse. The gradient is therefore a distance estimate measured partly over links the data cannot use in reverse: g can underestimate the true forward distance and a strictly-descending chain from g=4 can never be longer than five nodes. GradCor survives this for two reasons. First, forwarding is anycast — it never needs the specific reverse of any planting hop, only some decoded receiver with a lower g. Second, the corridor widens per hop, not only with age: a holder that finds no strictly-closer receiver retries, then admits equal-g receivers, then any receiver (Algorithm 1), so an optimistic gradient degrades into guided exploration rather than failure — and the moment one packet gets through, the delivery refresh (§3.5) re-stamps every node on the working path with its true forward-path distance, converting the exploration cost into a one-time fee per gradient lifetime. In the Norway replay this is measurable: 22.8% of gradient-guided deliveries took more hops than the sender's g promised (median one extra, maximum +19) and when g strictly underestimated the true reachable distance, packets still got through at a degraded rate (16% vs 64% baseline) at roughly 6× the airtime — degraded, not dead and self-correcting on first success. The evaluation in §5–6 replays this asymmetry faithfully rather than assuming it away.

3.3  Forwarding: broadcast once, let receivers race

The holder of a packet does not pick a next hop. It broadcasts once and eligibility is decided at the receivers: every node that decodes the frame compares its own gradient with the gh in the header. Eligible receivers start a contention timer proportional to their gradient (ties broken by link quality); the timer that fires first forwards the packet and the other candidates hear that forward and suppress themselves. One clarification to carry through the rest of the paper: this contention timer is a one-shot, milliseconds-scale MAC backoff armed by a packet reception — a race resolver, not a clock. It is unrelated to the hours-scale state-aging clocks that §3.6 later removes and it is intrinsic to anycast: some tie-breaker must decide which eligible receiver goes first. This is the ExOR trick[2], and it is what converts LoRa's broadcast medium from a liability into the protocol's main asset: one transmission simultaneously probes every potential relay, so the packet advances if any of them is awake and heard it — where unicast schemes stall if the named one didn't.

Two mechanics deserve spelling out, because both differ from the hop-count intuition a reader may carry in. First, gh is not a TTL: no one decrements it. Each forwarder overwrites the field with its own stored gradient before rebroadcasting, so the header always reads “the current holder believes it is N hops away.” Under the strict rule the value falls at every hop — which is what provides the termination a TTL normally buys — but it falls because each node stamps its own better knowledge, can snap downward mid-flight when the packet reaches a node with a shortcut and (in the widened corridors) may hold or rise where a countdown would already have killed the packet. Second, the winner's forward transmission is itself the per-hop acknowledgment: the holder overhears it and goes quiet — the same passive-ack-by-rebroadcast the managed flood uses today, so no explicit ACK packet exists at any hop. Silence within the contention window is therefore the holder's failure signal and it is exactly what arms the escalation of §3.4: hear nothing, retry; still nothing, widen the corridor and broadcast again. Figure 3(d) traces one complete race on the time axis.

send(src, dst):
  if no gradient for dst at src or age ≥ AGE_DEAD (24 h):
      plant(dst)                          # one flood from dst; abort if src unreached
  holder ← src;  visited ← {src}

  loop:
      for level in 0..4:                  # at most 5 broadcasts per hop
          corridor ← STRICT  if level ≤ 2 and age < AGE_WIDEN (3 h)
                     EQUAL   if age ≥ AGE_WIDEN or level = 3
                     ANY     if level = 4
          broadcast(pkt, g_holder, corridor)                # one airtime unit
          # at each receiver v ∉ visited that decoded the frame:
          if v = dst:                     deliver; refresh gradient along path; return
          eligible(v) ↔  g(v) < g_holder            (STRICT)
                       |  g(v) ≤ g_holder            (EQUAL)
                       |  v decoded the frame         (ANY)
          if any eligible: winner ← contention race (min g, then best SNR); break
              # losers mute on overhearing the winner — its forward
              # is the holder's implicit ack (Figure 3d)
      if no winner: drop deterministically  # local minimum; RERR toward src (§3.5)
      visited ← visited ∪ {winner};  holder ← winner
Algorithm 1. GradCor v1 forwarding, evaluated head-to-head against its clock-free successor in §6 (constants from Table 2). In firmware the visited set is not carried in the packet: Meshtastic's existing packet-ID duplicate suppression plays the same role, since a node that already forwarded a packet ignores it thereafter. Note the key is forwarded, not seen: a contention loser that muted (Figure 3d) never transmitted and must stay eligible for later broadcasts of the same packet — §3.7 explains why this distinction carries real recovery value.

3.4  The corridor: uncertainty widens eligibility, never blocks it

The corridor rule is the novel piece and the reason the protocol has no cliff-edge failure mode (Figure 3). While the gradient is fresh (<3 h), only strictly-lower-gradient receivers may forward: the packet monotonically descends toward the destination at near-unicast cost and the strict decrease on a finite integer proves both loop freedom and termination without any hop-limit counter. As the gradient ages past 3 h — knowledge now suspect — equal-gradient receivers are admitted, buying lateral moves around a sleeping relay or a newly-dead link at modest extra cost. If even that fails, or the gradient is very old, any receiver qualifies: the packet is now effectively flooding, which is exactly the right behaviour when the node knows nothing — and it is reached gradually, hop by hop, not by a mode switch that must itself be signalled. The widening is strictly local: the source never decides to “send as flood,” and a corridor opened to ANY at one troubled hop does not stay open — the next holder starts again at STRICT, so the packet re-tightens the moment it is past the gap. Uncertainty in GradCor is not an error state; it is a dial that trades airtime for reach in proportion to how much the state deserves to be trusted. Note that v1 drives this dial with two triggers — wall-clock age and per-hop failure escalation — which are redundant with each other; §3.6 shows the per-hop trigger alone carries the whole effect on the real network and the final specification keeps only it.

Figure 3. The three corridor widths on a toy graph (labels show gradient g = estimated hops to destination; the source holds the packet and broadcasts once, reaching the shaded neighbourhood). The widths are identical in both forms of the protocol; only the trigger differs — per-hop failure escalation in the final spec (R), gradient age in v1. (a) Strict: only strictly-descending receivers (green) may forward — near-unicast cost. (b) Equal-g receivers admitted, allowing lateral escapes. (c) Any receiver eligible — including the g=4 neighbour that points away from the destination, which no narrower corridor admits — graceful degradation into flood. (d) The contention race among (a)'s three eligible g=2 receivers on the time axis: all decode the same broadcast, each arms a one-shot millisecond-scale delay keyed on (own g, tie-broken by SNR), the first to fire forwards — stamping its own g — and the others cancel on overhearing it, so normally exactly one of the three transmits. The holder's overhearing of that forward is the per-hop acknowledgment; a loser outside the winner's radio range forwards a duplicate, absorbed downstream by packet-ID dedup (§8, item 5).

3.5  Lifecycle: free refresh, honest death, deterministic drop

Delivery is itself the maintenance protocol. When a packet reaches the destination, every node on the path it took is stamped with its true distance-to-destination along that working path, age zero (Figure 2c; in firmware this rides the existing delivery ACK). Active conversations therefore keep their own gradients fresh for free and the corridor stays tight exactly where traffic flows — the state/freshness trade-off (§1) is paid only for destinations nobody is talking to. This mechanism is real but, on production traffic, it is the minor one. §6.13 measures the actual addressed-traffic process and finds most pairs exchange a single isolated packet, so there is usually no second packet for a delivery refresh to help; what actually keeps gradients fresh is the other free-planting source, §3.2's — the destination's own routine broadcasts, which production data shows arrive roughly hourly. The design does not change, but the reason it works does, and it is worth stating plainly here rather than leaving the reader with the wrong model. One asymmetry consequence is worth naming: the confirmation travels its own reverse path, which on an asymmetric mesh need not retrace the data's forward path — forward-path nodes out of earshot of the return leg simply miss that refresh. The miss is benign by construction: a node that misses a refresh merely keeps a staler g — there is no named next hop to be wrong about — and the cost is a little extra escalation on a later packet, precisely the failure the §3.3/§3.4 ladder absorbs (the replay's idealization of this refresh is disclosed in §8, item 6). A gradient that reaches 24 h without a refresh is declared dead and the next send replants it; Figure 4 summarises the states.

v1 — as drafted: the clock drives the corridor (no gradient) unknown destination FRESH  age < 3 h strict corridor: g↓ only AGED  3–24 h widened: g↓ or g= DEAD ≥ 24 h clock states — removed by the §3.6 ablation plant (1 flood) hourly aging aging next send replants (1 flood) delivery refresh: age ← 0, free (rides the ACK) §3.6: a failed broadcast already measures staleness — delete the clocks GradCor-R — final: two states, no clock; failure evidence drives the corridor per hop (no gradient) unknown destination g known  (one byte) corridor: strict → equal → any, per hop plant (1 flood) delivery refresh: re-stamp g, free no aging tick, no thresholds — staleness shows up as failed broadcasts and widens the corridor on the spot; an optional coarse eviction is memory hygiene only
Figure 4. Gradient lifecycle at one node for one destination, before and after the ablation. Top: v1 as drafted — a wall clock walks each entry through FRESH/AGED/DEAD and the corridor width is read off the state. Bottom: GradCor-R — the lifecycle collapses to two states, because corridor width is decided per hop from failure evidence instead of predicted from age. In both, the green transition is the load-bearing one: successful traffic continuously re-stamps exactly the state it depends on, at zero airtime cost, so control traffic is only ever spent on destinations that are both wanted and quiet.

In the final specification the lifecycle therefore simplifies as the lower band of Figure 4 shows: the delivery refresh survives unchanged, the FRESH/AGED/DEAD clock states disappear because eligibility no longer consults age and replanting degenerates to “plant if you have no gradient at all” (an optional coarse timer may evict rows that have not been refreshed for days, purely as memory hygiene).

The drop rule completes the lifecycle. A packet whose holder finds no eligible receiver at any corridor level is at a locally-verified dead end: across the whole escalation ladder — strict, equal, any — no un-visited receiver decoded the packet at all. It drops the packet there, deterministically and can report a route error back toward the source (the RERR is specified but not exercised in §6's evaluation). Contrast both alternatives: a hop-limit expiry drops at an arbitrary distance-driven point regardless of progress and a stale unicast route black-holes silently. GradCor's failure is localised, observable and actionable — the property the original design brief demanded when it argued for removing the hop-limit counter, with the packet's lifetime bounded instead by the visited-set/dedup rule and the finite gradient descent.

3.6  Are the clocks necessary? An ablation

Two constants in the specification look like magic numbers: widen at 3 hours, replant at 24. A fair objection is that these are statically chosen and the soft-state literature suggests the “right” staleness timer should track the measured hazard rate of state invalidation — the reasoning behind RPL's Trickle timer[6], which resets on observed inconsistency rather than on a schedule. We therefore ablated the clocks on the Norway replay (experiment_widening.py; Figure 5).

Three findings. First, staleness is real: delivery through a 1–2-hour-old gradient runs at 70%, decaying smoothly to 17% at 13–23 hours with airtime cost rising in step (Figure 5a) — a genuine hazard process with no cliff at any particular hour, so no specific threshold is “correct.” Second, the wall-clock threshold barely matters: sweeping the widening age from 0 (always widened) to never-widen moves delivery only between 47.2% and 50.6% (Figure 5b). Third, the clock is redundant: a variant that removes wall-clock age from the eligibility rule entirely — per-hop escalation only, strict → equal → any — delivers 50.9% vs the 49.7% baseline over 10 seeds, at equal cost; even deleting the replant timer too leaves it at 50.9%. The explanation is that a failed anycast broadcast is a direct, local measurement of the exact staleness the clock tries to predict: the age threshold decides in advance what the next three transmissions will discover anyway. Following the same Trickle logic, reacting to the inconsistency beats scheduling it — and the “two numbers” of §3.1 reduce to one (age survives only as an optional, insensitive replant hint). We also tested one adaptive variant in the opposite direction — a learned per-destination starting level that skips strict attempts a previous packet did not need — and it is worse (43.1%): skipping the cheap strict probes forfeits their frequent wins. The simple deterministic rule survives its own ablation; the statistical refinement does not.

This yields the final specification — what the design evolved to. It is Algorithm 1 with every age test deleted, nothing more:

# GradCor-R (final specification): Algorithm 1 with the clocks removed.
# State per active destination: g only (one byte + key). No aging tick.
send(src, dst):
  if src has no gradient for dst:
      plant(dst)                          # one flood; the only replant rule
  loop over holders as in Algorithm 1:
      for level in 0..4:
          corridor ← STRICT (g < g_holder)  if level ≤ 2
                     EQUAL  (g ≤ g_holder)  if level = 3
                     ANY                     if level = 4
          broadcast; receivers race exactly as before;
          delivery refreshes g along the path
      if no winner: drop deterministically + RERR      # unchanged
Algorithm 2. GradCor-R, the recommended form. Everything the evaluation praises about v1 — anycast diversity, monotone descent, no hop limit, graceful degradation, free refresh, deterministic drop — is preserved; only the prediction of staleness is replaced by its direct per-hop measurement. On the Norway replay it performs equal or better than every fixed-clock configuration — both in the isolated ablation (50.9% vs 49.7%, 10 seeds) and in the five-protocol head-to-head of Table 3 (50.7% vs 48.6%, equal cost).
Figure 5. Clock ablation on the Norway replay (10 seeds throughout). (a) Outcome by gradient age at the source (baseline protocol): usefulness decays smoothly — staleness is real but has no characteristic hour. (b) Delivery is nearly flat across the entire widening-threshold sweep (blue) and the clock-free reactive variant (green) matches or beats every fixed setting: the per-hop escalation already measures staleness at the moment it matters. This network's churn is duty-cycle-dominated with a stable router backbone, which left open whether wall-clock aging recovers value on a mesh that moves. §6.12 answers it: production position data puts 91.5% of nodes at a fixed location, and sweeping mobility to 100% never brings the clock ahead.

3.7  Relation to prior art and what a firmware port needs

Each ingredient is individually proven: monotone-metric descent is Babel's feasibility condition[3] and RPL's rank rule[4]; receiver-side anycast with contention timers is ExOR[2]; destination-rooted gradients go back to directed diffusion in sensor networks. The composition is what is new: coupling eligibility width to the protocol's own evidence of staleness — wall-clock age in v1, per-hop failure escalation in the final spec — so the protocol occupies the whole flood↔unicast spectrum continuously instead of switching between brittle modes and refreshing its state opportunistically from delivered traffic instead of periodic control packets.

A Meshtastic port is less invasive than it may sound. The firmware's router hierarchy (src/mesh/FloodingRouterNextHopRouter) already contains the two hard primitives: packet-ID duplicate suppression (the visited set) and an SNR-weighted rebroadcast contention window (RadioInterface::getTxDelayMsecWeighted) — the managed flood already makes far nodes rebroadcast first; GradCor re-keys that same timer on (gradient, SNR). The gradient table is a small addition to per-node state alongside the NodeDB, planting piggybacks on floods that already happen (NodeInfo, position) and the corridor level fits in spare header bits. Porting GradCor-R rather than v1 removes every scheduled timing element: no per-entry aging tick, no periodic timer, no state clock. The one timer that remains is the contention backoff of §3.3 — one-shot, milliseconds scale, armed only by a reception — and the firmware already ships its exact skeleton: getTxDelayMsecWeighted maps the received SNR to a contention-window size (−20…10 dB → CWmin…CWmax), gives ROUTER-role nodes an early window and picks a random slot inside it. GradCor-R re-keys the slot choice on (gradient, SNR) instead of SNR alone; no new timing machinery is introduced. The genuinely open engineering risk is contention-race correctness when several eligible receivers sit inside mutual capture range — duplicate forwards cost airtime but are absorbed by dedup; missed suppression is the case to instrument first (§8, item 5).

One dedup semantic needs deliberate care in the port. Meshtastic's wasSeenRecently drops a packet the node has seen; GradCor's visited-set equivalent must drop only what the node has forwarded. The difference is the protocol's self-recovery path: a contention loser that muted (§3.3) has seen the packet but never carried it — if the winner's branch dead-ends a few hops later and a widened-corridor broadcast reaches that loser again, it must be allowed to pick the packet up. Under seen-keyed dedup every raced-and-lost candidate is permanently consumed and a packet that commits to a failing branch cannot wander back to the branch that would have worked; under forwarded-keyed dedup only the actual chain of forwarders is burned. The replay models forwarded-keyed dedup throughout and its recovery behaviour is visible in the data — 22.8% of gradient-guided deliveries took more hops than the sender's gradient promised (§3.2), i.e. walks that detoured and still arrived. A seen-keyed port would silently forfeit exactly those deliveries; the change is one predicate, but it belongs on the firmware-prototype checklist next to the contention race.

4  Data: the Norwegian mesh, one production week

All inputs were extracted from RelayMesh production systems for network “norway” (a public community network) over 2026-06-27 → 07-04:

SourceContentVolume
Memgraph (topology graph):Node rows of the 7-day build window (roles, GPS; its pre-fix :HEARD edges were not used — see below)499 nodes, 3,613 edges
ClickHouse messagesraw TRACEROUTE_APP packets (fwd path, route_back, per-link SNR)18,919 packets
ClickHouse node_activity_5minper-node hourly message counts (presence)326 active nodes

Extracting links from traceroutes requires one correctness step the production topology builder missed at the time of this study: 31% of stored TRACEROUTE_APP rows are responses, not requests. A response is a new packet whose envelope source and destination are swapped while route[] still lists the relayers in requester→responder order; parsing it as a request creates reversed edges and parsing mid-flight rows as complete paths invents endpoint hops that never happened. The corrected extraction — developed alongside this paper and since shipped to RelayMesh production — classifies each row by payload shape and hop accounting, orients both legs correctly and appends endpoints only on arrival evidence. The effect is large: the naive parse yields 4,249 directed links, the corrected one 1,423 — and measured link reciprocity rises from 36% to 57%, i.e. much of the apparent asymmetry in the production graph is an extraction artifact, though 43% of real links still have no observed reverse. Return legs (route_back), previously discarded in production, contribute 303 links (~21%) seen in no other way. Role distribution: 141 CLIENT, 66 CLIENT_MUTE, 55 CLIENT_BASE, 24 ROUTER, 6 ROUTER_LATE, 1 TRACKER, 206 unset. The largest weakly connected component covers 200 nodes, the largest strongly connected component 125 and the graph is hub-dominated: the busiest routers (degree 53–107) anchor the geographic clusters.

Figure 6. The real 7-day topology after corrected extraction, plotted at GPS positions (241 nodes with valid fixes shown; 566 of 1,423 directed links have both endpoints positioned). Red/orange: ROUTER / ROUTER_LATE backbone; blue: CLIENT_BASE. Three clusters (Oslo region, Innlandet, Trøndelag) are joined by a small number of long bridge links — the same structure visible in the RelayMesh Optimization view.

5  Evaluation method

The study is a trace-driven replay: rather than inventing a radio environment, we reconstruct the network the production pipeline actually observed — which links existed, how good they were and when each node was awake — and then run all five forwarding protocols (the four strategies of §2, with GradCor in both its v1 and final form) over that reconstruction, hour by hour, for the same week the data describes. Figure 7 shows the pipeline end to end; the subsections below walk through each stage.

ClickHouse  messages 18,919 TRACEROUTE_APP packets route[], snr_towards, route_back, snr_back ClickHouse  node_activity_5min per-node hourly message counts 326 active nodes Memgraph  :Node 499 nodes: role, GPS, hardware 7-day build window directed link graph  (§5.1) response-oriented fwd + return legs → 1,423 links: obs count, mean SNR presence model  (§5.4) online-hours set per node, ≤3 h gaps filled; median 22/168 h role semantics  (§5.5) 66 CLIENT_MUTE nodes receive but never relay traceroute appearances calibrated link model  p(SNR, obs)  (§5.2–5.3) grid search of (x₀, p_max, p_min, k, rev_p) against two empirical anchors: real hop-count distribution  +  23% round-trip completion rate 168-hour replay  (§5.5–5.6) 5 protocols × 30 real (src,dst) pairs × 10 seeds, traffic only when both endpoints online → delivery %, tx/packet, tx/delivered, hourly series
Figure 7. The trace-driven replay pipeline. Production observations (top) are reduced to three models — who can hear whom and how well, who is awake when and who is allowed to relay — which are combined into a calibrated stochastic world that the five protocols are replayed against. Note the hour quantities here (online-hours presence, ≤3 h gap fill, the 168-hour window) are properties of the evaluation world — when nodes were really awake and how long the replay runs — not protocol state; GradCor-R itself keeps no clock.

5.1  Reconstructing the link graph from traceroutes

Meshtastic's traceroute is the only mechanism in the production data that reveals individual RF hops. A traceroute request floods toward its destination; every node that relays it appends itself to the packet's route[] array and records the SNR at which it heard the previous hop in snr_towards[]. The responder then emits a new packet — envelope source and destination swapped, route[] copied unchanged, its own return relayers accumulating in route_back[]/snr_back[]. The true forward path of the probe is therefore [requester] + route + [responder] and every consecutive pair in that sequence is one real, directed, SNR-annotated radio reception (Figure 8). Three correctness rules matter (each was validated against the raw data): responses are detected by payload shape and hop accounting and their legs oriented requester→responder regardless of envelope direction; endpoints are appended only when the SNR array proves the leg actually arrived (mid-flight copies heard by a gateway must not invent the final hop); and duplicates, broadcast and placeholder IDs are dropped. The response heuristic is needed because the canonical discriminator — the protobuf request_id — is absent from encrypted rows (~98% of traffic); on the recent unencrypted rows where it is present, the heuristic matches the canonical label on 98.7% of 871 ground-truth responses. Because part of that heuristic is hop accounting, it depends on hop_start being present and truthful; the 2.8 line proposes dropping packets with a missing or invalid hop_start[8], which would make this anchor strictly more reliable on future windows than on ours.

one TRACEROUTE_APP packet request:  route = [R1, R2],  snr_towards = [s₁, s₂, s₃] reply:     route_back = [R3],  snr_back = [s₄, s₅] from R1 R2 to s₁ s₂ s₃ R3 s₄ s₅ extracted directed links:  from→R1 (s₁), R1→R2 (s₂), R2→to (s₃) +  to→R3 (s₄), R3→from (s₅)   ← reply links a naive forward-only parse drops
Figure 8. How one traceroute exchange becomes directed links. Solid blue: the forward leg [requester]+route+[responder], whose consecutive pairs are real SNR-annotated receptions. Dashed red: the return leg from route_back, which travels different links when the mesh is asymmetric. Parsing both legs of all 18,919 rows with correct response orientation yields 1,423 distinct directed links; the return legs alone contribute 303 (~21%) that the pre-fix production builder discarded.

Aggregating across all packets, each directed link (u→v) accumulates an observation count and a mean SNR. Direction is radio direction: u transmitted, v decoded. This graph is deliberately not symmetrized — asymmetry is a finding, not noise (§4).

5.2  Link delivery model

The trace tells us a link existed; a simulation additionally needs the probability that a single transmission on it succeeds. We model this with two multiplicative factors (Figure 9):

p(u→v)  =  w · [ pmin + (pmax − pmin) / (1 + e−(SNR − x₀)/4) ],     w = obs / (obs + k)

The bracket is a logistic packet-success curve in the link's mean measured SNR (raw snr_towards divided by 4 to approximate dB): strong links approach pmax, weak ones fall toward pmin. The evidence weight w encodes a different fact: a link observed once in seven days — a ducting event, a moment of antenna alignment — is not a dependable link, however good its single SNR sample looked, while one observed hundreds of times is infrastructure. Without this term the simulator treats every fleeting link as permanently available and over-delivers dramatically at short hop counts.

Figure 9. The fitted link model. Top: per-transmission delivery probability vs link SNR, one curve per observation count — evidence, not just signal strength, determines dependability. Bottom: the distribution of mean SNR over the 1,423 real links (median −3 dB), showing most of the network's mass sits below the curve's midpoint, i.e. marginal links dominate.

Because reverse directions could be under-observed, the model includes an optional prior: an unobserved reverse link (v→u) may be granted rev_p · p(u→v) rather than zero, with rev_p calibrated rather than assumed. As §5.3 shows, the calibration drove rev_p to zero — once response rows are oriented correctly, the observed round-trip rate is already explained without inventing reverse links.

5.3  Calibration against two empirical anchors

The model has five free parameters (x₀, pmax, pmin, k, rev_p). None are hand-picked: all were grid-searched to jointly minimise the distance to two statistics the trace itself provides.

  1. Hop-count distribution. For 400 real (src,dst) traceroute pairs, we simulate floods at the hour each real probe was sent and compare the hop-count histogram of delivered packets with the real one (Figure 10), restricted to the 5,917 rows whose SNR array proves the forward leg actually arrived — mid-flight rows only prove a lower bound and would bias the target short. This anchors the effective per-link success rate along paths the network actually uses.
  2. Round-trip completion rate. 23% of real traceroutes carry a route_back, i.e. completed the round trip. Simulated forward-then-reverse floods between the same pairs must approximate this rate. This anchors reverse-link generosity — exactly the quantity PATH's viability depends on.

Best fit: x₀ = 8 dB, pmax = 0.8, pmin = 0.25, k = 10, rev_p = 0. Both anchors now fit well (Figure 10; round trip 0.31 simulated vs 0.23 real). One denominator caveat: the two round-trip rates are not the same statistic — the real 23% is the share of all observed traceroute rows carrying a route_back, while the simulated 31% is the fraction of delivered forward floods whose reverse flood also delivered — so this anchor is directional rather than exact. Under either reading the simulator treats reverse paths at least as generously as the trace shows, the direction that can only flatter PATH (§8, item 1). Notably the grid chose rev_p = 0 — with correctly-oriented links, the data is best explained by unobserved reverse directions simply not existing, so asymmetry is fully binding in the replay. The small remaining round-trip optimism can only overstate PATH (§8).

Figure 10. Calibration anchor 1. Blue: hop-count distribution of the 5,917 arrival-confirmed traceroute forward legs. Orange: hop counts of simulated floods between the same node pairs under the fitted link model. The simulator cannot reach 8 hops (its hop limit 7 binds) but reproduces the 1–7-hop mass closely.

5.4  Presence: real churn instead of synthetic mobility

The synthetic study modelled churn as random node movement. The production data says the real phenomenon is duty cycling: the median Norwegian node was online 22 of 168 hours and the 90th percentile 167. We therefore give every node a set of online hours — the union of its hourly activity buckets and its appearances inside any traceroute path, with gaps of ≤3 hours filled (a node relaying at 09:00 and 11:00 was almost certainly on at 10:00). A node that is offline in a given hour cannot receive, relay, or originate. This is a stronger and more honest stressor than mobility: state rots not because topology changed shape but because the node holding the route simply went to sleep.

5.5  Protocol implementations

All five protocols run over the identical world model; none is given information the others lack. Table 2 lists the fixed parameters. FLOOD and the discovery/fallback floods inside the other four protocols share one implementation, including a first-order MAC model: when several copies of a packet arrive at one receiver in the same flood round they collide and the strongest frame survives with capture probability 0.8 (otherwise all are lost). CLIENT_MUTE nodes never rebroadcast in any protocol.

ParameterValueApplies to
Hop limit7all floods
Per-hop unicast retries3PATH, NEXTHOP
Extra strict-corridor broadcast tries2GRADCOR v1 & R
Corridor widens at gradient age3 hGRADCOR v1 only
Gradient replanted at age24 hGRADCOR v1 only
Collision capture probability0.8all floods
Safety cap, transmissions per packet2,000all protocols
Table 2. Fixed simulation parameters (from sim_norway.py). The hop limit is set to Meshtastic's protocol ceiling of 7 rather than the shipped default of 3 — a choice that favours FLOOD, PATH and NEXTHOP, whose reach it bounds. Planting floods obey the same limit, so the replay never exercises gradient descent beyond 7 hops: the unlimited-hop property of §3 is a spec-level guarantee, not something this evaluation stresses. The limit's value matters asymmetrically for GradCor's internal floods (experiment_hoplimit_flood.py): raising it to unlimited gains almost nothing (50.9 → 51.3% and an unlimited flood costs only ~9% more transmissions than hop-7, since dedup already bounds it), but lowering it to the shipped default of 3 starves planting reach and costs 13 delivery points (50.9 → 37.9%).

PATH discovers a route with one flood, returns the recorded path hop-by-hop over the reverse links (the discovery fails if the reply dies), then source-routes data strictly along the path with 3 tries per hop; a broken hop invalidates the route and permits one rediscovery per packet. NEXTHOP keeps, at every node, the relay that last delivered a packet toward each destination — learned free of charge from the parent chain of any successful flood — and chains down these caches, falling back to a flood (which re-teaches caches) on any miss or per-hop failure. GRADCOR v1 implements Algorithm 1 exactly as specified in §3, with gradient ages advancing once per simulated hour (the upper band of Figure 4); GRADCOR-R implements Algorithm 2, the clock-free final specification. Both run in parallel in the head-to-head so the evolution claim of §3.6 is tested in the same arena as the competing protocols, not only in isolation. For both, the contention race is resolved ideally (lowest gradient always wins, suppression never fails) — a simplification whose consequences are discussed in §8. A second GradCor-specific shortcut also needs flagging: a source with no gradient triggers the planting flood from the destination at that instant, charged to the triggering packet. Causally, nothing in a deployment tells a destination to flood on demand; the deployable cold-start paths are seeding by the destination's ordinary periodic broadcasts and the flood-the-packet fallback of §3.2. §8, item 7 replaces the shortcut with both deployable forms and measures the difference.

5.6  Workload and metrics

Traffic draws its pairs from the real demand pattern: 30 (source, destination) pairs sampled from the actual traceroute pair-frequency distribution (weighted, without replacement), restricted to endpoints online ≥20 hours in the week so conversations can exist at all. Its cadence, however, is a modelling choice and not a measurement: each pair sends one packet per hour, but only in hours where both endpoints were genuinely online — a message to a sleeping node is not a routing failure and must not be scored as one. That metronome is roughly 90× denser than production addressed traffic, and §6.13 measures the real process and re-runs the headline under it. The results in §6 through §6.12 should be read as holding at this fixed, generous conversational density; §6.13 states what changes when it is replaced with the measured one, and it changes the explanation of the result more than the result. Ten seeds give ≈30,000 packet attempts per protocol. Every broadcast or unicast attempt costs one transmission; gradient-planting, discovery and fallback floods are charged to the packet that triggered them, so no protocol externalises its control traffic. We report delivery rate, transmissions per packet sent (offered-load cost) and transmissions per packet delivered (the airtime price of a success — the currency that matters, per §1).

6  Results

ProtocolDelivery %Tx / packet sentTx / packet delivered
FLOOD (hop limit 7)25.317.971.0
PATH (stylized source routing, §2)4.018.9473.2
NEXTHOP (2.6-style cache)30.418.259.9
GRADCOR v1 (age-widened corridor)48.610.421.4
GRADCOR-R (final spec, no clocks)50.710.821.3
Table 3. Aggregate results over 10 seeds, 30 real pairs, 168 real hours, with both GradCor forms evaluated in parallel. Absolute percentages are model outputs; the ranking and the ratios are the robust findings. Per-seed delivery varies with the sampled traffic pairs (s.d. 4.5–5.4 points), but the per-seed differences are tight (a seed fixes the sampled pairs, presence and world for all protocols alike, though each protocol draws its channel outcomes independently): GradCor-R beats FLOOD on all ten seeds (+19.5 to +29.9 points, mean +25.3, s.d. 3.4), while R − v1 is +2.1 ± 3.0 — a genuine tie within noise, slightly ahead, independently confirming the §3.6 ablation in the full head-to-head.
Figure 11. Delivery rate (bars, left axis) and airtime cost per delivered packet (diamonds, right axis, log scale), with both GradCor forms side by side. GradCor-R dominates flooding on both axes simultaneously: +25.4 points of delivery (2.0×) at 3.3× less airtime per delivered packet — with no state clocks.
Figure 12. Delivery rate across the real week (6-hour buckets); light green is v1, dark green GradCor-R — the two track each other throughout (R converges slightly faster in the cold-start hours, before v1's aged gradients earn their first refresh), and the advantage over flooding is stable through daily presence cycles. PATH never recovers because its routes break faster than they amortize.

6.1  Does it generalise? Five more production networks

A single network, however real, is one draw from the space of mesh topologies. To test whether the result is a property of the protocol or a property of Norway, we ran the identical harness on the five other production networks with the most traceroute traffic in the same week: Bay Area Mesh (3,637 nodes), Florida Mesh, SoCal Mesh, Meshtastic Portugal and Meshtastic Italia. Everything was recomputed from scratch per network — link graph, presence, roles and crucially the link-model calibration, re-fitted against each network's own hop-count distribution and round-trip completion rate (the fitted parameters genuinely differ: x₀ ranges 0–12 dB and the reverse-link prior stays at zero everywhere except Florida and SoCal). Reusing Norway's constants elsewhere would have been methodologically wrong; nothing about the protocols themselves was changed or tuned.

Figure 13. Cross-network validation on six production networks (5 seeds each, same 168-hour week, per-network calibration). Top: delivery rate; bottom: airtime per delivered packet (log). On every network with adequate traceroute evidence the ranking is identical — GradCor-R ≈ GradCor v1 > NEXTHOP > FLOOD >> PATH — with GradCor-R delivering 1.8–2.5× flood's rate at 2.5–3.7× less airtime per delivered packet. Italia (starred) is the honest boundary case: its 2,492 traceroutes yield only 146 usable links for 2,207 nodes, gradients have almost nothing to descend on and gradient routing degrades to flood level while NEXTHOP's simpler cache wins.
NetworkNodesLinksRecip % FLOOD %GRADCOR-R %R vs floodR tx/dlv
Norway4991,4235725.551.22.0× @ 3.3× cheaper21.3
Meshtastic PT1,2084,1473624.043.21.8× @ 3.5× cheaper25.1
SoCal Mesh1,3513,4613113.227.82.1× @ 3.7× cheaper35.2
Bay Area Mesh3,6376,895325.612.92.3× @ 3.4× cheaper54.3
Florida Mesh1,3943,994374.711.92.5× @ 2.5× cheaper46.1
Meshtastic Italia2,207146306.25.10.8× — below eligibility bar39.9
Table 4. Per-network summary (full five-protocol data in Figure 13 and multinet_results.csv). Norway's row is its 5-seed re-run within this harness, hence the small differences from Table 3's 10-seed values — a useful incidental read on seed noise (≈±0.5 points). Absolute delivery scales with evidence density — the dense US networks have far more nodes per usable link, so every protocol delivers less there — but the ratios hold. Italia's row is the operative precondition, not a counterexample: gradient routing needs enough traceroute-derived link evidence to plant gradients on — exactly the evidence the extraction fixes of §7 (now shipped) increase.

Two observations deserve emphasis. First, the ranking and the ratios — the two things §8 argues are the trustworthy outputs of this methodology — survive a 7× range in network size, a 30–57% range in link reciprocity and independently fitted link models; the Norway result is not a topology accident. Second, the one failure is diagnostic rather than embarrassing: where traceroute evidence is too thin to define gradients (Italia, 0.07 links per node), GradCor degrades to roughly flood performance instead of below it — the graceful-degradation property doing exactly what it was designed to do at the extreme — while the evidence-free NEXTHOP cache takes the lead. Evidence density is thus a measurable eligibility criterion for deploying gradient routing on a given mesh.

6.2  What would 99% delivery take? The ceiling and a reliability ladder

A natural next question is how far delivery could be pushed — say, to 99%. The replay can answer this honestly because it can first measure what no algorithm can exceed: the fraction of attempts for which a forward path over online, relay-capable nodes exists at all. On the Norway world that instant-reachability ceiling is 93.2% and patience barely moves it: a packet held and retried for up to 24 hours can reach at most 95.6% of attempts, because 4.4% of them never see a forward path in the entire week (experiment_target99.py, experiment_custody.py; 10 seeds). 99% absolute delivery is therefore not a protocol problem on this network — it is unreachable by any routing scheme whatsoever — and the meaningful question becomes distance to the ceiling.

That distance can be closed almost entirely with three additions, each a single rule that leaves the one-byte state and the forwarding walk untouched (Figure 14a). First, the fallback-flood cold start already measured in §8, item 7 (+4 points). Second, a rescue flood at the local minimum: instead of the deterministic drop, the stuck holder floods the packet from where it stands (+5 points) — philosophically consistent, since the drop point is by construction the node that has just proven, across the whole escalation ladder, that its knowledge is exhausted and a flood from there is the narrowest flood that can still help. Third — the single biggest lever — end-to-end retries: re-sending the packet up to k times on failure lifts delivery to 75.3% (k=1), 84.9% (k=3) and 88.0% (k=5), i.e. 94.4% of everything reachable. Each retry draws fresh channel outcomes and benefits from whatever gradients the failed attempts refreshed; the machinery is not hypothetical — Meshtastic's reliable-delivery layer already retransmits unacknowledged DMs today and the headline evaluation deliberately granted no protocol this help (§8, item 8). Finally, custody — holding a failed packet and retrying hourly for up to 24 h, the one genuinely new architectural element (a store-and-forward queue with expiry) — reaches 94.5%: 98.9% of the temporal ceiling. The anatomy of what still fails at k=5 confirms the diagnosis (Figure 14b): 56.6% of remaining failures are physically unreachable that hour, versus 23.9% mid-walk losses and 19.5% failed cold starts.

The price is stated just as plainly: transmissions per delivered packet rise from 21.5 (published spec) to 42.7 at k=5 and 79.7 with custody — at the extreme, airtime per delivered packet returns to flood's level (71) while delivering 3.7× more. The efficient knee is k=3–5 without custody: 85–88% delivery at 38–43 tx per delivered packet, still well ahead of flood on both axes simultaneously. Three caveats bound the claim: the replay's independent per-transmission link draws flatter retries relative to real correlated fading (§8, item 8); no duty-cycle budget is modelled and 38–75 transmissions of offered load per packet may be regionally infeasible; and custody changes the application contract (deliveries arriving hours late). The structural conclusion survives all three: simple reliability envelopes take GradCor essentially to the network's own ceiling, and the ceiling itself — the last five to seven points — is an operations problem, router coverage and evidence density, the same deployment precondition §6.1's evidence-starved network surfaced.

Figure 14. The road toward 99% on the Norway replay (10 seeds). (a) Delivery (bars) and airtime per delivered packet (diamonds) as reliability mechanisms stack: cold-start fallback, rescue flood at the dead end, end-to-end retries, store-and-forward custody. The dashed lines are the network's own limits — 93.2% of attempts have a forward path at send time and only 95.6% gain one within 24 h — so the final rung stands at 98.9% of what is physically deliverable. (b) Remaining failures at k=5 by cause: the majority are attempts with no existing forward path — capacity the network does not have, which no routing change can add.

6.3  The broadcast side: what the hop limit actually costs

GradCor removes the hop limit only where strict descent certifies progress — unicast. The majority of a mesh's load (positions, telemetry, NodeInfo, channel texts) is broadcast and keeps managed flooding (§8, item 9), so a fair question is whether that traffic could shed its hop limit too. Structurally it can, safely: duplicate suppression, not the counter, is what terminates a flood — every node relays a given packet at most once, so an unlimited flood is bounded by mesh size. Whether it is worth anything is an empirical question and the answer across all six networks is: almost nothing (experiment_hoplimit_multinet.py, Table 5). Raising the limit from 7 to unlimited adds at most 2.2 points of reachable-node coverage (Norway) for 11–25% more transmissions; on four of the six networks the gain is under one point. The limit does still bind at 7 — on 8–38% of floods the frontier is alive when the counter cuts it — but that surviving frontier is so attenuated by per-link loss that letting it run adds little. Floods die of link loss, not of the counter.

The consequential knob sits at the other end: Meshtastic's shipped default of 3. On every network with usable link evidence, hop limit 3 forfeits between a third and two thirds of the flood coverage that limit 7 achieves (Norway 20.8% → 32.2% of reachable nodes, SoCal 4.3% → 12.8%, Portugal 6.3% → 15.2%) — and it starves GradCor's planting floods (Table 2: −13 delivery points). For broadcast, then, hop-limit removal is safe but immaterial, while raising the default toward the protocol ceiling of 7 is the change that matters — both for classic flood reach and as the planting substrate gradient routing stands on.

NetworkReachable covered, hl 3hl 7 unlimitedTx/flood 3 → 7 → unlim Limit binds at 7
Norway20.8%32.2%34.4%6.5 → 16.3 → 18.122%
Meshtastic PT6.3%15.2%15.8%5.6 → 22.4 → 25.338%
SoCal Mesh4.3%12.8%14.2%4.3 → 21.0 → 26.337%
Bay Area Mesh2.2%4.8%4.9%3.9 → 12.9 → 14.317%
Florida Mesh1.1%1.9%2.0%2.9 → 6.1 → 6.88%
Meshtastic Italia30.3%30.3%30.3%1.2 → 1.2 → 1.20%
Table 5. Broadcast flood coverage of BFS-reachable online nodes at hop limit 3 / 7 / unlimited (250 real sources at real hours, 2 seeds, per-network calibration). “Limit binds” = floods whose frontier was still alive when the hop-7 counter cut it. The 3 → 7 step is large everywhere the graph has depth; the 7 → unlimited step is marginal everywhere. Italia's 146-link graph is too shallow for any limit to matter. Coverage of online nodes is additionally bounded by connectivity itself (on Norway only ~35% of online nodes are reachable from a typical source at all — the same evidence-density ceiling as §6.1/§6.2). Figure 15 shows the same data side by side.
Figure 15. Table 5 visualized: broadcast flood coverage of reachable online nodes (bars) and transmissions per flood (inset) at hop limit 3 / 7 / unlimited on all six networks. The shipped default of 3 is the costly setting — it forfeits a third to two thirds of achievable coverage wherever the graph has depth — while removing the limit beyond 7 changes almost nothing on either axis. Italia's shallow 146-link graph is indifferent to every limit.

If not the hop limit, can broadcast reach be bought at all? Only with redundancy: the flood's one structural deficit is that every link gets a single transmission trial, so every remedy is some placement of retransmissions — and the pricing is roughly linear (Table 6; experiment_broadcast.py, Norway, 900 floods).

VariantRuleCovers reachableTx/floodVerdict
Baselineone tx per relay32%16reference
ROUTER ×2backbone relays send twice41%25best coverage per tx; one firmware predicate
SRC ×2source resends (new packet ID)48%33no firmware change; for broadcasts that matter
ECHOrelay retransmits only on hearing no onward echo46%35adaptive rule loses to dumb resend — see below
RELAY ×2every relay sends twice57%58max reach, worst efficiency; invites collisions
Table 6. Broadcast redundancy ladder (coverage of BFS-reachable online nodes; Norway calibration, 300 real sources × 3 seeds).

The ECHO row is the recorded negative result: redundancy targeted at coverage frontiers — GradCor's silence-arms-retry applied to flooding — does not beat resending from the source. Without a destination there is no direction to be smart about and in a loss-dominated mesh nearly every relay is a frontier, so the targeted rule degenerates into blanket retransmission with bookkeeping. Of GradCor's three assets — persistence, steering, concentration — only persistence transfers to one-to-all traffic; steering is undefined and concentration (suppression) trades reach for airtime (§8, item 4). Managed flood is, in this precise sense, already the broadcast-optimal form of the same machinery.

6.4  One price curve: the all-to-all strategy space

To make sure this is not an artifact of the particular variants we picked, we swept the strategy space systematically, starting from the objective rather than from any protocol: deliver one message to every reachable node, at minimum airtime. Every known scheme decomposes into three orthogonal variables — who relays (all relay-capable nodes, as Meshtastic does; or dedicated infrastructure only, as MeshCore does — its clients never relay and its group channels flood via repeaters alone), persistence (each relay transmits k times) and source repeats (the sender re-floods s times, re-rolling every link) — plus the adaptive gatings already tested. We simulated the grid on the Norway replay (experiment_alltoall.py; Figure 16).

The result is one finding: on a log-airtime axis, every strategy family collapses onto a single price curve — on Norway roughly eleven points of reachable-node coverage per doubling of transmissions — and nothing rides above it, while both adaptive rules sit slightly below it. The collapse itself reproduces on all six networks (experiment_alltoall_multinet.py: log-linear fit R² 0.87–0.98), but the slope — the network's airtime-to-coverage exchange rate — is a network property, ranging from 10.7 points per doubling (Norway) down to 1.6 (Florida) in the same evidence-density order as Table 4: the sparser the usable link graph, the less any amount of airtime can buy. The MeshCore pattern is not a better curve but a different point on the same one: with Norway's organic role mix (30 backbone nodes) it is the cheapest configuration measured (2.7 tx per message) at correspondingly low coverage (12.6%); its real-world efficiency comes from engineered repeater placement, which is topology, not protocol. The same collapse settles whether broadcast could be folded under GradCor as the special case dst = everyone: with every node a destination the gradient is zero everywhere, the corridor admits every receiver and GradCor reduces to managed flood exactly. Unicast and all-to-all are the two ends of Figure 1's knowledge spectrum and each end is already at its optimum — corridors cannot help a flood, floods cannot match corridors for a named destination. What remains is menu pricing per message class (beacons at k=1 and hop limit 7; higher-value broadcasts one rung up: backbone ×2 or a source re-flood) and the one lever that moves the whole curve instead of sliding along it: connectivity itself.

Figure 16. The all-to-all delivery strategy space. (a) Norway (900 floods per point): coverage of BFS-reachable online nodes vs transmissions per message (log scale). Families: blanket persistence (every relay k times), infrastructure-only relaying (MeshCore pattern, with and without fixed base stations), source re-floods, targeted and gated variants and combos. All families track one Pareto frontier (dashed): placement chooses a point on the curve, persistence climbs it and no adaptive rule beats it — consistent with §6.3's finding that without a destination there is nothing to steer by. (b) The same reduced grid on all six networks: the log-linear shape is universal (fitted lines, R² 0.87–0.98) while the slope — what a doubling of airtime buys — is a network property, ordered exactly by the evidence density of Table 4.

6.5  PIVOT: a self-organizing relay backbone

The price curve (§6.4) prices how much redundancy to buy; it does not say who should spend it. Relay pressure (§6.9) answers that per packet from duplicate counts, but it learns only after forwarding and cannot express the star-topology fact that one hub should relay for a hundred leaves. PIVOT (Pivot Relay Backbone) closes that gap with a local two-hop multipoint-relay heuristic adapted from OLSR MPRs: each node learns reliable one-hop neighbours from ordinary traffic; on every broadcast hop it computes which nodes are still uncovered in its two-hop horizon, marks relays that are the only path to any such node as mandatory, then greedily completes the set by greatest residual coverage, tie-breaking on bidirectional ETX, uptime, observed forwarding novelty and remaining channel headroom. Winners receive short relay leases; utilization lowers a hub’s score so load migrates. Non-pivots wait for a pivot echo and relay only if residual coverage stays silent — direct reception always wins.

Two digest modes bracket deployability. PIVOT-exact carries the full covered set (a simulation ceiling). PIVOT-64/256 replaces it with a k-hash bloom sketch (8 or 32 bytes) in the packet header, charging the byte overhead in every transmission. Edge cases confirm the rule: a 101-node star costs 1 transmission from the centre and 2 from a leaf (vs 96 for managed flood); a three-node triangle suppresses the middle node when the direct link is strong and elects it when direct is weak (100% delivery at ~half the airtime in both cases, experiment_pivot.py). But the sketch has a hard limit worth stating plainly: a 64-bit filter saturates once a neighbourhood exceeds a few dozen nodes, and a saturated filter reports uncovered nodes as covered, so pivots under-relay. On the dense synthetic world this collapses coverage from 81% (exact) to 33% (64-bit); 256 bits recovers it to 62% at proportionally more header airtime. This is the honest separation of algorithmic gain from sketch cost the digest sweep was built to expose: PIVOT’s ceiling is real, but a byte-bounded firmware sketch only reaches it in sparse neighbourhoods.

That caveat is why the production result is a capacity gain, not a coverage gain. Across six networks (experiment_pivot_multinet.py; Figure 17) all six are sparse enough that 64-bit suffices, and there PIVOT does not beat the §6.4 price curve — it picks a cheaper point on it. On Norway it matches managed-flood coverage (31.5% vs 30.6% reachable) at similar airtime (14.2 vs 15.4 tx), and sits far below relay pressure’s 54% coverage — pressure simply buys a more expensive point (59 tx). The value is capacity, and it is bought with reach: integrated end-to-end (experiment_integrated_stack.py, PIVOT replacing relay pressure with GradCor-R, message classes and setpoint unchanged), at today’s mix p95 utilization falls 17.3% → 12.9% and the p99 hotspot warning rises from 445 (managed) to 634 traffic-equivalent nodes (+42%) — but broadcast reach drops from 6.3 to 5.2 receivers per packet (unicast delivery is unchanged, 66.7% vs 66.1%). PIVOT trades coverage for capacity along the one curve; it does not escape it (Figure 18a,b). PIVOT and pressure are complementary: pivots are persistent neighbourhood infrastructure, pressure the packet-local fallback when sketches saturate or topology is too sparse for two-hop summaries.

Figure 17. PIVOT relay backbone. (a) Three-node rule: direct beats pivot on expected cost; weak direct elects the middle relay. (b) 101-node star: centre broadcast = 1 tx, leaf = 2 tx. (c) Six-network scorecard: PIVOT-64 vs managed flood and relay pressure. (d) Integrated stack at 499 nodes: fast+PIVOT lowers utilization with similar delivery; hotspot capacity ~634 nodes.

6.6  The load axis: payload aggregation, and why capacity grows by factors not orders

A pattern now recurs across §6.3–§6.5: PIVOT, relay pressure and the price curve all reduce transmissions per flooded packet, and each buys a constant factor — PIVOT ~1.4× airtime on Norway, the whole stack ~1.5×. None of them reduces the number of packets, yet 79% of the traffic is periodic BULK broadcast (position, telemetry, NodeInfo) that is inherently aggregatable. That is a second, orthogonal axis: a hub can collect its neighbours’ reports over a short window and flood one packet carrying G reports instead of G separate floods (experiment_aggregate.py). We modelled LoRa time-on-air exactly (Semtech ToA, LongFast-like SF11/BW250) and accounted airtime per node, because the constraint that caps node count is the busiest node (§6.11), not the channel total.

The result draws the real boundary on how far software can go. Aggregation cuts the hotspot node’s airtime, but the win is bounded by two things it cannot change. First, the payload/preamble ratio: an aggregate of G reports pays one preamble but G payloads, so with 20-byte reports the ceiling is ~ (star, hub load 42→21 s at G≥20) — and it only reaches ~4× if reports shrink to 4-byte deltas, where the preamble dominates (Figure 18c). This makes the ceiling a moving target in the wrong direction: per-packet signing and the name-field size changes on the 2.8 roadmap[8] both add bytes, and every added byte pushes the payload/preamble ratio further from where aggregation pays. The lever that lifts the ceiling and the lever that authenticates traffic therefore pull against each other, which is an argument for delta/compressed telemetry encodings rather than against signing. Second, and decisively, density: aggregation needs hubs that hear many neighbours. On the dense synthetic world it delivers 1.7× hotspot relief; on the real, sparse Norway mesh it delivers essentially nothing (0.99×), because most nodes are their own best hub and batches never fill. Information coverage is preserved or slightly improved throughout (a receiver of the aggregate gets every report in it), and the price paid is latency — the window W is added to every report. Production beacon cadence shows that price is steeper than a window parameter suggests (experiment_beacon.py): at the measured typical-node telemetry interval, filling a batch of 20 takes roughly 30 hours in Norway and about four days pooled across the six networks. Aggregation is therefore a tool for tolerant traffic only, and the batch sizes at the right-hand end of Figure 18c are not reachable for anything a user waits on.

This is the answer to “why doesn’t node count grow an order of magnitude?” Coverage and capacity are not two goals but two ends of one price curve (Figure 18a): every relay policy slides along it, so more capacity is bought with less reach and vice versa, and the curve’s slope is fixed by physical placement, not protocol. Aggregation is the only lever that moves off that curve, by attacking offered load — but it is capped at a small factor by radio physics and pays out only where the mesh is already dense. The compounding that a large network needs (PIVOT’s ~1.4× × aggregation’s ~2× × the stack’s governors) yields a few-fold headroom, not a decade. An order-of-magnitude increase requires either byte-level change — delta/compressed telemetry that lifts the aggregation ceiling — or spatial reuse: more geography, not more logic on the same spectrum.

Figure 18. The coverage–capacity–load tension. (a) Every Norway broadcast policy is a point on one airtime→coverage price curve; PIVOT is a cheaper point (more capacity, less coverage), not a better curve. (b) The integrated stack’s explicit trade: fast+PIVOT raises node capacity but lowers broadcast reach; full mode raises reliability but cuts capacity. (c) Aggregation attacks a different axis — hotspot airtime reduction vs batch size G — but is bounded by the payload/preamble ratio (star 20 B caps ~2×, 4 B deltas reach ~4×) and by density (sparse Norway gains nothing). Latency is the price of the window.

6.7  Message classes: quantizing the price curve

A smooth curve offers no natural breakpoints, so any operational split into “message classes” is a policy quantization — but three empirical facts pin where a small set of classes should sit. First, temporal repetition rides the same curve: the union coverage of m successive baseline floods from the same source grows 13.5% (m=1) → 24% (m=3) → 39% (m=12) — about six points per doubling of cumulative airtime (experiment_temporal.py), inside the family band of Figure 16. Periodic traffic — positions, telemetry, NodeInfo, the bulk of a mesh's load — therefore already climbs the curve by repeating; granting it per-flood redundancy would pay for the same coverage twice. Second, one step has distinctly best marginal efficiency (backbone ×2: nine points for +56% airtime, one firmware predicate). Third, returns diminish steeply past about two doublings (blanket k≥3 spends 100+ transmissions to stay under 75%) — beyond that is not a class but an emergency action. Three classes, spaced roughly one airtime-doubling apart, exhaust the useful range (Table 7); finer splits are not resolvable against seed noise (±0.5–1 point) and would pretend a precision the network-specific slope (1.6–10.7 points per doubling) cannot honour. The class labels generalize across networks; the absolute coverage each buys does not.

ClassMechanismNorway pointDefault assignment
BULKk=1, hop limit 732% per flood, compounding over repeatsperiodic broadcasts: position, telemetry, NodeInfo
STANDARD+ backbone relays ×2~41%one-shot human broadcasts: channel texts
ASSURED+ source re-flood (×2)~58%alert-class traffic
Table 7. A three-class quantization of Figure 16's price curve. Each class costs roughly one airtime doubling more than the previous; coverage figures are Norway model outputs — on sparser networks the same classes buy proportionally less (Figure 16b).

Should classes be pre-assigned per message type, or user-selectable? The economics are asymmetric: the class multiplier is paid by the neighbourhood's airtime, not the sender's, so unconstrained user choice is a commons problem. The defensible middle is defaults by port/type (the Table 7 assignment) plus a rate-limited per-node override — a user may pin, say, a critical telemetry stream one class up, within a budget of above-BULK sends per hour; an exhausted budget degrades the sender back to BULK rather than degrading the mesh. Implementation is small: the class fits in two header bits — and can share the field GradCor already uses, since a packet is either unicast (the bits carry corridor level) or broadcast (they carry the redundancy rung), never both — relays act on it locally (backbone doubles at STANDARD and above) and only the source acts on ASSURED's re-flood.

6.8  Does the walk survive real depth? A synthetic stress test

One bias in the replay deserves its own experiment: the link graph is built from hop≤7 traceroutes and all floods obey the wire's hop field, so gradient descent beyond seven hops is never exercised on real data — the no-hop-limit property of §3 is proven at the spec level, not stress-tested. We therefore built synthetic “braid” topologies with three controlled parameters: d, the depth — the true hop distance from source to destination, which the construction fixes exactly (the source sits in column 0, the destination in column d, and a packet must cross every column); w, the number of parallel lanes per column (the receiver diversity anycast feeds on); and p, one per-transmission delivery probability on every link. Full links connect consecutive columns, every node is always on, so depth is isolated from churn. We then measured GradCor-R at depths the replay never sees (experiment_depth.py; Figure 19).

The walk is not the thing that breaks at depth. With warm gradients on a two-lane braid at p = 0.7, delivery is 100% at 7 hops, 99% at 100 and 97% at 200 hops, with cost staying linear at roughly 1.4 transmissions per hop — per-hop anycast persistence (up to five broadcasts across parallel candidates) makes each hop nearly certain, so depth only multiplies cost, not risk. Carrying the same packets by unlimited-hop flood instead decays geometrically — 74% → 43% → 3% → 0% over the same depths — because a flood front gets one trial per link and must survive every stage. The honest failure regime is equally visible: a single chain (w=1) of weak links (p=0.5) collapses at depth (77% → 0%); anycast diversity is the load-bearing ingredient, and GradCor needs some receiver redundancy per hop — which any real mesh dense enough to span 20 hops provides.

Cold start at depth exposes a wall, and it is not GradCor's. With no prior state, the first packet falls back to a discovery flood — and today's 3-bit wire hop field caps that flood at 7, so a fresh 20-hop conversation delivers zero, for every protocol, ours included. Grant the fallback flood unlimited depth and the ramp is quick: at depth d=20 the first packet delivers 47% and packets 4 onward ~95–99% as each success re-stamps true distances; at d=50, 20% ramps to 80% by the tenth packet. The practical reading for deep meshes: the corridor removes the per-packet hop ceiling today, within the existing packet format; extending discovery beyond 7 needs a header change that any 20-hop ambition requires regardless of routing scheme. Caveats: the braid is synthetic and fully awake — it isolates depth, deliberately excluding the churn and asymmetry the Norway replay covers.

And broadcast itself can be fixed the same way the corridor was. The corridor survives depth because of per-hop persistence, and that mechanism transplants directly into the flood: let a relay retransmit (up to R times) until it overhears an onward arrival — the same implicit-ack primitive as the contention race of §3.3 and the managed flood's overhear-cancellation, with the policy inverted: repeat-until-heard instead of mute-when-heard. Measured on the same braids (experiment_persist.py; Table 8): today's flood (R=1) delivers 7.7% of packets to the 200-hop far end at p=0.7; R=3 delivers 100%, at ~2.5 transmissions per relay — geometric decay converted to a linear cost factor, exactly as in the unicast walk, and even weak links reach 99.7% at R=5. Depth is therefore solved in mechanism for broadcast too; what remains unsolved is arithmetic: one packet crossing 200 hops costs ~500 transmissions mesh-wide, fine for a rare alert, catastrophic for periodic telemetry from every node in a mesh large enough to have such a diameter (O(N) transmissions per beacon, N beaconing nodes). No forwarding mechanism fixes that; only scoping does — aggregation at region boundaries, interest-based forwarding toward subscribers (which quietly turns the traffic back into unicast, where the corridor already operates), or accepting that beacons are a neighbourhood fact and long-haul telemetry travels to named collectors. Large networks never flood measurements; they aggregate — this is why.

DepthLink pR=1 (today's flood) R=3R=5Tx/packet at R=3
20 hops0.774.3%99.3%100%55
50 hops0.753.0%100%100%127
200 hops0.77.7%100%100%487
200 hops0.50.0%88.0%99.7%572
Table 8. Echo-persistent flooding on the two-lane braid (300 packets per cell): delivery to the far end when each relay retransmits up to R times until it overhears an onward arrival. R=1 is the plain flood of Figure 19a. Persistence converts depth from a survival problem into a linear cost multiplier (~2.5× per relay at R=3) — but the mesh-wide bill per deep packet (last column) is what confines this to rare, alert-class traffic.
Figure 19. Depth stress test on synthetic braids (300 packets per point). (a) Two-lane braid: warm-gradient corridor delivery (solid) vs unlimited-hop flood transport (dashed) as true path length grows from 7 to 200 hops, per link quality. Persistence per hop survives depth at linear cost; front-survival flooding decays geometrically. (b) Cold start on a fresh mesh (w=2, p=0.7): per-packet delivery as the refresh plants state — fast ramp to steady state once the discovery flood can reach at all; at today's hop-7 wire ceiling, depth > 7 cold start is zero for any protocol.

6.9  Relay pressure: replacing roles with two measured numbers

§6.5's PIVOT backbone elects persistent neighbourhood pivots from two-hop coverage; this section tests the complementary per-packet rule that needs no topology summary — useful when digest sketches collide or evidence is too sparse for MPR convergence. Sections 6.3–6.7 leave the flood with a structural tension: the right relay policy is regime-dependent — sparse meshes reward persistence (§6.8), dense neighbourhoods reward suppression (§8, item 4), and a few well-placed nodes carry outsized value (the backbone rungs of Table 6) — yet the mechanism that assigns policy today is a hand-configured role enum, the last piece of human-guessed structure in the system. Physical placement is the constant no protocol can change; everything above it should calibrate itself. We tested whether it can, with the same move GradCor applied to unicast: replace configured structure with evidence the radio already provides. Each node maintains two numbers, both free of control traffic: crowding c, an EWMA of how many duplicate copies of each packet it decodes (“how covered is my neighbourhood without me?”), and novelty, an EWMA of whether its own relays still produce first-copy receivers (“does my transmission create coverage that would not otherwise exist?”). Its relay pressure is the maximum of the two terms, and pressure alone sets the policy ladder: contention slot (high pressure transmits first), cancellation threshold (low pressure mutes on the first overheard relay, high pressure never mutes) and echo-persistence (§6.8's R, from 1 to 3). A node in a crowd fades toward silence unless its transmissions keep reaching someone new — which is precisely the definition of a bridge.

The test is falsifiable and three-legged (experiment_pressure.py; Figure 20, Table 9): one config-free rule must match the best static policy in every regime, where each regime's best static differs. It does. On a dense world (three 20-node cliques joined by two single bridges — the regime where suppression wins) pressure reaches 93% of the best coverage at a quarter of its airtime, Pareto-dominating the plain flood on both axes, and the twelve nodes that self-promote to high pressure include all four bridge endpoints — discovered by passive listening, with no role ever assigned. On the sparse braid (the regime where persistence wins) it matches blanket persistence, 97.7% vs 96.3% at equal cost. On the Norway replay it matches the best static policy (54.1% vs 53.9%) — 21 points above today's flood — with every node allowed to relay and no CLIENT_MUTE configuration consulted. The mechanism also failed informatively along the way: crowding alone suppresses the bridges together with the crowd (dense coverage collapses to 34%), and an active variant that probed by occasionally staying silent learned too slowly; the signal that works is passive outbound novelty. Caveats mirror the section's others: the simulator observes first-copy receptions directly, while firmware must infer novelty from onward relays and acks — under-crediting nodes that serve silent leaves is the design problem to solve in a port — and the dense world's slotted contention model is still gentler than a truly congested channel, which is the regime where suppression's value should grow further.

World (regime)Best static policyBest static Pressure (no config)
Dense: cliques + bridges (suppression)blanket persist R=390.4% @ 80.0 tx83.7% @ 18.4 tx
Braid d=50 (persistence)blanket persist R=396.3% @ 134.0 tx97.7% @ 133.2 tx
Norway replay (mixed)blanket persist R=353.9% @ 59.0 tx54.1% @ 58.3 tx
Table 9. Relay pressure vs the best static configuration per world (300 packets after a 30-packet learning warm-up; Norway: 200 real sources, 2 seeds, coverage of BFS-reachable nodes). The self-calibrating rule matches the best static effectiveness everywhere and beats it on airtime exactly where suppression matters — without any per-world tuning, which is the point: the best static column requires knowing the regime in advance.
Figure 20. Relay pressure. (a) Effectiveness (bars) and airtime (inset) for three static policies and the pressure rule across the three regimes — each regime's best static differs, pressure tracks it everywhere. (b) The dense world after 330 packets, node colour = learned pressure: clique interiors fade toward silence while all four bridge endpoints self-promote to maximum pressure. The role system's job — deciding who relays with what priority — has been performed by measurement.

Running the same head-to-head on all six production networks (experiment_pressure_multinet.py; Figure 21) adds the cross-network check: pressure matches the best static policy on every network — which is persistence on all six, since every evidence-sparse organic mesh is a persistence regime — while spending less airtime wherever local redundancy exists (Florida −19%, Italia −32%). The six real networks therefore validate the rule's safety, not its full range: none of them is dense enough to exercise the suppression side, which is exactly the urban regime Figure 20's synthetic dense world covers and the regime a firmware trial in a city core would test first. A persistence-only-off variant sharpens the boundary: with muting as the only lever, pressure reproduces plain flood's coverage and airtime almost exactly on all six networks (Norway 32.8% at 18.3 tx vs 32.8% at 18.5) — sparse floods are already near-efficient, because suppression can only harvest redundancy and sparse meshes have almost none. The airtime dividend lives in density, where pressure collects it automatically.

Figure 21. Relay pressure on the six production networks (per-network calibration, every node allowed to relay, 30-packet warm-up). Bars: coverage of BFS-reachable online nodes; insets: transmissions per flood. The self-calibrating rule tracks the best static policy on each network without knowing which network it is on.

Two governors bound the rule in deployment. First, pressure composes with the message classes of §6.7: pressure decides who relays and with what priority, the class caps how much persistence a packet may buy (BULK at R=1 — periodic traffic is already self-redundant, §6.7 — STANDARD at 2, ASSURED at 3), so the multi-fold airtime of full persistence is spent only on traffic that warrants it. Second, an airtime governor makes “best effort without breach” self-calibrating rather than configured: the two required signals — the node's own duty-cycle spend and observed channel utilization — are already measured by the firmware for regulatory accounting, and effective persistence is simply Reff = min(Rpressure, Rclass, Rbudget), where Rbudget derates toward 1 as either signal approaches its ceiling. The hard budget guarantees regulatory compliance; the surplus, where it exists, is spent exactly where pressure says it buys coverage. No user knob is required — and none should be offered, since the airtime being spent is the neighbourhood's, not the sender's (§6.7). Figure 22 compresses the whole trade into one view: the direction the pressure rule moves each world's operating point reveals its regime.

Figure 22. What the pressure rule does to the plain flood's operating point, per world (arrow: flood → pressure in airtime/coverage space). On all six sparse production networks the arrow climbs the price curve — coverage is bought with persistence airtime, never free — and the mute-only variant (open circles) lands exactly on the flood point, confirming sparse floods are already near-efficient. In the dense world — a synthetic construction, since no network in our dataset exhibits this regime — the arrow turns up-and-left: suppression harvests redundancy, and only density has redundancy to harvest. The rule needs no telling which case it is in.

6.10  Headroom control: sliding the operating point by channel state

The message-class and pressure governors above only derate near breach. The full form of the idea is a bidirectional controller: since the price curve (§6.4) is a menu, no node should pick one point on it forever — it should slide along it with the channel's current state, spending surplus airtime on coverage when the medium is idle and retreating below plain flood as it fills. The signal is again one the firmware already measures: local channel utilization u. The controller tested here is three thresholds referenced to the 25% community health cap — u < 18%: full persistence, never cancel (top of the curve); u < 24%: R=2, cancel after two echoes; beyond: R=1, cancel on the first echo (below plain flood). For the test to be fair, congestion must cost something, so reception in this experiment suffers utilization-coupled interference (extra loss min(0.6, 0.8u)); offered load is then swept from quiet to saturating on the dense world (experiment_headroom.py; Figure 23, Table 10).

The controller does what it promises, and its first version taught a design lesson worth recording. At idle both versions are indistinguishable from the best static policy — the surplus is spent, nothing is left on the table. The lesson emerged at moderate load: our first ladder retreated from full persistence to (R=2, cancel after two echoes), coupling every persistence reduction with more cancellation — and since cancellation starves bridge feeding, that ladder skipped the plain flood's operating point entirely, covering 65% at a load where plain flood managed 74%. The corrected ladder descends through the Pareto frontier — full persistence → plain flood (R=1, never cancel) → emergency cancel@1 — and at moderate load matches plain flood's coverage exactly (74.1%) while staying compliant. At saturating load the trade becomes genuine and no ladder abolishes it: every static policy breaches the health cap (plain flood included: 29% mean utilization, 100% of nodes over), and the controller must choose what to protect — the frontier ladder preserves more coverage (55.9%) at partial compliance, the strict (cancellation-coupled) ladder enforces the cap almost perfectly (98.3% of nodes under) at 52.3%. Cap strictness is thus the one genuine policy choice in the stack; everything else self-calibrates. Two further disclosures: on the sparse production networks the controller is a no-op — even 120 broadcasts per hour cannot push Norway past 0.7% utilization, so it sits at full persistence permanently — and the thresholds needed one referencing pass against the cap.

The completed form of the idea inverts the framing once more: do not defend the cap, fill it. The setpoint controller drops thresholds entirely — each node hill-climbs a rung ladder ordered by airtime appetite (emergency → plain flood → persist R=2…5), climbing while its measured utilization sits below 80% of a target just under the cap and descending above it. Coverage is then whatever the allowed airtime buys, and it matches or beats every static and every threshold ladder at every load (Table 10, last column): above the best static at idle (86.1% vs 85.0%, having climbed past R=3 on its own), above full persistence at moderate load while compliant (79.0% vs 78.7%, where plain flood manages 74.0%), and the best compliant coverage at saturation (58.2%). One honest caveat: a cap-filler is optimal for “maximize coverage subject to u ≤ cap,” not per-transmission efficiency — at idle it spends 30% more airtime than persist R=3 for one extra point, because the price curve's diminishing returns are invisible to a controller that only watches utilization; a deployment wanting frugality would add a marginal-gain floor. With this, the governor stack is complete: pressure decides who relays (topology), class decides how much a packet may buy (value), headroom/setpoint decides how much the channel can afford right now (state) — three dials, three already-measured signals, zero user knobs.

Load (floods/h)Persist R=3Plain flood Headroom (frontier ladder)Headroom strictSetpoint (fill the cap)
1085.0% @ 72 tx, u=2%80.1% @ 48 tx, u=2%85.0% @ 72 tx85.0% @ 72 tx86.1% @ 94 tx
4083.6% @ 68 tx, u=9%78.0% @ 47 tx, u=8%83.6% @ 68 tx83.6% @ 68 tx83.5% @ 85 tx
9078.7% @ 64 tx, u=19%74.0% @ 44 tx, u=18%74.1% @ 49 tx, u=18%65.2% @ 46 tx, u=15%79.0% @ 80 tx, u=18%
16073.7% @ 63 tx, all nodes over cap68.4% @ 41 tx, all nodes over cap55.9% @ 20 tx, 67% over52.3% @ 24 tx, 1.7% over58.2% @ 27 tx, 67% over
Table 10. Load sweep on the dense world under utilization-coupled interference (24 hours per cell): coverage @ transmissions per flood, u = mean node channel utilization. The statics ride their fixed point into the cap. The threshold ladders defend the cap; the setpoint controller fills it, taking the best compliant coverage at every load — at saturation the residual choice between coverage (setpoint/frontier) and near-perfect compliance (strict) is a policy decision no controller design removes.
Figure 23. Headroom control under rising offered load (dense world). (a) Coverage: the setpoint controller fills the allowed airtime and takes the best compliant coverage at every load; the threshold ladders defend the cap and retreat earlier. (b) Mean channel utilization against the 25% health cap: every static policy crosses it at saturating load, the controllers stay under.

Cross-network validation closes the section (experiment_setpoint_multinet.py; Figure 24): on all six production networks, under the same utilization-tracked harness, the setpoint controller takes the best coverage on every network — Norway 46.0% vs the best static's 40.4%, and ahead on all five others — while utilization never exceeds 0.2% anywhere: sparse meshes cannot approach the cap, so the controller correctly never retreats and simply spends the abundant headroom. The same caveat scales with it: the top rungs buy those extra points at steep marginal cost (Norway: +5.6 points for +57% airtime), which is optimal against the cap objective and wasteful against a frugality objective — the choice of objective, like cap strictness, is the deployment's one real decision.

Figure 24. The setpoint controller on all six production networks (20 broadcasts/hour, per-network calibration, utilization-tracked): best coverage everywhere, cap never approached (utilization ≤ 0.2% on every network), dense-regime retreat never triggered — the controller validates as safe on real sparse meshes while its cap-enforcement value lives in the dense regime of Figure 23.

6.11  When is a mesh too large? A traffic-equivalent bound

Node count alone has no physical limit: what fills the channel is airtime within one interference neighbourhood, so geographic expansion can add nodes while reusing the same spectrum elsewhere. A useful Norway-specific answer must therefore hold the RF footprint and per-node behaviour fixed. We deduplicated the study week's production rows by canonical (source_node_id, packet_id): 51,892 packets, 309 per hour on average and 387 in the p95 hour, from 96.1 active sources per hour among 499 known nodes; 79.2% were broadcast. The integrated replay (experiment_integrated_stack.py) fixes the calibrated Norway topology and presence trace, multiplies that p95 offered load, and reports the multiplier as “traffic-equivalent nodes.” It preserves the canonical packet mix: 78.93% BULK broadcast, 0.25% STANDARD broadcast, 14.17% want_ack addressed and 6.65% best-effort addressed. Managed current floods both kinds and gives ACKed traffic three end-to-end retries. Integrated fast executes the stack jointly: relay pressure (or PIVOT backbone in the fast+PIVOT variant), message-class caps and the utilization setpoint govern broadcast; GradCor-R with fallback-flood cold start, local rescue and the same ACK retries carries addressed traffic. Integrated full adds the final reliability-envelope step: failed ACKed packets enter 24-hour custody and retry hourly when both endpoints are online. Identical event schedules run for three seeds; 0.5 s transmissions feed the utilization-coupled loss proxy of §6.11. Thus every box in Figure 28 executes, but the reliability envelope is measured as an optional operating mode rather than assumed free.

At today's 499-node equivalent, integrated fast+PIVOT is the capacity winner: transmissions per offered packet fall 11.3 → 8.7, p95 utilization 17.3% → 12.9% and p99 27.8% → 20.1%, while unicast delivery rises 41.0% → 66.7% (vs 66.1% for integrated-fast without PIVOT). Its p99 hotspot warning moves from 445 to 634 traffic-equivalent known nodes (+42%), and its p95 broad-pressure point from 833 to 1079 (+30%). Integrated-fast without PIVOT still beats managed current (+19% / +16%) but leaves broadcast airtime on the table. Integrated-full raises unicast delivery again to 79.1%, but costs 15.0 transmissions per offered packet and moves the two capacity thresholds inward to 312 / 565 nodes. Custody is doing its job — spending later airtime on packets the present topology could not carry — but that makes the design rule empirical rather than stylistic: the fast+PIVOT stack is the default; 24-hour custody belongs only to rate-limited ASSURED traffic. Turning it on for every ACKed exchange converts reliability into congestion. For this footprint the operational fast-mode answer is therefore ~630 nodes as the local warning and ~1080 as broadly loaded, not a universal Meshtastic ceiling; geographic spread can reuse airtime, while denser overlap or bursty traffic lowers the bound.

Figure 25. The complete stack under production-shaped load (identical event schedules, three seeds). (a) P95 local utilization: integrated fast+PIVOT combines every airtime-saving mechanism and stays lowest; integrated-full adds 24-hour custody and trades capacity for reliability. (b) The 25% thresholds make the policy boundary explicit: fast+PIVOT expands hotspot / broad capacity 42% / 30%; universal custody contracts them 30% / 32%. Counts assume the same RF footprint and per-node traffic; geographic growth with spatial reuse does not obey this x-axis.

6.12  Three assumptions the headline rested on

Review of this work by a Meshtastic maintainer identified three places where the result leaned on something assumed rather than something observed: that anycast contention suppression works on real radios; that a mesh cycles duty rather than moves, which is what justified deleting the state clocks; and that the delivery half of the headline belonged to GradCor rather than to the reliability envelope around it. All three are now measured. Two came out in the paper's favour by a wider margin than expected. The third was a fair hit, and correcting it produces a stronger claim than the one it replaces.

Contention suppression is not load-bearing, because the corridor is narrow. Every result above charges one transmission per forwarding decision: the lowest-gradient candidate takes the packet and every other eligible receiver hears it and stays silent. If a loser fails to hear the winner it transmits anyway, so this assumption sits underneath every airtime number in the paper. The first thing to measure is not whether suppression works but how often it is needed: across 165,430 forwarding decisions the mean contention set is 1.38 candidates and 63.2% of decisions have no loser at all. Strict gradient descent is its own admission control, and it has already discarded most of the neighbourhood before any timer runs. Relaxing the assumption confirms what that geometry implies (experiment_suppression.py, Figure 26a). Whether a loser hears the winner need not be a free parameter — both are nodes in the reconstructed topology, so a loser can be made to suppress only when it actually receives the winner's transmission over the same fitted link probabilities as everything else. That graph-derived floor is brutal, and deliberately so: traceroutes only record links that carried traffic, so loser←winner edges are mostly unobserved and the model concludes that just 9.5% of suppressions succeed — far worse than any real radio neighbourhood, where candidates sharing a transmitter are usually within earshot of each other. Even there GradCor-R costs 57.8 transmissions per delivered packet against flood's 70.8. Forcing suppression to fail outright, on every decision, gives 61.5. There is no break-even: the airtime advantage degrades smoothly from 3.3× to 1.15× and never inverts, because a suppression failure converts the walk into a gradient-restricted flood rather than an ordinary one. Delivery is unaffected throughout (48–51% at every failure rate), since redundant forwarders add path diversity as fast as they add cost. Contention timing therefore sets where in that range the protocol lands; it does not decide whether the claim holds.

The mesh does not move. Deleting wall-clock aging (§3.6) was justified on the grounds that this network cycles duty rather than moves, and Figure 5's caption concedes that a high-mobility mesh could recover the clock's value. Both halves of that were assertion. Production position packets settle it, though the measurement is mostly data-quality work (experiment_mobility.py). Meshtastic lets a node broadcast a deliberately imprecise position, and the most common setting in production, precision_bits = 13, quantizes to a ~5.8 km grid — a stationary node on that setting manufactures kilometres of phantom movement. Positions at (0, 0) recur when a node has no fix, and so do positions at (0.0262, 0.0262), which is exactly (0, 0) after the 13-bit centring offset. A minority of nodes emit occasional antipodal garbage while sitting still, which destroys any maximum- or p95-based radius. The robust statistic is therefore r50, the median distance from a node's own median position, computed only on exact-precision nodes with the origin excluded. Across all six networks and 1,381 such nodes, 91.5% never move — 80.3% report the byte-identical coordinate every time — 5.0% stay within a kilometre, and 3.5% roam (Figure 26b). Only 3.2% are in motion while reporting, at a median sustained 27 km/h. The obvious objection is self-selection: privacy-conscious users who reduce precision might be the mobile ones. That is testable, because a 1.5 km grid still resolves a 5 km move. Reduced-precision nodes do roam more — 3.90% against 1.76% past 5 km, a 2.2× bias in exactly the predicted direction — and folding every precision class together gives a population-wide roaming rate of 3.2% of 5,289 nodes. The bias is real, it has been quantified, and the conclusion survives it.

Even where the mesh does move, the clock does not help — and neither does anticipating motion. A measured 3% is not an answer on its own, because the mechanism in question is about what happens when nodes do move: age anticipates staleness, whereas per-hop escalation only discovers it after a transmission fails. So mobility is swept from 0 to 100% of nodes relocating roughly every two hours, with each variant given a private world driven by an identical relocation schedule so the comparison is properly paired across 30 seeds (experiment_mobility_sim.py, Figure 26c; per-seed results and paired bootstrap intervals in mobility_ci.csv). Relocation is local drift — a mover adopts the neighbourhood of a node two hops away, which is what driving through a mesh looks like. The clock-free variant is significantly ahead of the age-widened one at every level from 0 to 50% mobility — paired gaps of 1.0 to 1.6 delivery points, every 95% confidence interval excluding zero — and statistically tied at total churn (−0.10 points, 95% CI [−0.22, +0.02], p=0.23). The anticipated crossover never arrives: at no mobility level does the clock overtake. The reason is a timescale the original argument had backwards: escalation is not the slow mechanism here. It reacts within a single packet's walk, at the hop where the stale entry actually misleads; a three-hour widening threshold reacts in hours, and reacts identically whether anything changed or not. Reactive correction is the faster adaptation, not the lazier one.

That result suggested a mechanism worth trying, and it failed instructively. A node that relocates knows it relocated — it carries the GPS or IMU that detected the move — so it can invalidate its own gradient entries at the instant they become wrong. This is anticipation without a clock: event-driven, zero airtime, firing exactly when staleness is created. It should have been the best of both. Instead it is indistinguishable from plain GradCor-R at the measured 3% rate (−0.74 points, 95% CI [−1.64, +0.16]) and clearly worse once mobility rises — −3.5 points at both 25% and 50% — with or without a re-plant beacon. It wins at no level tested. The explanation generalises past this protocol: after a local drift the mover's old distance estimate is usually still approximately right, so invalidating it discards information that was mostly still true and forces a costly replant, whereas keeping it and letting escalation correct it pays one extra transmission only at the hop where it is actually wrong. Both anticipatory mechanisms lose to reactive correction for the same reason — anticipation throws away partially-valid state, while reaction pays only at the point of real failure. We report this because it is the strongest available argument for the clock-free design, and it is an argument we did not expect to be making.

The delivery claim needed restating, and the restatement is stronger. The original headline pairs two numbers from one operating point: GradCor-R delivers 50.7% where flood delivers 25.3%, at 3.3× less airtime per delivered packet. The airtime half is like-for-like. The delivery half is more fragile than it looks, because §6.2 shows most of the remaining gap to the ceiling closing with end-to-end retries — machinery that is not GradCor's, that Meshtastic's reliable-delivery layer already has, and that the headline evaluation denied to every protocol equally (§8, item 8). If retries lift flooding just as much, "double the delivery" describes the envelope rather than path selection. Giving every protocol the identical retry budget settles it (experiment_matched.py, Figure 26d). Retries do lift flooding hard — from 24.7% to 61.3% at k=5, which is the honest part of the objection — but they lift it along a much more expensive curve, and the two protocols never touch. Read at matched budgets, on one axis at a time:

Matched atFLOODNEXTHOPGRADCOR-R
Delivery at flood's own offered load (17.9 tx/pkt)24.7%30.7%68.4%
Delivery at flood's k=5 load (63.7 tx/pkt)61.3%64.2%84.2%
Offered load to reach flood's best delivery (61.3%)63.755.614.9
Table 11. Matched-envelope comparison, 10 seeds, identical traffic and worlds, every protocol wearing the same end-to-end retry budget k = 0..5. Each row holds one axis fixed and reads the other, so neither figure mixes them.

At the airtime managed flooding already spends, GradCor-R delivers 2.8× what flooding delivers; to reach flooding's best delivery it spends 4.3× less airtime. Those replace "double the delivery at 3.3× less airtime" everywhere it appeared. They are larger numbers, but the reason to prefer them is not that they are larger: it is that each fixes one axis and varies the other, so neither can be read as crediting GradCor with a gain the retry envelope supplied.

Figure 26. The assumptions, measured. (a) Forcing contention suppression to fail: the airtime advantage decays from 3.3× to 1.15× and never crosses flood, while delivery stays flat — the diamond is the graph-derived floor, where only 9.5% of suppressions succeed. (b) Robust position spread across all six networks, exact-precision nodes only: 91.5% never move. (c) Sweeping mobility to 100% over 30 paired seeds: the clock-free variant leads significantly to 50% mobility and ties at total churn, never falling behind, and motion-triggered invalidation (dotted) is never better than either. (d) The same retry envelope for every protocol; stars mark the equal-airtime reading, the dashed line the equal-delivery one.

6.13  The workload was an assumption too — and it was the wrong one

§6.12 tested three assumptions the reviewer named. Auditing our own method afterwards surfaced a fourth that nobody had raised and that turned out to matter more than any of them: the traffic. §5.6's replay has every active pair sending one packet per hour. That number was never measured; it was chosen to give each protocol a workload dense enough to exercise state. Since GradCor's central claim is that state stays fresh because people are talking, a metronome that guarantees hourly talking is not a neutral choice — it assumes the conclusion.

Production addressed traffic is roughly two orders of magnitude sparser than the replay assumed. Extracting 30 days of addressed packets across all six networks (experiment_traffic.py) gives 0.261 packets per pair per day on Norway and 0.112 pooled, against the metronome's 24 — a factor of 92 and 215 respectively. The shape is worse than the rate: 43% of Norwegian pairs and 63% of pairs overall send exactly one packet in 30 days, the median gap between consecutive packets is 7.0 hours, 34% of gaps exceed a day, and only 19% of ordered pairs ever see reverse traffic. For most pairs there is no second packet for a delivery refresh to ride. Two side findings are worth recording: MQTT gateway duplication is 5.91× on addressed traffic and 11.65× on broadcast, ranging from 1.12× in Portugal to 31.8× in the Bay Area, so any per-packet rate computed without deduplicating on (network, source, packet_id) is wrong by a network-dependent factor; and addressed traffic is overwhelmingly infrastructure rather than chat — 53.9% NodeInfo, 14.3% traceroute, 4.6% routing ACK, and 0.1% visible text. The §6.11 capacity work assumed a 79.2/20.8 broadcast/addressed split; measured, it is 76.6/23.4, which holds.

The mesh does, however, beacon constantly — and that is what actually plants gradients. §3.2's second planting path costs nothing because it rides broadcasts the destination already sends. Measuring their cadence (experiment_beacon.py) puts the median gap between a typical Norwegian node's own broadcasts at 59 minutes, and two to three hours pooled across six networks depending on how aggressively thinly-observed nodes are filtered (Portugal and SoCal worst, at three hours or more); 51% of Norwegian nodes broadcast at least hourly and 78% at least three-hourly. NodeInfo clusters exactly on the firmware's 3-hour default, which is the point: this cadence is a firmware constant, not user behaviour. These are upper bounds, since a beacon no gateway heard looks like a longer gap, so the true cadence is faster and the figures below are conservative. The same extraction validates this paper's cost currency: time-on-air shares computed from real payload sizes track packet shares closely (position 46% vs 44%, telemetry 26% vs 24%, NodeInfo 19% vs 23%), so counting transmissions does not misrepresent airtime on this traffic mix.

An external check on the simulator, which the paper previously lacked. Routing ACKs cannot ground-truth delivery — that table is a 12% decoder subset, observes under 1% of deliveries we can confirm by other means, and contains no joinable rows at all for Norway's calibration week. But where the destination is itself an MQTT gateway, its reception is observed directly. On that subset Norway delivers 26.7% over 30 days and 29.9% on calibration-week traceroutes, against the model's 25.3% for flooding (experiment_ack.py). Same universe, marginally pessimistic against a subset biased toward powered, well-sited destinations — the direction one would want. It does not license a claim that production delivery is 25%: per-network rates run from 8.4% in Portugal to 54% in SoCal, and the six-network aggregate landing near 25% is coincidence.

Re-running the comparison on the measured process moves the explanation, not the direction. Replacing the metronome with the fitted generator and sweeping the beacon interval B (experiment_realtraffic.py, 200 seeds, Figure 27a) separates the two free-planting mechanisms cleanly, because flooding and next-hop caching do not use gradients and so are unaffected by B:

Beacon interval BFLOODNEXTHOPGRADCOR-RR cold startsR airtime vs flood
none (conversation only)21.7%25.0%21.9%69.7%1.34×
24 h21.7%25.0%28.7%54.7%1.89×
6 h21.7%25.0%34.4%39.9%2.27×
3 h (pooled measured)21.7%25.0%37.0%34.2%2.48×
1 h (Norway measured)21.7%25.0%39.8%24.5%2.57×
0.5 h21.7%25.0%40.5%21.4%2.60×
Table 12. Norway replay under the measured traffic process, 200 seeds, 8,996 attempts per arm. Beacon transmissions are ambient: they occur regardless of routing protocol, so charging them to GradCor would double-count traffic the mesh already sends. The charged-cost sensitivity is reported below.

The reading is unambiguous. With conversation alone and no beacons at all, GradCor-R ties flooding (21.9% vs 21.7%) and loses to next-hop caching; seven in ten sends have no usable gradient. Delivery then rises monotonically with beacon frequency to 40.5%. The mechanism that makes GradCor work is ambient broadcast planting, not conversational refresh — the opposite emphasis to the one §3.5 originally gave, and an artifact of the metronome, which was quietly supplying about 90 follow-on packets per pair per week for the delivery refresh to ride. We state this plainly because the corrected mechanism is the more useful claim: it is falsifiable, it identifies a firmware constant as the protocol's operating parameter, and it predicts that meshes which beacon faster benefit more.

At the measured cadence the headline survives, reduced. Re-running §6.12's matched retry envelope on the real process (experiment_matched_real.py, 30,191 attempts per arm — matched to the original study's 30,190, Figure 27b) gives:

Matched readingMetronome (§6.12)Norway, B=1 hPooled, B=3 hNo beacons
Delivery at flooding's own offered load2.8×2.27×2.08×1.35×
Airtime to reach flooding's best delivery4.3× less3.14× less2.69× less1.67× less
Table 13. Matched envelope under the measured traffic process. GRADCOR-R leads both FLOOD and NEXTHOP at both matched points on both models at measured cadence. The no-beacon column is Norway's pessimistic bound; pooled with no beacons the delivery reading falls to 1.14× and GradCor-R is worse than flooding at k=0 (19.4% vs 21.9%).

So the paper's headline pair loses roughly a fifth of its delivery advantage and a quarter of its airtime advantage, and keeps the qualitative claim: at the airtime managed flooding already spends, GradCor-R delivers about 2.3× as much, and to match flooding's best delivery it spends about 3.1× less airtime. One sensitivity bounds this from the other side. If beacon airtime is charged to GradCor rather than treated as ambient, the delivery-at-equal-airtime reading is essentially unchanged (2.3×) but the airtime-at-equal-delivery reading collapses to 1.01×. We report the ambient convention as primary because those broadcasts exist in the mesh whether or not the routing protocol changes, and because flooding is charged identically — but a deployment that had to add beaconing to get this cadence would find the airtime case, though not the delivery case, disappears. Norway does not have to add it; it already beacons hourly.

Figure 27. The workload, measured. (a) Delivery against ambient beacon interval on the real traffic process: flooding and next-hop caching are flat because they hold no gradients, while GradCor-R rises monotonically with beacon frequency and converges to flooding when nothing beacons. The dotted line is Norway's measured cadence. (b) The matched retry envelope re-run on real traffic: the metronome curve (dotted) sits above the real one, and the no-beacon curve shows what conversation alone buys. (c) Addressed packets per pair per day, six networks, against the replay's assumed 24. (d) Measured broadcast cadence per network — the parameter the protocol actually rides.

7  Discussion

The real topology strengthens the synthetic result. On the synthetic mesh GradCor matched flood's delivery at 2.5–3× less airtime; on the real Norwegian mesh — sparser and harsher than the naive extraction suggested — GradCor-R doubles flood's delivery while keeping the airtime ratio. The mechanism is visible in the graph structure: the ROUTER backbone (degree 53–107 hubs) is present nearly 24/7, so gradients through it stay valid for many hours, while anycast forwarding exploits the receiver diversity that broadcast gives for free — any of several hub-adjacent nodes can carry the packet downhill, so no single lossy link is load-bearing. Notably, the corrected (sparser) graph widened the margin: when links are scarce and marginal, spending them precisely matters more. And the head-to-head confirms the evolution claim in vivo: the clock-free final spec runs even with — here slightly ahead of — the age-widened original (50.7% vs 48.6% at equal cost), with two fewer constants and half the state.

Source routing fails for a measurable, structural reason. PATH needs every hop of a recorded path to work in the named direction and needs the reverse path to deliver the route reply. With 43% of links lacking any working reverse and median 3–4 hop paths, the probability that a full source route remains valid is small and each failure triggers a discovery flood plus a reply that often dies on an asymmetric link. Its 473 tx per delivered packet is worse than never routing at all. This is the algorithm stripped of MeshCore's real-world advantage — deployments built around fixed elevated repeaters with engineered symmetric links — so it should be read as “source routing on an organic mesh,” not as a verdict on MeshCore in its intended setting.

Next-hop caching under-delivers under duty-cycle churn. NEXTHOP edges out flooding (30.4% vs 25.3% at slightly lower cost) but captures little of GradCor's gain: with the median node online 13% of the time, per-destination caches go stale between conversations and most packets fall back to the teaching flood anyway. Its one virtue — zero control traffic and trivial state — remains, which is presumably why Meshtastic 2.6 shipped it. The comparison is to 2.6 as shipped during the study week: the 2.8 line extends the NodeDB address list for next hop[8], and a richer cache would raise this baseline — though not, we expect, the churn that causes its staleness, which is a property of the duty cycle rather than of the table.

Production findings. Building the replay surfaced two extraction defects in the production topology builder, one of them substantial. First, it parsed traceroute responses as requests, misorienting their edges and inventing endpoint hops from mid-flight rows: the corrected extraction shrinks the Norway graph from 3,613 to 1,423 directed links and raises measured reciprocity from 36% to 57% — meaning the old graph both overstated connectivity and overstated asymmetry, distorting any coverage, bridge, or role-recommendation rule computed on it. Second, it discarded route_back/ snr_back, which contribute ~21% of real links and the only direct per-link bidirectionality evidence — the single most decision-relevant link property for any routing improvement. Both defects were fixed and shipped to production during this work; because topology windows are ephemeral and rebuilt from raw rows, every subsequent build self-heals with no data migration.

Concurrent work: the firmware is already moving this way. The central claim of §6.5, §6.9 and §6.10 — that fixed, hand-set broadcast parameters should be replaced by values a node derives from what it measures — is not ours alone. The 2.8 roadmap carries an item for automatic variable hop limits based on mesh activity and size estimation, explicitly on an empirical or simulated basis, with the rule that the more nodes a sender influences the less often it should send[8]. That is the same thesis arriving independently, and it matters for how this paper should be read in two ways. First, it strengthens the framing: our §6.3 result that the shipped hop limit is the costly setting, and our relay-pressure and setpoint controllers, are not proposals against the grain of the project but arguments for a direction it is already taking — which lowers the adoption barrier the Reddit and Discord responses worried about. Second, it bounds our novelty honestly: adaptive broadcast control is now contested ground, and what remains distinctive here is the unicast side — the one-byte gradient corridor with no hop limit (§3) — together with the finding that adaptive relaying slides along a fixed price curve rather than beating it (§6.6). Those two claims are orthogonal to a variable hop limit: a mesh-size estimate changes where on the curve broadcast sits, not the curve, and GradCor's termination argument never consults a hop counter at all. The natural comparison for future work is therefore direct: measure a mesh-size-estimating hop limit against the setpoint controller of §6.10 on the same six replays, since both claim to spend exactly the airtime the neighbourhood can afford.

8  Threats to validity

  1. Residual round-trip optimism. Simulated round-trip completion (0.31) still slightly exceeds the observed rate (0.23), so reverse-path behaviour is modelled a little generously even with rev_p = 0. This can only inflate PATH; a tighter model would push its 4.0% lower. FLOOD, NEXTHOP and GRADCOR never require a named reverse hop and are insensitive to it.
  2. Survivorship bias. Traceroutes reveal only links that worked at least once in the window; usable-but-unexercised links are missing, which slightly disadvantages every protocol equally and understates true connectivity.
  3. Observation bias. Ground truth comes from MQTT gateways; RF events no gateway heard are invisible. Absolute delivery rates are therefore model outputs, not measurements — only rankings and ratios should be quoted.
  4. MAC simplification. Same-round collision with 0.8 capture is crude; the headline replay has no inter-packet airtime contention or duty-cycle budget. The capacity sweep of §6.11 adds an aggregate utilization-coupled loss proxy, not a packet-level channel scheduler. Real broadcast storms are collision-dominated, so this likely still flatters FLOOD, i.e. the real gap would widen in GradCor's favour — but that remains unproven here, as it was in the synthetic pilot. One simplification cuts the other way: the replay's baseline flood omits Managed Flood Routing's overhear-cancellation (a node cancelling its queued rebroadcast when it hears another node relay first), which understates flood's real airtime efficiency — while GradCor's analogous suppression is granted ideally (item 5). We measured this bias by re-running the head-to-head with a cancellation-aware flood (contention-ordered: routers first, clients worst-SNR-first, ROUTER_LATE last; experiment_cancellation.py): cancellation saves flood 15% of transmissions but also costs it 2.0 points of delivery (25.3 → 23.3%) — cancelled rebroadcasts are sometimes the only path to edge nodes, the coverage hole ROUTER_LATE exists to patch. Net effect on the headline: GradCor-R's delivery advantage widens slightly (2.0× → 2.1×) and its airtime-per-delivered advantage compresses modestly (3.3× → 3.0×). No conclusion changes. The headline tables deliberately keep the baseline flood: the link model was calibrated so baseline floods reproduce real traceroute statistics and those real floods already ran with cancellation — its aggregate effect is partially absorbed into the fitted link probabilities, so layering explicit cancellation on top without recalibrating would double-count the suppression. This run is therefore a bias bound, not a better model.
  5. Contention-timer realism. The headline tables assume GradCor's anycast suppression (“lowest gradient wins, others overhear and mute”) is perfect. Near-simultaneous forwarders within capture range are the mechanism most likely to misbehave on real radios, and this is the one timing mechanism the §3.6 ablation could not remove — it is a reception-armed race resolver, not a state clock — which makes it the entire remaining timing surface of GradCor-R. §6.12 relaxes the assumption rather than defending it: the mean contention set holds 1.38 candidates and 63.2% of forwarding decisions have no loser to suppress, so with suppression forced to fail on every decision the airtime advantage falls from 3.3× to 1.15× without inverting, and delivery does not move. The assumption sets where in that band the protocol lands. It remains the first thing to prototype in firmware, now as a calibration rather than as a validation.
  6. Free, lossless gradient refresh. The replay re-stamps every node on a delivered packet's path instantly and at zero cost; in firmware the refresh rides the delivery confirmation, which travels real links and can be lost — particularly on asymmetric paths. This optimism is GradCor-specific. Its consequence is bounded — a missed refresh only leaves gradients staler, costing exploration rather than creating a black hole and the v1↔R equivalence suggests low sensitivity to exactly how staleness is handled — but the refresh delivery rate belongs on the firmware-prototype checklist alongside contention.
  7. On-demand planting. In the replay, a source with no gradient triggers a planting flood from the destination at that instant, charged to the triggering packet (§5.5). Causally that is an oracle — nothing in a deployment tells the destination to flood on demand. We measured the shortcut by replacing it with the two deployable cold-start mechanisms (experiment_plant.py, 10 seeds): gradient seeding by the destination's ordinary periodic broadcasts (3-hourly / 6-hourly while online, NodeInfo-style) and, for a source still without a gradient, falling back to managed-flooding the data packet itself — today's Meshtastic behaviour — whose delivery lets the §3.5 refresh plant the path. The result runs against the bias one would fear: the fully oracle-free form (no seeding at all, fallback + refresh only) delivers 55.0% at 23.0 tx per delivered packet vs the shortcut's 49.3% at 22.2 in the same harness (FLOOD: 25.1% at 71.1) — the fallback flood delivers some cold packets itself, which the on-demand plant never does. Broadcast seeding lands in between (51.7–52.2%, 23.7–24.1 tx/dlv packet-charged; 26.7–29.9 if the seeding floods — traffic the mesh sends anyway — are fully charged to GradCor). The headline tables keep the shortcut, which this run shows to be the conservative choice for delivery; no conclusion changes.
  8. Independent link trials and no end-to-end retries. Every transmission is an independent Bernoulli draw from the fitted link probability; real LoRa links fail in correlated bursts (fading, temporary obstructions), which penalises rapid per-hop retries more than receiver diversity and no protocol was granted end-to-end retransmissions (a real Meshtastic DM retries several times). Both simplifications apply to all five protocols equally, but they mean absolute delivery rates should not be compared with field ACK statistics. The relative consequence of withholding retries is no longer unknown: §6.12 gives every protocol the same retry budget and finds that retries lift flooding substantially (24.7% → 61.3% at k=5) along a much costlier curve, leaving GradCor-R ahead on both axes at every matched budget; §6.13 repeats that matched comparison on the measured traffic process, where the lead narrows to 2.3× and 3.1× but does not invert.
  9. The workload's cadence was assumed, and the assumption favoured us. §5.6's one packet per pair per hour is roughly 90× production density (§6.13), and because GradCor's state is refreshed by traffic, a denser workload flatters it specifically rather than uniformly. §6.13 re-runs the headline on the measured process and reports the corrected figures, but the results of §6.1–§6.12 were computed at the original density and should be read as holding at that operating point. The broadcast-side results (§6.3 onward) are unaffected, since broadcast load was always modelled from measured rates.
  10. Mobility is measured, but only for the nodes that disclose it. §6.12's finding that 91.5% of nodes are stationary rests on nodes broadcasting exact coordinates; those reporting a deliberately imprecise position cannot be resolved below their quantization grid. That subset is measurably more mobile (2.2×) and the population-wide estimate corrects for it, but the correction is an extrapolation from the coarse grids that remain resolvable, not a direct observation of every node. A mesh whose mobile nodes uniformly hide their positions would read as more static than it is.
  11. Unicast scope. Every claim in this paper concerns unicast traffic (DMs, ACKs, traceroute-class exchanges). Broadcast traffic — positions, telemetry, channel messages, the majority of load on many meshes — is inherently flood-shaped and is neither helped nor harmed by GradCor: it keeps its managed flood and its hop limit unchanged (the no-hop-limit property belongs to gradient descent only, which broadcasts do not use). The one interaction is positive, and §6.13 shows it is the dominant one — every destination-originated broadcast doubles as a free planting wave (§3.2), so public traffic is what keeps the unicast routing state fresh, at a measured cadence of about an hour on Norway. A mesh that suppressed broadcast traffic to save airtime would degrade GradCor toward flooding. The replay carries no background broadcast load contending for airtime and the airtime savings shown here apply to the unicast fraction of a mesh's traffic only.
  12. Comparators are pinned to shipped firmware. FLOOD and NEXTHOP model Meshtastic as deployed during the study week (2.6-era Managed Flood Routing and next-hop cache). The 2.8 line in progress at the time of writing[8] changes several of the quantities this paper measures: an extended NodeDB address list for next hop would raise the NEXTHOP baseline, automatic variable hop limits would move the broadcast baseline along the price curve of §6.4, and per-packet signing plus larger name fields add bytes that lower the aggregation ceiling of §6.6. Every margin quoted against a Meshtastic baseline should therefore be read as against 2.6-era behaviour, not as a standing claim over the current firmware.

9  Conclusions and next steps

On a real 499-node national mesh with empirically calibrated links and real duty-cycle churn, gradient-corridor anycast in its final clock-free form delivered twice the unicast packets of managed flooding at 40% less per-packet airtime and 3.3× less airtime per delivered packet — or, stated one axis at a time under an identical retry envelope (§6.12), 2.8× the delivery at flooding's own airtime and 4.3× less airtime at flooding's best delivery. Those figures hold at the replay's assumed conversational density; re-measured on the real traffic process and the real broadcast cadence (§6.13) they become 2.3× the delivery at flooding's own airtime and 3.1× less airtime at flooding's best delivery, which we take as the paper's defensible headline. The same correction replaces the mechanism we originally proposed: gradients survive on the destination's routine broadcasts, not on conversation, and the protocol's margin is a function of a firmware beacon constant — a more useful claim than the one it displaces, because it is falsifiable and it names the knob. Meanwhile source routing was structurally non-viable and next-hop caching was only marginally better than flooding. The ordering matches the synthetic study's prediction; the corrected, sparser real topology widened the margin. And the ordering is not Norway's: four of the five further production networks, independently calibrated, reproduce it — the fifth, too evidence-starved to plant gradients on, degrades gracefully to flood level rather than failing, defining a measurable eligibility criterion (usable links per node) for deployment. The same replay also simplified the design itself: the clock ablation (§3.6) showed the wall-clock thresholds the method started with are redundant and the head-to-head (Table 3) confirms it — the specification the paper ends with, GradCor-R (Algorithm 2), matches the original with one byte of state per active destination and one deterministic escalation ladder: no state clocks, no tuning constants beyond retry counts and the millisecond contention window it inherits unchanged from the existing managed flood and no statistical estimation anywhere in the forwarding path. What began as “two numbers and two thresholds” ends as “one number and one rule.”

Figure 28 assembles the full system that §3–§6 built piece by piece and Figure 25 then exercised jointly — gradient-corridor unicast, the flood kept where it is optimal, PIVOT backbone and relay pressure choosing who relays, message classes and the setpoint controller choosing how much and how fast, with the ASSURED reliability envelope explicitly outside the default fast path — alongside the gains over managed flooding each piece was measured to buy. The through-line is a single design rule applied five times: every mechanism reads a signal the radio already provides, and nothing else. What that rule removed along the way — the hop counter, the state clocks, the role enum, every user knob — is the measure of how much of today's configuration was standing in for measurement.

Figure 28. The complete stack and its measured gains. (a) All mechanisms of §3–§6 as one system, each annotated with its section: unicast routing, its rate-limited ASSURED reliability envelope, the broadcast side, and the three self-calibrating governors, resting on the one thing no protocol changes — physical placement. (b) Gains over managed flood, one line per claim, each traceable to a table in the body. Absolute values are model outputs; rankings and ratios are the findings.

Recommended next steps, in order of information value:

  1. Close the decoder-engine request_id gap (extract it at ingest, optionally redecode historical rows): this upgrades the response classification of §5.1 from a 98.7%-accurate heuristic to canonical labels. The extraction fix itself (response orientation, arrival-gated endpoints, route_back ingestion) already shipped to production during this work.
  2. Cross-validate reverse-link behaviour with NEIGHBORINFO edges where available and re-fit against the 23% round-trip anchor.
  3. Replace the aggregate utilization-loss proxy of §6.11 with packet-level concurrent traffic and duty-cycle scheduling to validate the collision-dominated capacity bound.
  4. Prototype the anycast contention race on hardware (two RAK4631s plus one Heltec V3 within capture range) before any firmware investment in the full protocol. With the state clocks gone, this millisecond backoff is GradCor-R's entire timing surface and the primitive to extend already exists (RadioInterface::getTxDelayMsecWeighted, re-keyed on gradient); the experiment to run is suppression reliability: how often does the losing candidate fail to hear the winner and forward a duplicate? §6.12 has since bounded what that measurement can change — the airtime advantage lands somewhere between 3.3× and 1.15× and delivery is insensitive — so the bench test now calibrates a known curve instead of deciding whether the result stands. It remains the highest-value experiment because it is the only one that replaces a model with a radio.
  5. Re-run the head-to-head against a 2.8 build once it stabilises, rather than the 2.6-era FLOOD and NEXTHOP baselines used here (§8, item 10), and compare the variable hop limit of #10176 directly against the setpoint controller of §6.10 on these same six replays.
  6. Treat beacon cadence as a first-class protocol parameter. §6.13 shows GradCor-R's margin is a function of how often destinations broadcast, which makes the interval a joint airtime/routing decision rather than a NodeDB freshness setting. The open question is where the optimum sits: beacons cost broadcast airtime but buy unicast efficiency, and nothing here searches that trade-off jointly. It also interacts directly with 2.8's variable hop limits and with the message classes of §6.7, since a cheaper beacon changes the price of the state GradCor rides on.

Acknowledgements

§6.12 exists because a Meshtastic maintainer read this paper closely enough to find the three places where it was resting on an assumption rather than a measurement, and said so precisely. Two of those three now support the work more firmly than the original argument did; the third corrected a claim that should not have been made in the form it was. §6.13 is an indirect consequence of the same review: having been shown one assumption we had not measured, we went looking for others and found a larger one in our own workload. Review of that quality is scarce and this paper is materially better for it.

AI assistance disclosure

This work was produced with substantial AI assistance (Cursor agents). The AI wrote the simulation and experiment code, ran the data extraction and analysis, generated the figures and drafted the text. The author directed the research questions, supplied the production data access, reviewed intermediate results, challenged and corrected the methodology at several points (including the link-extraction fix of §5.1 and the decision to ablate the state clocks) and takes full responsibility for the final content. All numbers are reproducible from the published code and data exports [5] independent of any AI involvement.

References

  1. Meshtastic firmware 2.6 release notes — next-hop routing for direct messages.
  2. S. Biswas, R. Morris, “ExOR: Opportunistic Multi-Hop Routing for Wireless Networks,” SIGCOMM 2005 (anycast contention forwarding).
  3. J. Chroboczek, “The Babel Routing Protocol,” RFC 8966 (feasibility condition / monotonic metric descent).
  4. T. Winter et al., “RPL: IPv6 Routing Protocol for Low-Power and Lossy Networks,” RFC 6550 (rank rule).
  5. RelayMesh production data, Norway network, 2026-06-27–07-04. Reproduction package (data exports and all simulation, experiment and figure code: sim_norway.py, experiment_asymmetry.py, experiment_widening.py, experiment_multinet.py, experiment_cancellation.py, experiment_plant.py, experiment_target99.py, experiment_custody.py, experiment_hoplimit_multinet.py, experiment_broadcast.py, experiment_alltoall.py, experiment_alltoall_multinet.py, experiment_temporal.py, experiment_depth.py, experiment_persist.py, experiment_pressure.py, experiment_pressure_multinet.py, experiment_headroom.py, experiment_setpoint_multinet.py, experiment_capacity.py, experiment_capacity_compare.py, experiment_integrated_stack.py, experiment_pivot.py, experiment_pivot_multinet.py, experiment_aggregate.py, experiment_suppression.py, experiment_mobility.py, experiment_mobility_sim.py, experiment_matched.py, fig_*.py; cross-network exports under data_networks/, position-mobility exports under data_mobility/) is published at github.com/relaymonkey/mesh-routing-paper.
  6. P. Levis, T. Clausen, J. Hui, O. Gnawali, J. Ko, “The Trickle Algorithm,” RFC 6206 (suppress on consistency, react to inconsistency — not to a schedule).
  7. Meshtastic documentation: “Mesh Broadcast Algorithm” and “Why Meshtastic Uses Managed Flood Routing.”
  8. Meshtastic firmware, “2.8 punchlist” (issue #10462), accessed 2026-07-26 — in-progress work items including automatic variable hop limits from mesh-size estimation (#10176), extended NodeDB address list for next hop, xEDDSA packet signing, name-field size changes and dropping packets with an invalid hop_start. Cited as concurrent work; nothing in this paper is evaluated against a 2.8 build.