Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions connecting/creating-endpoint.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ This method initializes a new endpoint and binds it to a local address, allowing
to listen for incoming connections.

```rust
use iroh::Endpoint;
use iroh::{Endpoint, presets};
use anyhow::Result;

#[tokio::main]
async fn main() {
let endpoint = Endpoint::bind().await?;
async fn main() -> Result<()> {
let endpoint = Endpoint::bind(presets::N0).await?;
// ...
Ok(())
}
```

Expand Down
4 changes: 2 additions & 2 deletions connecting/dns-discovery.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ options](https://cal.com/team/number-0/n0-protocol-services?overlayCalendar=true


```rust
use iroh::Endpoint;
use iroh::{Endpoint, presets};
use iroh_tickets::endpoint::EndpointTicket;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

println!("endpoint id: {:?}", endpoint.id());

Expand Down
4 changes: 2 additions & 2 deletions connecting/gossip.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ There are different ways to structure your application around topics, depending
### Example

```rust
use iroh::{protocol::Router, Endpoint, EndpointId};
use iroh::{protocol::Router, Endpoint, EndpointId, presets};
use iroh_gossip::{api::Event, Gossip, TopicId};
use n0_error::{Result, StdResultExt};
use n0_future::StreamExt;
Expand All @@ -56,7 +56,7 @@ use n0_future::StreamExt;
async fn main() -> Result<()> {
// create an iroh endpoint that includes the standard discovery mechanisms
// we've built at number0
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

// build gossip protocol
let gossip = Gossip::builder().spawn(endpoint.clone());
Expand Down
32 changes: 13 additions & 19 deletions connecting/local-discovery.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
---
title: "mDNS and Bluetooth"
title: "mDNS"
---

Local discovery adds the ability to use physical radios to discover other iroh
endpoints. This is useful for local networks where the internet may not be available or reliable.
The mDNS discovery mechanism will automatically broadcast your endpoint's
presence on the local network, and listen for other endpoints doing the same. When
another endpoint is discovered, the dialing information is exchanged, and a
connection can be established directly over the local network without needing a relay.

Local connections can be faster and more reliable than internet-based connections, especially in
environments with poor connectivity. They also enhance privacy by keeping communications within a local area.
Devices need to be connected to the same local network for mDNS discovery to
work. This can be a Wi-Fi network, an Ethernet network, or even a mobile
hotspot. mDNS is not designed to work over the internet or across different
networks.

## mDNS
## Usage

Local Discovery is _not_ enabled by default, and must be enabled by the user.
Local Discovery is _not_ enabled by default, and must be enabled explicitly.
You'll need to add the `discovery-local-network` feature flag to your
`Cargo.toml` to use it.

Expand All @@ -23,23 +27,13 @@ iroh = { version = "0.nn", features = ["address-lookup-mdns"] }
Then configure your endpoint to use local discovery concurrently with the default DNS discovery:

```rust
use iroh::Endpoint;
use iroh::{Endpoint, presets};

let mdns = iroh::address_lookup::mdns::MdnsAddressLookup::builder();
let ep = Endpoint::builder()
.address_lookup(mdns)
.bind()
.bind(presets::N0)
.await?;
```

The mDNS discovery mechanism will automatically broadcast your endpoint's
presence on the local network, and listen for other endpoints doing the same. When
another endpoint is discovered, the dialing information is exchanged, and a
connection can be established directly over the local network without needing a relay.

For more information on how mDNS discovery works, see the [mDNS documentation](https://docs.rs/iroh/latest/iroh/address_lookup/mdns/index.html).

## Bluetooth

Bluetooth discovery is currently under development and will be available in a
future release of iroh. For more information, please [contact us](https://cal.com/team/number-0/n0-protocol-services).
12 changes: 10 additions & 2 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
]
},
{
"group": "Forming a Network",
"group": "Creating Connections",
"pages": [
"connecting/creating-endpoint",
"connecting/custom-relays",
Expand All @@ -46,7 +46,7 @@
]
},
{
"group": "Building your App",
"group": "Sending Data",
"pages": [
"protocols/kv-crdts",
"protocols/blobs",
Expand All @@ -58,6 +58,14 @@
"protocols/using-quic"
]
},
{
"group": "Transports",
"pages": [
"transports/tor",
"transports/nym",
"transports/bluetooth"
]
},
{
"group": "Deployment",
"pages": [
Expand Down
12 changes: 6 additions & 6 deletions examples/chat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,12 @@ Topics are the fundamental unit of communication in the gossip protocol. Here's
```rust
use anyhow::Result;
use iroh::protocol::Router;
use iroh::Endpoint;
use iroh::{Endpoint, presets};
use iroh_gossip::{net::Gossip, proto::TopicId};

#[tokio::main]
async fn main() -> Result<()> {
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

println!("> our endpoint id: {}", endpoint.id());
let gossip = Gossip::builder().spawn(endpoint.clone());
Expand Down Expand Up @@ -324,7 +324,7 @@ use std::collections::HashMap;
use anyhow::Result;
use futures_lite::StreamExt;
use iroh::protocol::Router;
use iroh::{Endpoint, EndpointId};
use iroh::{Endpoint, EndpointId, presets};
use iroh_gossip::{
api::{GossipReceiver, Event},
net::Gossip,
Expand All @@ -334,7 +334,7 @@ use serde::{Deserialize, Serialize};

#[tokio::main]
async fn main() -> Result<()> {
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

println!("> our endpoint id: {}", endpoint.id());
let gossip = Gossip::builder().spawn(endpoint.clone());
Expand Down Expand Up @@ -535,7 +535,7 @@ use std::{collections::HashMap, fmt, str::FromStr};
use anyhow::Result;
use clap::Parser;
use futures_lite::StreamExt;
use iroh::{protocol::Router, Endpoint, EndpointAddr, EndpointId};
use iroh::{protocol::Router, Endpoint, EndpointAddr, EndpointId, presets};
use iroh_gossip::{
api::{GossipReceiver, Event},
net::Gossip,
Expand Down Expand Up @@ -591,7 +591,7 @@ async fn main() -> Result<()> {
}
};

let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

println!("> our endpoint id: {}", endpoint.id());
let gossip = Gossip::builder().spawn(endpoint.clone());
Expand Down
4 changes: 2 additions & 2 deletions protocols/blobs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ This is what manages the possibly changing network underneath, maintains a conne
async fn main() -> anyhow::Result<()> {
// Create an endpoint, it allows creating and accepting
// connections in the iroh p2p world
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

// ...

Expand All @@ -124,7 +124,7 @@ It loads files from your file system and provides a protocol for seekable, resum
async fn main() -> anyhow::Result<()> {
// Create an endpoint, it allows creating and accepting
// connections in the iroh p2p world
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(presets::N0).await?;

// We initialize an in-memory backing store for iroh-blobs
let store = MemStore::new();
Expand Down
18 changes: 9 additions & 9 deletions protocols/rpc.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ But we said that we wanted to be able to seamlessly switch between remote or loc
```rust
enum Client {
Local(mpsc::Sender<FullRequest>),
Remote(quinn::Connection),
Remote(noq::Connection),
}

impl Client {
Expand Down Expand Up @@ -206,7 +206,7 @@ But what about all this boilerplate?

**The `irpc` crate is meant solely to reduce the tedious boilerplate involved in writing the above manually.**

It does *not* abstract over the connection type - it only supports [iroh-quinn] send and receive streams out of the box, so the only two possible connection types are `iroh` p2p QUIC connections and normal QUIC connections. It also does not abstract over the local channel type - a local channel is always a `tokio::sync::mpsc` channel. Serialization is always using postcard and length prefixes are always postcard varints.
It does *not* abstract over the connection type - it only supports [noq] QUIC send and receive streams out of the box, so the only two possible connection types are `iroh` p2p QUIC connections and normal QUIC connections. It also does not abstract over the local channel type - a local channel is always a `tokio::sync::mpsc` channel. Serialization is always using postcard and length prefixes are always postcard varints.

So let's see what our kv service looks like using `irpc`:

Expand Down Expand Up @@ -286,8 +286,8 @@ converting the result into a futures `Stream` or the updates into a futures
services that can be used in-process or across processes, not to provide an
opinionated high level API.

For stream based rpc calls, there is an issue you should be aware of. The quinn
`SendStream` will send a finish message when dropped. So if you have a finite
For stream based rpc calls, there is an issue you should be aware of. The noq
QUIC `SendStream` will send a finish message when dropped. So if you have a finite
stream, you might want to have an explicit end marker that you send before
dropping the sender to allow the remote side to distinguish between successful
termination and abnormal termination. E.g. the `SetFromStream` request from
Expand Down Expand Up @@ -330,9 +330,9 @@ If you are reading from a remote source, and there is a problem with the connect

But what about writing? E.g. you got a task that performs an expensive computation and writes updates to the remote in regular intervals. You will only detect that the remote side is gone once you write, so if you write infrequently you will perform an expensive computation despite the remote side no longer being available or interested.

To solve this, an irpc Sender has a [closed](https://docs.rs/irpc/0.5.0/irpc/channel/mpsc/enum.Sender.html#method.closed) function that you can use to detect the remote closing without having to send a message. This wraps [tokio::sync::mpsc::Sender::closed](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html#method.closed) for local streams and [quinn::SendStream::stopped](https://docs.rs/iroh-quinn/latest/iroh_quinn/struct.SendStream.html#method.stopped) for remote streams.
To solve this, an irpc Sender has a [closed](https://docs.rs/irpc/0.5.0/irpc/channel/mpsc/enum.Sender.html#method.closed) function that you can use to detect the remote closing without having to send a message. This wraps [tokio::sync::mpsc::Sender::closed](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html#method.closed) for local streams and [noq::SendStream::stopped](https://github.com/n0-computer/noq) for remote QUIC streams.

## Alternatives to iroh-quinn
## Alternatives to noq

If you integrate iroh protocols into an existing application, it could be that you already have a rpc system that you are happy with, like [grpc](https://grpc.io/) or [json-rpc](https://www.jsonrpc.org/).

Expand Down Expand Up @@ -361,9 +361,9 @@ and maintained.
## References

- [postcard](https://docs.rs/postcard/latest/postcard/)
- [iroh-quinn](https://docs.rs/iroh-quinn/latest/iroh_quinn/)
- [RecvStream](https://docs.rs/iroh-quinn/latest/iroh_quinn/struct.RecvStream.html)
- [SendStream](https://docs.rs/iroh-quinn/latest/iroh_quinn/struct.SendStream.html)
- [noq](https://github.com/n0-computer/noq)
- [RecvStream](https://github.com/n0-computer/noq)
- [SendStream](https://github.com/n0-computer/noq)
- [Stream](https://docs.rs/futures/latest/futures/prelude/trait.Stream.html)
- [Sink](https://docs.rs/futures/latest/futures/sink/trait.Sink.html)
- [snafu](https://docs.rs/snafu/latest/snafu/)
Expand Down
23 changes: 20 additions & 3 deletions protocols/using-quic.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,26 @@
title: "Using QUIC"
---

## Why this matters for iroh
Every endpoint uses QUIC over UDP by default — no configuration required.

iroh is built on top of QUIC, providing connectivity, NAT traversal, and encrypted connections out of the box. While iroh handles the hard parts of networking—holepunching, relay servers, and discovery—**you still need to design how your application exchanges data once connected**.
iroh's QUIC implementation is built on
[noq](https://github.com/n0-computer/noq), which includes multipath support and
QUIC NAT traversal.

All connections are encrypted and authenticated using TLS 1.3. Holepunching,
relay fallback, and multipath are all handled at the QUIC layer automatically.

## Custom transports

QUIC over UDP is the default, but iroh supports plugging in additional custom
transports alongside it.

All transports, even custom transports [Tor](/transports/tor), [Nym](/transports/nym), and
[Bluetooth](/transports/bluetooth) deliver QUIC datagrams.

## Using QUIC

While iroh handles the hard parts of networking—holepunching, relay servers, and discovery—**you still need to design how your application exchanges data once connected**.

Many developers reach for iroh expecting it to completely abstract away the underlying transport. However, iroh intentionally exposes QUIC's powerful stream API because:

Expand All @@ -15,7 +32,7 @@ Many developers reach for iroh expecting it to completely abstract away the unde
Think of iroh as giving you **reliable, secure tunnels between peers**. This guide shows you how to use QUIC's streaming patterns to build efficient protocols inside those tunnels. Whether you're adapting an existing protocol or designing something new, understanding these patterns will help you make the most of iroh's capabilities.

<Note>
iroh uses a fork of [Quinn](https://docs.rs/iroh-quinn/latest/iroh_quinn/), a pure-Rust implementation of QUIC maintained by [n0.computer](https://n0.computer). Quinn is production-ready, actively maintained, and used by projects beyond iroh. If you need lower-level QUIC access or want to understand the implementation details, check out the [Quinn documentation](https://docs.rs/iroh-quinn/latest/iroh_quinn/).
iroh uses [noq](https://github.com/n0-computer/noq), a pure-Rust QUIC implementation maintained by [n0.computer](https://n0.computer). noq is production-ready, actively maintained, and used by projects beyond iroh. If you need lower-level QUIC access or want to understand the implementation details, check out the [noq repository](https://github.com/n0-computer/noq).
</Note>


Expand Down
4 changes: 2 additions & 2 deletions protocols/writing-a-protocol.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Now, we can modify our router so it handles incoming connections with our newly

```rs
async fn start_accept_side() -> anyhow::Result<iroh::protocol::Router> {
let endpoint = iroh::Endpoint::bind().await?;
let endpoint = iroh::Endpoint::bind(iroh::presets::N0).await?;

let router = iroh::protocol::Router::builder(endpoint)
.accept(ALPN, Echo) // This makes the router handle incoming connections with our ALPN via Echo::accept!
Expand Down Expand Up @@ -164,7 +164,7 @@ This follows the [request-response pattern](/protocols/using-quic#request-and-re

```rs
async fn connect_side(addr: EndpointAddr) -> Result<()> {
let endpoint = Endpoint::bind().await?;
let endpoint = Endpoint::bind(iroh::presets::N0).await?;

// Open a connection to the accepting endpoint
let conn = endpoint.connect(addr, ALPN).await?;
Expand Down
Loading
Loading