Understanding iroh's EndpointId and SecretKey Identity System: A Complete Guide

iroh implements a self-authenticating identity system using Ed25519 key pairs, where SecretKey holds private signing material and EndpointId serves as the public peer identifier, eliminating the need for centralized certificate authorities.

The n0-computer/iroh networking library provides a decentralized identity layer that binds cryptographic verification directly to network addressing. Unlike traditional systems relying on certificate authorities, iroh uses a minimal PKI-free approach where the EndpointId itself is the verifiable public key. This architecture enables secure peer discovery and connection establishment without external trust anchors.

EndpointId vs SecretKey: The Core Identity Pair

iroh's identity system centers on a cryptographic key pair with distinct roles for public and private components.

EndpointId is the public identity of a node. Defined in [iroh-base/src/key.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) (lines 58-71), it is simply a type alias for PublicKey. Internally, it stores a CompressedEdwardsY—the y-coordinate of an Ed25519 curve point—guaranteeing that every identifier represents a valid curve point.

SecretKey holds the private signing material. Located in [iroh-base/src/key.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) (lines 59-62, 98-102), this struct wraps ed25519_dalek::SigningKey and implements ZeroizeOnDrop to ensure sensitive material is cleared from memory when the key is destroyed.

use iroh_base::{SecretKey, EndpointId};

// Generate a fresh cryptographic identity
let secret = SecretKey::generate();
let endpoint_id: EndpointId = secret.public();

Generating and Deriving Keys

Creating a new identity involves cryptographically secure random number generation followed by public key derivation.

SecretKey::generate() uses rand::random() to obtain 32 random bytes and constructs a valid Ed25519 signing key. This method is the entry point for creating new node identities in iroh.

SecretKey::public() derives the corresponding EndpointId by converting the private key to its public counterpart. Since EndpointId is an alias for PublicKey, this operation provides the self-authenticating identifier that peers use to address and verify your node.

Serialization and Human-Readable Encodings

iroh provides multiple serialization formats to support DNS-based discovery and configuration storage.

Z-base-32 Encoding (PKARR Protocol)

For human-readable identification compatible with the PKARR protocol, iroh implements Z-base-32 encoding. The methods PublicKey::to_z32() and PublicKey::from_z32() handle conversion between the binary key and the standardized text representation. This implementation resides in [iroh-base/src/key.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs#L62-L70).

TLS Server Name Encoding

To support TLS 0-RTT tickets and DNS-compatible addressing, iroh encodes EndpointId into a fake domain name using base-32 encoding. The .iroh.invalid TLD (guaranteed never to resolve per RFC 2606) prevents accidental collisions with real domains.

The encoding logic lives in [iroh/src/tls/name.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/name.rs):

  • encode (lines 17-22): Converts an EndpointId into a DNS label
  • decode (lines 24-35): Parses a TLS server name back to an EndpointId
use iroh::tls::name;

// Encode for TLS handshake
let tls_name = name::encode(endpoint_id);
// Result: "7dl2ff6emqi2qol3l382krodedij45bn3nh479hqo14a32qpr8kg.iroh.invalid"

// Decode back to identity
let decoded_id = name::decode(&tls_name).expect("valid TLS name");

Network Addressing with EndpointAddr

An EndpointId alone specifies who a peer is, but not where to find them. The EndpointAddr struct bridges this gap by bundling the identity with transport addresses.

Defined in [iroh-base/src/endpoint_addr.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/endpoint_addr.rs), EndpointAddr pairs an EndpointId with a vector of TransportAddr values. These transport addresses can represent direct IP sockets, relay URLs, or custom transport endpoints.

use iroh_base::{EndpointAddr, EndpointId, TransportAddr, RelayUrl};
use std::net::SocketAddr;

fn create_node_address(endpoint_id: EndpointId) -> EndpointAddr {
    let relay: RelayUrl = "https://relay.example.com".parse().unwrap();
    let direct_ip: SocketAddr = "203.0.113.42:4000".parse().unwrap();
    
    EndpointAddr::from_parts(endpoint_id, vec![
        TransportAddr::Relay(relay),
        TransportAddr::Ip(direct_ip),
    ])
}

The TransportAddr enum (referenced in [iroh-base/src/relay_url.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/relay_url.rs)) supports multiple path types, allowing nodes to advertise both direct connections and relay-backed routes simultaneously.

Security Guarantees and Memory Safety

iroh's identity system provides several critical security properties:

Self-Authenticating Identities: Because the EndpointId is the public key, any peer presenting a signature can be verified against the same key that identifies them. This eliminates the need for certificate chains or trusted third parties.

Zero-Clearing Memory: The SecretKey implementation uses #[derive(Clone, zeroize::ZeroizeOnDrop)] to ensure that private key material is securely wiped from memory when the key is dropped, minimizing the risk of exposure through memory dumps or core files.

Format-Aware Serialization: All serialization implementations respect the is_human_readable flag, ensuring that binary formats (like Postcard) remain compact while text formats (JSON, YAML) use readable Z-base-32 or hex representations.

Summary

  • EndpointId is the public Ed25519 key (alias for PublicKey) used to identify peers, stored as a CompressedEdwardsY in [iroh-base/src/key.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs).
  • SecretKey is the private signing material wrapped with ZeroizeOnDrop for memory safety, generating the corresponding EndpointId via secret.public().
  • Z-base-32 encoding provides human-readable identifiers compatible with PKARR-based discovery.
  • TLS name encoding converts identities to DNS-compatible .iroh.invalid domains for secure handshake initialization.
  • EndpointAddr combines identity with network paths (IP, relay) to enable multi-path connectivity.
  • The system is PKI-free and self-authenticating, using cryptographic signatures rather than certificate authorities for trust verification.

Frequently Asked Questions

What is the relationship between EndpointId and SecretKey?

SecretKey and EndpointId form a cryptographic key pair where the former is private and the latter is public. The SecretKey contains the 32-byte signing material used to authenticate traffic, while EndpointId (which is a type alias for PublicKey) serves as the node's addressable identity. You derive an EndpointId from a SecretKey using the public() method, but cannot reverse the operation to recover the secret key.

How does iroh serialize identities for DNS-based discovery?

iroh uses Z-base-32 encoding for PKARR compatibility and base-32 for TLS server names. The EndpointId converts to a Z-base-32 string via to_z32() for general discovery, while the TLS module in [iroh/src/tls/name.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/name.rs) encodes the identity as a base-32 domain ending in .iroh.invalid. This dual-encoding approach supports both human-readable sharing and secure TLS handshakes.

What makes iroh's identity system "PKI-free"?

The system is PKI-free because verification relies on the self-authenticating property of public keys rather than certificate authorities. Since the EndpointId is literally the Ed25519 public key, any cryptographic signature can be verified directly against the identifier itself. This eliminates the need for certificate chains, revocation lists, or trusted third parties, enabling truly decentralized peer verification.

How are transport addresses associated with node identities?

The EndpointAddr struct in [iroh-base/src/endpoint_addr.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/endpoint_addr.rs) binds an EndpointId to concrete network paths. This composite type holds the identity alongside a vector of TransportAddr values, which can include direct IP addresses, relay URLs, or custom transport endpoints. This separation allows the cryptographic identity to remain stable while network paths change dynamically.

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 →