How SecretKey and EndpointId Manage Identity in iroh
SecretKey and EndpointId form a cryptographic key pair that provides PKI-free identity in iroh, where EndpointId serves as the public Ed25519 identifier and SecretKey holds the private signing material used to authenticate all traffic.
iroh implements a minimal, self-authenticating identity system where every node is identified by its cryptographic public key. The SecretKey and EndpointId types in iroh-base encapsulate this dual-key architecture, eliminating the need for certificate authorities while ensuring verifiable peer authentication.
The Cryptographic Foundation
EndpointId as the Public Identity
EndpointId is simply an alias for PublicKey defined in iroh-base/src/key.rs (lines 58–71). It represents the public part of a node's identity, used to address peers across the network. Internally, it stores a CompressedEdwardsY—the y-coordinate of an Ed25519 curve point—guaranteeing valid cryptography without requiring external validation.
SecretKey as the Private Signing Material
SecretKey contains the private part of the identity, wrapping ed25519_dalek::SigningKey (defined in iroh-base/src/key.rs, lines 59–62 and 98–102). This type signs all traffic and can derive the corresponding EndpointId. The struct implements ZeroizeOnDrop, ensuring the 32-byte seed is securely cleared from memory when the key is destroyed.
Generating and Deriving Keys
Creating a new identity starts with generating cryptographically secure random bytes. According to the source code in iroh-base/src/key.rs, SecretKey::generate() uses rand::random() to obtain 32 random bytes and constructs the signing key.
use iroh_base::{SecretKey, EndpointId};
let secret = SecretKey::generate(); // creates a fresh key pair
let endpoint_id: EndpointId = secret.public(); // derives the public identifier
The public() method returns the PublicKey type, which is identical to EndpointId, establishing the cryptographic link between the secret and public components.
Serialization and Human-Readable Encodings
iroh requires stable, human-readable representations of EndpointId for DNS-based discovery and configuration storage.
Z-base-32 Encoding
For PKARR protocol compatibility, PublicKey provides to_z32() and from_z32() methods (implemented in iroh-base/src/key.rs, lines 62–70). This encoding produces shorter, case-insensitive strings compared to standard base64.
TLS Server Names
For TLS 0-RTT tickets, iroh encodes the endpoint ID into a DNS-compatible label using base32. The encoder resides in iroh/src/tls/name.rs (lines 17–22), while the decoder appears in lines 24–35.
The resulting TLS server name follows this format:
<base32(endpoint_id)>.iroh.invalid
The .invalid TLD is reserved by RFC 2606, preventing accidental resolution on the public internet.
use iroh_base::SecretKey;
use iroh::tls::name;
let secret = SecretKey::generate();
let endpoint_id = secret.public();
// Human-readable encodings
println!("Z-base-32: {}", endpoint_id.to_z32());
println!("TLS name: {}", name::encode(endpoint_id));
Combining Identity with Network Paths
An EndpointId alone does not specify how to reach a peer on the network. The EndpointAddr struct (defined in iroh-base/src/endpoint_addr.rs) bundles the identifier with transport addresses, including IP sockets, relay URLs, or custom transports.
use iroh_base::{EndpointAddr, TransportAddr, EndpointId, RelayUrl};
use std::net::SocketAddr;
fn create_address(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 available path types, allowing the EndpointAddr to carry multiple routes to the same cryptographic identity.
Security Guarantees
iroh's identity system provides several critical security properties:
- Self-authenticating identities: The public key (
EndpointId) verifies signatures directly, eliminating the need for a PKI infrastructure. - Memory safety: The
SecretKeytype uses#[derive(ZeroizeOnDrop)]to prevent key material from persisting in memory after the object is dropped. - Encoding safety: All serialization formats respect the
is_human_readableflag, ensuring binary encodings (like postcard) remain compact while JSON and YAML use readable base-32 or hex representations.
Summary
- SecretKey wraps
ed25519_dalek::SigningKeyand represents the private identity component, securely zeroizing on drop. - EndpointId is an alias for
PublicKey(stored asCompressedEdwardsY), serving as the self-authenticating public identifier. - Key derivation uses
SecretKey::generate()withrand::random(), producing 32-byte Ed25519 keys. - Serialization supports Z-base-32 for PKARR compatibility and base-32 DNS encoding for TLS server names in
iroh/src/tls/name.rs. - Network addressing combines
EndpointIdwith transport paths viaEndpointAddriniroh-base/src/endpoint_addr.rs.
Frequently Asked Questions
What is the difference between EndpointId and SecretKey in iroh?
EndpointId is the public identifier used to address peers, while SecretKey contains the private cryptographic material used to sign messages and prove ownership of the identity. The SecretKey can derive the EndpointId via the public() method, but the reverse is computationally impossible.
How does iroh encode EndpointId for DNS discovery?
iroh uses Z-base-32 encoding via PublicKey::to_z32() for PKARR-based discovery, and base-32 encoding for TLS server names (formatted as <base32>.iroh.invalid), as implemented in iroh/src/tls/name.rs. Both formats ensure case-insensitive, human-readable identifiers compatible with DNS constraints.
Where does iroh store the cryptographic key material?
SecretKey wraps ed25519_dalek::SigningKey in a struct that implements ZeroizeOnDrop, ensuring the 32-byte key material is securely cleared from memory when the object is destroyed. The implementation resides in iroh-base/src/key.rs and prevents accidental exposure of private keys in memory dumps.
Can an EndpointId be used to reach a peer directly?
No. An EndpointId only provides cryptographic identity. To establish network connectivity, you need an EndpointAddr (defined in iroh-base/src/endpoint_addr.rs), which pairs the EndpointId with specific transport addresses like IP sockets or relay URLs.
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 →