iroh_quic
Pure-Dart binding for iroh 1.0 - peer-to-peer QUIC
networking (endpoints, connections, streams, relays, address lookup) on desktop Linux, macOS, and
Windows. No Flutter required. For Android / iOS, use the companion
iroh_flutter plugin — same API, native build bundled into
your app.
iroh_quic wraps the iroh Rust core; it does not re-implement iroh
in Dart. The Dart API mirrors iroh 1.0's nouns exactly (Endpoint / EndpointId / EndpointAddr,
addressLookup), so iroh's docs and the n0 examples transfer directly.
Quick start
Download the signed prebuilt native library for your platform once — no Rust toolchain needed:
dart pub add iroh_quic
dart run iroh_quic:setup # downloads + verifies the prebuilt lib into a per-user cache
(Prefer to build it yourself? cd rust && cargo build --release instead — the loader finds that too.)
import 'dart:typed_data';
import 'package:iroh_quic/iroh_quic.dart';
Future<void> main() async {
// Loads the native library + verifies the ABI handshake. With no arguments the loader checks, in
// order: a local `cargo build` (rust/target/{debug,release}), the `iroh_quic:setup` cache, and a
// lib bundled next to the executable. Pass libraryPath: for a custom location.
await Iroh.init();
// Identity & addressing (pure data, no runtime).
final secret = SecretKey.generate();
print('my id: ${secret.publicKey.toZ32()}'); // publicKey == EndpointId
// A server that echoes one bidirectional stream.
const alpn = 'my-app/echo/0';
final server = await Endpoint.bind(alpns: [alpn.codeUnits]);
final serving = () async {
final conn = await server.accept();
final (send, recv) = await conn!.acceptBi();
await send.writeAll(await recv.readToEnd(1 << 20));
await send.finish();
}();
// A client that connects and reads the echo back.
final client = await Endpoint.bind();
final conn = await client.connect(server.addr, alpn.codeUnits);
final (send, recv) = await conn.openBi();
await send.writeAll(Uint8List.fromList('hello'.codeUnits));
await send.finish();
print(String.fromCharCodes(await recv.readToEnd(1 << 20))); // hello
await serving;
await client.close();
await server.close();
}
Platforms
Signed prebuilt libraries (downloaded by dart run iroh_quic:setup) cover the desktop targets a
pure-Dart program actually runs on:
| Linux x64 | Linux arm64 | macOS x64 | macOS arm64 | Windows x64 | Windows arm64 |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
The loader finds the prebuilt in its cache, a local cargo build under
rust/target/{debug,release}/libirohdart_ffi.{so,dylib,dll}, or a copy next to your compiled
executable — or pass an explicit libraryPath:.
Mobile (Android / iOS): these are not dart run targets. Use the
iroh_flutter plugin, which compiles and bundles the native
library into your app automatically.
Building the native library yourself
Don't want the prebuilt download (offline build, full audit, an unpublished arch, or a custom
profile)? Compile the crate from source — a Rust toolchain is the only
prerequisite. The command installs the library into the same cache iroh_quic:setup uses, so
Iroh.init() finds it automatically:
dart run iroh_quic:build # cargo build --release, then install
dart run iroh_quic:build --debug # faster, unoptimized
Equivalently, build by hand and point the loader at the result:
cd rust && cargo build --release # produces target/release/libirohdart_ffi.{so,dylib,dll}
The loader finds rust/target/{debug,release}/ automatically when you run from the package root, or
pass await Iroh.init(libraryPath: '/path/to/libirohdart_ffi.so') for any location.
API surface
- Identity & addressing -
SecretKey,PublicKey/EndpointId,Signature,EndpointAddr,RelayUrl,RelayMode,RelayMap. - Endpoint -
Endpoint.bind({secretKey, alpns, relayMode}),close,isClosed,setAlpns,boundSockets,addr,connect,accept,acceptIncoming. - Connection & streams -
openBi/openUni/acceptBi/acceptUni, datagrams,stats,remoteId,alpn,closed;SendStream(writeAll/finish/reset),RecvStream(read/readExact/readToEnd/stop). - Accept filter -
Incoming(remoteAddr,accept/refuse/retry/ignore). - Reactive streams -
Endpoint.watchAddr(),Endpoint.homeRelayStatus(),Connection.pathEvents()(sealedPathEvent). - Multi-protocol routing -
Endpoint.router()->RouterBuilder.accept(alpn, handler)->spawn()/Router.shutdown(): register a Dart handler per ALPN and let iroh'sRouterdispatch. - Custom address lookup -
Endpoint.bindWithAddressLookup(resolve:): dial peers known only by theirEndpointId, resolving theirEndpointAddrin Dart.
Lazy-stream footgun: a freshly opened
SendStreamis invisible to the peer until the firstwriteAll.
Run the bundled headless example (dart run, no Flutter):
cd rust && cargo build --release && cd ..
dart pub get
dart run example/echo.dart
Develop
dart pub get
cargo install flutter_rust_bridge_codegen --version '^2' # standalone cargo binary
./tool/frb_codegen.sh # regen FRB glue after editing rust/src/api/*
./tool/check.sh # cargo build+test, then dart analyze + dart test
Requires Rust >= 1.91 (edition 2024; pinned to 1.96.0) and, for Android, NDK r28+.
License
Apache-2.0 - see LICENSE. The iroh crate is MIT OR Apache-2.0.
Libraries
- iroh_quic
- Pure-Dart binding for iroh 1.0 - peer-to-peer QUIC networking (endpoints, connections, streams, relays, address lookup) over the shared Rust core.