#4448·iroh

platform-verifier cannot verify iroh's own default relays on Windows (trailing-dot hostnames)

Author: yukimemiCreated Jul 29, 2026Updated Sep 17, 2026

Summary

On Windows, enabling the platform-verifier feature and building an endpoint with CaTlsConfig::system() makes every default relay fail TLS with NotValidForName — including relays that verify fine with the default embedded roots.

The cause is that iroh's default relay hostnames are absolute FQDNs, with the root label's trailing dot (iroh-1.0.3/src/defaults.rs:27):

rust
pub const NA_EAST_RELAY_HOSTNAME: &str = "use1-1.relay.n0.iroh.link.";

rustls-platform-verifier passes that name to CryptoAPI verbatim, which compares aps1-1.relay.n0.iroh.link. against the certificate's dot-less SAN and returns CERT_E_INVALID_NAME. webpki drops the root label during name matching and never sees a problem.

I filed the underlying bug as rustls/rustls-platform-verifier#240 — it reproduces with plain example.com. and no iroh involved. Opening this separately because the combination is what makes the feature unusable in practice, and iroh is where the two halves meet.

Repro

rust
let endpoint = Endpoint::builder(iroh::endpoint::presets::N0)
    .ca_tls_config(CaTlsConfig::system())
    .alpns(vec![b"my/alpn/1".to_vec()])
    .bind()
    .await?;

with iroh = { version = "1.0.3", features = ["platform-verifier"] }, on Windows:

ERROR endpoint:actor:reportgen-actor:run-probe{proto=Https relay=https://aps1-1.relay.n0.iroh.link./}:
  failed to verify TLS certificate: invalid peer certificate: NotValidForName
 WARN endpoint:actor:reportgen-actor:run-probe{proto=Https relay=https://aps1-1.relay.n0.iroh.link./}:
  probe failed: Failed to run HTTPS probe: HTTP request failed:
  error sending request for url (https://aps1-1.relay.n0.iroh.link./ping):
  client error (Connect): invalid peer certificate: NotValidForName

Every region, on a loop. Dropping back to the default CaTlsConfig::embedded() makes the same relays connect immediately, so it is not the certificates and not the network.

Why this matters more than the feature being niche

platform-verifier is exactly what a machine behind a TLS-inspecting proxy (Cato SASE, Zscaler, any corporate MITM appliance) needs: the interception CA is in the OS store, and webpki-roots reads neither SSL_CERT_FILE nor SSL_CERT_DIR, so there is no way to widen trust from outside a shipped binary. Those users are on Windows more often than not, and Windows is where the feature does not work.

CaTlsConfig is otherwise well-shaped for this — the escape hatch below only exists because with_extra_roots is there.

Workaround, for anyone who finds this

Take the roots from the OS but leave the name matching to webpki:

rust
let found = rustls_native_certs::load_native_certs();
let ca = CaTlsConfig::embedded().with_extra_roots(found.certs);

rustls-native-certs reads the same stores platform-verifier would (CryptoAPI on Windows, Keychain on macOS, the OS bundle on Linux), and honours SSL_CERT_FILE / SSL_CERT_DIR as a bonus. Keeping embedded() underneath rather than custom_roots also means a host with no CA bundle at all still binds instead of failing at bind() with InvalidCaRootConfig.

Possible fixes on this side

Whichever you prefer, and none of them are urgent if the upstream fix lands:

  1. Nothing here — wait for rustls-platform-verifier to strip the root label, then bump.
  2. Normalise the server name where the relay URL becomes a ServerName, so the trailing dot never reaches the verifier.
  3. Document on CaTlsConfig::system() that it is currently incompatible with the default relay map on Windows. Right now the feature reads as the supported answer for this problem, and following it makes things worse than doing nothing.

Environment

  • Windows 11 Pro 26200
  • iroh 1.0.3, rustls-platform-verifier 0.7.0
  • Reproduced against the default presets::N0 relay map