# Securely Managing Endpoint Secret Keys and Endpoint IDs in iroh

> Securely manage endpoint secret keys and endpoint IDs in iroh. Generate or inject a SecretKey during Endpoint construction for local security and a shareable PublicKey as your EndpointId.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: best-practices
- Published: 2026-07-02

---

**To securely manage endpoint secret keys and endpoint IDs in iroh, generate or inject a `SecretKey` during `Endpoint` construction, knowing the secret key remains local while the derived `PublicKey` serves as the shareable `EndpointId`.**

The n0-computer/iroh networking library uses cryptographic keys to establish peer identity and authenticate connections. Understanding how to securely manage endpoint secret keys and endpoint IDs in iroh is essential for building applications that require persistent identities or secure key storage. This guide examines the source code implementation to show how secret keys are generated, stored, and exposed.

## How Endpoint Identity Works in iroh

### The Cryptographic Foundation

Every iroh endpoint is backed by an Ed25519 keypair. The `SecretKey` type in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) represents the private signing material, while the `PublicKey` serves as the immutable **endpoint identifier** (`EndpointId`). According to the source code at line 298, the public key is derived from the secret key using compressed Edwards-Y coordinates:

```rust
pub fn public(&self) -> PublicKey {
    let key = self.0.verifying_key().to_bytes();
    PublicKey(CompressedEdwardsY(key))
}

```

### Key Generation and Binding

When you create an endpoint using the builder pattern, iroh handles key material automatically. In [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) at line 226, the `Builder::bind` method checks for an existing secret key and generates one if missing:

```rust
let secret_key = self.secret_key.unwrap_or_else(SecretKey::generate);

```

This lazy generation ensures every endpoint has cryptographic identity without requiring explicit configuration.

## Generating and Configuring Secret Keys

### Automatic Key Generation

The `SecretKey::generate` method in [`iroh-base/src/key.rs`](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) at line 180 uses the `rand` crate to produce 32 cryptographically secure random bytes:

```rust
pub fn generate() -> Self {
    Self::from_bytes(&rand::random())
}

```

This creates a fresh Ed25519 signing key suitable for TLS authentication. The generated key exists only in memory unless your application explicitly persists it.

### Injecting Custom Secret Keys

For applications requiring persistent identity, inject an existing key via `Builder::secret_key`. As implemented in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) at line 516, this method stores the user-provided key before binding:

```rust
let my_key = SecretKey::from_bytes(&my_key_bytes);
let endpoint = Endpoint::builder(presets::N0)
    .secret_key(my_key)
    .bind()
    .await?;

```

This bypasses automatic generation and uses your securely stored key material instead.

## Security Architecture and Guarantees

### Local-Only Secret Storage

The secret key never leaves the local process. In [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) at line 1171, the `Endpoint::id` method exposes only the public derivative:

```rust
pub fn id(&self) -> EndpointId {
    self.inner.static_config.tls_config.secret_key.public()
}

```

The actual secret remains confined to the `tls::TlsConfig` structure, where it signs QUIC handshakes without exposing the private material to network peers.

### Safe Public Identifier Exposure

The `EndpointId` (which is the `PublicKey`) is designed for public distribution. You can safely share this identifier via PKARR, DNS, or other discovery mechanisms without compromising security. The architecture ensures zero-knowledge signing: peers verify your identity using the public key while the secret key remains in your local TLS configuration.

## Practical Implementation Examples

### Basic Endpoint with Auto-Generated Key

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

#[tokio::main]
async fn main() -> n0_error::Result<()> {
    let endpoint = Endpoint::builder(presets::N0).bind().await?;
    println!("Endpoint ID: {}", endpoint.id().fmt_short());
    Ok(())
}

```

### Loading a Persisted Secret Key

```rust
use iroh::{Endpoint, endpoint::presets};
use iroh_base::SecretKey;
use std::fs;

// Assume the key was stored as raw 32-byte binary
let stored = fs::read("my_secret_key.bin")?;
let secret = SecretKey::from_bytes(&stored.try_into().expect("bad key length"));
let endpoint = Endpoint::builder(presets::N0)
    .secret_key(secret)
    .bind()
    .await?;
println!("Re-used Endpoint ID: {}", endpoint.id().fmt_short());

```

### Publishing the Endpoint ID

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

let endpoint = Endpoint::builder(presets::N0).bind().await?;
let endpoint_id = endpoint.id(); // PublicKey == EndpointId
// Encode as z-base-32 for DNS records
let z32 = endpoint_id.to_z32();
println!("Publish this ID: {}", z32);

```

## Summary

- **SecretKey remains local**: The private key never transmits over the network or serializes to disk unless explicitly persisted by your application.
- **EndpointId is public**: The derived `PublicKey` serves as the shareable endpoint identifier for peer discovery and connection establishment.
- **Flexible generation**: Use `SecretKey::generate` for ephemeral endpoints or `Builder::secret_key` for persistent identities.
- **TLS integration**: The secret key authenticates QUIC connections through the internal `tls::TlsConfig` without exposing signing material.

## Frequently Asked Questions

### How do I persist an endpoint secret key securely?

Serialize the 32-byte representation of `SecretKey` using `from_bytes` and store it in your operating system's keychain, a hardware security module, or encrypted file storage. Load it at runtime and inject via `Builder::secret_key` before calling `bind()`.

### What happens if I don't provide a secret key when building an endpoint?

If no key is provided, `Builder::bind` automatically calls `SecretKey::generate` to create a fresh random key. This ephemeral key exists only for the duration of the process and provides a new random `EndpointId` on every restart.

### Is the endpoint ID the same as the public key?

Yes. In iroh, the `EndpointId` type is an alias for `PublicKey`. You can verify this in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) where `Endpoint::id()` returns `self.inner.static_config.tls_config.secret_key.public()`, confirming the deterministic relationship between secret key and endpoint identity.

### Can I rotate or change an endpoint's secret key without changing the ID?

No. Because the `EndpointId` is cryptographically derived from the `SecretKey` via Ed25519 public key derivation, changing the secret necessarily changes the public identifier. To maintain a stable ID, you must persist and reuse the same secret key across application restarts.