Skip to main content

Bootstrap sequence

Identity lifecycle

  • iroh NodeId — ephemeral per session. Clients rotate NodeIds on session boundaries to break linkability across subscriptions (privacy).
  • Ethereum address — persistent. Identifies channel ownership and settlement.
  • Ephemeral NodeId↔Ethereum binding — per-connection, with nonce=0 as a sentinel indicating ephemeral (not slashable) binding.
Clients do not register their NodeId in StakingRegistry. There’s no on-chain identity for clients.

Registry query with retry

Primary source: the on-chain StakingRegistry via your configured RPC provider. Query paginated to keep gas/bandwidth bounded. Retry policy: exponential backoff with jitter. After N retries, fall back to the bundled peers.json.

peers.json fallback

A bundled static list of known-good peers. Used only if the registry is unreachable — once you have any working peer, they can gossip-announce others and the peer table fills in.

Multi-source bootstrap

The client queries three independent sources:
  • On-chain registry (primary).
  • DNS seed list (_decdn._tcp.seeds.decdn.net) returning a handful of entry points.
  • Operator-provided static list (optional, configured by advanced users).
Requires agreement from at least 2 of 3 sources to defeat single-source eclipse attacks. After bootstrap, the client enforces minimum peer diversity — the peer table must contain peers from at least K distinct operators before accepting trust-critical data (e.g., reputation scores). Prevents late-stage eclipse even if bootstrap passed.

Gossip subscription

Three topics:
  • cdn/global/v1 — global NodeAnnounce, RateChange, WatchtowerAnnounce.
  • cdn/region/{cc}/v1 — regional NodeAnnounce for the region you configured.
  • cdn/reputation/v1 — reputation reports.
Clients subscribe but do not publish. You never announce your presence or your interests over gossip.

Periodic refresh

The client re-queries the registry periodically (default: every 10 minutes) to pick up:
  • Newly registered nodes.
  • Newly deregistered nodes (removed from selection).
  • Multiaddr updates (nodes that rebooted on new IPs).
Between refreshes, gossip fills the gap: new nodes’ NodeAnnounce messages are received in near-real-time.

Peer table

The peer table holds:
  • NodeInfo — from registry (ed25519 NodeId, multiaddrs, Ethereum address, region).
  • Rate — from most recent NodeAnnounce or RateChange.
  • Local reputation — observed from direct interactions.
  • Gossip reputation — aggregated from cdn/reputation/v1.
  • Load hint — from recent NodeAnnounce or ProbeResponse.
Selection pulls from this table. Keep it fresh; a stale table means you select against obsolete rates.

Eclipse attack posture

ThreatMitigation
RPC provider returns fabricated nodesMulti-source bootstrap — 2-of-3 agreement required
Gossip-only view of an attacker’s islandMinimum peer-operator diversity + multi-source bootstrap
NodeId squattingOn-chain ed25519 ownership proof enforced at registration
See the three-tier trust boundary in trust model.

Observability

Clients expose a small set of metrics:
  • decdn_client_peer_table_size
  • decdn_client_registry_refresh_seconds
  • decdn_client_bootstrap_fallback_total — incremented whenever peers.json fallback is hit (a signal your RPC is flaky)
  • decdn_client_channels_open, decdn_client_vouchers_signed_total
  • decdn_client_bytes_received_total, decdn_client_verification_failures_total
decdn_client_verification_failures_total > 0 indicates a node sent bytes that didn’t match the BLAKE3 hash. The stream is cancelled automatically, but an elevated rate may indicate a targeted attack or a buggy peer.