iroh v0.90 - The Canary Series 🐥
by ramfoxWelcome to a new release of iroh, a library for building on direct connections between devices, putting more control in the hands of your users.
TL;DR: We’re jumping to v0.90 as the canary release series leading to 1.0. If you’re cool working on the bleeding edge, use 0.90. If you want to wait for 1.0, stick with v0.35.x until we cut 1.0 toward the end of the year.
Last time we published a release blog, we said our next release would be our release candidate for 1.0. But, while working through all of our breaking changes and updates, we realized that waiting until we had everything worked through and finished before releasing was not actually the best way for us to get a stable release into the hands of our users.
In the pre 1.0 era, we’d found that three-week release cycles were the sweet spot for our team. Not only did three-week cycles help our working cadence, but it also made sure we got user feedback quickly, and allowed us to move forward with confidence. We want that confidence more than ever going into 1.0.
So, we’ve decided that along with maintaining the 0.35 release (as well as the public infrastructure that supports 0.35), we will be doing a series of releases that we will refer to as “canary” releases. They will be versioned releases starting with 0.90, and continuing up until we are ready for our 1.0-rc.
The biggest difference between the way we are thinking about our new series of releases is, starting with 0.90, we are not guaranteeing network/protocol level stability until we make it to 1.0-rc.
So for those who:
- are starting fresh with iroh
- want to be on the cutting edge
- do not need worry about backwards-compatibility for their networks
- are still in the prototyping stage, or have small, easily updated deployments
We highly recommend coming along with us on our 0.90 canary journey. Our 0.90 release, for example, already includes performance improvements, and we want you to benefit from these kinds of changes ASAP.
But, if your network cannot tolerate multiple breaking changes over a short period of time (on the order of months), then stay on the 0.35 series until we have a release candidate.
We appreciate your patience as we figure out the best way to get to a stable 1.0 as soon as possible!
There are MANY breaking changes in this release, please do check out the “Breaking Changes” section below to see what those changes are in detail. Below are the highlights:
💥 Concrete Errors 💥
All of our APIs now return concrete errors!
This is going to be most obvious for those of you who have implemented versions of Discovery Services, or written your own protocols on iroh.
PLEASE give us feedback on our new concrete errors. This was a large change that touched most of the codebase and we are still lacking some documentation. But we want to make sure that this change works for you.
🚧 TLS x509 certificate option removed 🚧
As described in our last release blog post, we no longer support x509 TLS certifications in iroh . We have removed the iroh::endpoint::Builder::tls_x509 and the iroh::endpoint::Builder::tls_raw_public_keys endpoint builder options, since we now default to using raw public keys.
😯 iroh-relays no longer support STUN 😯
iroh no longer uses the STUN protocol to learn about our external addresses. We now rely on QUIC address discovery to do that. To that effect, the iroh-relays starting from 0.90 no longer support the STUN protocol. Our iroh configuration has also changed, as the RelayNode now only has quic configuration options.
👀 n0-watcher crate and the Watcher trait 👀
Many of our APIs now return impl Watcher, like the Endpoint::node_addr method. Knowing all of the details of your node’s NodeAddr may take a few milliseconds.. Something like the local address may be almost instant, but knowing its home RelayUrl or its publicly available addresses may take more time, since it needs to do some external probing to discover.
Returning a Watcher here allows our users to “wait” for the NodeAddr in the way that is most useful for them. If you just want the earliest value, let's say because you know you are on an local network, call initialize to get the first value that appears.
If you want updates, let's say because you need to know the RelayUrl or public facing addresses, you can call stream to get updates when new information comes in.
use iroh::Endpoint;
use n0_watcher::Watcher;
let endpoint = Endpoint::builder()
.alpns(vec![b"my-alpn".to_vec()])
.bind()
.await?;
let node_addr = endpoint.node_addr().initialized().await?;
let stream = endpoint.node_addr().stream();
Take a look at the n0-watcher crate docs for more info, but just know that the Watcher trait is re-exported in iroh, so if you want to use the Endpoint::node_addr or Endpoint::home_relay methods in iroh, you will also need to import the iroh::Watcher trait.
❗ Subtle expectation changes around Endpoint::node_addr and Endpoint::home_relay ❗
With some shifting of internals, two specific methods have changed in a subtle way, without changing names.
Endpoint::node_addr used to wait until a full net-report ran, so the first update to node_addr would usually include a RelayUrl. Now, node_addr returns whatever direct addresses we know about ASAP, without waiting for a relay_url. If you had code that relied on the existence of a RelayUrl in the NodeAddr, you must now listen for updates to check for its existence, or just use the Endpoint::home_relay method.
Also, previously the home_relay and node_addr updated at the same time, which means that previously you could use the existence of one to imply the existence of the other. This is no longer the case, so make sure that your code does not rely on this logic.
⚠️ Breaking Changes
- iroh
-
removed
iroh::endpoint::Builder:: tls_x509removed, this is the tls mechanism that has been removediroh::endpoint::Builder:: tls_raw_public_keysremoved, this is the default mechanism now, so not needed anymoreDisplayimplementation was removed forSecretKey, use.to_bytes()and encode as hex to get the previous bytes explicitly, for example:
let encoded_key = data_encoding::HEXLOWER.encode(&secret_key.to_bytes())- removed
iroh_relay::protos::stun::StunError - removed
iroh_relay::server::testing::stun_config - removed
iroh_relay::protos::stun - removed
iroh_relay::quic::QuicClient::get_addr_and_latency - removed
DEFAULT_STUN_PORT - Removed
iroh::discovery::dns::DnsDiscovery::new, useDnsDiscovery::builderinstead - Removed
iroh::discovery::pkarr::PkarrPublisher::new, usePkarrPublisher::builderinstead - Removed
iroh::discovery::pkarr::PkarrPublisher::with_options, usePkarrPublisher::builderinstead - Removed
iroh::discovery::pkarr::PkarrResolver::new, usePkarrResolver::builderinstead
-
changed
- all public APIs return concrete error types, rather than
anyhow::Error iroh::protocol::ProtocolHandlermethods now returnimpl Futureinstead ofBoxFuture. You can simply remove theBox::pin(async move {})from the implementations and instead implement the methods asasync fn. See the updated documentation for theiroh::protocolmodule for an example.iroh::protocol::ProtocolHandleris no longer dyn-compatible. If you need a dyn-compatible version, you need to build your own dyn-compatible wrapper trait. See the (non-public)DynProtocolHandleriniroh::protocolas an example.iroh::watcheris now its own craten0-watcher, but theWatchertrait is still a top level export inirohiroh::endpoint::Endpoint::node_addrnow returnsimpl Watcher<Value = Option<NodeAddr>>iroh::endpoint::Endpoint::home_relaynow returnsimpl Watcher<Value = Vec<RelayUrl>>iroh::endpoint::Endpoint::bound_socketsnow returnsVec<SocketAddr>iroh-quinnis updated to0.14.0iroh::protocol::RouterBuilder::acceptnow takesimpl Into<Box<dyn DynProtocolHandler>>instead ofimpl ProtocolHandler. Because of a blanketFromimpl this change does not need any changes by users: you can still pass anyimpl ProtocolHandlertoaccept. Additionally, if you have your own builder struct upstream, you can now also pass aBox<dyn DynProtocolHandler>toaccept, which wasn't possible previously.iroh::discovery::Laggedchanged from a tuple to a structiroh::watcher::Disconnectedis changed from a tuple to a structiroh::watcher::Disconnectedis no longerUnwindSafeorRefUnwindSafeiroh::watcher::InitializedFutis no longerRefUnwindSafeiroh-baseiroh::endpoint::Builder::add_discoverynow takes animpl iroh::discovery::IntoDiscoveryargument instead of a closure that returns aDiscovery. You can implement that on a builder struct, and anyT: Discoveryhas an auto-impl ofIntoDiscovery.iroh::discovery::Discovery::resolveno longer gets a&Endpointargument. If you need an endpoint in your discovery service, add a builder struct and implementIntoDiscoveryfor it, which gives you an endpoint that you can clone into your serviceiroh::discovery::pkarr::PkarrPublisher::n0_dnsnow takes no arguments and returns aPkarrPublisherBuilder. The secret key is set onPkarrPublisherBuilder::buildinstead.
- all public APIs return concrete error types, rather than
-
iroh-basechangediroh_base::ticket::Erroris renamed toiroh_base::ticket::ParseErroriroh_base::key::KeyParsingErrorhas changed from athiserrorerror to asnafuerroriroh-relay
iroh-relaychangediroh_relay::node_info::MaxLengthExceededErroris no longerUnwindSafeorRefUnwindSafeiroh_relay::node_info::MaxLengthExceededErrorwas changed from athiserrorto asnafuerroriroh_relay::client::ConnSendErroris nowiroh_relay::client::SendErroriroh_relay::protos::stun::Erroris nowiroh_relay::protos::stun::StunError
But wait, there's more!
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.90.0.
If you want to know what is coming up, check out the v0.99.0 milestone, and if you have any wishes, let us know about the issues! If you need help using iroh or just want to chat, please join us on discord! And to keep up with all things iroh, check out our Twitter.
To get started, take a look at our docs, dive directly into the code, or chat with us in our discord channel.