reticiulum-specification/audits/link-lxmf-tier1-rns-1.2.4-lxmf-0.9.7.md
John Poole 7ffbb0ef5e Completed the full link-delivered LXMF unit:
Tier 1 audit: `link-lxmf-tier1-rns-1.2.4-lxmf-0.9.7.md`
Tier 2 vectors/verifier: link-lxmf.json, regen_link_lxmf.py, and verify_link_lxmf.py
Tier 3 promotion: updated SPEC.md, flows, status, and documentation
Key correction: the 319/320 boundary uses upstream’s computed LXMF content_size, not simply raw message content length.
Also corrected stale flow descriptions for KEEPALIVE (0xFA) and encrypted LINKCLOSE teardown (0xFC).
Verification:
Deterministic vector regeneration: identical SHA-256
Portable-path and formatting checks: pass
Full pinned suite: 17 passed, 0 failed
2026-06-08 13:54:27 -07:00

3.1 KiB

Tier 1 Audit: Link-Delivered LXMF

Question: How does upstream LXMF 0.9.7 select, emit, receive, and acknowledge DIRECT LXMF over an established RNS 1.2.4 Link?

Evidence baseline:

  • RNS package: rns==1.2.4
  • LXMF package: lxmf==0.9.7
  • Sources: LXMF/LXMessage.py, LXMF/LXMRouter.py, RNS/Packet.py, RNS/Link.py, and RNS/Resource.py
  • Audit date: 2026-06-08

The Tier 2 evidence is tools/verify_link_lxmf.py and the deterministic test-vectors/link-lxmf.json. Confirmed findings are promoted into SPEC.md and the Link-LXMF flow documents.

Confirmed Model

  1. LXMessage.pack() computes:

    content_size = len(packed_payload) - TIMESTAMP_SIZE - STRUCT_OVERHEAD
    

    DIRECT selects PACKET when content_size <= LINK_PACKET_MAX_CONTENT and Resource otherwise (LXMessage.py:405-421). The threshold applies to this computed value, not simply len(content) and not the complete signed LXMF body.

  2. With default RNS 1.2.4 / LXMF 0.9.7 parameters, LINK_PACKET_MAX_CONTENT = 319. The deterministic boundary fixtures use empty title/fields, so their raw content lengths also happen to be 319 and 320. The PACKET fixture's complete canonical LXMF body is 431 bytes, equal to Link.MDU; after Link Token framing its complete wire packet is 499 bytes.

  3. DIRECT/PACKET passes the complete canonical body destination_hash || source_hash || signature || msgpack_payload to RNS.Packet(link, packed) (LXMessage.py:627-635). Packet packing emits HEADER_1 DATA/LINK with context NONE and Link-derived Token encryption (Packet.py:176-219).

  4. DIRECT/RESOURCE passes that same complete canonical body to RNS.Resource(packed, link, ...) (LXMessage.py:643-653). Resource then applies its own whole-stream Link encryption and fragmentation.

  5. On receive, LXMRouter.delivery_packet() proves the Link DATA packet, classifies destination_type == LINK as DIRECT, and passes the decrypted body unchanged to lxmf_delivery() (LXMRouter.py:1822-1850). Unlike the opportunistic path, it does not prepend a destination hash.

  6. delivery_resource_concluded() passes the assembled Resource body to lxmf_delivery(..., method=DIRECT) (LXMRouter.py:1876-1885).

  7. PACKET completion is the regular Link DATA proof callback; Resource completion is the Resource callback. Both converge on LXMessage.__mark_delivered() (LXMessage.py:471-490, 594-603).

Tier 2 Scope

tools/verify_link_lxmf.py verifies:

  1. Exact computed-content boundary: 319 selects PACKET, 320 selects Resource.
  2. Deterministic Link DATA bytes using the session key from links.json.
  3. DIRECT/PACKET decrypts to the complete canonical LXMF body and validates Alice's signature.
  4. DIRECT/RESOURCE decrypts and reassembles to the same canonical form.
  5. A wrong Link key cannot decrypt the DIRECT/PACKET ciphertext.
  6. LXMRouter.delivery_packet() proves Link DATA, classifies it DIRECT, and forwards the full plaintext unchanged.

This work unit does not run a live threaded Link exchange. Link establishment, LRRTT activation, generic Link proof format, and generic Resource behavior are covered by their existing focused verifiers.