distributed-topic-tracker 0.1.1

auto bootstrap for iroh-gossip topics
Documentation

distributed-topic-tracker

Decentralized, rate-limited auto-discovery and bootstrap for iroh-gossip, backed by the BitTorrent mainline DHT and rotating shared secrets. No centralized components.

  • Zero servers. Uses public DHT + encrypted payloads.
  • Deterministic, per-minute discovery keys.
  • Rate-limited publishing to reduce DHT load.
  • Bubble detection and merging to heal partitions.

Links:

Status: preparing for production. API may evolve; protocol is defined.

Features

  • Fully decentralized bootstrap for iroh-gossip
  • Ed25519-based signing; shared-secret-based encryption
  • DHT rate limiting (caps per-minute records)
  • Resilient bootstrap with retries and jitter
  • Background publisher with bubble detection and peer merging

Quick start

Add dependencies (names subject to final crate publish):

[dependencies]
iroh = "*"
iroh-gossip = "*"
distributed-topic-tracker = { git = "https://github.com/rustonbsd/distributed-topic-tracker", branch = "main" }

Minimal example:

use anyhow::Result;
use iroh::{Endpoint, SecretKey};
use iroh_gossip::{api::Event, net::Gossip};

// Crate imports
use distributed_topic_tracker::{
    AutoDiscoveryBuilder, AutoDiscoveryGossip, DefaultSecretRotation, TopicId,
};

#[tokio::main]
async fn main() -> Result<()> {
    // Generate a fresh node key
    let secret_key = SecretKey::generate(rand::rngs::OsRng);

    // Endpoint with discovery enabled
    let endpoint = Endpoint::builder()
        .secret_key(secret_key)
        .discovery_n0()
        .bind()
        .await?;

    // Gossip with auto-discovery
    let gossip = Gossip::builder()
        .spawn_with_auto_discovery::<DefaultSecretRotation>(endpoint.clone(), None)
        .await?;

    // Protocol router
    let _router = iroh::protocol::Router::builder(endpoint.clone())
        .accept(iroh_gossip::ALPN, gossip.gossip.clone())
        .spawn();

    // Topic and initial shared secret (pre-agreed out of band)
    let topic_id = TopicId::new("my-iroh-gossip-topic".to_string());
    let initial_secret = b"my-initial-secret".to_vec();

    // Join + subscribe
    let (sink, mut stream) = gossip
        .subscribe_and_join_with_auto_discovery(topic_id, &initial_secret)
        .await?
        .split();

    // Listener for incoming events
    tokio::spawn(async move {
        while let Ok(event) = stream.recv().await {
            if let Event::Received(msg) = event {
                let from = &msg.delivered_from.to_string();
                let from_short = &from[0..8];
                let body = String::from_utf8(msg.content.to_vec()).unwrap();
                println!("\nMessage from {}: {}", from_short, body);
            } else if let Event::NeighborUp(peer) = event {
                let peer_short = &peer.to_string()[0..8];
                println!("\nJoined by {}", peer_short);
            }
        }
    });

    // Simple stdin loop
    let mut buffer = String::new();
    let stdin = std::io::stdin();
    loop {
        print!("\n> ");
        stdin.read_line(&mut buffer).unwrap();
        let msg = buffer.clone().replace('\n', "");
        sink.broadcast(msg.into()).await.unwrap();
        print!(" - (sent)\n");
        buffer.clear();
    }
}

Configuration (defaults)

  • DHT get timeout: 10 s
  • Publish retries: 3 attempts
  • Retry jitter: 0–2000 ms
  • Per-minute record cap: 10 (MAX_BOOTSTRAP_RECORDS)
  • Join pacing: 100 ms between attempts, 500 ms final wait
  • Publisher backoff: 1–60 s (exponential), success jitter: 0–60 s
  • Bubble detection:
    • Small cluster: fewer than 4 neighbors
    • Message overlap: non-overlapping recent message hashes

See PROTOCOL.md for exact procedures.

Security model (summary)

  • Public discovery index: deterministic, per-minute Ed25519 keypair derived from topic and time.
  • Content confidentiality: records encrypted using a key derived from a rotating shared secret.
  • Authentication and integrity: records signed by publisher's node key.
  • Replay and access control: per-minute binding and secret rotation.

Details: PROTOCOL.md.

Architecture

  • Bootstrap loop queries DHT, decrypts, verifies, and connects to discovered peers with pacing.
  • Publisher runs in the background post-join to publish activity and merge bubbles.
  • Records encode topic, time window, publisher, active peers, and recent message proofs.

See ARCHITECTURE.md for diagrams and flows.

Testing

Unit Tests

Run unit tests for core components:

cargo test

End-to-End Tests

Test peer discovery across multiple Docker containers:

# Requires Docker and Docker Compose
./test-e2e.sh

The e2e test verifies that multiple nodes can discover each other through the DHT and successfully join the same gossip topic.

Roadmap

  • Finalize crate name and publish to crates.io
  • Doc tests and examples
  • Optimize configuration settings
  • Add more examples

Contributing

License: to be added (e.g., MIT/Apache-2.0).