# How iroh EndpointId and SecretKey Authentication Works

> Understand iroh EndpointId and SecretKey authentication. Learn how Ed25519 keys create a PKI-free identity system for secure communication.

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

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/endpoint_addr.rs)) bundles the identifier with transport addresses—including IP sockets and relay URLs—using `EndpointAddr::from_parts`.

```rust
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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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.