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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! # dig-nat — abstract NAT traversal for DIG Node peer connections
//!
//! One API, [`connect`], establishes a **mutually-authenticated (mTLS)** connection to a peer using
//! the best available NAT-traversal method, transparently. **The caller never chooses the method** —
//! they describe the peer once and get back a verified [`PeerConnection`]; which technique got there
//! is reported for observability but is not something the caller handles.
//!
//! ## Traversal order (first success wins, relay last)
//!
//! Internally the [`strategy`] attempts, in this order:
//! 1. **Direct** — peer publicly reachable / already port-forwarded ([`method::direct`])
//! 2. **UPnP/IGD** port mapping ([`method::upnp`])
//! 3. **NAT-PMP** (RFC 6886, [`method::natpmp`])
//! 4. **PCP** (RFC 6887, [`method::pcp`])
//! 5. **Relay-coordinated hole-punch** (RLY-007, [`method::hole_punch`])
//! 6. **Relayed transport** via `relay.dig.net` — the LAST resort ([`relay`])
//!
//! [`stun`] (RFC 5389) discovers this node's reflexive address for candidate advertisement +
//! hole-punch coordination.
//!
//! ## Streaming-first + multiplexed transport
//!
//! Whatever tier establishes the connection, the result is uniform: a [`PeerConnection`] wrapping a
//! single mTLS byte stream in [`yamux`](mux) multiplexing. The caller opens **many cheap concurrent
//! logical streams** ([`PeerConnection::open_stream`]) with no head-of-line blocking, and
//! **byte-range streams** ([`PeerConnection::open_range_stream`]) scoped to `[offset, len)` of a
//! resource — so a downloader fetches DIFFERENT ranges from DIFFERENT peers in parallel and
//! reassembles. The API is streaming (read bytes as they arrive), never buffer-the-whole-response.
//!
//! ## Identity + mTLS — delegated to `dig-tls`
//!
//! Every peer connection is mutual TLS, and the entire certificate model is owned by the canonical
//! [`dig-tls`](dig_tls) crate (L00): the shipped public DigNetwork CA, the per-peer CA-signed
//! [`NodeCert`], `peer_id = SHA-256(TLS SPKI DER)`, the #1204 BLS-G1 cert binding, and the ready
//! rustls mutual-auth configs. dig-nat presents this node's [`NodeCert`] and uses
//! [`dig_tls::client_config`] to pin the remote's `peer_id` to the [`peer::PeerTarget::peer_id`] the
//! caller asked for — so the transport is self-authenticating. dig-nat holds NO cert/binding/peer_id
//! code of its own (it was extracted to dig-tls in 0.6.0); the names below are re-exports for
//! convenience.
//!
//! ## Graceful fallback + relay resilience
//!
//! Each method is bounded by a per-method timeout; if ALL fail, [`connect`] returns a clear
//! [`NatError::AllMethodsFailed`] (never panics, never hangs). The [`relay`] client — used both as
//! the last-resort transport and as a node's persistent reachability channel — establishes and
//! maintains its session with keepalive + capped-exponential-backoff reconnect, tolerates the relay
//! being down (retries in the background, never crashes the node), logs once per state change, and
//! honours the `DIG_RELAY_URL=off` opt-out. See [`relay::RelayStatus`].
//!
//! ## Example
//!
//! ```no_run
//! # use std::sync::Arc;
//! # use dig_nat::{connect, NatConfig, NodeCert, PeerTarget, PeerId};
//! # async fn run(node: Arc<NodeCert>, peer_id: PeerId, addr: std::net::SocketAddr) -> Result<(), dig_nat::NatError> {
//! let peer = PeerTarget::with_addr(peer_id, addr, "DIG_MAINNET");
//! let conn = connect(&peer, &node, &NatConfig::default()).await?;
//! println!("connected to {} via {:?}", conn.peer_id, conn.method);
//! # Ok(()) }
//! ```
use Arc;
// --- Certificate / mTLS / identity model — re-exported from the canonical `dig-tls` crate (L00).
// dig-nat CONSUMES dig-tls for ALL of these (it holds no copy of its own), so a single source of
// truth means the DIG cert shape can never byte-drift between crates. ---
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
use MtlsDialer;
use DirectMethod;
/// Establish a mutually-authenticated connection to `peer`, choosing the traversal method
/// transparently (first success wins; relay is the last resort).
///
/// `node` is this node's [`NodeCert`] — its CA-signed mTLS identity from [`dig-tls`](dig_tls),
/// presented as the client certificate; `config` selects which methods are enabled, the per-method
/// timeout, the relay/STUN endpoints, and the [`BindingPolicy`] applied to the peer's #1204 cert
/// binding. On success the returned [`PeerConnection`] carries the verified remote `peer_id`, the
/// [`TraversalKind`] that established it, the peer's bound BLS pubkey (when present), and the
/// authenticated stream.
///
/// # Errors
/// - [`NatError::NoMethodsEnabled`] — the config enabled no methods.
/// - [`NatError::AllMethodsFailed`] — every enabled method failed (with per-method reasons).
/// - [`NatError::InvalidConfig`] — the relay/STUN config could not be used.
///
/// This function never panics and never hangs: every method (and its dial) is bounded by
/// [`NatConfig::per_method_timeout`].
pub async
/// Assemble the enabled [`TraversalMethod`] trait objects for a config.
///
/// NOTE: the UPnP/NAT-PMP/PCP/hole-punch methods need runtime inputs the caller has not yet supplied
/// through the current minimal config surface (gateway address, this node's local port + reflexive
/// address, a live relay coordinator). Until those are wired through the builder, `connect` composes
/// the methods it can construct from the config alone — currently the always-available **Direct**
/// method. The other methods are fully implemented + tested and are composed explicitly by callers
/// (e.g. `dig-node`) that hold that runtime context, via [`strategy::connect_with_strategy`]. This
/// keeps `connect` honest (it never claims a method it cannot actually run) while the richer
/// auto-composition lands with the discovery inputs.