A network laboratory in CI
A unit test can tell you a gap-healing request is well-formed. It cannot tell you what happens when the only router on the ridge dies at hour six while a client is walking across three cells. Those are network questions — topology + physics + time + faults — and answering them on real hardware would take a hillside, a van, and a summer.
LoRa Relay Chat’s answer is lrcsim: a unix-native binary that runs
whole-network scenarios against the real node logic — the same
compiled lrc::Node that ships in the daemon and the firmware — driven by
a virtual clock over a geometric field model. It exists because of an
architectural bet the project made on day one: keep the core portable and
pure (no sockets, no Arduino, platform access through seams), and
everything becomes testable on a host. The scenario lab is that bet paying
off.
Scenarios are text files
Section titled “Scenarios are text files”A scenario is a flat, diff-friendly text file. The flagship one, abridged:
# hilltop_asym.scnseed 42field pathloss 3.0 shadow 2.0node hill role=router x=0 y=0 z=150 tx_dbm=27node plain[1..200] role=client grid=1000x1000 tx_dbm=14link hill->plain[*] snr=+8 # everyone hears the hilllink plain[6..200]->hill snr=off # the hill hears only plain[1..5]at 60s chanmsg from=plain[137] chan=#field text="can anyone hear me"at 300s kill hillat 420s revive hill # reboot-with-persistenceassert converged chan=#field by 900sNote what the link lines are doing: the hilltop router is heard by all
200 nodes but hears only five of them — asymmetry as a first-class
input, not an approximation layered onto a symmetric model. Directional
SNR overrides exist precisely because the routing design lives or dies on
asymmetric links, and a test harness that couldn’t express “loud downhill,
deaf uphill” would quietly certify the wrong protocol.
The physics underneath is deliberately modest but honest: log-distance path loss with per-scenario exponent, optional log-normal shadowing, preset SNR floors shared with the rest of the test stack, co-SF collisions unless one signal captures, and — a detail most simulations get wrong — imperfect orthogonality between spreading factors, because “SFs don’t interfere” is a myth that flatters everyone’s airtime math.
Determinism is the feature
Section titled “Determinism is the feature”A scenario run is a pure function of (scenario file, seed). Even the
shadowing draws are seeded per-(seed, from, to, time) rather than pulled
from a shared stream, so replays and out-of-order queries can’t smear
randomness across runs. That buys the three things a network lab actually
needs: a failure reproduces exactly, a failure bisects (same seed,
narrower scenario), and CI can run the whole scenario library on every
commit and mean something when it’s green — thousands of simulated
network-hours per CPU-minute, no bench time consumed.
The long-term rule the project has set for itself is the best part:
every field incident becomes a scenario. Weird behavior on a real
hillside gets encoded as a .scn with an assertion, joins the library,
and can never regress silently again. The lab grows a case file instead of
a folklore channel.
Deeper reading: lrcsim (normative, what ships), the testbed design (research tier), and Testing for the whole host-first ladder.