reticiulum-specification/todo.md
Rob 588dcc9982 Expand §8.3 with the full RNode air-frame split-packet protocol
The previous one-sentence §8.3 was wrong about scope: it said KISS hosts
treat the 1-byte header as opaque pass-through, which is misleading —
the byte lives between RNodes on the LoRa air-frame, not on the KISS
channel. Hosts (RNS, Sideband, etc.) never see it. Any alternative
implementation that talks LoRa to an RNode must construct/parse it
bit-exactly, or its TX is invisible and its RX mistakes the header for
the first payload byte.

New text covers:
  - Header byte layout: bit 7..4 random seq nibble, bit 0 FLAG_SPLIT,
    SEQ_UNSET=0xFF sentinel (Framing.h:105-108).
  - TX rules: header = random(256) & 0xF0 | (FLAG_SPLIT iff
    payload > 254). Both halves of a split share the same byte byte-
    for-byte. Split at 255 bytes total per LoRa frame; max reassembled
    payload 508. (RNode_Firmware.ino:716-742; Config.h:59-61.)
  - RX state machine: at most one buffered first-half keyed by seq
    nibble; four cases for inbound frames (RNode_Firmware.ino:359-446).
  - Reassembly timeout: upstream firmware has none (relies on
    subsequent traffic to evict). The clean-room repeater adds a 500ms
    defensive timeout (reticulum-lora-repeater/src/Radio.cpp:189-194)
    — implementation-private, not part of the wire spec.
  - Sequence-collision ceiling: 4 random bits = 1/16 collision per
    overlapping split-packet pair from the same sender. Don't burst.
  - Note that a "header rotates between transmissions" memory of this
    protocol is a fading recall of the per-TX random seq nibble — there
    is no retransmit-driven byte rotation or rechunk. LoRa TX is
    fire-and-forget; higher-layer retransmit just re-runs the TX path
    and gets a fresh random seq.

todo.md gets an entry for tools/verify_rnode_split.py to lock the
new §8.3 in with a runtime test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 10:34:18 -04:00

6.2 KiB

TODO

Outstanding work for the spec repo.

Outreach

  • File a community-documentation issue on markqvist/Reticulum. Link this repo as a community-maintained byte-level spec. Ask whether the maintainer would like to bless / link from the official Reticulum manual. Frame it as a complement to (not a replacement for) the existing operator-focused docs.

Test infrastructure

  • Bootstrap test-vectors/identities.json — Alice + Bob identities populated against RNS 1.2.0. Regenerator at tools/regen_identities.py.

  • Bootstrap remaining test-vectors files (announces.json, lxmf.json, links.json) with the existing vectors from reticulum-mobile-app/reference/test-vectors.json. Convert to the proposed JSON format documented in test-vectors/README.md, adding the regenerator scripts so future contributors can verify vectors against newer upstream RNS releases.

  • Write the priority verifier scripts listed in tools/README.md, in this order (highest interop value first): 1. [x] verify_destination_hash.py — pure-function check, no RNS state needed 2. [x] verify_packet_header.py — bit layout + HEADER_1/HEADER_2 round-trip + originator HEADER_1→HEADER_2 conversion 3. [ ] verify_announce_roundtrip.py — closes the SPEC.md §4 gap (partial coverage in verify_announce_app_data.py) 4. [ ] verify_token_crypto.py — closes SPEC.md §3 gap 5. [ ] verify_lxmf_opportunistic.py — closes SPEC.md §5 gap 6. [ ] verify_link_handshake.py — closes SPEC.md §6 gap 7. [x] verify_path_request.py — closes SPEC.md §7.1, §7.2 gaps 8. [ ] verify_msgpack_quirk.py — closes SPEC.md §9.3 gap

    Each verifier should remove its corresponding `⚠️ UNVERIFIED` /
    `🔮 SPECULATION` callout in `SPEC.md` (per `agent.md` §1).
    

Open ⚠️ UNVERIFIED items in SPEC.md

