Skip to content

Consolidated Routing Plan (greenfield routing redesign)

Role of this document. This is the CONSOLIDATOR output reconciling ASYNC_ROUTING_DESIGN.md (the routing/RF track) and ROUTING_THREAT_MODEL.md (the authentication track). It is the join of the two [SEC-Hn] tags and the R1..R14 requirements contract. Where the two tracks already contain the pieces to agree, the reconciled rule is written here normatively so the unified plan is self-contained. Where a track must actually change its model, the item is kicked back in § ISSUES FOR ROUNDTRIP.

Grounding sources read for this reconciliation: core/src/packet.cpp::canonical_image (the mutable mask is ViaTcp | Path; the path block is skipped entirely from the tag/sig image), core/include/lrc/packet.h (kMutableMask = ViaTcp | Path), and docs/SECURITY_AUDIT.md Findings 1, 2, 4, 5, 7 (path is “a hint, not security”; backbone carries an RK-pinned per-frame MAC

  • monotone sequence; LANEGRANT is SK-Tag8’d with a src-check and replay protection; Tag/MAC compares are constant-time).

(Pass 2.) Pass 1 returned NEEDS-ROUNDTRIP with three issues, all on one seam: ERB.TRAIL[] feeding Return-Descriptor / return-candidate / RELAY_VIA trust decisions without authentication. The reconciliation editor has applied Option B to both sibling docs — TRAIL is demoted to a diagnostics-only / unverified-candidate hint, and all RD / RELAY_VIA / return-candidate trust now derives from router-observed delivery, with first-contact RD constrained to router-measured BROADCAST or UNKNOWN. On this second pass I have verified the fix rigorously (not charitably): all three issues are genuinely closed, no previously-SATISFIED or RESOLVABLE-HERE requirement was regressed, the R1–R14 contract text is unchanged, and the Option-B framing introduces no new blocking gap. The verdict therefore advances to CONSOLIDATED. Two minor, non-blocking follow-ups are recorded below (not blockers).

The earlier root cause — the ERB made TRAIL[] a trusted input to RD/return/candidate construction without a per-hop authenticator and without a hard observed-delivery gate — is removed at the source. Option B chooses the behavior-rule fork (no wire change, no per-hop MAC, no canonical_image re-audit): no routing trust decision consumes an unauthenticated appended hop, so R2 is satisfied vacuously, R5/R7 by the router’s own observed-delivery measurement (a Tier-1, router-local authority), and R8/R10 by “presence/count never confers trust; nothing to verify per-hop.” See the Pass 2 resolution log for the quoted normative rules.

Counts (pass 2): SATISFIED 11 · RESOLVABLE-HERE 3 · CONFLICT/GAP 0. (Pass 1 was SATISFIED 5 · RESOLVABLE-HERE 6 · CONFLICT/GAP 3; the three former-conflict rows — R2, R5, R10 — are now SATISFIED, and R7/R8 which were flagged “with” them are likewise clear.)


