Feature request: Seamlessly resume interrupted file transfers
What would you like to see?
Alt SendMe should preserve and clearly resume interrupted file transfers instead of silently losing progress when a connection drops, a browser page reloads or closes, or a native app is suspended or restarted.
The baseline promise should be:
- verified progress is retained;
- reopening Alt SendMe shows the interrupted transfer;
- the user can choose Resume or Discard;
- Resume requests only missing verified data rather than restarting from zero;
- sender unavailability, storage errors, and lost destination permission have distinct, actionable messages;
- users do not need to keep watching the screen to protect completed progress.
Continuous transfer while an app is backgrounded or a browser is closed is a separate, platform-specific guarantee. When an OS or browser cannot keep execution alive, Alt SendMe should pause safely and resume cleanly when execution returns.
Why would this be helpful?
This request comes from a failed real-world transfer. I was sending a file to someone who was in a hospital. They opened the transfer in their browser, but the page was closed or the connection became unreliable. Each interruption forced them to restart from the beginning, without clearly explaining what happened or how to continue. After repeated restarts and confusion, we stopped using Alt SendMe for that transfer.
Resumability is therefore not only a bandwidth optimization. The experience needs to be calm, clear, and seamless for someone who does not know or care how the transfer protocol works.
Feasibility in the current stack
Alt SendMe already has much of the low-level receiver machinery:
- The native receiver uses a content-hash-keyed
FsStore, preserving verified partial content after cancellation or failure. - The shared receive protocol calls
local.missing()beforeexecute_get(...), so it can request missing verified ranges rather than download the complete object again. - Existing engine tests cover partial-store preservation and retrying the same content.
The missing layer is durable product state and lifecycle recovery:
- receive tickets, destinations, status, and progress are held in memory;
- sender shares and endpoints are ephemeral and are not restored after process death;
- startup currently removes
.sendme-recv-*directories, including potentially recoverable partial stores; - the browser receiver creates a fresh
MemStore, so closing/reloading the page destroys both partial content and transfer state; - the UI has no durable Interrupted / Waiting for sender / Resuming job model.
Relevant implementation:
download_to_storeuses the store's missing ranges- Native receive preserves the partial store on cancel/error
- Native receive storage is keyed by content hash
- WASM currently uses memory-only send/receive stores
- Receiver UI state is currently in-memory
Comparison with other transfer approaches
| Approach | Strengths | Limitations / lessons for Alt SendMe |
|---|---|---|
| Blip | Publicly promises automatic recovery from network interruption, unplugged drives, and full disks; direct transfer when possible with encrypted relay fallback; polished account/device UX. | Closed-source client. Public material does not establish exactly what survives force-quit, reboot, or a hard browser/app termination. Treat it as a UX benchmark, not an implementation specification. |
croc (croc send) |
Open source; PAKE-derived end-to-end encryption; relay-based connectivity; reconnect attempts during a running command; can inspect an existing partial output and negotiate missing chunks after restarting the command. | Both sides still need a live/restarted command and the code phrase. It demonstrates useful partial-file reuse but not a persistent graphical transfer queue or universal background execution. |
| Alt SendMe / Iroh | Authenticated encrypted QUIC; direct-path preference with encrypted relay fallback; content-addressed verified blobs; the current receiver already knows how to request missing content. | Relays do not store the transfer job or keep a dead application alive. Alt SendMe must persist jobs, retain partial stores, restore sender/receiver state, and build understandable recovery UX. |
Primary sources:
- Blip security and relay model
- Blip product claims
- Blip comparison with WeTransfer
- croc README and documented resume behavior
- croc receive/reconnect implementation
- Iroh documentation
- Iroh relay FAQ
- Browser storage quotas and persistence
- Browser OPFS
- Background Fetch limitations
Proposed staged scope
Stage 1: native restart recovery
- Persist a small receive-job record: ticket/content hash, destination, status, timestamps, and required connection options.
- Replace blanket startup deletion with reconciliation of known jobs and partial stores.
- Restore incomplete jobs on startup with Resume and Discard actions.
- Preserve partial data after cancellation, network loss, and process termination.
- Remove it after successful completion, explicit discard, or a documented expiry/storage policy.
- Show Waiting for sender when the content is not currently available.
Stage 2: durable browser recovery
- Replace the receive
MemStorewith durable browser-backed storage where supported—likely OPFS for partial blob data and IndexedDB for job metadata. - Treat stored blob state as authoritative; persisted progress is only a UI summary.
- Handle quota, eviction, private mode, and destination-permission loss explicitly.
- Clearly disclose when a browser/platform cannot provide durable recovery.
Follow-up work
- Sender-session restoration or re-offering the same content after sender restart.
- Native background/foreground-service behavior on Android and equivalent lifecycle work on other platforms.
- Browser background execution experiments. A closed tab must not be claimed to keep transferring unless a supported implementation proves it.
- Cross-link: #251 covers avoiding full Android cache duplication and should remain a separate storage/source concern.
Acceptance criteria
- Interrupt a large native receive, terminate and reopen the app, and see the incomplete job with accurate status and retained progress.
- Reload or close/reopen a supported browser receive and recover the job and partial data.
- Resume requests only missing verified content and produces byte-identical output.
- Repeated connectivity loss does not reset progress or corrupt the result.
- Sender unavailability is shown as Waiting for sender, with a safe retry path.
- If background execution is unavailable, the transfer pauses without corruption and resumes visibly when the user returns.
- Completion exports once and removes the partial store.
- Discard removes the job and partial data after confirmation.
- Storage exhaustion, browser quota/eviction, unreachable ticket, and destination-permission loss produce distinct actionable errors.
- Automated tests cover process restart, retained partial data, resume integrity, stale-store reconciliation, discard cleanup, and browser reload recovery where supported.
Non-goals
- Replacing Iroh or inventing another transfer protocol.
- Promising that every browser continues transferring after its tab is closed.
- Solving Android share-source cache duplication tracked by #251.
Source: tonyantony300/dashbeam