Expand description
§dig-peer-selector — the self-optimizing peer-selection middleware of the DIG Node
The selector is a pure decision + learning layer. It sits between dig-download (the
executor that fetches bytes over dig-nat mTLS) and the peer-discovery layers (dig-dht,
dig-gossip/dig-pex, dig-nat), and answers one question — “of these candidate peers,
which subset should serve this content, and in what order?” — learning the answer from the
real, measured outcome of every transfer it influenced. It has no user-facing
configuration: every tradeoff (saturation point, relayed penalty, decay) is self-tuned from
observed data.
This crate is the authoritative implementation of SPEC.md (version 1). The SPEC is the
contract; this documentation summarizes it — read SPEC.md for the normative statements.
§The closed loop
- The node calls
dig_dht::DhtService::find_providersand maps theProviderRecords intoCandidates, then asks the selector toselectthe best subset for aContentRequest. dig-downloadexecutes the multi-source byte-range transfer overdig-natmTLS mux streams.- Every per-range / per-request
TransferOutcomestreams back viarecord_outcome, updating the models in real time so the nextselect— and a mid-transferrebalance— is smarter.
§What is learned (measured-only, non-gameable)
A peer’s quality is refined EXCLUSIVELY from measured outcomes (SPEC §3, §9.2): there is no input path by which a peer raises its own score, and observed capacity always overrides advertised (SPEC §9.3). Throughput/RTT are recency-weighted estimators whose decay is derived from each peer’s observed volatility — no baked constant (SPEC §3.2, §4.3). The scorer learns a per-class saturation point (anti-thundering-herd, SPEC §4.1) and an adaptive relayed penalty (SPEC §4.2), and orients toward minimizing P99 request latency (SPEC §4.4).
§Boundaries (what the selector is NOT)
It never queries the DHT, runs the gossip pool, opens a socket/TLS session/mux stream, or
fetches/verifies/persists bytes — those belong to dig-dht, dig-gossip, dig-nat, and
dig-download respectively (SPEC §1.2). The selector only reads their outputs or drives their
choices. It re-uses the transport-verified PeerId (= SHA-256(TLS SPKI DER)) and the
dig-dht content/candidate types verbatim — it defines no parallel identity (SPEC §5, §11).
§Implementers’ note — how dig-node embeds the selector (P3 integration, digstore)
The selector is the source-selection seam between dig-dht and dig-download. dig-node
wires it as follows (the wiring lives in the node, NOT this crate — SPEC §6.1, §7.5):
- Construct one
PeerSelectorper node:PeerSelector::new(SelectorConfig::default()). - Feed the registry continuously:
- subscribe to
dig_gossip::GossipHandle::subscribe_pool_events()and forward each event — convertdig_gossip::PoolEvent→PoolEventwith a trivial 1:1 field map (identical shapes; seepool_event) — intoon_pool_event; - on each established
dig-natconnection, callon_connection_classwith itsdig_nat::TraversalKind; - optionally seed from a
connected_pool_peers()snapshot at startup viaupsert_candidate.
- subscribe to
- Per content want, call
find_providers(&content), map eachProviderRecordinto aCandidate(viaCandidate::from_provider_record), and callselect. Use eachSelectedPeer’smax_concurrencyas the per-peer concurrent-range cap indig-download(replacing its built-inpick_sourceheuristic). - Translate the
dig-downloadDownloadEventstream into outcomes as it flows (SPEC §6.2): mapRangeCompleted→ aRangeTransferOutcomewithresult: Success;RangeFailed→Failure { reason }(an integrity/verify failure →FailureReason::VerificationFailed); optionallyCompleted→ aRequestoutcome for whole-request P99 learning. Callrecord_outcomefor each. APausedis NOT a failure — do not record one (SPEC §6.4). - On a dropped source / relocate, call
rebalancewith the still- active peers and the still-needed ranges to get a replacement subset (SPEC §5.5). On resume,select/rebalanceonly the ranges NOT inDownloadState::done_ranges(SPEC §6.4).
§DigPeer hand-off — how consumers establish connections
The selector returns ranked peer identities (each SelectedPeer carries a PeerId); it does
NOT establish connections. The node is responsible for connecting to each selected peer via
the dig-peer crate: construct a dig_peer::PeerTarget from the peer_id + addresses, then
call dig_peer::DigPeer::connect to establish mTLS and get a dig_peer::Connected for the
actual streams (SPEC §6.1, §11). The selector’s role ends at selection; the transport is entirely
the node’s (and dig-download’s via dig-nat) responsibility.
Because TransferOutcome/Selection are defined here structurally, this crate does NOT depend on
dig-download — avoiding a dependency cycle; the event→outcome mapping lives in the node adapter
(SPEC §11).
Re-exports§
pub use config::ClockSource;pub use config::SelectorConfig;pub use config::DEFAULT_REGISTRY_CAPACITY;pub use engine::PeerSelector;pub use observe::peer_id_hex;pub use observe::PeerSnapshot;pub use observe::SelectorSnapshot;pub use pool_event::PoolEvent;pub use pool_event::PoolRemovalReason;pub use quality::Estimate;pub use quality::PeerQuality;pub use quality::Reliability;pub use registry::FeedResult;pub use registry::PeerEntry;pub use registry::DISPATCH_TTL_SECS;pub use scoring::PeerClass;pub use scoring::RelayModel;pub use scoring::SaturationModel;pub use types::Candidate;pub use types::ContentRequest;pub use types::FailureReason;pub use types::OutcomeKind;pub use types::OutcomeResult;pub use types::Provenance;pub use types::RangePlanDelta;pub use types::SelectedPeer;pub use types::Selection;pub use types::TransferOutcome;
Modules§
- config
SelectorConfig— wiring only, never behavior (SPEC.md §1.5, §5.6).- engine
PeerSelector— the public engine (SPEC.md §5): the closedselect → record_outcome → rebalancedecision + learning loop over the registry (§2), the measured-only quality model (§3), and the autonomous scorer (§4).- observe
- Read-only observability snapshots (SPEC.md §5.7) — the agent-friendly / machine-consumable surface.
- pool_
event PoolEvent/PoolRemovalReason— the topology/churn feed, byte-compatible withdig_gossip::PoolEvent(SPEC.md §5.4, §7.2).- quality
PeerQuality— the per-peer, measured-only capacity/RTT/reliability model (SPEC.md §3).- registry
- The dynamic peer registry:
peer_id -> PeerEntry, fed by churn + DHT candidates, bounded with lowest-value eviction (SPEC.md §2). - scoring
- The autonomous scoring function (SPEC.md §4): learned saturation per class, an adaptive relayed
penalty, volatility-driven decay (in
crate::quality), the min-P99 + anti-thundering-herd objective, and the ranked-subset output — satisfying invariants A–G (SPEC §4.4). - types
- The frozen public value types of the selector API (SPEC.md §5.1, §5.2, §5.5).
Structs§
- Candidate
Addr - The candidate/content types re-used from
dig-dht(SPEC §7.1): what is fetched + how a provider is addressed. One candidate address for a provider:{ host, port, kind }(L7dig.getPeers§7). The finder dials these (most-direct-first) viadig_nat::connectto reach the provider. - PeerId
- The transport-verified peer identity (
peer_id = SHA-256(TLS SPKI DER)), re-used fromdig-nat. A peer’s stable network identity: the 32-byte SHA-256 of its TLS SPKI DER. - Provider
Record - The candidate/content types re-used from
dig-dht(SPEC §7.1): what is fetched + how a provider is addressed. The DHT’s stored value: peerprovider_peer_idholds the content whose key iscontent_key, reachable ataddresses, untilexpires_at.
Enums§
- Address
Kind - The candidate/content types re-used from
dig-dht(SPEC §7.1): what is fetched + how a provider is addressed. How a candidate address was learned — the L7dig.getPeersaddresses[].kindtokens (§7). The lowercase serde spelling is the frozen wire form; the ordering is most-direct-first (a dialer picks the lowest-rank dialable candidate). - Content
Id - The candidate/content types re-used from
dig-dht(SPEC §7.1): what is fetched + how a provider is addressed. A DIG content identifier at store / root(capsule) / resource granularity — the key a provider record is stored under and a lookup asks for. - Traversal
Kind - The
dig-natconnection-class ladder the selector reads observationally (SPEC §7.3). Which traversal technique produced a result — used to order methods, tag failures, and report (observability) which method actually succeeded WITHOUT the caller caring.