RNode Link Coordination Protocol
Status: Design draft. Depends on
future-rnode-ew-considerations.mdfor context.Problem: When two styrene RNodes need to change LoRa parameters (due to interference, degradation, or optimization), both ends must agree on the new settings or the link drops. This document defines the minimal coordination message and the knobs it operates on.
RNode Configuration Knobs
Every LoRa link is defined by a set of PHY parameters that must match on both ends for communication to succeed.
Parameters That Must Match (Link-Breaking)
Both sides of a link must agree on these or they cannot hear each other:
| Parameter | Type | Range | Wire Encoding | Notes |
|---|---|---|---|---|
| Frequency | u32 | Hz (e.g., 902000000–928000000 US) | 4 bytes | Must be identical. 1 Hz resolution. |
| Spreading Factor | u8 | 5–12 | 4 bits | Must match. Different SFs are orthogonal — nodes on different SFs cannot decode each other. |
| Bandwidth | u32 | 7800–500000 Hz | 3 bytes (encoded as index, see below) | Must match exactly. |
| Coding Rate | u8 | 5–8 (denominator of 4/N) | 2 bits | Must match for reliable decode. |
Parameters That Can Differ (Link-Compatible)
These affect behavior but don’t break the link if they differ between sides:
| Parameter | Type | Range | Notes |
|---|---|---|---|
| TX Power | i8 | -4 to +22 dBm | Each side sets independently. Asymmetric power is fine. |
| Preamble Length | u16 | 6–65535 symbols | Receiver needs at least the minimum to sync. Longer is compatible. |
Bandwidth Index Encoding
Standard LoRa bandwidths can be encoded as a 4-bit index to save wire space:
| Index | Bandwidth | Common Use |
|---|---|---|
| 0 | 7.8 kHz | Maximum range, minimum throughput |
| 1 | 10.4 kHz | |
| 2 | 15.6 kHz | |
| 3 | 20.8 kHz | |
| 4 | 31.25 kHz | |
| 5 | 41.7 kHz | |
| 6 | 62.5 kHz | |
| 7 | 125 kHz | Default for most LoRa deployments |
| 8 | 250 kHz | |
| 9 | 500 kHz | Maximum throughput, minimum range |
The Problem: Coordinated Parameter Changes
When conditions degrade, a node decides it needs to change LoRa parameters (per the scenarios in future-rnode-ew-considerations.md). But changing unilaterally kills the link — the other side is still listening on the old settings.
Two coordination strategies:
Strategy A: Shared Channel Plan (Pre-Agreed)
Both nodes share an ordered list of parameter configurations (the “channel plan”) established during the subscription handshake. When conditions degrade, both sides independently evaluate and step through the plan. The coordination message says which plan step to move to, not the raw parameters.
- Pro: Minimal message size (just an index). Both sides already know the parameters.
- Con: Requires prior agreement. Inflexible to novel conditions not in the plan.
Strategy B: Explicit Parameter Delta
The coordination message carries the actual parameter changes. No prior agreement needed beyond mutual identity trust.
- Pro: Can adapt to any condition. Works between nodes that haven’t done a subscription handshake.
- Con: Larger message. Must encode parameter values on the wire.
Recommendation: Both, Layered
- Primary: Channel plan stepping (Strategy A) — used between nodes with an established relationship.
- Fallback: Explicit parameter delta (Strategy B) — used for ad-hoc coordination or when the plan is exhausted.
Link Coordination Request (LCR)
The minimal “please help me reach you” message. Sent on the current settings (which still work, but are degrading) to coordinate a move to new settings.
Design Constraints
- Absolute minimum footprint — this message is sent when conditions are worst. Every byte costs.
- Authenticated — a bad actor must not be able to spoof a coordination request to force nodes onto a bad configuration, breaking their link.
- Self-contained — no round-trip negotiation. The sender proposes, the receiver decides.
- Timely — includes a validity window so stale/replayed messages are rejected.
Wire Format
LinkCoordinationRequest {
// Identity (who is asking)
sender: [16 bytes] // RNS identity hash of the sender
// Timing
valid_from: u32 // unix seconds (truncated to 32-bit, good until 2106)
valid_until: u32 // unix seconds — reject if now > valid_until
// Coordination (one of two modes)
mode: u8 // 0 = channel plan step, 1 = explicit delta
// Mode 0: Channel Plan Step
plan_step: u8 // index into shared channel plan
// Mode 1: Explicit Delta (only present if mode = 1)
frequency: u32 // target frequency in Hz
sf: u8 // target spreading factor (5-12), packed with bw_index
bw_index: u8 // bandwidth index (see table above)
coding_rate: u8 // coding rate denominator (5-8)
// Authentication
signature: [64 bytes] // Ed25519 signature over all preceding fields
}
Message Sizes
| Mode | Payload | Signature | Total |
|---|---|---|---|
| Channel plan step (mode 0) | 26 bytes | 64 bytes | 90 bytes |
| Explicit delta (mode 1) | 33 bytes | 64 bytes | 97 bytes |
Why 64 Bytes of Signature Is Non-Negotiable
The signature is the largest part of the message. It would be tempting to truncate it. Do not.
Without authentication, an attacker can:
- Send a fake LCR telling both nodes to move to different settings → link dies
- Send a fake LCR pulling nodes to a frequency the attacker is jamming → targeted denial
- Replay old LCRs to force nodes back to compromised settings → the
valid_untilfield mitigates this, but only if the signature is verified
RNS already uses Ed25519 (via Curve25519 identity keypairs). The 64-byte signature is the standard Ed25519 output. The receiver verifies against the sender’s known public key (which it has from the RNS identity exchange). If verification fails, the message is silently dropped.
Replay Protection
valid_from/valid_untilcreate a time window (recommend 30–120 seconds)- Receiver tracks the highest sequence seen per sender (or highest
valid_from) and rejects anything older - A replayed message outside the validity window is rejected
- A replayed message inside the window but with an already-seen
valid_fromis rejected
Coordination Sequence
Happy Path (Channel Plan)
Node A Node B
│ │
│ (detects degradation on current settings) │
│ │
├──► LCR { mode=0, plan_step=3 } ────────────► │
│ │
│ (A continues on current settings │
│ for validity window, then switches) │
│ │
│ (B verifies signature, │
│ validates plan_step=3, │
│ waits until valid_from, │
│ switches to plan step 3) │
│ │
│ ◄─────────── (link re-established) ─────────► │
Key Timing Detail
Both sides must agree on when to switch, not just what to switch to. The valid_from field is the switch time. The sender transmits the LCR, continues operating on current settings until valid_from, then switches. The receiver does the same. Both sides transition simultaneously (within clock drift tolerance).
Clock drift: RNS nodes don’t have synchronized clocks. The valid_from should allow a generous window (e.g., “switch 30 seconds from now”). Both sides switch and listen for the other. If the first attempt fails, retry with longer preamble on the new settings.
Failure Path: LCR Not Received
If Node B never receives the LCR (conditions too degraded), the link breaks. Recovery options:
-
Channel plan walking: Both sides independently step through the channel plan at a pre-agreed rate (e.g., try each step for 60 seconds). They will eventually rendezvous. This requires no coordination message at all — just synchronized plan traversal.
-
Beacon scanning: After losing a link, a node cycles through known configurations listening for beacons. The pub/sub beacon serves double duty — it’s both an event notification and a “I’m alive on this configuration” signal.
-
Fallback to IP: If available, coordinate the LoRa change over the IP backhaul. Graceful degradation.
Channel Plan Structure
A channel plan is an ordered list of LoRa configurations, shared between nodes during subscription handshake.
ChannelPlan {
plan_id: [8 bytes] // unique identifier for this plan
steps: Vec<ChannelStep>
walk_rate: u16 // seconds to dwell on each step during autonomous walking
}
ChannelStep {
frequency: u32 // Hz
sf: u8 // spreading factor
bw_index: u8 // bandwidth index
coding_rate: u8 // denominator (5-8)
}
Plan design guidance:
- Step 0 should be the nominal (best conditions) configuration
- Steps should progress from highest throughput to highest resilience
- Final step should be the most resilient configuration possible (SF12, minimum BW, maximum CR)
- Plans should include frequency diversity — don’t just change modulation parameters, change channels
- Keep plans short (8–16 steps) to ensure rendezvous happens within a reasonable time during autonomous walking
Plan Distribution
- Distributed during styrene subscription handshake (wire protocol message)
- Can be updated via LCR-like message when conditions are nominal (low-cost operation)
- Both sides must confirm plan receipt before it becomes active
- Old plan remains valid until new plan is acknowledged
Security Considerations
Threat: Forced Degradation
An attacker who knows the channel plan could jam the nominal frequency, wait for nodes to step through the plan, and jam each successive step. This forces nodes to their most resilient (slowest) configuration.
Mitigation: Channel plans should include frequency diversity across the full ISM band. Jamming 26 MHz of bandwidth (US 915 ISM) requires significantly more power and hardware than jamming a single channel. At some point the attacker needs a broadband jammer, which is expensive, illegal, and detectable.
Threat: Identity Compromise
If an attacker obtains a node’s private key, they can forge LCRs. This is a general RNS identity compromise, not specific to this protocol.
Mitigation: Standard key management practices. RNS identity keys are stored in ~/.reticulum/storage/. Physical security of edge nodes matters.
Threat: Timing Oracle
An attacker observing LCR transmissions learns when a frequency change will happen (valid_from), giving them time to pre-position jamming on the target frequency.
Mitigation: The valid_from window should be short (30 seconds or less). The LCR itself is encrypted within the RNS link — an external observer sees only that a transmission occurred, not the content. They would need to break RNS encryption to extract the target parameters.
Relationship to RNode Firmware
Current RNode firmware capabilities that affect this design (verify during implementation):
| Capability | Required | Status |
|---|---|---|
| Runtime frequency change | Yes | Believed supported — RNode exposes frequency as a configurable parameter |
| Runtime SF change | Yes | Believed supported |
| Runtime BW change | Yes | Believed supported |
| Runtime CR change | Yes | Believed supported |
| Atomic parameter change (all-at-once) | Preferred | Unknown — may require radio reset between changes |
| RSSI/SNR reporting per packet | Yes (for spectrum sensing) | Supported — RNode reports these values |
| Channel activity detection (CAD) | Preferred (for spectrum sensing) | LoRa hardware supports CAD mode — unclear if RNode firmware exposes it |
If atomic parameter changes are not supported (radio must restart between changes), the switchover has a brief deaf period. The valid_from window must account for this.
References
- OSINT platform architecture:
../../misc/osint/docs/architecture.md - Radio resilience scenarios:
future-rnode-ew-considerations.md - RNS identity and cryptography: https://reticulum.network/manual/understanding.html
- Ed25519 signature size: 64 bytes (RFC 8032)
- LoRa modulation orthogonality: different spreading factors cannot decode each other (Semtech AN1200.22)