R# Requirement (short) Design mechanism / [SEC-Hn] that bears on it Status Resolution / Issue
R1 Suppression must be ignorable; honest relay fires if no authenticated forward-progress within T §3.3 suppress rule (overheard rprog), §3.3 RACK collapse, [SEC-H1] rprog, [SEC-H2] RACK RESOLVABLE-HERE Design’s “RACK shortens but never cancels” + BACKSTOP/retry is the seed; written as the tiered suppression rule below. Collapse benefit retained for registered relays, degraded (not lost) for keyless.
R2 A trusted appended hop must carry a per-hop MAC over SRC‖MSGID‖prev_hop‖my_id‖ctr; else treat as hint only ERB TRAIL[] §6.2 (now “diagnostic / unverified candidate hint only”), §4.2 observed-delivery gate, §7.2 candidate≠trusted, [SEC-H5] RESOLVED, THREAT §4.4 SATISFIED Closed pass 2 (Option B). No appended hop is ever trusted, so R2 is satisfied vacuously (R2 itself permits “treat as unverified hint, MUST NOT feed any trust decision”). TRAIL is a (d)-class hint; trust comes only from router-observed delivery. THREAT §4.4 ratifies.
R3 Reported/measured SNR is self-affecting only; never an input to another node’s suppress/prune §3.2 CWsize uses locally-measured SNR rung; [SEC-H4] (no SNR on wire) SATISFIED [SEC-H4]: SNR feeding the window is measured on receive, never received from the wire. The only on-wire term is rprog (R1/[SEC-H1]). Invariant already held.
R4 Unauth prune removes only the pruner’s own hop; pruning others’ needs auth §7.1 forward-prune (self-prune only), §7.2 retained return, [SEC-H7] RESOLVABLE-HERE Design’s prune is self-prune (R inserts/removes itself), time-bounded (prune_until_ms), driven by overheard real frames. Written as the prune rule below; needs the explicit “self only + hard timer cap” statement to satisfy R4 cleanly.
R5 Async return seed must be authenticated; never an unsigned reverse of discovery §4.2 observed-delivery gate + first-contact RD rule, [SEC-H6] RESOLVED, THREAT §4.4 SATISFIED Closed pass 2 (Issue 3). First-contact RD MUST be router-measured BROADCAST or UNKNOWN; RELAY_VIA via_uid1 is populated only after an observed delivery names a real relay — never TRAIL-seeded. The seed’s authority is the router’s own measurement (Tier-1, router-local), which R5 explicitly admits (“router-attested (Tier-1)”).
R6 Every routing-control frame freshness-bound + replay-proof (dedup + boot_id/seq) §6.4 RACK (group C, TTL=1, never relayed), [SEC-H2], [SEC-H8] LANEGRANT (Finding 1 fixed) RESOLVABLE-HERE LANEGRANT already compliant (Findings 1/2 resolved: dedup + src-check). RACK needs the same treatment: must transit (SRC,MSGID) dedup + a freshness binding. Written as the RACK control rule below.
R7 Return-path and forward-path independently authenticated; poisoned forward must not seed trusted return §4 RD decoupled from forward edge, §4.2 observed-delivery gate, §4.3 federation boundary, [SEC-H6] SATISFIED Closed pass 2 (with Issue 3). RD is structurally decoupled and the seed is now independently authenticated: it derives from the router’s own observed delivery, never from the forward TRAIL. A poisoned forward discovery cannot seed a trusted return (THREAT §4.4 R5/R7).
R8 Path authority beats path diversity; relay UID count is never trust §4.2 observed-delivery gate, §7.2 “candidate ≠ trusted,” §8.3 delivered==true gate, [SEC-H5] RESOLVED SATISFIED Closed pass 2 (with Issue 1). §7.2 states “TRAIL presence and the count of distinct candidate UIDs never raise a candidate’s selection priority… only router-observed delivery does.” A Sybil swarm of ghost TRAIL entries confers no trust (THREAT §4.4 R8).
R9 No security-relevant state in a mutable (canonical-image-excluded) region §6.1 ERB in kMutableMask (replaces Path), [SEC-H1] SATISFIED The ERB is deliberately in the mutable region and the design’s premise — now normative post-Option-B — is that no ERB field is security-relevant (§6.1: “safe precisely because no ERB field feeds a trust decision”). Because Option B adds no per-hop MAC, the pass-1 R9 hazard (a MAC landing in the masked region) is moot; canonical_image is unchanged and needs no re-audit. See note below the matrix.
R10 Router-mediated O(hops) verification; SipHash-class, never per-hop Ed25519, never O(hops²) §4.2 router seeds RD from its own observed delivery (O(1) local measurement), no per-hop tag in ERB, THREAT §4.4 SATISFIED Closed pass 2 (Option B fork B). Satisfied more cheaply than a per-hop MAC: there is nothing to verify per-hop. The router’s delivery observation is an O(1) local measurement, well inside the ESP32/nRF52 budget (THREAT §4.4 R10).
R11 Loops + excess relaying stay airtime-bounded (TTL≤15 + dedup as hard ceiling) §6.2 TRAIL loop-drop (drop if own UID in TRAIL), §9 dedup kept verbatim, TTL unchanged SATISFIED Dedup ring + 4-bit TTL kept verbatim (§9); ERB sizes bounded by 4-bit fields (§6.3); TRAIL loop-dampening is belt-and-suspenders over dedup. Worst-case manipulation is bounded airtime.
R12 No new single-router blast-radius amplification; return/prune authority belongs to the UID’s own router §4.3 RD lives at recipient’s router; §9 RD not gossiped (out of RTRSYNC); [SEC-H6] SATISFIED RD is per-router-local and not gossiped (§9, [SEC-H6]); cross-router return uses the recipient router’s own RD over the existing federation roster (§4.3). A rogue router steers only its own scope — no widening vs Tier-1 today.
R13 Unauth fields advisory by default (fail-closed to hint); length-exact decode §3 HINT is “bias only, never a gate,” §6.2 TRAIL “never a forwarding gate,” §6.3 bounded sizes SATISFIED [SEC-H3] HINT and TRAIL are explicitly non-gating; rprog is explicitly “liveness optimization, not authorization” ([SEC-H1]). Decode must stay length-exact (Finding 10) — call it out as a wire-change checklist item, not a conflict.
R14 Sticky routing state must be expirable + bounded §4.2 ttl_quart RD decay → UNKNOWN, §7 prune_until_ms, §8 all structures fixed-cap LRU/TTL SATISFIED Every sticky structure (RD, return-candidate ring, forward_prune, pending_relay_slots) carries a TTL or bounded LRU (§8). One-shot poisoning self-heals on decay.

Note on R9 (why it is SATISFIED, not a conflict, despite the ERB being mutable)

Section titled “Note on R9 (why it is SATISFIED, not a conflict, despite the ERB being mutable)”

R9 forbids security-relevant state in the masked region. The design’s load-bearing claim is that no ERB field is security-relevant: rprog, HINT, TRAIL are all “degrade, not lose” hints. That claim is true for the forward edge (progress is an optimization; the router’s payload Tag8 is the delivery authority, unchanged per [SEC-H1] and §10 item 1). So putting the ERB in kMutableMask is the correct, R9-compliant move, and pass 2 confirms Issues 1/2/3 were resolved without making any masked ERB field a trust input. Resolution of the pass-1 hazard: the pass-1 plan flagged that if Issue 2 were closed by adding a per-hop MAC into the ERB, that MAC would be security-relevant and would force a canonical_image re-audit. Option B took the no-wire-change fork — no per-hop MAC is added — so that hazard does not materialize: the ERB stays entirely free of trust-bearing state, the mutable mask is unchanged (ViaTcp | ERB), and canonical_image needs no re-audit. The Finding-5 warning still stands as a standing rule for any future change that would put security-relevant state in a masked region (track under /lrc-wire-change if that ever happens), but it is not triggered by this design as consolidated.


