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 EndpointId is the public key, any signature produced by the corresponding SecretKey can be verified against the identifier itself, eliminating the need for centralized certificate authorities.
  • Memory Safety: The SecretKey implementation uses zeroize::ZeroizeOnDrop to ensure sensitive key material is cleared from memory when no longer needed.
  • Serialization Flexibility: The types respect the is_human_readable flag during serialization, ensuring binary formats like Postcard remain compact while JSON and YAML use human-readable Z-base-32 or hex representations.

Summary

  • EndpointId is the public Ed25519 key (stored as CompressedEdwardsY in iroh-base/src/key.rs) used to cryptographically address and verify peers.
  • SecretKey wraps the private signing key from ed25519_dalek with ZeroizeOnDrop protection 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.invalid suffix to avoid DNS collisions while enabling 0-RTT connection resumption.
  • EndpointAddr combines 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →