Milestone 4: LAN discovery, trust gate, auto-sync, rate limits, events

mDNS-style LAN discovery (iroh MdnsDiscovery, discovery flag, default
on) feeds a presence tracker; trusted peers that appear trigger fetches
of every incomplete pin, and Pin itself now fetches from present
trusted peers. Serving is our own ProtocolHandler: trusted NodeIds get
the full store, everyone else a filtered view limited to open_lan pins
and ticket-exported hashes (plus hashseq children) that answers "not
found" for the rest. Ticket export records standing serve-consent for
that hash; trust changes take effect on new connections. ShapedStore
implements the full iroh-blobs Store trait to charge provider reads to
an upload token bucket and downloader writes to a download bucket;
upload cap 0 closes incoming connections at accept. Subscribe now
streams transfer_progress both ways, peer_joined, and pin_complete.

Tests: forged-ticket trust gating (denied untrusted, served after
trust), 256 KiB/s upload cap enforced by wall clock, event stream
during a transfer, and real-mdns auto-sync between two daemons
(skips where multicast is unavailable).

Dependencies: n0-future, async-channel, futures-lite, bytes — all
already in the tree via iroh; needed directly to name types in
iroh-blobs trait signatures and channels. iroh feature
discovery-local-network for MdnsDiscovery.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Lynghaug
2026-07-14 22:27:58 +02:00
co-authored by Claude Fable 5
parent 61171a5ea8
commit 20bdf56668
12 changed files with 1308 additions and 66 deletions
+21
View File
@@ -40,6 +40,10 @@ struct MetaState {
/// Trusted peer NodeIds (z-base-32).
#[serde(default)]
trusted_peers: BTreeSet<String>,
/// Hashes (hex) the user shared via ticket export: standing consent
/// to serve them to anyone, like an `open_lan` pin.
#[serde(default)]
exported: BTreeSet<String>,
}
/// Handle to the metadata file. Cheap to share behind an `Arc`.
@@ -125,6 +129,23 @@ impl Meta {
.unwrap_or(StoredFormat::Raw)
}
/// Record standing consent to serve `hash` to anyone (set by ticket
/// export — sharing a ticket is deliberate publication).
pub fn record_exported(&self, hash: &str) -> Result<()> {
self.mutate(|s| {
s.exported.insert(hash.to_string());
})
}
/// Hashes with standing serve-to-anyone consent.
pub fn exported(&self) -> BTreeSet<String> {
self.state
.lock()
.expect("meta lock poisoned")
.exported
.clone()
}
/// Add a trusted peer. Returns false if it was already trusted.
pub fn trust_peer(&self, node_id: &str) -> Result<bool> {
self.mutate(|s| s.trusted_peers.insert(node_id.to_string()))