Pass 1 returned NEEDS-ROUNDTRIP with Issues 1/2/3, all on one seam (TRAIL feeding RD/return/candidate trust without authentication). The reconciliation editor applied Option BERB.TRAIL[] demoted to a diagnostics-only / unverified-candidate hint; all RD / RELAY_VIA / return-candidate trust derives from router-observed delivery; first-contact RD = router-measured BROADCAST or UNKNOWN. This pass verifies the fix against the unchanged R1–R14 contract, skeptically. Each issue is recorded closed with the quoted normative rule.

Issue 1 — RELAY_VIA / return-candidate admission gated on observed delivery — CLOSED

Section titled “Issue 1 — RELAY_VIA / return-candidate admission gated on observed delivery — CLOSED”

The admission rule is now stated once and normatively as the observed-delivery gate (DESIGN §4.2) and applied at every consumer (§4.2, §7.2, §8.3). TRAIL can only order already-gated candidates; it can never admit/pin/select one. Verbatim (DESIGN §4.2):

Observed-delivery gate. A relay is admitted to the RD (as via_uid1), to the return-candidate ring (§7.2), or to a RELAY_VIA selection only after the router has observed that relay actually deliver a frame … A relay is never admitted from ERB.TRAIL[] presence. TRAIL[] is a diagnostic / unverified candidate hint only; it may propose a candidate and, among candidates that have already passed the gate, it may only influence their ordering — it can never, by itself, pin an RD, select a RELAY_VIA, admit a return-candidate, or prune another node’s hop.

Cross-checked at the other two consumers and found consistent — no remaining path admits from TRAIL:

  • §7.2 (“Candidate ≠ trusted route”): a candidate “is eligible for RELAY_VIA only after it passes the observed-delivery gate … TRAIL presence and the count of distinct candidate UIDs never raise a candidate’s selection priority … only router-observed delivery does. An unconfirmed candidate ages out of the bounded ring without ever being selected.”
  • §8.3 return_candidates: “admissible to RELAY_VIA only with delivered==true — set solely by the observed-delivery gate (§4.2), never by TRAIL presence. TRAIL/keepalive proposals enter with delivered=false and age out unconfirmed.”
  • §8.3 state-machine prose: “the first-contact seed is only ever BROADCAST or UNKNOWN, and RELAY_VIA is reached only after a router-measured delivery names a real relay — never from a TRAIL byte.”

The “what counts as an observed successful delivery” sub-rule (§4.2) is present and adequate: “a frame attributable to a relay that advances one of the router’s own outbound frames toward the client … The attribution is from the router’s measurement, not from a TRAIL byte.” Verdict: closed. (Satisfies R2/R8.)

Issue 2 — both docs agree on Option B; ERB stays unsigned/in kMutableMask; THREAT §4.4 ratifies — CLOSED

Section titled “Issue 2 — both docs agree on Option B; ERB stays unsigned/in kMutableMask; THREAT §4.4 ratifies — CLOSED”

ROUTING demotes TRAIL to diagnostics and justifies the unsigned/masked ERB on that basis (DESIGN §6.1): keeping the ERB in kMutableMask “is safe precisely because no ERB field feeds a trust decision: rprog/HINT are non-gating optimizations and TRAIL[] is a diagnostic / unverified candidate hint whose only consumers (RD seeding, return-candidate ring) admit nothing without the §4.2 observed-delivery gate. No security-relevant state lives in the masked ERB, so the canonical image needs no re-audit and no per-hop MAC is added.”

THREAT adds a new §4.4 RATIFICATION that explicitly adopts Option B (and records why Option A’s per-hop MAC was not adopted: it is impossible for keyless foreign relays — they share no secret with the converging router — so it could not cover the Sybil-ghost case that is the only one that matters). Verbatim:

Option B (observed-delivery-only gating + TRAIL[] demoted to a diagnostic hint) SATISFIES R2, R5, R7, R8, and R10 without per-hop MACs, because no routing trust decision consumes an unauthenticated appended hop.

The per-field verdict rows were updated to match, not merely asserted consistent: the Appended hop / ERB.TRAIL[] row is now “(d) safe unsigned — demoted to a non-trust diagnostic hint (Option B, ratified §4.4)” and the Async return path row is “(c) satisfied via router-observed delivery … (d)-from-TRAIL forbidden.” §8 summary tables likewise updated (appended-hop row: “N/A under Option B — no appended hop feeds a trust decision”).

Consistency cross-check (the skeptical part): I found no place where ROUTING still consumes TRAIL for trust while THREAT says it doesn’t. Every ROUTING consumer of TRAIL (§4.2 seed, §4.2 maintain-flip, §7.2 ring, §8.3 ring) routes through the observed-delivery gate; every THREAT row treats the hop as a (d)-class non-trust hint. The two tracks are now actually consistent, not merely asserting consistency. Verdict: closed. (Satisfies R2/R10, and via the ratification R5/R7/R8.)

Issue 3 — first-contact RD forbids TRAIL-seeded RELAY_VIA; mandates router-measured BROADCAST/UNKNOWN — CLOSED

