Skip to main content
An endpoint is the main API interface to create connections to, and accept connections from other iroh endpoints. The connections are peer-to-peer and end-to-end encrypted. Endpoints have a EndpointID (the public half of an Ed25519 keypair) and the private key used to sign and decrypt messages. Generally, an application will have a single endpoint instance. This ensures all the connections made share the same peer-to-peer connections to other iroh endpoints, while still remaining independent connections. This will result in more optimal network behaviour.

Connections

Because we’re in a peer-to-peer context, either endpoint might be operating as the “server”, so we use connect and accept to distinguish between the two. The connect method is used to create a new connection to a remote endpoint, while accept is used to accept incoming connections from a remote endpoint. Connections are full-fledged QUIC connections, giving you access to most features of QUIC / HTTP3, including bidirectional and unidirectional streams. A Relay server can be used to make the connections reliable.
Due to the light-weight properties of QUIC streams a stream can only be accepted once the initiating peer has sent some data on it.

Endpoint Identifiers

Each endpoint in iroh has a unique identifier (EndpointID) created as a cryptographic key. This can be used to globally identify an endpoint. Because EndpointIDs are cryptographic keys, they are also the mechanism by which all traffic is always encrypted for a specific endpoint only. See the EndpointID documentation for more information.

Endpoint Addresses

Endpoint Addresses or EndpointAddrs are a common struct you’ll interact when working with iroh to tell iroh what & where to dial. In rust they look like this:
pub struct Addr {
    pub id: PublicKey,
    pub addrs: BTreeSet<TransportAddr>,
}
You’ll interact with EndpointAddrs a fair amount when working with iroh. It’s also quite normal to construct addresses manually from, say, endpoint identifiers stored in your application database. When we call connect on an Endpoint, we need to pass either a EndpointAddr, or something that can turn into a EndpointAddr. In iroh Endpoints will have different fields populated depending on where they came from, and the discovery services you’ve configured your endpoint with.

Interaction with discovery

From the above struct, the only required field is the id. And because of this, there’s an implementation of From that can turn EndpointIDs directly into EndpointAddrs. but this will only work if you have a discovery service that can resolve EndpointIDs enabled. Thankfully, we enable discovery by default:
use iroh::Endpoint;

// enables dialing by EndpointAddrs that only have EndpointIDs by default:
let ep = Endpoint::builder()
    .bind()
    .await?;
This is why we actively encourage configuring a discovery service, and DNS is the most common one we recommend. Because we’re in p2p land dialing details & even home relays for an endpoint can change on very short notice, making this data go stale quickly. Endpoint Identifiers are a practical source of stability that counteracts this.

When to provide full details

If you have full dialing details, it’s well worth providing them as part of a EndpointAddr passed to connect. Iroh can use this to skip the network roundtrip required to either do initial address discovery, or update cached addresses. So if you have a source of up to date home relay & dialing info, provide it!

Don’t store relay_url & direct_addresses values

If you’re persisting the contents of EndpointAddrs in your app, it’s probably not worth keeping the relay_url and direct_address fields, unless you know these details are unlikely to change. Providing stale details to the endpoint can slow down connection construction.

Building on Endpoints

Endpoints are a low-level primitive that iroh exposes on purpose. For some projects like dumbpipe, endpoints are 95% of what you need to connect any two devices on the planet. For others, like Blobs, endpoints are the foundation that higher-level protocols are built on. Most projects will not work with endpoints beyond constructing one and feeding it to one or more protocols.