1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Relayed-transport method (TURN-like) — the **last resort**, tier 6.
//!
//! This tier is **sharply distinct** from the tier-5 hole-punch ([`super::hole_punch`]):
//!
//! | Tier | Method | Relay's role | Relay bandwidth |
//! |------|--------|--------------|-----------------|
//! | 5 | [`HolePunchMethod`](super::hole_punch::HolePunchMethod) | **signaling only** — brokers a candidate exchange, then the DATA path is peer-to-peer direct | minimal (a few coordination messages) |
//! | 6 | [`RelayedTransportMethod`] (this) | **carries ALL data** — every byte of the peer connection is proxied through the relay (RLY-002 `relay_message`) | highest — the relay proxies the whole stream |
//!
//! Because tier 6 costs the relay the most bandwidth, it is tried **only after** the tier-5 hole
//! punch fails: prefer brokering an introduction (hole punch) over proxying the stream (TURN). The
//! [`crate::strategy`] enforces this via [`super::TraversalKind::rank`] (HolePunch=4 < Relayed=5).
//!
//! After the relay opens the tunnel, the resulting byte stream is still wrapped in the same mTLS
//! (peer_id = SHA-256(SPKI)) as every other tier — the relay proxies ciphertext it cannot read.
//!
//! The relay data-plane is abstracted behind [`RelayedTransport`] so the method is unit-tested with
//! a mock (no real relay). The production impl — [`ReservationRelayedTransport`] — opens an RLY-002
//! forwarding channel to the target peer THROUGH the node's persistent relay reservation socket
//! (never a second connection), and hands the caller a [`RelayTunnel`] for the byte stream.
use SocketAddr;
use Arc;
use async_trait;
use crateMethodError;
use crate;
use cratePeerTarget;
use crate;
/// Abstraction over the relay **data plane**: open a stream to `target_peer` whose bytes are
/// proxied THROUGH the relay (RLY-002). This is the tier-6 TURN-like fallback — distinct from the
/// tier-5 [`HolePunchCoordinator`](super::hole_punch::HolePunchCoordinator), which only signals.
///
/// Returns the relay endpoint the data flows over (for observability — the mTLS session then runs
/// over that tunnel). `Err` = the relay could not open the forwarding channel (peer offline / relay
/// down / disabled).
/// The tier-6 relayed-transport (TURN-like) method — proxies ALL peer data through the relay. Only
/// reached when every more-direct method (including the tier-5 hole punch) has failed.
/// The PRODUCTION [`RelayedTransport`]: opens the RLY-002 forwarding channel over the node's LIVE
/// persistent relay reservation (never a second socket), reusing the same [`RelayStatus`] handle the
/// reservation loop publishes its outbound sink through. This is the tier-6 TURN fallback made real.
///
/// `open_relayed` (the ladder seam) confirms the reservation is held and the target is reachable via
/// the relay, returning the relay endpoint for observability. The actual byte stream is obtained with
/// [`open_tunnel`](Self::open_tunnel), which yields a [`RelayTunnel`] that forwards A→relay→B; per
/// NC-1 the caller seals every payload to the recipient, so the relay forwards ciphertext only.