---
title: Security and privacy
description: What an iroh connection guarantees, what your endpoint ID and addresses reveal, and the knobs go-iroh gives you.
icon: lock
---

Two different questions get confused here. *Security* is whether someone can
read or forge your traffic. *Privacy* is who learns that you exist and where
you are. iroh is strong on the first and requires care on the second.

Upstream's [Security & Privacy](https://docs.iroh.computer/concepts/security-privacy)
page covers the protocol-level story for the whole project. This page covers
what go-iroh specifically gives you and what its defaults do.

## What a connection guarantees

Every direct peer-to-peer connection is TLS 1.3 with mutual authentication
using RFC 7250 raw public keys — the ed25519 key *is* the credential. Two
consequences:

- **Both sides are authenticated.** After the handshake, `conn.RemoteID()` is
  the peer's actual public key, cryptographically proven. There is no
  certificate authority to trust or to be compromised.
- **The relay cannot read your traffic.** A relay forwards encrypted QUIC
  packets between endpoints that authenticated to each other, not to it. It is
  a dumb pipe with respect to your data.

This is why an endpoint ID is a complete identity: possession of the secret key
is the only thing that lets anyone speak as that endpoint. Protect the secret
key accordingly — `iroh.WithSecretKey` takes one you have stored, and
`iroh.Bind` with no key generates a fresh identity every run.

## Post-quantum key exchange

Direct connections negotiate a TLS key-exchange group, and **the default is
already post-quantum**. `iroh.WithKeyExchangePolicy` selects the policy:

| Policy | Effect |
| --- | --- |
| `KeyExchangeDefault` | the package default |
| `KeyExchangeClassical` | disables post-quantum key exchange |
| `KeyExchangePreferPQ` | prefers `X25519MLKEM768`, keeps classical fallback |
| `KeyExchangePQOnly` | requires `X25519MLKEM768` |

`Conn.KeyExchangeGroup()` reports what was actually negotiated. Binding two
endpoints under each policy and connecting them prints:

```text
default   -> X25519MLKEM768
classical -> X25519
pq-only   -> X25519MLKEM768
```

The threat this addresses is harvest-now-decrypt-later: an adversary recording
traffic today to decrypt once quantum computers can break X25519. A hybrid
group protects the session even if one half is broken later.

`KeyExchangePQOnly` refuses to talk to peers that cannot do
`X25519MLKEM768` — including older iroh peers. Use `PreferPQ` when
interoperability matters more than a hard guarantee, and check
`Conn.KeyExchangeGroup()` if you need to know what you got.

## What a relay does learn

A relay does not see your plaintext, but it does see metadata: which endpoint
IDs are connected to it, which pairs are exchanging traffic, timing, and
volume. It also sees your IP address, because you connected to it.

That is the honest trade. If relay metadata is part of your threat model, run
your own relay — see [Commands](commands) for `iroh-relay` and
[Relays and discovery](relays-and-discovery) for pointing endpoints at it with
`relay.ModeCustomURLs`.

## What discovery publishes

This is where most privacy surprises live. Publishing to a public pkarr relay
makes a record queryable by anyone who knows your endpoint ID.

go-iroh's default is conservative, and it is worth knowing you are relying on
it: **`PkarrPublisher` publishes only relay addresses by default.** The godoc
is explicit that this avoids "leaking direct IP addresses to a public pkarr
relay." The mechanism is `iroh.RelayOnlyFilter`, the default value of
`PkarrPublisherConfig.AddrFilter`:

```go
// AddrFilter controls which addresses are published. If nil,
// [RelayOnlyFilter] is used. Use a filter that returns its input unchanged
// to publish all addresses.
```

go-iroh ships `iroh.IPOnlyFilter` as the inverse (direct IP and custom
addresses, dropping relays), and any `func([]netaddr.TransportAddr)
[]netaddr.TransportAddr` works, so you can write your own policy — for example
publishing only private-range addresses on a trusted LAN.

So if you set `AddrFilter` to publish everything, you are publishing your
direct IP addresses — including local ones — to a public server, keyed by your
endpoint ID. That may be exactly what you want on a trusted network. Do it
knowingly.

Two related notes:

- **mDNS discovery** (`iroh/mdns`) broadcasts your direct addresses on the
  local link by design. That is its whole function; it does not reach beyond
  the local network, but everyone on that network sees it.
- **Endpoint tickets** embed whatever addresses the `EndpointAddr` carried. A
  ticket built from direct addresses hands those addresses to whoever receives
  the ticket.

## Direct connections reveal your IP to the peer

Once a direct path is established, the peer knows your IP address — that is
what "direct" means. NAT traversal exists to make this happen. If you do not
want a given peer to learn your address, you must keep the connection on a
relay, which is a routing decision, not something the handshake can hide.

## Controlling who may connect

Authentication answers "is this peer who they claim to be." It does not answer
"should this peer be allowed in." For that, use
[endpoint hooks](endpoint-hooks): `AfterHandshake` runs with an authenticated
`conn.RemoteID()` and can reject with a close code, which is the supported way
to implement an allow list.

Note the caveat documented on that page: a hook rejection is admission control
on the accepting side, not a synchronous refusal the dialer sees immediately.

## Deliberately not covered

go-iroh has no equivalent of upstream's onion-routing and multi-hop-relay
discussion, because those are deployment and roadmap topics for the Rust
project rather than Go API surface. For the protocol-level threat model and
what n0 plans, read
[upstream's page](https://docs.iroh.computer/concepts/security-privacy).

## Next steps

- [Observing and rejecting connections](endpoint-hooks) — allow lists and
  custom authentication.
- [Relays and discovery](relays-and-discovery) — self-hosting, and what each
  discovery service publishes.
