Set up
Pick your language and create a project with the iroh bindings installed. Each tab assumes you already have the language toolchain on your machine. For full setup details per language (Xcode project for Swift, NDK targets for Kotlin/Android, etc.) see the language guides.Protocols and ALPN
A protocol defines how two endpoints exchange messages. Just like HTTP defines how web browsers talk to servers, iroh protocols define how peers communicate over iroh connections. Each protocol is identified by an ALPN (Application-Layer Protocol Negotiation) string. When a connection arrives, the router uses the ALPN string to decide which handler processes the data.iroh-ping is a diagnostic protocol that lets two endpoints exchange lightweight ping/pong messages to prove connectivity and measure round-trip latency. You can build your own protocol handlers or use existing ones like iroh-ping.
To write your own protocol, see the protocol documentation page.
What is a ticket?
When an iroh endpoint comes online, it has an address containing its Endpoint ID, relay URL, and direct addresses. The address is a structured representation that other iroh endpoints can use to dial it. AnEndpointTicket wraps this address into a serializable format: a short string you can copy and paste. Share this string with senders so they can dial the receiver without manually exchanging networking details.
This out-of-band information must reach the sender somehow so that endpoints can discover each other while still bootstrapping a secure, end-to-end encrypted connection. In this example we just use a string for users to copy and paste, but in your app you could publish it to a server, send it as a QR code, or pass it as a URL query parameter. It’s up to you.
For more on how this works, see Tickets and Address Lookup.
The receiver
The receiver creates an iroh endpoint, brings it online, prints a ticket containing its address, and accepts incoming ping requests until you press Ctrl+C. The Rust version uses theiroh-ping protocol crate; the other languages open the bidirectional stream by hand and echo the payload back, since iroh-ping isn’t bound yet.
Python: what is
uniffi_set_event_loop? iroh’s Rust runtime runs on its own threads, and when an operation completes (a connection arrives, a read finishes) one of those threads has to wake your Python code up. A Rust thread has no running asyncio loop, so the bindings cannot find yours on their own: this call hands them your loop explicitly. Make it the first line of any coroutine you pass to asyncio.run(), before touching any other iroh API. Without it, calls from Rust back into Python fail with RuntimeError: no running event loop, which typically shows up as an await that never completes. See the Python guide for details.The sender
The sender creates its own endpoint, parses the receiver’s ticket, and dials. Rust delegates toiroh-ping; the other languages open a bidirectional stream, write hello, await the echo, and time the round trip.
Wiring main
Parse the command-line argument to decide whether to run as receiver or sender. Each language tab is a complete file — drop the receiver and sender from the previous sections above the entry point.Run it
In one terminal, start the receiver. It will print a ticket. Copy that ticket and run the sender in another terminal — you should see the round-trip time printed.Connection issues? If the sender can’t reach the receiver, see the troubleshooting guide to enable detailed logging or use
iroh-doctor to diagnose network problems.Optional: send metrics to Iroh Services
If you want to see how your endpoints are performing (direct data rate, NAT traversal success, traffic volume) you can wire in Iroh Services as an optional client. Add the dependency:run_receiver (and/or run_sender), conditionally connect to Iroh Services if the IROH_SERVICES_API_SECRET environment variable is set. If the variable isn’t set the connection is skipped silently and your endpoint runs as before. If it is set, your endpoint shows up in the Iroh Services dashboard with live metrics.