RNode Link Coordination Protocol

Status: Design draft. Depends on future-rnode-ew-considerations.md for 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.

Both sides of a link must agree on these or they cannot hear each other:

ParameterTypeRangeWire EncodingNotes
Frequencyu32Hz (e.g., 902000000–928000000 US)4 bytesMust be identical. 1 Hz resolution.
Spreading Factoru85–124 bitsMust match. Different SFs are orthogonal — nodes on different SFs cannot decode each other.
Bandwidthu327800–500000 Hz3 bytes (encoded as index, see below)Must match exactly.
Coding Rateu85–8 (denominator of 4/N)2 bitsMust match for reliable decode.

These affect behavior but don’t break the link if they differ between sides:

ParameterTypeRangeNotes
TX Poweri8-4 to +22 dBmEach side sets independently. Asymmetric power is fine.
Preamble Lengthu166–65535 symbolsReceiver 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:

IndexBandwidthCommon Use
07.8 kHzMaximum range, minimum throughput
110.4 kHz
215.6 kHz
320.8 kHz
431.25 kHz
541.7 kHz
662.5 kHz
7125 kHzDefault for most LoRa deployments
8250 kHz
9500 kHzMaximum 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.

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

  1. Absolute minimum footprint — this message is sent when conditions are worst. Every byte costs.
  2. Authenticated — a bad actor must not be able to spoof a coordination request to force nodes onto a bad configuration, breaking their link.
  3. Self-contained — no round-trip negotiation. The sender proposes, the receiver decides.
  4. 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

ModePayloadSignatureTotal
Channel plan step (mode 0)26 bytes64 bytes90 bytes
Explicit delta (mode 1)33 bytes64 bytes97 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:

  1. Send a fake LCR telling both nodes to move to different settings → link dies
  2. Send a fake LCR pulling nodes to a frequency the attacker is jamming → targeted denial
  3. Replay old LCRs to force nodes back to compromised settings → the valid_until field 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_until create 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_from is 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:

  1. 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.

  2. 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.

  3. 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):

CapabilityRequiredStatus
Runtime frequency changeYesBelieved supported — RNode exposes frequency as a configurable parameter
Runtime SF changeYesBelieved supported
Runtime BW changeYesBelieved supported
Runtime CR changeYesBelieved supported
Atomic parameter change (all-at-once)PreferredUnknown — may require radio reset between changes
RSSI/SNR reporting per packetYes (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)

Graph