Reporter implemented §7.2.6 minimum-leaf path-request responder + §7.3 ratchet rotation in thatSFguy/reticulum-lora-webclient and surfaced five small gaps. Each is fixed below; the first is a real spec correction backed by a new runtime verifier. #### 1. §7.3 dedup-mechanism claim was wrong (verified) Earlier §7.3 claimed transit nodes dedup on '(destination_hash, ratchet_pub)' tuples. Reporter pointed out this can't be right: upstream's RATCHET_INTERVAL = 30 min × ANNOUNCE_INTERVAL = 5-15 min means most upstream announces share a ratchet across 2-6 emissions. If relays really dropped on ratchet_pub equality, upstream wouldn't function. Confirmed by new tools/verify_ratchet_dedup.py: builds two announces with same ratchet_pub but distinct random_hash[:5], walks the upstream replay-defence machinery (Transport.py:1707,1732,1745 'not random_blob in random_blobs' check) by hand. Both announces ACCEPTED — dedup is keyed on random_blob, not on ratchet_pub. §7.3 rewritten: - Drops the wrong dedup claim with an explicit ⚠️ Spec correction callout naming the bug. - Reframes ratchet rotation as forward-secrecy hygiene, not a mesh-visibility requirement. - Points at §4.5 step 6.3 / §4.1 for the actual replay-defence mechanism. - Documents upstream's at-most-every-30-min rotation cadence (rotate_ratchets is a no-op if RATCHET_INTERVAL hasn't elapsed). - Says clean-room MAY rotate per-announce or follow upstream's cadence — either is interop-correct. #### 2. Path-response ratchet rotation guidance — §7.3.4 (new) Added explicit guidance: path-response announces SHOULD reuse the current ratchet rather than rotate. Burst-rotating on identical-target path? requests would burn ratchet-ring slots without forward-secrecy benefit. Upstream's no-op-if-recent gate enforces this implicitly. #### 3. Leaf dedup-table size — §7.2.6 step 4 Added: 'A leaf-appropriate cap is 128–256 entries with FIFO eviction; the upstream max_pr_tags = 32000 is sized for a transit node.' #### 4. PR_TAG_WINDOW body cache for leaves — §7.2.6 trailing Added: 'Leaves may skip the §7.2.5 PR_TAG_WINDOW body cache' with explanation that step 4's dedup table already collapses identical-tag retransmits and a leaf isn't fanning to multiple downstream relays. #### 5. PLAIN destination recipe link — §7.2.1 Added: 'The path-request destination is a PLAIN destination ... per the PLAIN/GROUP recipe in §1.4.3 (the identity == None branch).' Surfaces the connection that's currently buried in §1.4 titled 'GROUP destinations' but actually covers PLAIN too. agent.md §5 audit table updated — §7.3 entry corrected to note the prior 'verified' claim was actually mis-attributed; the test result came from incidental random_hash rotation, not ratchet rotation. 13 of 13 verifiers in tools/ now pass. Closes #1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| flows | ||
| test-vectors | ||
| tools | ||
| agent.md | ||
| LICENSE | ||
| README.md | ||
| SPEC.md | ||
| todo.md | ||
Reticulum Specifications
Byte-level interoperability specifications for the Reticulum Network Stack and LXMF — the parts that aren't in the upstream manuals but are needed to build a working client from scratch.
Upstream Reticulum has excellent operator-facing documentation (config, deployment, design philosophy). What's missing — and what every alternative implementation has had to reverse-engineer from the Python source — is an authoritative wire-level spec: header bit layouts, msgpack field types, signature input formats, the exact behavior of Transport.outbound, and the long list of "would never guess from reading the manual" gotchas that cost hours of debugging each.
This repo collects those findings in one place. The hope is that future client authors (Kotlin, Swift, Rust, Go, embedded C — pick your stack) can read this instead of re-deriving everything from RNS/Transport.py.
Status
Early days, contributions welcome. Current content was bootstrapped from the working notes of two reverse-engineering efforts:
- The web-based Reticulum client at
reticulum-lora-webclient - The native Android client at
reticulum-mobile-app
Each finding is grounded in upstream source citations (file + line) so it can be re-verified as RNS evolves.
What's here
SPEC.md— the single combined spec document, organized by protocol layerflows/— chronological end-to-end narratives (e.g. "send a message"), cross-referencing SPEC.md sectionstools/— self-contained Python verifier scripts that test SPEC.md claims against upstream RNS / LXMFtest-vectors/— known-good byte sequences each implementation should be able to round-trip (intent: grow into a compliance suite)
As content grows, SPEC.md will be split into per-layer files (packet header, identity, announce, token-crypto, LXMF, link, resource, transport).
Scope
In scope:
- Wire formats: byte layouts, field encodings, framing
- Signing inputs and what's hashed where
- Cross-cutting behaviors required for interop (path requests, ratchet rotation, retransmit semantics)
- "Gotchas" — things upstream code does that aren't obvious from the manual or RFC-style sketches
- Test vectors that any implementation must be able to round-trip
Out of scope:
- Operator/user documentation — see the official manual
- API design choices for any specific implementation
- Networking layer config (interfaces, transport modes) — already well documented
Source citations
Where a finding cites upstream Python code, the path is relative to a standard pip install rns lxmf installation, e.g. RNS/Transport.py, LXMF/LXMF.py. Where the bundled umsgpack is referenced, the path is RNS/vendor/umsgpack.py.
When upstream code changes such that a citation no longer matches, file an issue or PR — the goal is to track the de-facto wire spec as it actually behaves, not as it was at any single snapshot.
Contributing
If you've debugged a Reticulum interop problem and the answer wasn't in the upstream docs, please add it. Format:
### N.M Short description of the finding
**Symptom:** what you observed that prompted the investigation.
**What's happening:** the actual mechanism, ideally with upstream source citation (file + line).
**Implication / fix:** what an implementation must do to interop.
**Source:** upstream file paths and approximate line numbers.
Add a worked test vector to test-vectors/ if the finding is byte-level.
License
CC BY 4.0 — use freely, attribution appreciated.