Stage 3 / Checkpoint C deliverable. Status: Draft normative spec. Date: 2026-06-09.
Depends on: packet-format.md, mac-spec.md, routing-spec.md, security-spec.md.
Normative language per RFC 2119/8174. This document specifies the behaviour of each node role by composing the layer specs into a per-role state machine. Roles are separate firmware builds (CLAUDE.md asymmetric model); role is not runtime-negotiable in v1 — but coordinator status within the INFRA role is dynamic and earned (§4,
routing-spec.md§5).
| Role | Hardware | Always-on RX | Relays | Stores | Coordinates |
|---|---|---|---|---|---|
| CLIENT | ESP32/nRF52 class | no (may sleep) | no (unless CLIENT_RELAY) |
own pending only | no |
| REPEATER | ESP32/nRF52 class | yes | yes (per routing table) | neighbour/route state | no |
| INFRA | Linux SBC | yes | yes | persistent store-&-forward | if elected (§4) |
CLIENT_RELAY is a CLIENT submode that opts into relaying (§2.4). All roles share the same wire format
and security; they differ in which behaviours they run and their power/airtime profile.
A CLIENT is the leaf: a user's device, BLE/USB-attached to an app (node-roles per CLAUDE.md). It
minimizes airtime and power.
┌─────────┐ hear valid BEACON ┌────────────┐ slot granted ┌───────────┐
│ SCANNING │ ────────────────────────▶ │ REGISTERING │ ──────────────▶ │ SCHEDULED │
└─────────┘ └────────────┘ └───────────┘
│ no BEACON for BEACON_MISS_MAX ▲ │
▼ │ BEACON returns │ coordinator lost
┌──────────┐ send/recv via epoch-hash │ ▼
│ FALLBACK │ ◀─────────────────────────────────┴──────────────────── (mac-spec §7)
└──────────┘
mac-spec.md §8). No scheduled TX.routing-spec.md §3.1) with CAP_FLAGS
(power class, not relay-capable unless CLIENT_RELAY), LINK_REPORTS. Retry via epoch-hash.REG_PERIOD.mac-spec.md §6,
routing-spec.md §6).TC), REGISTER, identity ADVERT (rate-capped), ACKs for DATA it
receives with A=1.HOPLIM decrement/forward is a no-op for a plain CLIENT). This
is the explicit asymmetry: clients do not carry others' traffic, so a dense crowd of phones does not
multiply flood load.A CLIENT MAY sleep aggressively: it MUST wake to receive BEACONs at the cadence needed to hold
slot timing (mac-spec.md §3) and to receive traffic addressed to it; between those it MAY power
down the radio. EMERGENCY/INTERACTIVE responsiveness is bounded by its wake cadence, a
configurable power/latency trade.
A CLIENT that has acquired a coordinator (TOFU, security-spec.md §2) MUST abandon it and return to
SCANNING when either liveness signal fails — otherwise it can bind permanently to a coordinator that
can no longer serve it. This is normative because the field already produced the failure: on 2026-06-12
two clients REGISTERed indefinitely into a TX-alive / RX-deaf coordinator (it could beacon but not hear
them), never failing over.
MAX_REG_ATTEMPTS REGISTERs (default 12, ≈12 superframes)
with no slot grant naming the client's address, the client MUST treat the coordinator's uplink as
unreachable from its position and abandon it. (A grant is the only positive evidence the coordinator
heard the client — this is also the uplink-confirmation rule of red-team finding §2.2.)BEACON_LOSS (default 12 s, ≥ a few superframes), the coordinator is presumed vanished and the
client MUST abandon it. This is the mechanism of tier re-homing — when an infrastructure tier
fails, its clients detect the silence and re-elect onto the next surviving tier
(../analysis/tiered-infrastructure-scenario.md §4).On abandonment the client places the coordinator's address on a cooldown blacklist for
COORD_COOLDOWN (default 30 s) and MUST NOT re-acquire it during that window — otherwise it
immediately re-TOFUs the loudest (still-dead) coordinator and loops. After cooldown it may retry it
(the coordinator may have recovered). With no other coordinator available, the client runs the
FALLBACK regime (§2.1) while scanning.
Asymmetric hysteresis. Displacing a working coordinator for a marginally better one is slow and sticky (
routing-spec.md§5.2, anti-thrash); replacing a vanished coordinator is fast (this rule). The trigger distinguishes them: liveness loss ⇒ fast re-acquire; a higher corroborated claim ⇒ slow displace. The two are the same diff in the implementation (the liveness watchdog).
A CLIENT explicitly configured CLIENT_RELAY sets the relay-capable CAP_FLAGS bit and MUST then
behave as a REPEATER for forwarding (§3.2) while retaining client app attachment. It gives up
aggressive sleep (relaying needs always-on RX). This is opt-in, never automatic.
A dedicated relay on client-class hardware — no user app, configured once at setup. Optimized for field-deployed solar/battery, always-on RX.
Same SCANNING→REGISTERING→SCHEDULED→FALLBACK shape as CLIENT, minus sleep: a REPEATER MUST
keep RX on whenever powered (it cannot relay what it does not hear). It registers with CAP_FLAGS
relay-capable so the coordinator places it in routing.
DST ≠ self and HOPLIM > 0 toward the next hop
(routing-spec.md §3.2), decrementing HOPLIM, transmitting in its own scheduled slot (not
immediately — scheduled discipline is what prevents the relay storm).routing-spec.md §6).Optimized low-power always-on-RX: duty-cycle the MCU, not the receiver. A REPEATER SHOULD report its power class so a coordinator can avoid over-scheduling a battery relay.
A Linux node — full routing, persistent storage, optional multiple radios, optional IP backhaul. The INFRA role is the only one that MAY coordinate; whether it does is elected, not configured.
This is the normative composition of the Checkpoint-B finding (elevated-nodes.md,
high-site-backbone.md) that role ≠ vantage:
routing-spec.md §5) carrying corroborable
reach/vantage/capability evidence. If it wins election for its area, it enters COORDINATING;
otherwise it runs as a (capable, always-on, storing) participant in another coordinator's cell.When coordinating, an INFRA node MUST:
EPOCH, CELL_DESC, SLOTMAP, contention
pointer (mac-spec.md §2, packet-format.md §8.1). It is the time authority (§3 there).mac-spec.md §4), keep the superframe ≤ FRAME_T and the contention floor.(SF,BW) from clients' LINK_REPORTS (mac-spec.md §8).routing-spec.md §4) — never letting backhaul become a dependency.BACKHAUL_INJECT,
admission-control it into the schedule under BH_CAP, locality-filter it (mac-spec.md §9). It
MUST NOT raw-flood backhaul onto RF.TC above the utilization ceiling (mac-spec.md §10).An INFRA node MAY present multiple radios as separate cells (each its own CELL_DESC): e.g. a
robust long-reach plane + a fast high-capacity plane (design-decision.md §5, sim-result-v4). The
radios may be one dual-radio HAT, a HAT + USB radio, or two boxes joined by a site-local fabric
(the dual-radio-pi-hat-hardware finding; the local fabric is intra-node, not external backhaul, so
goal #4 holds). A real co-located dual-radio example exists in the wild (Rky/RFM1,
high-site-backbone.md).
An INFRA node has stable storage and MUST persist its EPOCH counter, advancing cleanly across
reboot (security-spec.md §8) — so it never reissues a used (EPOCH,SEQ) and clients can trust its
schedule continuity.
packet-format.md,
security-spec.md); a role difference never changes the frame contract.sim-result-v6).A conformant CLIENT MUST NOT relay unless CLIENT_RELAY, MUST restrict storage to its own
traffic, and MAY sleep within the timing constraints of mac-spec.md §3. A conformant REPEATER
MUST keep RX always-on and forward only in scheduled slots / rate-capped fallback. A conformant
INFRA node MUST coordinate only when elected, and when coordinating MUST perform all §4.2
duties; it MUST maintain the LoRa-only reachability view and govern backhaul per mac-spec.md §9.
No role MAY originate or relay a frame that fails the packet-format.md §9 validation order.
Composes the four layer specs into per-role behaviour. This completes the Stage-3 specification set;
see checkpoint-status.md / the Checkpoint-C summary for the review ask before any firmware.