Section titled “Issue 3 — first-contact RD forbids TRAIL-seeded RELAY_VIA; mandates router-measured BROADCAST/UNKNOWN — CLOSED”

The first-contact RD rule is now explicit and consistent (DESIGN §4.2). Verbatim:

First-contact RD rule (Issue 3): before any observed delivery, a client’s RD MUST be BROADCAST (the router heard the client directly with margin — a router-measured fact, not a wire claim) or UNKNOWN (re-discover via the §3 forward edge). It MUST NOT be RELAY_VIA seeded from TRAIL: a TRAIL byte is unauthenticated forward data and pinning a return through it would be a return route chosen from attacker-touchable input. via_uid1 is populated only after an observed successful delivery (below) names a real relay.

The prior pass-1 contradiction (§4.2 once said “the uplink frame seeds mode and via_uid1”) is gone: §4.2 now says the router “seeds mode only from router-measured reception — its own SNR margin to the client — never from the recorded hops,” and RD mode RELAY_VIA (§4.1) is annotated “RELAY_VIA is never the first-contact mode.” [SEC-H6] is marked RESOLVED (Issue 3) on the same basis. An RD that never gets a delivery confirmation decays to UNKNOWN (ttl_quart), preserving R14. Verdict: closed. (Satisfies R5/R7.)

Regression sweep (R1–R14) — PASS, no regressions

Section titled “Regression sweep (R1–R14) — PASS, no regressions”

Re-walked all fourteen. The R1–R14 contract text itself is unchanged — every requirement reads exactly as the pass-1 contract (I diffed the wording: R2 still demands a per-hop MAC for any trusted hop, and explicitly permits the “treat as unverified hint, MUST NOT feed any trust decision” path that Option B takes; R5 still admits “router-attested (Tier-1)” as a valid seed authority). Nothing the reconciliation touched broke a previously-SATISFIED or RESOLVABLE-HERE item:

  • R1 (tiered suppression): untouched. Rule A stands; THREAT §4.4 explicitly states “R1 … stand[s] exactly as written.” SATISFIED preserved.
  • R3 (SNR self-affecting / local): untouched; [SEC-H4] and Rule D unchanged. SATISFIED preserved.
  • R4 (self-prune only): untouched; §7.1 and Rule B unchanged. RESOLVABLE-HERE preserved.
  • R6 (freshness / replay-proof control frames): untouched; Rule C and RACK/LANEGRANT handling unchanged. RESOLVABLE-HERE preserved.
  • R9 (mutable-mask): strengthened, not weakened — the no-trust-in-ERB premise is now normative in §6.1, and because Option B adds no per-hop MAC, the pass-1 R9 hazard is moot and canonical_image is untouched. SATISFIED preserved.
  • R11 / R13 / R14 (airtime-bounded / advisory-default / expirable): TRAIL is now even more firmly a bounded (d)-class hint; bounded rings and TTL decay unchanged. All SATISFIED preserved.

These do not gate the CONSOLIDATED verdict; flag them for the implementation / RF-sign-off phase:

  1. Wire-level mechanism of “observed successful delivery” is under-specified. §4.2 defines it at design altitude (“a relay-sourced delivery confirmation, or the router overhearing the relay forward the router’s downlink”) but does not name the exact frame/flag that constitutes a “relay-sourced delivery confirmation.” That is an implementation-spec detail for the /lrc-wire-change pass, not a contract gap — the trust semantics are fully pinned regardless of which of the two listed mechanisms a given delivery uses.

  2. Bootstrapping latency of Option B is acceptable and documented, but worth a field-tuning note. A brand-new relay cannot be pinned as RELAY_VIA until it has delivered once. This is not an availability deadlock: first contact falls back to BROADCAST (if the router hears the client) or UNKNOWN, and UNKNOWN re-runs the §3 opportunistic forward edge, which delivers without requiring any pre-trusted relay. THREAT §4.4 records the residual honestly (“Option B pays a slower / less-targeted first return … bounded and self-healing (R14)”). The only open question is the tuning of how quickly a first delivery is observed in a relay-heavy edge — a simulation/chaos-harness item (cf. DESIGN §11 open questions 2–3), not a wire or trust question.


RECONCILED RULES (normative, for the RESOLVABLE-HERE items)

Section titled “RECONCILED RULES (normative, for the RESOLVABLE-HERE items)”

These are written so the unified plan stands alone. Each replaces the looser prose in the design with a rule that satisfies the cited R#.

Rule A — Tiered suppression (satisfies R1 + R3, keeps maximal collapse)

Section titled “Rule A — Tiered suppression (satisfies R1 + R3, keeps maximal collapse)”

This is the reconciliation of the headline tension. The design’s §3.3 has two suppression triggers — overheard-copy and RACK — and [SEC-H2] offers “RACK shortens but never cancels.” Written precisely:

A.1 — Overheard-copy suppression (the §3.3 clause-2 path). A relay R with a pending forward of F MAY defer (not cancel) on hearing F′ with dedup_key(F′)==dedup_key(F) and rprog(F′) ≤ rdist_R. Because rprog(F′) is unauthenticated ([SEC-H1]), this deferral MUST convert to a fire if, within bounded timeout T, R has not heard authenticated forward-progress for F. “Authenticated forward-progress” is exactly one of:

  • an authenticated RACK from a router R can verify (A.2 tier-1), OR
  • the frame carried onward by a hop R can verify (only available once Issue 2 gives the ERB a per-hop MAC; until then this disjunct is empty for keyless relays), OR
  • R itself is at rdist==0 (R is the router/co-located ingress — delivery is locally observed, no signal needed).

