Skip to main content

dig_dht/
lib.rs

1//! # dig-dht — a Kademlia DHT with provider records for the DIG Node peer network
2//!
3//! The DHT answers exactly one question for a DIG Node: **"which peers hold this content?"** A node
4//! that holds a store / capsule / root / resource PUTs a **provider record** keyed by a
5//! [`ContentId`]; a node that wants that content runs an iterative [`find_providers`] lookup and
6//! gets back the set of holder `peer_id`s (with candidate addresses). It then connects to those
7//! peers over [`dig_nat`] (mTLS, `peer_id = SHA-256(TLS SPKI DER)`) and fetches the bytes over the
8//! **L7 peer RPC** (`dig.getAvailability` → `dig.fetchRange`). The DHT *locates* peers; dig-nat and
9//! the peer RPC *move the bytes*.
10//!
11//! [`find_providers`]: DhtService::find_providers
12//!
13//! ## The Kademlia core
14//!
15//! Nodes and content share one 256-bit XOR-metric keyspace ([`Key`]): a node's key is its `peer_id`
16//! verbatim, and a content key is the SHA-256 of its [`ContentId`]. Closeness is XOR distance, and
17//! the routing table is 256 [`k-buckets`](routing::RoutingTable) keyed by the shared-prefix length
18//! between this node and a peer. Lookups are **iterative** with α-parallelism, converging on the
19//! `k` closest peers to a target ([`lookup`]). This is textbook Kademlia (Maymounkov & Mazières).
20//!
21//! ## Provider records (the point)
22//!
23//! [`announce_provider`](DhtService::announce_provider) PUTs a [`ProviderRecord`] at the `k` nodes
24//! closest to the content key (and stores it locally); [`find_providers`](DhtService::find_providers)
25//! walks toward the key and collects the providers found along the way. Records are **TTL'd** and
26//! **republished** before expiry, so a provider that goes offline ages out automatically.
27//!
28//! ## Transport — riding dig-nat
29//!
30//! The four DHT RPC methods (`find_node`, `find_providers`, `add_provider`, `ping`) ride an
31//! authenticated dig-nat [`PeerConnection`](dig_nat::PeerConnection): each RPC opens a logical
32//! stream, writes a length-prefixed JSON [`wire`] request, and reads a length-prefixed JSON
33//! response. The transport is abstracted behind the [`DhtTransport`] trait so the whole lookup +
34//! provider machinery is tested over an **in-memory harness** (many virtual nodes in one process,
35//! no real network).
36//!
37//! ## Bootstrap + maintenance
38//!
39//! A node seeds its routing table from bootstrap peers (the dig-gossip peer pool / relay introducer,
40//! supplied as input — the crate never hard-depends on a live relay) and keeps it fresh with a
41//! periodic self-lookup and per-bucket refresh.
42
43#![forbid(unsafe_code)]
44#![warn(missing_docs)]
45
46pub mod config;
47pub mod content;
48pub mod error;
49pub mod key;
50pub mod lookup;
51pub mod provider_store;
52pub mod record;
53pub mod routing;
54pub mod service;
55pub mod transport;
56pub mod wire;
57
58pub use config::DhtConfig;
59pub use content::ContentId;
60pub use error::DhtError;
61pub use key::{Distance, Key};
62pub use record::{AddressKind, CandidateAddr, ProviderRecord, MAX_ADDRESSES_PER_RECORD};
63pub use routing::{Contact, RoutingTable};
64pub use service::{BootstrapPeer, DhtService};
65pub use transport::DhtTransport;
66pub use wire::{DhtRequest, DhtResponse};
67
68// Re-export the peer identity from dig-nat so consumers use ONE `PeerId` type across the transport
69// and the DHT (no divergent shape).
70pub use dig_nat::PeerId;