|
| 1 | +import { BlogPostLayout } from '@/components/BlogPostLayout' |
| 2 | +import { MotionCanvas } from '@/components/MotionCanvas' |
| 3 | + |
| 4 | +export const post = { |
| 5 | + draft: false, |
| 6 | + author: 'ramfox', |
| 7 | + date: '2025-10-22', |
| 8 | + title: 'iroh 0.94.0 - The Endpoint Takeover', |
| 9 | + description: 'Release of iroh v0.94', |
| 10 | +} |
| 11 | + |
| 12 | +export const metadata = { |
| 13 | + title: post.title, |
| 14 | + description: post.description, |
| 15 | + openGraph: { |
| 16 | + title: post.title, |
| 17 | + description: post.description, |
| 18 | + images: [{ |
| 19 | + url: `/api/og?title=Blog&subtitle=${post.title}`, |
| 20 | + width: 1200, |
| 21 | + height: 630, |
| 22 | + alt: post.title, |
| 23 | + type: 'image/png', |
| 24 | + }], |
| 25 | + type: 'article' |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +export default (props) => <BlogPostLayout article={post} {...props} /> |
| 30 | + |
| 31 | +Welcome to a new release of `iroh`, a library for building direct connections between devices, putting more control in the hands of your users. |
| 32 | + |
| 33 | +We have a bunch of API changes and features for you in this one, all of which are part of the push toward a 1.0 API: |
| 34 | + |
| 35 | +* We've completed the transition away from iroh `node`, now referring to everything as `endpoints`. |
| 36 | + |
| 37 | +* You can now add and remove `Relay`s to and from your endpoint dynamically, even after the endpoint has already been spawned. |
| 38 | + |
| 39 | +* A new `Presets` API for endpoint construction. |
| 40 | + |
| 41 | +* New defaults for Discovery |
| 42 | + |
| 43 | +* Updated `EndpointAddr` (rpPreviously `NodeAddr`) to support additional transports |
| 44 | + |
| 45 | +* `iroh-base::ticket` has moved to its own `iroh-tickets` crate! |
| 46 | + |
| 47 | +**Public Relay Versions & Lifecycle:** |
| 48 | + |
| 49 | +The current canary relays are on version `0.94`. These are compatible with `iroh` `0.93`. However, we *are* still running relays that are compatible with `0.91` and `0.92`, and the default relays for these versions are still valid. `0.90` is no longer supported. We will continue to run `0.35` nodes until 3 months after 1.0 comes out. |
| 50 | + |
| 51 | +Finally, we have some updates about the 1.0 roadmap at the [end of this post](#roadmap). |
| 52 | + |
| 53 | +## ➡️ Changing from `Node` to `Endpoint` everywhere |
| 54 | + |
| 55 | +We’ve officially made the decision to remove the word “node” from our vocabulary. It’s a hold over, from when `iroh` was trying to be more than just a networking library. `iroh` doesn’t have “nodes” anymore, `iroh` is represented by endpoints! |
| 56 | + |
| 57 | +This shouldn’t be too big a shift if you think about the way you are currently using `iroh`; the main entry point into the codebase is `iroh::Endpoint`. However, although we changed from thinking about `iroh` in terms of endpoints rather than nodes a while back, we never changed much of our internal language to reflect that. |
| 58 | + |
| 59 | +In preparation for 1.0, that has been rectified. |
| 60 | + |
| 61 | +For the most part, this is a search-and-replace change in our structs from using the word `Node` to using the word `Endpoint`, (eg, `NodeAddr` → `EndpointAddr`), and changing from `node_id` to `endpoint_id`. |
| 62 | + |
| 63 | +There are a few exceptions worth noting: |
| 64 | + |
| 65 | +- `RelayNode` is now `RelayConfig`, which honestly makes more sense anyway. |
| 66 | +- `Endpoint::node_id` is now `Endpoint::id` . |
| 67 | +- `Endpoint::node_addr` is now `Endpoint::addr` . |
| 68 | + |
| 69 | +For specific changes, check out the [breaking changes](#breaking-changes) section below. |
| 70 | + |
| 71 | +## 🌉 Dynamic `RelayMap`s! |
| 72 | + |
| 73 | +Previously, you could only add custom relays once, while building the `Endpoint`. Also, once they were set, they couldn’t be removed. |
| 74 | + |
| 75 | +Now we have `Endpoint::insert_relay` and `Endpoint::remove_relay` that allow you to dynamically adjust your relay map. This will trigger another net-report to allow your endpoint to gather new latency information from this relay. |
| 76 | + |
| 77 | +This prepares us for later features where relays are created or destroyed while an endpoint is running. Now that endpoints can modify their set of relays, we can build relay networks that dynamically scale. |
| 78 | + |
| 79 | +## 🕵️ `Discovery` defaults and Presets |
| 80 | + |
| 81 | +Previously, the `Endpoint::builder` was a blank canvas, and if you wanted to add a discovery service you needed to do it through one of our helper functions on the builder. |
| 82 | + |
| 83 | +A few things have changed since then. It is now WAY easier to add custom or n0-written discovery services, you can add them to the builder using `EndpointBuilder::discovery` or add them to the endpoint after it has been created using `Endpoint::discovery().add` (you can also remove all service using `Endpoint::discovery().empty` and add back the ones you want afterwards). |
| 84 | + |
| 85 | +So now, the `Endpoint::builder` will, by default, set you up with what was previously set when running `discovery_n0` (`PkarrDiscovery` and `DnsDiscovery`). |
| 86 | + |
| 87 | +But what if you don’t want the default? Easy! You have two options. You can build from the `Endpoint::empty_builder` or use the new `EndpointBuilder::presets` method and `Preset` trait. |
| 88 | + |
| 89 | +```rust |
| 90 | +// build your custom discovery service |
| 91 | +let cool_discovery = CoolDiscovery::new(); |
| 92 | +let endpoint = Endpoint::empty_builder(RelayMode::Disabled) |
| 93 | + .discovery(cool_discovery) |
| 94 | + .bind() |
| 95 | + .await?; |
| 96 | +``` |
| 97 | + |
| 98 | +The `empty_builder` method requires a `RelayMode`. By default, `Endpoint::builder` sets the relay mode to `RelayMode::Default`, which uses the n0 public relays. You can also disable any relaying (`RelayMode::Disabled`), or add a set of custom relays (`RelayMode::Custom(_)` ). |
| 99 | + |
| 100 | +Let’s say you create a lot of endpoints, or you have a few different categories of the types of endpoints that you create (the most basic would be production vs testing, for example). |
| 101 | + |
| 102 | +You can use presets to write blanket configuration for a type of endpoint. You do this by implementing the `Preset` trait on a custom struct. Here is a simplified version of the `NO` preset that we use in our defaults: |
| 103 | + |
| 104 | +```rust |
| 105 | +use crate::discovery::pkarr::PkarrPublisher; |
| 106 | +use crate::discovery::dns::DnsDiscovery; |
| 107 | + |
| 108 | +#[derive(Debug, Copy, Clone, Default)] |
| 109 | +pub struct N0; |
| 110 | + |
| 111 | +impl Preset for N0 { |
| 112 | + fn apply(self, mut builder: Builder) -> Builder { |
| 113 | + // `Preset::apply` is additive, so any other presets added before |
| 114 | + // will still exist, unless overridden. |
| 115 | + builder = builder.clear_discovery(); |
| 116 | + // Publish using HTTPS requests to our DNS server's /pkarr path |
| 117 | + builder = builder.discovery(PkarrPublisher::n0_dns()); |
| 118 | + // Resolve using DNS queries outside browsers. |
| 119 | + builder = builder.discovery(DnsDiscovery::n0_dns()); |
| 120 | + builder = builder.relay_mode(RelayMode::Default); |
| 121 | + |
| 122 | + builder |
| 123 | + } |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +Then, when you go to build the endpoint: |
| 128 | + |
| 129 | +```rust |
| 130 | +use iroh::presets::N0; |
| 131 | + |
| 132 | +let endpoint = Endpoint::builder().preset(NO).bind().await?; |
| 133 | +``` |
| 134 | + |
| 135 | +And now you have an endpoint with all the endpoint configuration you want. |
| 136 | + |
| 137 | +**Presets are used for more than just `Discovery`, any config you normally add to the builder can be added in a preset!** |
| 138 | + |
| 139 | +## 🤖 Future proofing: Introducing `TransportAddr` |
| 140 | + |
| 141 | +Let’s talk about the future for a second, a future even beyond `iroh` `1.0`. We want to be able to add more transports than just UDP/IP traffic. WebRTC would be incredibly useful, for example. We will be able to develop this once we switch to QUIC multipath. |
| 142 | + |
| 143 | +But in order to pave the way for that future, we need to get our internals set up well now. One of those internals is how we represent addresses in our `EndpointAddr` (formerly `NodeAddr`). Before, we only had two hardcoded possibilities for transports. Your connection could be going over a relay, which we identify using `RelayUrl`s, or over UDP on IPv4 or Ipv6, which we identify using `SocketAddr`s. These were represented on the `EndpointAddr` as `EndpointAddr::relay_url: Option<RelayUrl>` and `EndpointAddr::direct_addresses: BTreeSet<SocketAddr>` . |
| 144 | + |
| 145 | +To combine them, and to allow for additions in the future, they are now represented as variants on a `TransportAddr`: |
| 146 | + |
| 147 | +```rust |
| 148 | +#[non_exhaustive] |
| 149 | +pub enum TransportAddr { |
| 150 | + Relay(RelayUrl), |
| 151 | + Ip(SocketAddr), |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +This effects the `EndpointAddr`, which now only has two fields: |
| 156 | + |
| 157 | +```rust |
| 158 | +pub struct EndpointAddr { |
| 159 | + pub id: PublicKey, |
| 160 | + pub addrs: BTreeSet<TransportAddr>, |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +To help ease the transition, we have two additional methods on `EndpointAddr`: |
| 165 | + |
| 166 | +```rust |
| 167 | +pub fn ip_addrs(&self) -> impl Iterator<Item = &SocketAddr> |
| 168 | + |
| 169 | +pub fn relay_urls(&self) -> impl Iterator<Item = &RelayUrl> |
| 170 | +``` |
| 171 | + |
| 172 | +This replaces `NodeAddr::direct_addresses` almost directly and `NodeAddr::relay_url` pretty directly as well. |
| 173 | + |
| 174 | +Currently, your endpoint will only ever have one `RelayUrl`. This may change in the future (post 1.0) if we add relays with different transports or protocols. But, for now, any instance of `NodeAddr::relay_url() -> Option<RelayUrl>` , can be directly replaced with `EndpointAddr::relay_urls().next()`, which will return an `Option<RelayUrl>`. |
| 175 | + |
| 176 | +## 🎫 `iroh-tickets` - removing `Ticket` from `iroh-base` |
| 177 | + |
| 178 | +Ahhh, tickets. So convenient! We love using tickets in our `iroh-examples` and `iroh-experiments`, as well as in our protocols. We encourage folks to use them when needing to serialize the information your protocol or project needs to make sure you can get connections happening between your endpoints. |
| 179 | + |
| 180 | +However, they were not at the right level in our codebase, and are really something that builds on top of `iroh`, rather than something that belongs in `iroh-base`. They now have their own crate `iroh-tickets`! |
| 181 | + |
| 182 | +<a name="breaking-changes"></a> |
| 183 | +## ⚠️ Breaking Changes |
| 184 | + |
| 185 | +- `iroh` |
| 186 | + - renamed |
| 187 | + - `iroh_relay::RelayNode` -> `RelayConfig` |
| 188 | + - `iroh_base::NodeAddr` -> `EndpointAddr` |
| 189 | + - `iroh_base::NodeAddr.node_id` -> `endpoint_id` |
| 190 | + - `iroh_base::NodeId` -> `EndpointId` |
| 191 | + - `iroh_base::NodeTicket` -> `EndpointTicket` |
| 192 | + - `iroh::Endpoint::node_addr` -> `iroh::Endpoint::addr` |
| 193 | + - `iroh::Endpoint::watch_node_addr` -> `iroh::Endpoint::watchaddr` |
| 194 | + - `iroh::Endpoint::node_id` -> `iroh::Endpoint::id` |
| 195 | + - `iroh_relay::RelayMap::urls` |
| 196 | + - `iroh_relay::RelayMap::nodes` |
| 197 | + - `iroh_relay::RelayMap::get_node` |
| 198 | + - `direct_addresses` are now called `ip_addresses` |
| 199 | + - `EndpointAddr` type is completely changed |
| 200 | + - Use `EndpointAddr::ip_addrs` to get a list of ip addresses for this endpoint |
| 201 | + - Use `EndpointAddr::relay_urls` to get a list of relay urls for this endpoint |
| 202 | + - currently, there will only be one relay url per endpoint, but in the future, beyond 1.0, we will potentially have multiple |
| 203 | + - APIs with `addresse(s)` are now normalized to `addr(s)` to be consistent in the code base |
| 204 | + - removed |
| 205 | + - `iroh::Endpoint::add_node_addr_with_source` is removed. Use a discovery service instead. |
| 206 | + - added |
| 207 | + - `iroh_base::Signature` which replaces `ed25519_dalek::Signature` in the public API of `iroh_base` |
| 208 | + - `iroh::Endpoint::insert_relay` |
| 209 | + - `iroh::Endpoint::remove_relay` |
| 210 | + - `iroh::Endpoint::RelayMap::insert` |
| 211 | + - `iroh::Endpoint::RelayMap::remove` |
| 212 | +- `iroh-relay` |
| 213 | + - wire-level breaking change |
| 214 | + - `iroh-relay` servers can no longer issue captive portal challenges to pre `0.93` iroh nodes |
| 215 | +- `iroh-base` |
| 216 | + - changed |
| 217 | + - `iroh_base` error types have changed |
| 218 | + - removed |
| 219 | + - `Into` and `From` conversions for `PublicKey` - `ed25519_dalek::VerifyingKey` |
| 220 | + - `Into` and `From` conversions for `SecretKey` - `ed25519_dalek::SigningKey` |
| 221 | + - removed `ticket` s from `iroh-base` , they are now in their own `iroh-ticket` crate |
| 222 | + |
| 223 | +<a name="roadmap"></a> |
| 224 | +## 🎉 The end of the (0.)90s is coming |
| 225 | + |
| 226 | +We are almost at the finish line folks and are excited to let you know that we expect only two more releases in the 9Xs canary series. `0.95` to round our our API changes and `0.96` to bring you multipath. Once all criticial issues are fixed, we expect to publish our first release candidate `1.0.0-rc.0` later this year |
| 227 | + |
| 228 | +All the nitty-gritty details can be found in the [updated roadmap](https://iroh.computer/roadmap), and in [our milestones on github](https://github.com/n0-computer/iroh/milestones), which we will keep updating as we go. |
| 229 | + |
| 230 | +### But wait, there's more! |
| 231 | + |
| 232 | +Many bugs were squashed, and smaller features were added. For all those details, check out the full changelog: [https://github.com/n0-computer/iroh/releases/tag/v0.94.0](https://github.com/n0-computer/iroh/releases/tag/v0.94.0). |
| 233 | + |
| 234 | +If you want to know what is coming up, check out the [v0.95.0 milestone](https://github.com/n0-computer/iroh/milestone/49), and if you have any wishes, let us know about the [issues](https://github.com/n0-computer/iroh/issues)! If you need help using iroh or just want to chat, please join us on [discord](https://discord.com/invite/DpmJgtU7cW)! And to keep up with all things iroh, check out our [Twitter](https://x.com/iroh_n0), [Mastodon](https://mastodon.social/@n0iroh), and [Bluesky](https://bsky.app/profile/iroh.computer). |
0 commit comments