Android
The same transfer, as an app.
A Flutter client on the same protocol and the same signaling service as this page, so a phone and a browser are two ends of one transfer rather than two products. What it adds is the thing a tab cannot have: the transfer keeps running when you leave.




What the app adds
It survives the background
A foreground service holds the transfer open when you leave the app, with progress in the notification. A browser tab cannot promise that — leave it and the transfer dies with it.
Links open the app
A share link sent in a chat is claimed by the app rather than the browser, verified against this domain. The offer arrives on the phone straight from the tap.
Files land in Downloads
Bytes are written in 64 KB pieces as they arrive, never held whole in memory. Each file moves into the phone's Downloads only once its SHA-256 matches, so a half-received file never appears there under a name that suggests it is ready.
One protocol, two clients
The wire contract lives in a pure Dart package with no Flutter dependency, so it is tested without a device — and its tests deliberately assert the same things as the TypeScript ones: manifest validation, filename sanitising, and the arithmetic that routes one chunk across a file boundary.
That duplication is the point. If the two drift, a phone-to-browser transfer fails somewhere down in WebRTC, which is a painful place to find out. Pinning the contract on both sides means it fails in a test instead.
Getting it
Android only, and not on a store: the APK comes straight from the releases page. Most phones want the first one.
Android will ask before installing an APK from outside the Play Store, because it should. The build is reproducible from the source below if you would rather not take a binary on trust.
An iOS client is the same Dart on the other side of a platform channel, but the background transfer is the reason this app exists and iOS does not grant it the same way, so it is not worth shipping until that part has an honest answer.