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:
@@ -0,0 +1,55 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user