# How EndpointId, PublicKey, and SecretKey Work Together in iroh

> Understand the relationship between EndpointId, PublicKey, and SecretKey in iroh. Learn how these keys create a deterministic identity and authenticate your endpoint for secure network identification.

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

---

**In iroh, `EndpointId` is a type alias for `PublicKey`, which is cryptographically derived from a `SecretKey` using Ed25519 signatures, creating a deterministic identity chain where the secret key authenticates the endpoint and its public derivative serves as the network identifier.**

The iroh networking library establishes peer identity through a strict cryptographic hierarchy. Understanding the relationship between **EndpointId**, **PublicKey**, and **SecretKey** is essential for implementing secure node authentication, as these three types form the backbone of identity and verification in the n0-computer/iroh protocol.

## The Cryptographic Hierarchy

iroh implements a one-way derivation chain: `SecretKey` → `PublicKey` → `EndpointId`. This design ensures that endpoint identities are self-verifiable and cryptographically secure.

### SecretKey (the private signing key)

The `SecretKey` struct holds the private Ed25519 signing key used to authenticate connections and sign protocol messages. Defined in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs), this type provides the `generate()` method for creating cryptographically secure random keys, and the `public()` method for deriving the corresponding public key.

### PublicKey (derived from SecretKey)

The `PublicKey` represents the public counterpart to the secret key. According to the source code in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) at lines 98-101, calling `SecretKey::public()` derives the public key deterministically using Ed25519 key generation algorithms. This public key serves as the cryptographic identity that other peers use to verify signatures.

### EndpointId (type alias for PublicKey)

`EndpointId` is defined as a direct type alias for `PublicKey` in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) at lines 58-70. This means every endpoint's network identifier is exactly its public key, with no additional encoding or transformation layer. Because `EndpointId` is just the public key, any two endpoints sharing the same `EndpointId` must possess the same underlying `SecretKey`.

## Implementation in the iroh Source Code

When constructing an `Endpoint`, the builder either accepts a user-provided `SecretKey` or generates one automatically using `SecretKey::generate()`. As implemented in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) at lines 24-31, the builder extracts the endpoint's ID via `secret_key.public()` and stores it in the internal `EndpointInner` struct.

The `Endpoint::id()` method defined at lines 66-72 in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) simply returns this pre-computed value, while `Endpoint::secret_key()` (lines 61-64) provides access to the original secret key for persistence or cryptographic operations.

## Practical Usage Examples

### Manual Key Generation and Identity Verification

The following example demonstrates the direct relationship between the three types:

```rust
use iroh_base::{SecretKey, EndpointId};

fn main() {
    // 1. Create a new secret key (randomly)
    let secret = SecretKey::generate();

    // 2. Derive its public key
    let public = secret.public();

    // 3. The endpoint identifier is exactly this public key
    let endpoint_id: EndpointId = public; // type alias, no conversion needed

    // 4. All three are linked
    assert_eq!(endpoint_id, secret.public());
    println!("SecretKey:   {:?}", secret);
    println!("EndpointId:  {}", endpoint_id); // prints hex representation
}

```

### Endpoint Builder Integration

When using the high-level `Endpoint` API, the relationship is managed automatically:

```rust
use iroh::{Endpoint, endpoint::presets};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Builder generates a random SecretKey internally
    let ep = Endpoint::builder(presets::N0).bind().await?;

    // The generated secret key can be inspected
    let secret = ep.secret_key();
    let id = ep.id();               // <-- this is the public key of `secret`
    assert_eq!(id, secret.public());

    println!("Endpoint ID (public key): {}", id);
    Ok(())
}

```

## Summary

- **`SecretKey`** holds the private Ed25519 signing key and serves as the root of identity.
- **`PublicKey`** is deterministically derived from `SecretKey` via the `public()` method defined in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs).
- **`EndpointId`** is a type alias for `PublicKey`, meaning the endpoint's network address is exactly its public key.
- The `Endpoint` builder in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) stores the secret key and exposes its public derivative through the `id()` method.
- Because `EndpointId` equals `PublicKey`, possessing the `SecretKey` proves cryptographic ownership of the `EndpointId`.

## Frequently Asked Questions

### Is EndpointId just a wrapper around PublicKey?

No, `EndpointId` is not a wrapper but a direct type alias for `PublicKey` defined in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) at lines 58-70. This means the two types are identical and can be used interchangeably without conversion overhead or runtime cost.

### How do I retrieve the EndpointId from an existing Endpoint?

Call the `Endpoint::id()` method implemented in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) at lines 66-72. This method returns the pre-computed public key that was derived from the endpoint's secret key during the builder's `bind()` phase.

### Can I use an existing SecretKey to create an Endpoint with a specific EndpointId?

Yes. When building an `Endpoint`, provide your existing `SecretKey` to the builder. The resulting endpoint will have an `EndpointId` equal to `secret_key.public()`, ensuring deterministic identity across sessions. If you don't provide a secret key, the builder generates a random one via `SecretKey::generate()`.

### What happens if two endpoints use the same SecretKey?

Since `EndpointId` is derived deterministically from `SecretKey`, two endpoints using the same secret key will have identical `EndpointId` values. This creates a cryptographic identity collision where both endpoints claim the same network address, which can cause routing conflicts in peer-to-peer discovery systems like pkarr or DNS.