Files
varde/docs/milestone-4.md
T
bl 20bdf56668 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>
2026-07-14 22:27:58 +02:00

3.2 KiB

Milestone 4 — LAN discovery, trust, rate limiting, events

What landed

  • LAN discovery via iroh's MdnsDiscovery (the documented _iroh mDNS service — identifiable on the network, not anonymous noise), enabled by the discovery config flag (default on). Sightings stream to the daemon, which tracks peer presence and reacts based on trust.
  • Trust gate. The provider is now our own ProtocolHandler: connections from trusted NodeIds get the full store; everyone else gets a filtered view that answers "not found" for anything outside the openly-served set. Verified by the forged-ticket test: an untrusted peer that knows a private hash gets nothing until trusted.
  • Openly-served set = open_lan pins + ticket-exported hashes, plus their HashSeq children, kept in memory and recomputed on pin changes and fetch completion.
  • Auto-sync. When a trusted peer appears on the LAN, every incomplete pin is queued for download from them (pin also fetches immediately if a trusted peer is present — "fetch if absent" is now real). Verified end-to-end over actual mdns between two daemons in the test suite; the test skips gracefully where multicast is unavailable.
  • Token-bucket rate limiters wrap the store itself (ShapedStore implements the full iroh-blobs Store trait): provider data reads pay into the upload bucket, downloader batch writes pay into the download bucket. Upload cap 0 disables serving outright (connections closed at accept). Enforcement is proven by wall-clock in the test suite.
  • Event stream is live: transfer_progress (both directions), peer_joined, pin_complete.

Decisions and trade-offs

  • Ticket export grants standing serve-consent for that hash (persisted in meta.json as exported). Handing out a ticket is deliberate publication, and without this either every ticket import would require the exporter to pre-trust the importer, or tickets would silently stop working the moment the trust gate exists. This is the documented consent ladder: trusted peers see everything, open_lan/exported content is public, everything else is private.
  • Trust is evaluated at connection accept. Revoking (or granting) trust applies to new connections; an existing connection keeps its view until it closes. Cheap, predictable, and the window is bounded by QUIC idle timeouts. Request-level re-checks would need a fork of iroh-blobs' provider internals (ResponseWriter is not constructible from outside the crate).
  • Gating by filtered store view (get → None) means an untrusted peer probing a private hash can't distinguish "refused" from "absent" — deliberately, that's the less chatty posture.
  • "Connected" for peers means "seen via LAN discovery in the last two minutes" — presence, not an open QUIC connection. Good enough for peer list and the status count; honest about what it measures.
  • fetch only from trusted peers: auto-sync only ever dials trusted peers. Ticket import dials the ticket's provider — importing the ticket is the explicit consent for that single fetch.
  • Download progress events report per-child offsets for collections; good enough for progress bars, not an exact byte ledger.