reticiulum-specification/flows/propagation-peer-sync.md
John Poole 5aa3920b76 Completed the propagation-node announce and peer-sync three-tier unit.
Added:
Tier 1 audit
Peer-sync flow
Deterministic vectors
Regenerator
Verifier
Corrected §5.8 regarding:
Directional peering-key identity ordering.
Public versus control destination handlers.
Permissive announce parser behavior.
Autopeer rules.
Peer Resource framing and admission.
PN_STAMP_THROTTLE = 180 seconds.
Two documented LXMF 0.9.7 hazards.
Verification: deterministic regeneration passed; full pinned suite passed 20/20; git diff --check passed. No commit created.
2026-06-08 17:32:55 -07:00

3.4 KiB

Flow: propagation-node discovery and peer synchronization

How two LXMF propagation nodes discover each other, authenticate a sync offer, and transfer stored messages. Pinned against RNS 1.2.4 / LXMF 0.9.7; cross-references ../SPEC.md §5.8, §10, and §11.

Sequence

1. Node announces lxmf.propagation

The producer emits the seven-element §5.8.5 app_data array containing its timebase, enabled state, transfer/sync limits, stamp costs, peering cost, and metadata.

2. Receiving node evaluates autopeering

LXMFPropagationAnnounceHandler validates app_data. A direct announce creates or updates an autopeer only when the node is enabled, autopeering is enabled, and the path is within autopeer_maxdepth (default 4). Ordinary path responses do not create autopeers. An enabled=false direct announce removes an autopeer.

3. Initiator prepares a directional peering key

The offering node computes a proof-of-work key over:

receiving_identity_hash || offering_identity_hash

using the receiving node's announced peering cost. The key is cached and can be reused until the peer raises its cost.

The peer state machine sorts unhandled entries by ascending weight: priority_weight * age_weight * stored_size. It applies the peer's per-message and per-sync limits, opens a Link to lxmf.propagation, and identifies with its propagation-node identity.

LXMF 0.9.7 has a sender-side low-stamp prefilter defect: it uses min(0, required_cost - flexibility). Receivers use the correct max(0, ...) threshold, so low-value messages can be offered and then rejected.

5. Initiator sends /offer

The generic §11 request data is:

[peering_key, [transient_id_1, transient_id_2, ...]]

Each transient ID is the full 32-byte hash from §5.8. The receiver validates the directional key and marks the Link as validated.

6. Receiver selects wanted IDs

The /offer response value is:

  • False: receiver already has all offered messages.
  • True: receiver wants all offered messages.
  • [wanted_id, ...]: receiver wants only that subset.

7. Initiator transfers requested entries

Requested entries are read from the message store with their 32-byte propagation stamps and sent as a Resource:

msgpack.packb([time.time(), [stamped_entry_1, stamped_entry_2, ...]])

8. Receiver admits and stores the Resource

Multiple messages are accepted only when the Resource Link was validated by a successful /offer. An unvalidated Link may submit one message, which supports ordinary client submission. The receiver validates each propagation stamp, stores valid opaque bodies, and tears down/throttles on invalid stamps.

9. Initiator marks sync state

After a successful Resource transfer, the initiator moves transferred IDs from unhandled to handled state, updates counters, tears down the Link, and may continue immediately under the persistent strategy.

Source map

Step File Function / line
1 LXMF/LXMRouter.py get_propagation_node_app_data, line 306+
2 LXMF/Handlers.py LXMFPropagationAnnounceHandler, line 35+
3-5 LXMF/LXMPeer.py generate_peering_key / sync, line 242+
5-6 LXMF/LXMRouter.py offer_request, line 2145+
7, 9 LXMF/LXMPeer.py offer_response / resource_concluded, line 395+
8 LXMF/LXMRouter.py propagation_resource_concluded, line 2200+