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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
//! Peer-to-peer QUIC connections.
//!
//! iroh is a library to establish direct connectivity between peers. It exposes an
//! interface to [QUIC] connections and streams to the user, while implementing direct
//! connectivity using [hole punching] complemented by relay servers under the hood.
//!
//! An iroh endpoint is created and controlled by the [`Endpoint`], e.g. connecting to
//! another endpoint:
//!
//! ```no_run
//! # #[cfg(with_crypto_provider)]
//! # {
//! # use iroh::{Endpoint, EndpointAddr, endpoint::presets};
//! # use n0_error::{StackResultExt, StdResultExt};
//! # async fn wrapper() -> n0_error::Result<()> {
//! let addr: EndpointAddr = todo!();
//! let ep = Endpoint::bind(presets::N0).await?;
//! let conn = ep.connect(addr, b"my-alpn").await?;
//! let mut send_stream = conn.open_uni().await.std_context("unable to open uni")?;
//! send_stream
//! .write_all(b"msg")
//! .await
//! .std_context("unable to write all")?;
//! # Ok(())
//! # }
//! # }
//! ```
//!
//! The other endpoint can accept incoming connections using the [`Endpoint`] as well:
//!
//! ```no_run
//! # #[cfg(with_crypto_provider)]
//! # {
//! # use iroh::{Endpoint, EndpointAddr, endpoint::presets};
//! # use n0_error::{StackResultExt, StdResultExt};
//! # async fn wrapper() -> n0_error::Result<()> {
//! let ep = Endpoint::builder(presets::N0)
//! .alpns(vec![b"my-alpn".to_vec()])
//! .bind()
//! .await?;
//! let conn = ep
//! .accept()
//! .await
//! .context("accept error")?
//! .await
//! .std_context("connecting error")?;
//! let mut recv_stream = conn.accept_uni().await.std_context("unable to open uni")?;
//! let mut buf = [0u8; 3];
//! recv_stream
//! .read_exact(&mut buf)
//! .await
//! .std_context("unable to read")?;
//! # Ok(())
//! # }
//! # }
//! ```
//!
//! Of course you can also use [bi-directional streams] or any other features from QUIC.
//!
//! For more elaborate examples, see [below](#examples) or the examples directory in
//! the source repository.
//!
//!
//! # Connection Establishment
//!
//! An iroh connection between two iroh endpoints is usually established with the help
//! of a Relay server. When creating the [`Endpoint`] it connects to the closest Relay
//! server and designates this as the *home relay*. When other endpoints want to connect they
//! first establish connection via this home relay. As soon as connection between the two
//! endpoints is established they will attempt to create a direct connection, using [hole
//! punching] if needed. Once the direct connection is established the relay server is no
//! longer involved in the connection.
//!
//! If one of the iroh endpoints can be reached directly, connectivity can also be
//! established without involving a Relay server. This is done by using the endpoint's
//! listening addresses in the connection establishement instead of the [`RelayUrl`] which
//! is used to identify a Relay server. Of course it is also possible to use both a
//! [`RelayUrl`] and direct addresses at the same time to connect.
//!
//!
//! # Encryption
//!
//! The connection is encrypted using TLS, like standard QUIC connections. Unlike standard
//! QUIC there is no client, server or server TLS key and certificate chain. Instead each iroh endpoint has a
//! unique [`SecretKey`] used to authenticate and encrypt the connection. When an iroh
//! endpoint connects, it uses the corresponding [`PublicKey`] to ensure the connection is only
//! established with the intended peer.
//!
//! Since the [`PublicKey`] is also used to identify the iroh endpoint it is also known as
//! the [`EndpointId`]. As encryption is an integral part of TLS as used in QUIC this
//! [`EndpointId`] is always a required parameter to establish a connection.
//!
//! When accepting connections the peer's [`EndpointId`] is authenticated. However it is up to
//! the application to decide if a particular peer is allowed to connect or not.
//!
//!
//! # Relay Servers
//!
//! Relay servers exist to ensure all iroh endpoints are always reachable. They accept
//! **encrypted** traffic for iroh endpoints which are connected to them, forwarding it to
//! the correct destination based on the [`EndpointId`] only. Since endpoints only send encrypted
//! traffic, the Relay servers can not decode any traffic for other iroh endpoints and only
//! forward it.
//!
//! The connections to the Relay server are initiated as normal HTTP 1.1 connections using
//! TLS. Once connected the transport is upgraded to a plain TCP connection using a custom
//! protocol. All further data is then sent using this custom relaying protocol. Usually
//! soon after the connection is established via the Relay it will migrate to a direct
//! connection. However if this is not possible the connection will keep flowing over the
//! relay server as a fallback.
//!
//! Additionally to providing reliable connectivity between iroh endpoints, Relay servers
//! provide some functions to assist in [hole punching]. They have various services to help
//! endpoints understand their own network situation. This includes offering a [QAD] server,
//! but also a few HTTP extra endpoints as well as responding to ICMP echo requests.
//!
//! By default the [number 0] relay servers are used, see [`RelayMode::Default`].
//!
//!
//! # Connections and Streams
//!
//! An iroh endpoint is managed using the [`Endpoint`] and this is used to create or accept
//! connections to other endpoints. To establish a connection to an iroh endpoint you need to
//! know three pieces of information:
//!
//! - The [`EndpointId`] of the peer to connect to.
//! - Some addressing information:
//! - Usually the [`RelayUrl`] identifying the Relay server.
//! - Sometimes, or usually additionally, any direct addresses which might be known.
//! - The QUIC/TLS Application-Layer Protocol Negotiation, or [ALPN], name to use.
//!
//! The ALPN is used by both sides to agree on which application-specific protocol will be
//! used over the resulting QUIC connection. These can be protocols like `h3` used for
//! [HTTP/3][HTTP3], but more commonly will be a custom identifier for the application.
//!
//! Once connected the API exposes QUIC streams. These are very cheap to create so can be
//! created at any time and can be used to create very many short-lived stream as well as
//! long-lived streams. There are two stream types to choose from:
//!
//! - **Uni-directional** which only allows the peer which initiated the stream to send
//! data.
//!
//! - **Bi-directional** which allows both peers to send and receive data. However, the
//! initiator of this stream has to send data before the peer will be aware of this
//! stream.
//!
//! Additionally to being extremely light-weight, streams can be interleaved and will not
//! block each other. Allowing many streams to co-exist, regardless of how long they last.
//!
//! <div class="warning">
//!
//! To keep streams cheap, they are lazily created on the network: only once a sender starts
//! sending data on the stream will the receiver become aware of a stream. This means only
//! calling [`Connection::open_bi`] is not sufficient for the corresponding call to
//! [`Connection::accept_bi`] to return. The sender **must** send data on the stream before
//! the receiver's [`Connection::accept_bi`] call will return.
//!
//! </div>
//!
//! ## Address Lookup
//!
//! The need to know the [`RelayUrl`] *or* some direct addresses in addition to the
//! [`EndpointId`] to connect to an iroh endpoint can be an obstacle. To address this, the
//! [`endpoint::Builder`] allows you to configure an [`address_lookup`] service.
//!
//! The [`address_lookup::DnsAddressLookup`] service is an address lookup service which will publish the [`RelayUrl`]
//! and direct addresses to a service publishing those as DNS records. To connect it looks
//! up the [`EndpointId`] in the DNS system to find the addressing details. This enables
//! connecting using only the [`EndpointId`] which is often more convenient and resilient.
//!
//! See [the Address Lookup module] for more details.
//!
//!
//! # Examples
//!
//! The central struct is the [`Endpoint`], which allows you to connect to other endpoints:
//!
//! ```no_run
//! # #[cfg(with_crypto_provider)]
//! # {
//! use iroh::{Endpoint, EndpointAddr, endpoint::presets};
//! use n0_error::{Result, StackResultExt, StdResultExt};
//!
//! async fn connect(addr: EndpointAddr) -> Result<()> {
//! // The Endpoint is the central object that manages an iroh node.
//! let ep = Endpoint::bind(presets::N0).await?;
//!
//! // Establish a QUIC connection, open a bi-directional stream, exchange messages.
//! let conn = ep.connect(addr, b"hello-world").await?;
//! let (mut send_stream, mut recv_stream) = conn.open_bi().await.std_context("open bi")?;
//! send_stream.write_all(b"hello").await.std_context("write")?;
//! send_stream.finish().std_context("finish")?;
//! let _msg = recv_stream.read_to_end(10).await.std_context("read")?;
//!
//! // Gracefully close the connection and endpoint.
//! conn.close(1u8.into(), b"done");
//! ep.close().await;
//! println!("Client closed");
//! Ok(())
//! }
//! # }
//! ```
//!
//! Every [`Endpoint`] can also accept connections:
//!
//! ```no_run
//! # #[cfg(with_crypto_provider)]
//! # {
//! use iroh::{Endpoint, EndpointAddr, endpoint::presets};
//! use n0_error::{Result, StackResultExt, StdResultExt};
//! use n0_future::StreamExt;
//!
//! async fn accept() -> Result<()> {
//! // To accept connections at least one ALPN must be configured.
//! let ep = Endpoint::builder(presets::N0)
//! .alpns(vec![b"hello-world".to_vec()])
//! .bind()
//! .await?;
//!
//! // Accept a QUIC connection, accept a bi-directional stream, exchange messages.
//! let conn = ep
//! .accept()
//! .await
//! .context("no incoming connection")?
//! .await
//! .context("accept conn")?;
//! let (mut send_stream, mut recv_stream) =
//! conn.accept_bi().await.std_context("accept stream")?;
//! let _msg = recv_stream.read_to_end(10).await.std_context("read")?;
//! send_stream.write_all(b"world").await.std_context("write")?;
//! send_stream.finish().std_context("finish")?;
//!
//! // Wait for the client to close the connection and gracefully close the endpoint.
//! conn.closed().await;
//! ep.close().await;
//! Ok(())
//! }
//! # }
//! ```
//!
//! Please see the examples directory for more nuanced examples.
//!
//!
//! [QUIC]: https://quicwg.org
//! [bi-directional streams]: crate::endpoint::Connection::open_bi
//! [hole punching]: https://en.wikipedia.org/wiki/Hole_punching_(networking)
//! [socket addresses]: https://doc.rust-lang.org/stable/std/net/enum.SocketAddr.html
//! [QAD]: https://www.ietf.org/archive/id/draft-ietf-quic-address-discovery-00.html
//! [ALPN]: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation
//! [HTTP3]: https://en.wikipedia.org/wiki/HTTP/3
//! [`SecretKey`]: crate::SecretKey
//! [`PublicKey`]: crate::PublicKey
//! [`RelayUrl`]: crate::RelayUrl
//! [`address_lookup`]: crate::endpoint::Builder::address_lookup
//! [`address_lookup::DnsAddressLookup`]: crate::address_lookup::DnsAddressLookup
//! [number 0]: https://n0.computer
//! [`RelayMode::Default`]: crate::RelayMode::Default
//! [the Address Lookup module]: crate::address_lookup
//! [`Connection::open_bi`]: crate::endpoint::Connection::open_bi
//! [`Connection::accept_bi`]: crate::endpoint::Connection::accept_bi
pub
pub
pub
pub use ;
pub use ;
pub use dns;
pub use ;
pub use Watcher;
pub use ;