If none of these is heard within T, R fires. This makes suppression only ever delay an honest relay — satisfying R1 verbatim — while still saving the airtime of the common, honest, no-attack case (where the overheard copy is real and a RACK or the next-hop frame arrives well within T).

A.2 — RACK suppression, tiered by what the relay can verify:

  • Registered relay (shares an SK with the converging router): verifies the RACK’s Tag8 under that SK with src == RACK_RTR1’s router UID (the same check Finding 2 added for LANEGRANT). On success it MAY cancel outright — the instant-collapse win is retained for every relay that has registered.
  • Keyless / foreign relay (no SK for that router): cannot verify the RACK. Per [SEC-H2] fallback and R1, the RACK MAY only shorten T (e.g. clamp the remaining defer to a small floor), never cancel. The relay still fires at the shortened deadline unless it independently reaches rdist==0-confirmation or an authenticated signal.

Host implementation seam: RoutingSuppressionState stores the pending forward’s current fire_at_ms and hard deadline_ms (T). routing_suppression_apply() consumes abstract signal classes, not packets: authenticated RACK / authenticated forward progress / local ingress can cancel; untrusted overheard progress can only delay up to deadline_ms; keyless RACK can only shorten the pending fire time. Crypto verification and RACK parsing remain outside this host policy layer.

A.3 — Quantified cost of A.2 (stated honestly, not hand-waved). The design’s headline “the RACK collapses the whole edge mesh the instant the frame reaches a router” holds only for registered relays. For a mesh of keyless foreign relays, instant-collapse is lost: each keyless relay still fires after its shortened defer, so the worst case is one extra (mostly-redundant) relay TX per keyless relay in the contention set, per frame, bounded by the contention window and absorbed by dedup at the next hop. This is the airtime price of safety. In chIRpChat’s expected topology most edge relays near a router are registered clients of that router (they had to attach to use it), so the common case keeps instant collapse; the degradation bites only in foreign-relay-heavy meshes, where it degrades to “Meshtastic-grade overheard-frame suppression” — i.e. no worse than the unmanaged baseline, never a black-hole. This satisfies R1’s load-bearing guarantee (suppression delays, never cancels) at a quantified, bounded airtime cost rather than the design’s optimistic universal-collapse claim.

A.4 — rprog plausibility floor (R3-adjacent hardening, [SEC-H1]). A relay SHOULD reject an overheard rprog(F′) < heard_rdist(emitter) − 1 as implausible (an emitter cannot be more than one hop closer than R last heard it). This is the cheap rate-limit [SEC-H1] flags as optional. It does not feed another node’s decision (so R3 is untouched — it only filters R’s own deferral input), and it blunts the “lie low to win every window” black-hole to a transient.

Rule B — Self-prune only, hard-timer-bounded (satisfies R4)

Section titled “Rule B — Self-prune only, hard-timer-bounded (satisfies R4)”

§7.1’s prune is a self-prune: R removes itself from the forward fast path, never another node. Written to satisfy R4:

  • A relay R MAY prune only its own forward participation for a flow, and only on observing a real frame (one carrying a router-converging ERB it actually received on-air) that reaches a node N with rdist_N ≤ rdist_R at a preset rung ≥ R’s own. R MUST NOT prune, demote, or suppress any other node on the basis of any ERB field. (This is [SEC-H7] made into a hard rule and is exactly R4’s “unauth prune removes only the pruner’s own hop.”)
  • The prune is time-bounded by prune_until_ms with a hard cap (default ≤ a few seconds, per [SEC-H7]); on expiry R re-enters the §3 window. A sustained forgery therefore costs the attacker continuous airtime and yields only a transient forward gap that the window re-fills — never a permanent removal. (Satisfies R4 + R14.)

Because prune is self-only, no per-hop authentication is required for it (the attacker can only ever prune itself off its own path, which is harmless — the threat track’s §4.2 verdict). Prune is thus a (d)-class unsigned field that stays airtime-bounded (R11).

Rule C — RACK as a freshness-bound control frame (satisfies R6)

Section titled “Rule C — RACK as a freshness-bound control frame (satisfies R6)”

RACK is a routing-control frame (§6.4), so R6 applies in full:

  • RACK MUST transit the (SRC,MSGID) dedup gate like every other unicast/ control frame — it MUST NOT be handled in a firmware fast-path that returns before dedup (this is the exact Finding 1 LANEGRANT bug; do not reintroduce it for RACK). With TTL=1 and never-relayed-past-one-hop (§6.4) the dedup window only needs to cover the local neighborhood lifetime.
  • RACK MUST carry a freshness binding: the converging router’s boot_id (already accepted only from signed context) or a monotone per-session counter, so a captured RACK cannot be replayed to re-suppress a later flow (defeats §3.8 / [SEC-H2] replay). For registered relays this rides inside the SK-Tag8 (A.2 tier-1); for keyless relays the RACK is advisory-only anyway (A.2 tier-2), so a replayed RACK can at most shorten-not-cancel a defer once, which R1 already bounds.
  • Router→client/relay RACK is Tag8 under the router’s SK with src == issuing router enforced (Findings 1, 2). Router↔router never carries a RACK (the chain ends at the hub, §5; backbone uses RTRSYNC + the Finding-7 per-frame MAC).

