20bdf56668
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>
56 lines
3.2 KiB
Markdown
56 lines
3.2 KiB
Markdown
# 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.
|