The nightly workflow has failed every run since it was added. Fix the four distinct issues so it can actually exercise the slow checks: - cargo-mutants: drop bogus `--in-diff-base ""` flag (never existed in cargo-mutants; v27 rejects it). - fuzz smoke: pin `--target x86_64-unknown-linux-gnu`. Binstall ships a musl-static cargo-fuzz binary whose default target inherits its own triple, and libfuzzer's sanitizer is incompatible with `crt-static`. - miri: move multi-module filters after `--`. `cargo test` only accepts one TESTNAME positional; libtest accepts multiple substring filters. - stateright random_large: add `target_state_count(1_000_000)` so the simulation actually terminates instead of looping past the 30 min CI timeout. The comment already claimed "up to 10⁶ states". Also refreshes fuzz/Cargo.lock, regenerated by the local verification build (was stale vs. parent deps).
iroh-gossip-rendezvous
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). Kindependent DHT slot keys (defaultK=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_ofnever panics / overflows for any(i64 seq, u64 epoch_size ≥ 1);wire::decode_entriesnever 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 ≤ 3under weak fairness; randomized exploration of 10⁷ states forN = 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.