Rule D — SNR stays measured-local, never on-wire (confirms R3, [SEC-H4])

Section titled “Rule D — SNR stays measured-local, never on-wire (confirms R3, [SEC-H4])”

No SNR value travels in the ERB in a way that feeds another node’s window. The snr_rung in §3.2 is read from the relay’s own receive measurement against kPresets[].probe_snr_x10. If any future optimization puts a claimed SNR on the wire it MUST be re-tagged and re-audited (this is [SEC-H4]’s standing assumption promoted to a rule). No change needed today — R3 holds as written.


UNIFIED PLAN SYNTHESIS (merged routing + security, one narrative)

Section titled “UNIFIED PLAN SYNTHESIS (merged routing + security, one narrative)”

The greenfield routing layer splits at the router boundary and replaces source-recorded reverse paths with an opportunistic forward edge plus a router-owned return descriptor. Authentication is layered on per the threat contract. What follows is the merged design as consolidated — the three former CONFLICT items (Issues 1–3) are now closed under Option B (TRAIL demoted to a diagnostics-only / unverified-candidate hint; all RD / RELAY_VIA / return-candidate trust derives from router-observed delivery; first-contact RD is router-measured BROADCAST or UNKNOWN). The places that were flagged [OPEN] in pass 1 now state the reconciled rule.

The v1 FLAGS.PATH 1-byte-per-hop block is replaced by the fixed-shape ERB (FLAGS.PATH bit retired and reused as FLAGS.ERB; kMutableMask becomes ViaTcp | ERB). The ERB occupies the exact slot the old path block held, so layout() and canonical_image() change only that section. The ERB is in the mutable region (excluded from the tag/sig image) because relays rewrite rprog in flight — and this is safe under R9 because no ERB field is a trust input (the payload Tag8/SIGNED_FULL remains the sole delivery authority, unchanged).

ERB fields: rprog (4-bit router-distance the emitter claims, the §3 progress metric — an ignorable hint under [SEC-H1]/R13), HINT (1-byte soft contention bias, never a gate, [SEC-H3]/R13), RACK_RTR1 (the converging router’s UID first-byte, lets relays recognize a RACK), TRAIL_LEN + TRAIL[] (append-only breadcrumbs, never a forwarding gate, bounded by 4-bit hop space). Issue 2 — RESOLVED (Option B, fork B): TRAIL[] carries no per-hop MAC and needs none, because it is demoted to a diagnostics-only / unverified candidate hint that feeds no trust decision. The router never consumes a TRAIL byte for RD/return seeding; all such trust derives from the observed-delivery gate (below). No per-hop MAC column is added; the ERB stays in kMutableMask with no trust-bearing state and canonical_image is unchanged.

New control frame: RACK (PROGRESS-ACK, group C, SRC=router UID, empty payload, PRIO, TTL=1). Governed by Rule C (dedup + freshness + SK-Tag8 with src-check). LANEGRANT is reused verbatim for RD mode LANE_SCHED ([SEC-H8]; already replay-proof and src-checked per Findings 1/2).

A relay forwards an uplink frame only if it strictly improves router-progress (rdist_R < rprog_in). Among eligible relays, the SNR-and-progress contention window (§3.2) orders who fires first; the rest suppress under Rule A (tiered suppression). The progress term uses locally-measured SNR only (Rule D / R3). Suppression is ignorable: an honest relay always fires within T if it hears no authenticated forward-progress (Rule A.1), so an attacker’s forged rprog or unverifiable RACK can only delay delivery, never cancel it — this is R1, the load-bearing guarantee, and it is met. The instant-collapse RACK win is retained for registered relays (the common case) and degrades gracefully to overheard-frame suppression for keyless relays (Rule A.3), at a quantified bounded airtime cost.

Signed vs ignorable-hint, forward edge:

  • Signed / authority: the payload (router’s Tag8 / SIGNED_FULL over the canonical image) — unchanged; this is “who said this” and “did it deliver.”
  • Authenticated control: the RACK (Rule C), verifiable by registered relays.
  • Ignorable hints (degrade, never trust): rprog, HINT, TRAIL, and the unverified-RACK signal for keyless relays. Each is bounded by R11/R13.

Return is not “reverse the forward path.” The router that terminates a client’s uplink owns a Return Descriptor (RD) per client UID (BROADCAST / RELAY_VIA / LANE_SCHED / UNKNOWN), refreshed independently of any request (R14: ttl_quart decays to UNKNOWN). The RD is router-local, never gossiped (out of RTRSYNC, [SEC-H6]/R12) — cross-router return uses the recipient router’s own RD over the federation roster, so no single rogue router gains blast radius (R12 SATISFIED). The RD is structurally decoupled from the forward edge (R7’s independence invariant — SATISFIED structurally).

