iroh 0.95.0 - A New Relay, Error Handling, and Connection API Improvements
by ramfoxWelcome to a new release of iroh, a library for building direct connections between devices, putting more control in the hands of your users.
We've been busy shipping improvements across the iroh stack, from infrastructure updates to major API refinements.
First, we're expanding our relay infrastructure with a new west coast US region, giving you more options for potential low latency home relays.
We're also excited to share n0-error, our new error handling crate that strikes a better balance between the complexity needed for stack traces and the ergonomics we need for daily development.
On the API front, we've made important improvements to the connection lifecycle, especially around 0-RTT connections. The iroh::Connection type now provides infallible access to the remote endpoint ID and ALPN information, and we've restructured the 0-RTT API with clearer types and semantics. These changes make the API more intuitive and help prevent common usage errors.
🏋️♂️ NA West relay
We’ve added another region to our default public relays! We now host a relay, currently running with v0.95.0, on the west coast of the US.
This relay will continue to update alongside the other canary relays.
The canary relays are still valid for versions v0.93.0 and newer.
Public relays running versions v0.35.0 , v0.91.0, and v0.92.0 are still up.
❌ n0-error
We recently wrote a blog post describing our struggle to get backtraces in rust errors right.
After using our proposed strategy for a few releases now, we realized it wasn't working for us. It was often clunky and confusing to use, and worst of all, because of the ergonomics (or lack thereof) for errors in rust, we were still not actually able to get a full backtrace for all errors in a stack.
We settled on writing our own crate, called n0-error that had two main focuses:
- we wanted errors with call-site location data for the entire error stack for easier debugging
- we wanted it to be as ergonomic as we could make it, for example, we wanted it to:
- be simple to create error enums and structs
- be easy to use with
anyhowfor example crates and binaries - work well with IDEs, for example, making it easy to jump to definitions while coding
Take a look at the crate and docs for n0-error for more details.
❗ error basics
The main points of focus here are the StackErrors and AnyErrors.
StackError s contain additional methods beyond the normal std::error::Error, including the meta method, that reports call-site location data.
An AnyError can wrap both a StackError and std::error::Error, but has the added benefit of not erasing access to the extra meta data in the StackError. Unfortunately, converting from a StackError to a std::error::Error would remove the ability to get any meta data from the StackError.
🔎 displaying n0-errors
-
Display impl (
{error}) prints only the message of the outermost errorfailed to process input -
Alternate display impl (
{error:#}) prints the message for each error in the chain, in a single linefailed to process input: invalid input: wanted 23 but got 13 -
Debug impl (
{error:?}) prints the message and each source, on separate lines.failed to process input Caused by: invalid input wanted 23 but got 13If
RUST_BACKTRACE=1orRUST_ERROR_LOCATION=1, then call-site location data will also be collected. This will also print the call site of each error:failed to process input (examples/basic.rs:61:17) Caused by: invalid input (examples/basic.rs:36:5) wanted 23 but got 13 (examples/basic.rs:48:13)
📞 Connections
The connection API now has infallible Connection::remote_id() and Connection::alpn() methods. Previously, these methods could fail if called before the handshake completed or if the handshake data was unavailable. Now, Connection guarantees that it represents a fully authenticated connection with verified remote identity and ALPN protocol, since it now can only be constructed after successful handshake completion and authentication, eliminating the need for fallible accessors.
🔧 0-RTT API Improvements
The 0-RTT API has been restructured with clearer types and semantics:
- Use
Incoming::acceptto return anAccepting. UseAccepting::into_0rttto return anIncomingZeroRttConnection - Use
Connecting::into_0rttto return aOutgoingZeroRttConnection OutgoingZeroRttConnection: Represents client-side 0-RTT connections created viaConnecting::into_0rtt(). Allows sending 0-RTT data before the handshake completes. Callhandshake_completed()to get aZeroRttStatusindicating whether the 0-RTT data was accepted or rejected by the server.IncomingZeroRttConnection: Represents server-side 0-RTT/0.5-RTT connections created viaAccepting::into_0rtt(). Allows receiving 0-RTT data from clients or sending 0.5-RTT data before the handshake completes. Callhandshake_completed()to get a fully authenticatedConnection.ZeroRttStatusenum: Returned byOutgoingZeroRttConnection::handshake_completed()to indicate whether the server accepted or rejected the 0-RTT data:ZeroRttStatus::Accepted(Connection): 0-RTT data was accepted, streams opened before handshake remain validZeroRttStatus::Rejected(Connection): 0-RTT data was rejected, pre-handshake streams will error and data must be resent
These types replace the previous version of Connection & the ZeroRttAccepted type and provide a more explicit API for handling 0-RTT connection states and outcomes.
💡 Note to protocol developers
Adding an Accepting type, now means that the ProtocolHandler trait is different.
Rather than implementing ProtocolHandler::on_connecting, that accepts a Connecting, you now must implement ProtocolHandler::on_accepting, that accepts an Accepting.
This is largely just a name change unless you were using 0-RTT connections. In which case, it's possible you may want to handle your entire protocol in ProtocolHandler::on_accepting. This is fine, since on_accepting can handle long-running processes. If you choose to go down this path, it’s acceptable for the ProtocolHandler::accept method to be empty except for a return Ok(());.
See the documentation for more details.
⚠️ Breaking Changes
iroh-dns-server- upgraded to redb version 3 - you must run with version 0.93 at least once to upgrade the database
iroh- changed
- All errors have changed from
snafuerrors ton0-errorerrors. ConnectError::Connection- fields changedAcceptError::Connection- fields changedAcceptError::MissingRemoteEndpointId- fields changedAcceptError::NotAllowed- fields changedAcceptError::User- fields changedConnecting::into_0rtt-> returnsResult<OutgoingZeroRttConnection, Connecting>
- All errors have changed from
removedProtocolHandler::on_connecting()removed - implementon_accepting()instead, which takesAcceptingrather thanConnectingDynProtocolHandler::on_connecting()removed - implementon_accepting()insteadiroh::endpoint::IncomingFuture- useAcceptinginsteadiroh::endpoint::ZeroRttAccepted- replaced by explicit 0-RTT connection types
- changed
🎉 The end of the (0.)90s is coming
We have been working on these releases for quite a while now, and are excited to let you know that we expect only one more release in the 9Xs canary series. 0.96 will bring you multipath, finally, and once all critical issues are fixed, we expect to publish our first release candidate 1.0.0-rc.0 later this year
All the nitty and gritty details can be found in the updated roadmap, and in our milestones on github, which we will keep updating as we go
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.95.0.
If you want to know what is coming up, check out the v0.96.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, Mastodon, and Bluesky.
To get started, take a look at our docs, dive directly into the code, or chat with us in our discord channel.