2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00
2026-04-22 22:52:46 -06:00

iroh-gossip-rendezvous

crates.io docs.rs CI MSRV License: MIT

Zero-configuration peer rendezvous for iroh-gossip swarms. Two nodes that share a passphrase and an application label find each other over the public Mainline DHT and join the same gossip topic — no bootstrap server, no prior knowledge of peer addresses.

use iroh_gossip_rendezvous::Rendezvous;

# async fn run() -> anyhow::Result<()> {
let rendezvous = Rendezvous::join("my-shared-passphrase", "my-app/v1").await?;

// Send to the swarm.
rendezvous.broadcast(bytes::Bytes::from_static(b"hello")).await?;

// Receive swarm events.
let mut events = rendezvous.subscribe();
while let Ok(event) = events.recv().await {
    println!("{event:?}");
}
# Ok(()) }

How it works

The crate implements a clock-free variant of HyParView-style partition healing on top of BEP 44 mutable items. Every (passphrase, app_salt) pair deterministically derives:

  • A gossip TopicId (via HKDF-SHA256).
  • K independent DHT slot keys (default K=3), each with its own Ed25519 signing key and a per-slot AEAD wrapper key.

Nodes publish their endpoint ID to one of the K slots and read from a changing subset of slots to discover peers. Records age out naturally, so crashed or silently-departed nodes don't clutter the DHT indefinitely. A two-layer AEAD envelope (per-record one-time key wrapped under a seq-ratcheted per-epoch key) ensures content confidentiality against DHT observers.

See PROTOCOL.md for the full algorithm specification. See ARCHITECTURE.md for a module map.

Correctness

The crate ships with a test suite that goes beyond unit tests:

  • Property tests (proptest) for wire format, merge invariants, and AEAD roundtrips.
  • Integration tests driving multi-node scenarios over an in-memory DHT: cold start, partition healing, asymmetric minority visibility.
  • Mutation testing (cargo-mutants) on the algorithmic core.
  • Fuzz harnesses (cargo-fuzz) for decode_entries, crypto::open, merge, and wire-format roundtrip.
  • Bounded model checking (Kani) of absence of undefined behavior on pure algorithmic entry points: crypto::epoch_of never panics / overflows for any (i64 seq, u64 epoch_size ≥ 1); wire::decode_entries never panics on arbitrary byte input. Structural invariants (merge size bound, age bound, roundtrip) are covered by the stronger proptest + Stateright combination — Kani's niche here is UB-absence at the slice/iterator boundary.
  • Distributed-system model checking (Stateright): bounded safety + liveness witness for N ≤ 6, K ≤ 3 under weak fairness; randomized exploration of 10⁷ states for N = 20.
  • Miri UB checks on the synchronous modules (keys, wire, crypto, merge).

The precise claim: the record-size and age invariants always hold across all explored reachable states up to N=6, K=3; under a weak-fairness assumption on action scheduling, any bipartite-partitioned initial state eventually reaches a state where both components overlap. For larger N, randomized exploration has found no counterexample. This is not a proof for unbounded deployments — it is the strongest tractable evidence available short of a hand proof.

Security

See SECURITY.md for the threat model and known limitations. Short version: the design protects against DHT observers and passive eavesdroppers, but not against a compromise of the passphrase (no forward secrecy) or a malicious member (no post-compromise security). For threat models requiring those properties, layer MLS / RFC 9420 on top.

Features

Feature Default Purpose
sim off Discrete-event simulator (examples/simulator.rs) + test-support exports
kani off Build Kani proof harnesses (#[kani::proof] functions)
miri-compat off Skip Miri-incompatible tests (#[cfg_attr(miri, ignore)])
mainline-live off Tests against the real public Mainline DHT (flaky, nightly-only)
test-support off Exposes Builder::dht_backend for injecting an in-memory DHT in integration tests

MSRV

Minimum Supported Rust Version: 1.93.0. Enforced in CI. Bumps are minor releases; we don't raise MSRV without releasing a new minor version.

Pinned iroh / iroh-gossip major versions: 0.98.x. Bumping the iroh major will be a minor version of this crate.

Contributing

See CONTRIBUTING.md. Local testing via just:

just check            # fmt + clippy + nextest + doctests (fast CI lane)
just all              # everything including miri, kani, stateright

See the CHANGELOG for release history.

License

MIT. See LICENSE.

S
Description
Passphrase-based rendezvous for iroh-gossip swarms via Mainline DHT
Readme MIT
204 KiB
0 Stars 1 Watchers 0 Forks
Languages
Rust 97.2%
Just 2.8%