Issues 1 & 3 — RESOLVED: the RD seed is now hard-gated on observed delivery. The reconciled rule (DESIGN §4.2, now normative) is: on first contact the RD is router-measured BROADCAST or UNKNOWN — never RELAY_VIA-from-TRAIL; the RD enters RELAY_VIA only after an observed successful delivery confirms a return route through a specific relay. TRAIL and the return-candidate ring (§7.2/§8.3) are candidate pools to be confirmed, never trust inputs — neither TRAIL presence nor the count of distinct candidate UIDs raises selection priority (R8). The pass-1 §4.2 contradiction (“the uplink frame seeds mode and via_uid1”) is removed: the router seeds mode only from its own router-measured reception. A poisoned TRAIL can therefore at most propose a candidate that is never confirmed; it is never even attempted as a return, so it decays out of the ring (self-healing, R14) — never a sticky hijack (R5/R7 met).

A relay self-prunes out of the forward fast path under Rule B (self-only, overheard-real-frame-driven, hard-timer-bounded). It never prunes another node (R4). A pruned relay is retained in the return-candidate ring so it remains available for a (delivery-validated) RELAY_VIA — forward and return relay sets are separate bounded structures, and prune touches only the forward one (§7.2). The return-candidate retention is subject to the same observed-delivery gate as the RD seed (now resolved per Issues 1/3) so a pruned-then-poisoned relay can’t launder itself into a trusted return from a TRAIL byte alone (DESIGN §7.2).

Kept vs replaced from today’s chIRpChat (with security disposition)

Section titled “Kept vs replaced from today’s chIRpChat (with security disposition)”
  • Kept verbatim: dedup ring (and it is the suppression identity, §3.3), FLAGS.VIA_TCP egress, FLAGS.PRIO, CHANSYNC backfill, RTRSYNC directory, LANEGRANT/promotion lifecycle ([SEC-H8]), duty-cycle/CAD/beacon, store-and- forward, unknown-version blind-relay, the payload Tag8/SIGNED_FULL authority (the security spine — unchanged), and the backbone per-frame MAC + monotone sequence (Finding 7).
  • Replaced: the FLAGS.PATH 1-byte path block → ERB; reverse-path return → Return Descriptor. The pinned-path forwarding gate is removed; the pin survives only as the soft, non-gating HINT byte (R13).
  • Narrowed: flat relay jitter U(0, airtime×2.5) → the §3.2 contention window for progress-bearing frames; flat jitter retained as the fallback for foreign/blind-relay frames that carry no rprog.

What is signed vs ignorable-hint (one-line summary)

Section titled “What is signed vs ignorable-hint (one-line summary)”

Signed/authenticated: payload ingress (Tag8/SIGNED_FULL, unchanged); RACK (Rule C, verifiable by registered relays); LANEGRANT ([SEC-H8]); backbone frames (Finding 7). Return trust is established not by signing a wire field but by the router’s own observed delivery (a Tier-1, router-local measurement) — Option B added no per-hop TRAIL MAC (Issue 2, fork B). Ignorable hints (degrade, never trust; bounded by R11/R13): rprog, HINT, TRAIL (now a diagnostics-only / unverified-candidate hint that feeds no trust decision), measured SNR (self-affecting, R3), TTL, unverified RACK for keyless relays.


FORMER ROUNDTRIP ISSUES (pass 1 — all RESOLVED in pass 2)

Section titled “FORMER ROUNDTRIP ISSUES (pass 1 — all RESOLVED in pass 2)”

These were the three pass-1 NEEDS-ROUNDTRIP issues. All three are now CLOSED under Option B; see the Pass 2 resolution log above for the verification and the quoted normative rules. They are retained verbatim below as the historical record of what was reconciled (so the round-trip is auditable), each annotated with its closing disposition. No open roundtrip items remain.

Issue 1 — RELAY_VIA / return-candidate selection must be gated on observed delivery, not on TRAIL presence. → ROUTING — RESOLVED (DESIGN §4.2 observed- delivery gate; §7.2 candidate≠trusted; §8.3 delivered==true). Involves: R2, R8 (and [SEC-H5]). Conflict: §7.2 / §8.3 build a bounded per-client return-candidate ring of relay UIDs partly learned from the append-only, unauthenticated ERB.TRAIL[], and §4.1 RD mode RELAY_VIA selects via_uid1 to steer a victim’s inbound replies. R2 forbids any trust decision (and RELAY_VIA is one) from a hop that carries no per-hop authenticator; R8 forbids treating the presence/count of distinct relay UIDs as evidence of trust (relay UIDs are free to Sybil — [SEC-H5]). As written, a Sybil ghost that appends its UID to TRAIL can land in the return-candidate ring and be chosen for RELAY_VIA. The concrete question the ROUTING track must answer: state the hard rule under which a TRAIL-derived (or otherwise unauthenticated) relay UID may be selected for RELAY_VIA. The expected answer, already half-present in [SEC-H5], is “never selected until the router has observed at least one successful delivery through that relay; TRAIL only ever proposes a candidate to be confirmed, and confirmation comes from observed delivery, never from the TRAIL bytes.” Specify (a) what counts as an observed successful delivery, (b) how a candidate that is never confirmed is aged out of the ring, and (c) confirm that the count of TRAIL UIDs never raises a candidate’s selection priority (R8).

