How iroh EndpointId and SecretKey Authentication Works
iroh uses Ed25519 cryptographic key pairs where EndpointId acts as the public addressable identifier and SecretKey handles private signing, creating a self-authenticating PKI-free identity system.
The n0-computer/iroh repository implements a decentralized networking stack that relies entirely on cryptographic identities for peer authentication and addressing. At the core of this system are two primary types—EndpointId and SecretKey—which work together to verify node identity without requiring certificate authorities or centralized registries.
Cryptographic Identity: EndpointId and SecretKey
EndpointId as the Public Key
EndpointId is fundamentally a type alias for PublicKey and represents the public portion of the identity. As defined in iroh-base/src/key.rs (lines 58-71), it stores the key internally as a CompressedEdwardsY, which compresses the y-coordinate of the Ed25519 curve point into 32 bytes while guaranteeing the point lies on the valid curve.
SecretKey as the Private Signer
The SecretKey type, found in iroh-base/src/key.rs (lines 59-62, 98-102), wraps ed25519_dalek::SigningKey to handle all cryptographic signing operations. Security is hardened through the #[derive(Clone, zeroize::ZeroizeOnDrop)] attribute, ensuring that sensitive key material is zero-cleared in memory when the object is dropped to prevent accidental exposure.
Generating Keys and Deriving Identities
Creating a new identity starts with SecretKey::generate(), which uses rand::random() to obtain 32 cryptographically secure random bytes and construct the signing key. The corresponding EndpointId derives deterministically via the public() method.
use iroh_base::{SecretKey, EndpointId};
fn main() {
// Generate a fresh secret key.
let secret = SecretKey::generate();
// Derive the endpoint identifier.
let endpoint_id: EndpointId = secret.public();
// Display human-readable encodings.
println!("Hex (short) : {}", endpoint_id.fmt_short());
println!("Z-base-32 : {}", endpoint_id.to_z32());
}
Serialization for Discovery and Transport
Z-base-32 Encoding for PKARR
For human-readable addressing and PKARR protocol compatibility, iroh encodes EndpointId using Z-base-32 via PublicKey::to_z32() and PublicKey::from_z32() (implemented in iroh-base/src/key.rs, lines 62-70). This encoding avoids ambiguous characters and is case-insensitive, making it suitable for DNS-based discovery and manual configuration.
TLS Server Name Encoding
Because iroh uses TLS for transport security, it derives DNS-compatible server names from the endpoint identifier. The iroh/src/tls/name.rs file implements encode (lines 17-22) and decode (lines 24-35) functions that convert the raw bytes to Base32 format and append the .iroh.invalid suffix—a special-use TLD guaranteed never to resolve on the public internet per RFC 2606.
use iroh::tls::name;
fn main() {
let endpoint_id = EndpointId::from_bytes(&[/* 32 bytes */]);
// Encode for TLS server name
let tls_name = name::encode(endpoint_id);
println!("TLS server name: {}", tls_name);
// Decode back to EndpointId
if let Some(id) = name::decode(&tls_name) {
println!("Decoded successfully");
}
}
Network Addressing with EndpointAddr
An EndpointId alone provides identity but not network location. The EndpointAddr struct (defined in iroh-base/src/endpoint_addr.rs) bundles the identifier with transport addresses—including IP sockets and relay URLs—using EndpointAddr::from_parts.
use iroh_base::{EndpointAddr, TransportAddr, EndpointId, RelayUrl};
use std::net::SocketAddr;
fn make_addr(endpoint_id: EndpointId) -> EndpointAddr {
let relay: RelayUrl = "https://relay.example.com".parse().unwrap();
let ip: SocketAddr = "203.0.113.42:4000".parse().unwrap();
EndpointAddr::from_parts(endpoint_id, vec![
TransportAddr::Relay(relay),
TransportAddr::Ip(ip),
])
}
The TransportAddr enum enumerates the possible address kinds, including Relay, Ip, and custom variants.
Security Properties and Implementation Details
iroh's identity system provides several critical security guarantees:
- Self-Authenticating Identity: Because
EndpointIdis the public key, any signature produced by the correspondingSecretKeycan be verified against the identifier itself, eliminating the need for centralized certificate authorities. - Memory Safety: The
SecretKeyimplementation useszeroize::ZeroizeOnDropto ensure sensitive key material is cleared from memory when no longer needed. - Serialization Flexibility: The types respect the
is_human_readableflag during serialization, ensuring binary formats like Postcard remain compact while JSON and YAML use human-readable Z-base-32 or hex representations.
Summary
EndpointIdis the public Ed25519 key (stored asCompressedEdwardsYiniroh-base/src/key.rs) used to cryptographically address and verify peers.SecretKeywraps the private signing key fromed25519_dalekwithZeroizeOnDropprotection for secure memory handling.- Z-base-32 encoding via
to_z32()provides human-readable addressing compatible with the PKARR protocol. - TLS server names encode the identifier as Base32 with the
.iroh.invalidsuffix to avoid DNS collisions while enabling 0-RTT connection resumption. EndpointAddrcombines the cryptographic identity with network paths (IP addresses and relay URLs) to enable actual peer-to-peer communication.
Frequently Asked Questions
How is EndpointId different from a traditional IP address?
Unlike an IP address, which can change based on network location and is allocated by infrastructure providers, an EndpointId is a persistent cryptographic identity that remains constant regardless of where the node connects. It proves identity through mathematical verification rather than network routing, allowing peers to migrate between networks without changing their identifiers or breaking existing trust relationships.
What happens if a SecretKey is compromised?
If a SecretKey is compromised, the attacker can completely impersonate the node and decrypt traffic intended for that identity. Because iroh uses a PKI-free model, there is no central authority to revoke certificates. The only remediation is to generate a new key pair, distribute the new EndpointId to trusted peers, and discontinue use of the compromised identity. The ZeroizeOnDrop implementation minimizes exposure risk by ensuring memory is cleared when keys are dropped.
Why does iroh use Z-base-32 instead of standard Base64?
iroh uses Z-base-32 via PublicKey::to_z32() (in iroh-base/src/key.rs) because it optimizes for human readability and verbal transmission. The encoding excludes ambiguous characters like 0/O and 1/l, maintains case insensitivity, and has a specific character set designed to reduce transcription errors when users manually share or configure addresses.
Can EndpointId be used without a relay server?
Yes, EndpointId functions purely as the identity layer and operates independently of transport mechanisms. While EndpointAddr can include RelayUrl entries for NAT traversal scenarios, the identifier works equally well with direct IP addresses or custom transport mechanisms defined in the TransportAddr enum, allowing for fully peer-to-peer connections when network topology permits.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →