Worked for 4m 50s

Completed the destination-routed DATA and reverse-PROOF three-tier unit.

Key findings:

Intermediate relays retarget HEADER_2; the final relay delivers HEADER_1.
Each relay records independent reverse-table state.
PROOF destination hashes remain invariant across transport transformations.
A wrong-interface PROOF consumes the reverse entry before being dropped.
REVERSE_TIMEOUT is 480 seconds, not 30 seconds.
Added Tier 1 audit, two-relay flow, deterministic vectors, regenerator, and runtime verifier. Corrected affected specification, flows, playbook, and status documentation.

Verification:

Deterministic regeneration: identical SHA-256
Full pinned suite: 22 passed, 0 failed
git diff --check: passed
No commit created.
This commit is contained in:
John Poole 2026-06-08 18:32:26 -07:00
commit 9c3b628c6a
14 changed files with 578 additions and 16 deletions

View file

@ -22,6 +22,8 @@ The two views are complementary: SPEC.md tells you what each piece looks like; t
| [`receive-propagated-lxmf.md`](receive-propagated-lxmf.md) (recipient pulling messages via `/get`) | ✅ |
| [`propagation-peer-sync.md`](propagation-peer-sync.md) (propagation-node announce, `/offer`, and peer Resource sync) | ✅ |
| [`lxmf-outbound-retry.md`](lxmf-outbound-retry.md) (process_outbound retry loop, per-message state machine, fail_message) | ✅ |
| [`transport-link.md`](transport-link.md) (relayed Link establishment and established-Link traffic) | ✅ |
| [`transport-data.md`](transport-data.md) (ordinary DATA and returning PROOF through relays) | ✅ |
## Conventions

View file

@ -195,20 +195,18 @@ SPEC.md §9.5: if the operator runs both an originator and a transport node on t
## Wire-byte summary (mirror of the send-flow summary)
What arrives at the recipient before deframing — assumes a 0-hop direct send (HEADER_1) or a >1-hop relayed packet that arrives as HEADER_2 with a transport_id at offset 2:
What arrives at the recipient before deframing is HEADER_1 whether sent directly or delivered by a last-hop transport relay. Intermediate relays use HEADER_2, but the final relay strips the transport-id slot before transmission:
```
HEADER_1:
[ 1B flags ][ 1B hops ][ 16B dest_hash ][ 1B context=0x00 ]
[ 32B sender_ephemeral_X25519_pub ][ 16B iv ]
[ N×16B aes_ciphertext ][ 32B hmac_sha256 ]
HEADER_2 (after relay):
[ 1B flags ][ 1B hops ][ 16B transport_id ][ 16B dest_hash ][ 1B context=0x00 ]
[ 32B sender_ephemeral_X25519_pub ][ 16B iv ]
[ N×16B aes_ciphertext ][ 32B hmac_sha256 ]
```
See [`transport-data.md`](transport-data.md) for the intermediate HEADER_2
forms and reverse-PROOF path.
After AES-CBC decryption with the matching ratchet (or long-term) key, the plaintext is the opportunistic LXMF body **without** the recipient's dest_hash (SPEC.md §5.1):
```

View file

@ -142,7 +142,7 @@ Strictly speaking, the flow above ends at step 10. Steps 11-13 are about **what
### 11. Recipient processes the inbound DATA packet
Inverse of steps 7-9, in the order: deframe → optional HEADER_2 strip / hop-table lookup → packet enters `Transport.inbound` → handed to the destination → `RNS.Destination.decrypt` reverses the Token (HMAC verified **before** AES decrypt per SPEC.md §3.3) → LXMF body parsed → Ed25519 signature verified, with the dual-msgpack-variant tolerance described in SPEC.md §5.6 → message surfaced to the recipient's app.
Inverse of steps 7-9, in the order: intermediate relays process HEADER_2 via their path tables → the last relay strips the transport slot and emits HEADER_1 → recipient deframes → packet enters `Transport.inbound` → handed to the destination → `RNS.Destination.decrypt` reverses the Token (HMAC verified **before** AES decrypt per SPEC.md §3.3) → LXMF body parsed → Ed25519 signature verified, with the dual-msgpack-variant tolerance described in SPEC.md §5.6 → message surfaced to the recipient's app. See `transport-data.md` for the relay sequence.
The receive flow is its own document; see `receive-opportunistic-lxmf.md` (TODO) for the detailed step list.

46
flows/transport-data.md Normal file
View file

@ -0,0 +1,46 @@
# Flow: Destination-Routed DATA Through Transport Relays
This flow follows ordinary DATA and its returning PROOF across two transport
relays, pinned to RNS 1.2.4. Opportunistic LXMF is the common example.
## Forward DATA
1. The originator emits HEADER_2 because its path has more than one hop. The
`transport_id` names relay 1; the final destination hash remains in the
second address slot.
2. Relay 1 verifies that `transport_id` names itself and finds the final
destination in `path_table`.
3. With more than one remaining hop, relay 1 increments hops, replaces
`transport_id` with relay 2's identity hash, and preserves HEADER_2.
4. Relay 1 records `reverse_table[packet.getTruncatedHash()]` with the ingress
and egress interfaces.
5. Relay 2 receives the retargeted HEADER_2 packet. With one remaining hop, it
increments hops, strips the transport slot, and emits HEADER_1 toward the
destination.
6. Relay 2 records its own reverse entry under the same truncated packet hash.
The hash is stable because `Packet.get_hashable_part()` excludes hops,
transport type/header type, and the HEADER_2 transport-id slot.
## Returning PROOF
1. The destination emits a HEADER_1 PROOF addressed to the original DATA
packet's truncated hash.
2. Relay 2 pops that hash from `reverse_table`. If the PROOF arrived on the
recorded outbound interface, it increments hops and sends toward relay 1.
3. Relay 1 repeats the operation and sends toward the originator.
4. Each entry is one-shot. A duplicate PROOF has no route.
## Failure Diagnostics
| Observation | Meaning |
|---|---|
| First relay receives DATA but does not forward it | Check HEADER_2 `transport_id` and relay `path_table`. |
| Intermediate relay forwards HEADER_2 but final endpoint parser expects HEADER_2 | Endpoint receives HEADER_1; the last relay strips the transport slot. |
| DATA reaches endpoint but no PROOF returns | Inspect every relay's reverse entry and recorded interfaces. |
| PROOF appears first on the wrong interface | Upstream drops it after popping the reverse entry; a later correct copy cannot return. |
| Reverse entry remains indefinitely in a clean-room relay | RNS 1.2.4 expires it after 480 seconds or when a recorded interface disappears. |
Executable evidence: `tools/verify_transport_data.py` and
`test-vectors/transport-data.json`.