These need either a runtime test or a stronger upstream source citation to remove their markers:

  • §2.3 Originator HEADER_1 → HEADER_2 conversion. Verified against RNS 1.2.0 by tools/verify_packet_header.py, which seeds Transport.path_table with a multi-hop entry and confirms the converted wire bytes via stubbed Transport.transmit. Citation updated to RNS/Transport.py:1074-1083.

  • §4.3 The 3-element [name, stamp_cost, [capabilities]] app_data variant. Verified against LXMF 0.9.6 by tools/verify_announce_app_data.py. Finding: in this LXMF version the producer emits a 2-element form only (the supported_functionality line at LXMF/LXMRouter.py:999 is dead code); the parser is prepared for a 3-element form via compression_support_from_app_data. SPEC.md §4.3 updated to describe the actual current behavior.

  • §7.1 path? always precedes LXMF DATA. Verified against LXMF 0.9.6 by tools/verify_path_request.py. Finding: the preamble fires only when not has_path() AND method is OPPORTUNISTIC; the retry path can fire a second request_path after MAX_PATHLESS_TRIES (LXMRouter.py:2571+). SPEC.md §7.1 rewritten accordingly. Also fixed a documentation bug in §1.2 (path-request name_hash column).

  • §7.4 Ratchet ring count default = 8. False — actual upstream default is Destination.RATCHET_COUNT = 512 at RNS/Destination.py:85 in RNS 1.2.0, with RATCHET_INTERVAL = 30*60 (line 90) and RATCHET_EXPIRY = 60*60*24*30 (RNS/Identity.py:69). SPEC.md §7.4 corrected.

Open ⚠️ items needing a runtime verifier

  • tools/verify_rnode_split.py to lock in §8.3. The RNode air-frame split-packet protocol is now documented in SPEC.md §8.3 against direct citations in markqvist/RNode_Firmware/Framing.h, Config.h, Utilities.h, and RNode_Firmware.ino, plus the clean-room reimplementation in thatSFguy/reticulum-lora-repeater/src/Radio.cpp. A runtime verifier would: build a 300-byte synthetic Reticulum packet, run it through a Python implementation of the TX-side header rules, and confirm the byte-level frames match what RNode_Firmware.ino:716-742 would emit (header byte high nibble random + low-nibble FLAG_SPLIT bit, both frames sharing the same header, split point at 255 bytes total per LoRa frame). RX-side verifier should drive the state-table at SPEC.md §8.3 and confirm the four reassembly cases.

  • Lock in the §6.2 / §6.3 corrections with verify_link_handshake.py. The wire-byte order of the LRPROOF body (signature || responder_X25519_pub || signalling, not link_id || responder_X25519_pub || signature || signalling) and the link_id derivation offsets (N=2 for HEADER_1, N=18 for HEADER_2, not 18/34) were corrected against direct upstream source citations (RNS/Link.py:376, RNS/Packet.py:354-361) in SPEC.md §6.2/§6.3 while writing flows/send-link-lxmf.md. They are source-cited but not yet exercised by a runtime verifier. Add tools/verify_link_handshake.py that drives an upstream LINKREQUEST → LRPROOF → ACTIVE handshake and asserts byte-level layouts + link_id invariance under HEADER_1↔HEADER_2.

Spec polishing (lower priority)

  • Split SPEC.md into per-layer files as the document grows past ~1500 lines. Suggested layout per README.md: 00-overview.md, 01-packet-header.md, 02-identity.md, 03-announce.md, 04-token-crypto.md, 05-lxmf.md, 06-link.md, 07-resource.md, 08-transport.md, 09-paths-and-discovery.md, 10-implementation-gotchas.md.

  • Add a "last-verified-against-rns" line to SPEC.md frontmatter (per agent.md §7) so readers know which RNS version the spec was tested against.

  • Document the Reticulum Resource fragmentation protocol — currently absent from SPEC.md but needed for multi-packet LXMF over Link (NomadNet pages > 1 MTU, large file transfers).

  • Document the Propagation /get pull protocol for offline message retrieval. Used by Sideband when peers are out of range.