# 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.