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

> Master iroh's EndpointId and SecretKey identity system. Learn how Ed25519 key pairs provide self-authentication without central authorities. Dive into this complete guide.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-06-18

---

**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)](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)](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.

```rust
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)](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)](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`

```rust
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)](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.

```rust
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)](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)](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)](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)](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.