# Milestone 3 — Transfer The daemon now has an iroh endpoint: it serves its store over `iroh_blobs::ALPN` via the standard `Blobs`/`Router` stack, and fetches pinned content with the iroh-blobs `Downloader`. `TicketExport` / `TicketImport` complete the v1 out-of-band sharing loop; the pin created by an import triggers the fetch from the ticket-embedded provider. ## Decisions - **Endpoint identity** is an ed25519 key at `/secret.key` (hex, mode `0600`, created on first start). Node ids surface in the API as opaque strings in iroh's canonical encoding. - **Relay disabled unless `wan_upload = true`.** The default posture is LAN-only with zero WAN upload; with the relay off the endpoint is only reachable over direct paths and never moves bytes through third-party infrastructure. Tickets between machines that can't reach each other directly need `wan_upload` on both ends — documented behavior, not a bug. - **Downloader, not the rpc client.** The `Blobs` rpc client (`add_from_path`/`download`) would pull in the whole quic-rpc feature surface; the underlying `Downloader` gives queueing, retries and resume with no extra dependencies. One found-the-hard-way detail: the downloader dials by NodeId only, so the ticket's `NodeAddr` must be registered with the endpoint (`add_node_addr`) before queueing, or every dial fails with "no addressing information". - **Pin-before-fetch.** `TicketImport` records the pin (and the format from the ticket) before the fetch starts: the pin is the GC root that protects in-flight data, and the standing intent survives an unreachable provider or a crash. Re-issuing the import after a crash resumes from iroh-blobs' on-disk partial state. - **Fetches are background tasks.** `TicketImport` replies immediately with the pinned hash; completion is observable via `Status {hash}` polling or the `pin_complete` event on a subscribed connection. - **A bare `Pin` still fetches nothing** — there are no known providers until LAN discovery lands in milestone 4. The pin is recorded and the spec's "fetch if absent" activates when provider sources exist. - **No serving gate yet**: this milestone serves any peer that presents the hash (tickets are unguessable capability tokens; BLAKE3 hashes of private content should be treated as secrets). The trust allowlist gate is milestone 4's first change. ## Tests Two-daemon localhost integration: single blob and directory collection transfer, byte-identical after materialize on the fetcher. Crash test: SIGKILL the fetcher ~150 ms into a 64 MB transfer, restart on the same store, re-import the ticket, content completes and matches (proves clean restart with partial on-disk state; on a fast machine the kill may land after completion, which still exercises restart-with-state).