# How SecretKey and EndpointId Manage Identity in iroh

> Learn how SecretKey and EndpointId manage identity in iroh. This cryptographic key pair provides PKI-free identity, with EndpointId as the public identifier and SecretKey for authentication.

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

---

**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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs), `SecretKey::generate()` uses `rand::random()` to obtain 32 random bytes and constructs the signing key.

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

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

```rust
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 `SecretKey` type 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_readable` flag, ensuring binary encodings (like postcard) remain compact while JSON and YAML use readable base-32 or hex representations.

## Summary

- **SecretKey** wraps `ed25519_dalek::SigningKey` and represents the private identity component, securely zeroizing on drop.
- **EndpointId** is an alias for `PublicKey` (stored as `CompressedEdwardsY`), serving as the self-authenticating public identifier.
- **Key derivation** uses `SecretKey::generate()` with `rand::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`](https://github.com/n0-computer/iroh/blob/main/iroh/src/tls/name.rs).
- **Network addressing** combines `EndpointId` with transport paths via `EndpointAddr` in [`iroh-base/src/endpoint_addr.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/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`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/endpoint_addr.rs)), which pairs the `EndpointId` with specific transport addresses like IP sockets or relay URLs.