Architecture
Why not Bluetooth
iOS does not expose classic Bluetooth file profiles (OBEX/OPP). Third-party apps only get BLE, which is too slow and fragile for a typical photo. Eric's Drop to Phone uses Bluetooth’s product idea (nearby, no account) with HTTP on the LAN as the transport.
Roles
- Windows tray app hosts a Kestrel server on
0.0.0.0:17890, advertises_droptophone._tcp(Bonjour) with TXTrole=pc,name,id, and holds one drop on disk for a short session. It also browses Bonjour for nearby phones. - Phone apps (iOS / Android) are HTTP clients for PC drops and uploads. While the app is in the foreground, they also host a small HTTP server on port 17890 and advertise themselves (
role=iosorrole=android) so other devices can discover them andPOST /upload. - Browser receivers still work against the Windows host (
GET /d/{token}).
Any Drop to Phone device on the same Wi‑Fi can appear in a Nearby list. Connect links and polls GET /current. Send pushes multipart files with POST /upload (Windows also accepts legacy POST /from-phone).
When a device receives a push upload, it remembers the sender (id / name / role / origin) as a saved peer and sets them as the current link, so a return send works without another Connect or QR scan. Clients advertise their LAN origin with X-Drop-Peer-Origin on requests; if that header is missing, the receiver falls back to Bonjour/NSD match or invents http://{remote-ip}:17890.
Phones receive peer uploads only while the app is open (no background HTTP server in v1).
[Windows or phone advertises] _droptophone._tcp TXT role/name/id
|
v
[peer Connect] -> GET /current (pull drop when ready)
[peer Send] -> POST /upload (push files to peer host)
headers: X-Drop-Peer-Id/Name/Role/Origin
receiver remembers sender for the next send
Trust model (v1)
- No accounts, no TLS. Traffic is on the local network only.
- The URL token is 24 random bytes, base64url (~32 characters). Guessing it is impractical for a 10-minute window.
- Optional pairing PIN (Settings on Windows). When enabled, the QR includes
pin=and phones sendX-Drop-Pin. Loopback and/healthskip the check so the on-PC preview still works. - A session expires after 10 minutes or when a new drop replaces it.
RemainingGetsstill limits PC simulator Save (save-pc), not phone downloads (video Range requests would burn the counter). - Stable
peerId(local UUID fragment) lets devices filter themselves out of Nearby lists. - Identity headers on peer HTTP:
X-Drop-Peer-Id,X-Drop-Peer-Name,X-Drop-Peer-Role, andX-Drop-Peer-Origin(sender’shttp://lan-ip:17890). - Bind is all interfaces so peers can connect; choose the correct NIC on Windows so the first QR is not a VPN address.
- Windows Firewall often blocks inbound 17890 until a private rule is added.
This is not a substitute for HTTPS on untrusted networks (public café Wi-Fi).
Routes
| Method | Path | Result |
|---|---|---|
| GET | / |
Redirect to /iphone (Windows) |
| GET | /iphone |
Phone-sized preview for Cursor Simple Browser (Windows) |
| GET | /current |
JSON { ready, preparing, canReceive, role, peerName, peerId, token, items, progress, … } |
| GET | /health |
ok |
| GET | /d/{token} |
HTML receiver (gallery when several files) — Windows |
| GET | /d/{token}/file |
The single file, or a zip of a multi-file drop — Windows |
| GET | /d/{token}/item/{id} |
One file from a multi-file drop — Windows |
| POST | /d/{token}/clear |
Clear the session — Windows |
| POST | /d/{token}/save-pc |
Loopback only — copy into Downloads on this PC |
| POST | /upload |
Multipart upload to this host (phone or PC) |
| POST | /from-phone |
Alias of /upload on Windows (legacy clients) |
Relative file / item URLs keep the token in the path so Safari and desktop browsers both work.
Process model
WPF (net8.0-windows10.0.17763.0 so unpackaged toasts compile) + Microsoft.AspNetCore.App. WebApplication.StartAsync runs beside the UI thread. Closing the window hides it; tray Quit shuts down Kestrel and wipes the session.
Optional install: %LocalAppData%\Programs\DropToPhone plus a Start menu shortcut (no signed MSIX). Incoming files can show an unpackaged toast (Open / Folder / Undo).
Single instance: mutex Local\DropToPhone.SingleInstance. A second launch signals Local\DropToPhone.ShowWindow.
Version and About
version.json is MAJOR.MINOR.PATCH.REVISION. About shows that build plus website / support / privacy / support@welchsoftware.net. Phone About screens also link the free Windows zip. How-to text is on Support, not duplicated on About. See TESTING.md.
Limits
- Up to 40 files per drop, 250 MB total, in either direction.
- Photos, video (including WebM / animated WebP converted to MP4 on the way to the phone), PDFs, and other documents. Programs and scripts are blocked.
- Photos/video default to the phone Photos library / gallery; other files go to Files.
- Files sent to Windows land in
Downloads\DropToPhoneunless you pick another folder in Settings. - Phones receive only while the app is in the foreground. There is no background server. The iOS Share sheet copies into the App Group inbox and opens the host app; HTTP send still needs the app open.
- Guest-network isolation cannot be proven from the client. Windows warns if Bonjour stays empty after a short wait.
- Progress is reported while converting, packing a zip, sending, and receiving.
- Bonjour service type:
_droptophone._tcpon port 17890.