Issue 2 — The ERB provides no per-hop authenticator, but §4.2/§7.2 consume TRAIL for trust decisions; the two tracks must agree on which way to close this. → BOTH — RESOLVED via fork (B): observed-delivery-only gating, TRAIL demoted to diagnostic hint; no per-hop MAC, no wire change (THREAT §4.4 RATIFICATION; DESIGN §6.1). Involves: R2, R10 (and [SEC-H5], R9). Conflict: the THREAT track’s central construction (§4.3) is “relays append a short per-hop SipHash MAC over SRC‖MSGID‖prev_hop‖my_id‖ctr, the router verifies it in O(hops) and re-attests downward” (R2 + R10). The ROUTING track’s ERB (§6.2) has no per-hop tag field at allTRAIL[] is bare UID bytes — yet §4.2 (“seeds mode and via_uid1” from the uplink frame) and §7.2 (return-candidate set) then consume TRAIL. The two docs point opposite ways and neither defers. The concrete decision the two tracks must jointly make: EITHER (A) ROUTING adds a per-hop MAC column to the ERB — then specify its width (the THREAT cost table says 2–8 B/hop; pick one and justify the airtime on a ≤255-B frame), the key (router-mediated SK per §4.3 is the only affordable option), exactly which router verifies and when, and run the result through /lrc-wire-change; AND THREAT must confirm a self-authenticating per-hop tag placed in the (otherwise masked) ERB does not violate R9 (it must authenticate itself, like the Tag8 trailer, and force a canonical_image re-audit) — OR (B) THREAT explicitly blesses “router validates a return relay purely by observed delivery, never by any TRAIL byte” as sufficient for the return-seed case, downgrading R2’s applicability there to “TRAIL is a pure (d)-class diagnostic hint, R11/R13-bounded, never a trust input” — in which case ROUTING must delete every sentence that says TRAIL seeds the RD and replace it with “TRAIL is diagnostic only; RD/return-candidates are built solely from observed delivery.” State which of (A)/(B) is chosen and why; they have very different wire-size and CPU costs (A pays bytes+SipHash/hop; B pays a slower/less-targeted first return but zero wire cost). This issue blocks R2, R5, R8, R10 simultaneously, so it must be resolved first.

Issue 3 — The RD seed must not be pinned from unauthenticated forward data on first contact (R5/R7 vs §4.2). → ROUTING — RESOLVED (DESIGN §4.2 first-contact RD rule: BROADCAST/UNKNOWN only, never TRAIL-seeded RELAY_VIA; [SEC-H6] RESOLVED). Involves: R5, R7 (and [SEC-H6]). Conflict: [SEC-H6] correctly notes the RD is router-local and has no on-wire edit surface — but R5 governs the seed, not edits. §4.2 says the first uplink frame “seeds mode and via_uid1,” and §6.2 says TRAIL exists so the router can seed the RD. So on first contact an RD can enter RELAY_VIA with a via_uid1 taken from an unauthenticated TRAIL byte — a return route pinned from unauthenticated forward data, exactly what R5 forbids, for the window before any delivery confirmation. This contradicts the doc’s own UNKNOWN-mode rule (“first successful delivery teaches the RD”). The concrete question the ROUTING track must answer: make the first-contact RD state explicit and consistent. Specify that on first contact the RD is BROADCAST (if the router heard the client directly with margin — a router-measured fact, not a wire claim, so R5-safe) or UNKNOWN (re-discover via the §3 forward edge); RELAY_VIA is reached only after an observed successful delivery through a specific relay, never seeded from TRAIL on first contact. Confirm this preserves R7 (a poisoned forward discovery cannot seed a trusted return) and that an RD that never gets a delivery confirmation decays to UNKNOWN (R14). Resolve jointly with Issue 1 — both turn on the same “observed-delivery-only” gate.


Closing note for the consolidator’s caller

Section titled “Closing note for the consolidator’s caller”

Verdict: CONSOLIDATED (pass 2). The three pass-1 issues — all rooted in one seam (TRAIL feeding return/RD/candidate construction without a per-hop authenticator and without a hard observed-delivery gate) — are closed. The fork in the road (Issue 2, → BOTH) was resolved by the behavior-rule fork (B): TRAIL is demoted to a diagnostics-only / unverified-candidate hint, all return trust derives from router-observed delivery, and first-contact RD is router-measured BROADCAST or UNKNOWN. This required no wire change, no per-hop MAC, and no canonical_image re-audit, so it is strictly cheaper than the per-hop-MAC fork (A) and covers the Sybil-ghost / keyless-foreign-relay case that fork A could not. The suppression/RACK authentication tension remains reconciled under Rules A–D.

Regression sweep: PASS — no previously-SATISFIED or RESOLVABLE-HERE item regressed, and the R1–R14 contract text is unchanged.

Non-blocking follow-ups (for the implementation / RF-sign-off phase, NOT blockers): (1) the exact wire mechanism of a “relay-sourced delivery confirmation” is an /lrc-wire-change implementation detail; (2) Option B’s first-return bootstrapping latency is acceptable and documented (first contact falls back to BROADCAST/UNKNOWN + opportunistic §3 edge — no availability deadlock), with the tuning of first-delivery observation left to the simulation/chaos-harness phase.

Standing reminder unchanged from pass 1: the RF-touching parts of this redesign (ERB wire layout, RACK frame, TX/contention timing) still require the /lrc-wire-change checklist and human RF sign-off before implementation — the CONSOLIDATED verdict is a design/security reconciliation result, not an RF sign-off.