The Android version is available on the Play Store, or an APK is available here if you would like to sideload. It supports Android 10/SDK 29 and up.
The iOS version is available here or search the App Store for "Flying Carpet File Transfer".
Flying Carpet 10.0
Flying Carpet 10 adds Shared Network mode and rebuilds the encryption on the Noise
Protocol Framework.
⚠️ Version 10 is a breaking change. v10 devices cannot transfer with v9 or earlier —
you'll get a clear version-mismatch message instead of a hang. Update every device you
transfer between.
Shared Network mode
Until now Flying Carpet always created its own ad-hoc WiFi hotspot. You can now transfer over
a WiFi or wired network that both devices are already on — useful at home or in an office,
and much faster to start when there's a network handy.
- Discovery is authenticated: devices announce themselves on port 3290 with an HMAC keyed
from the transfer password, so you only ever see peers that hold your password. - The receiver generates a single-use password and displays it (with a QR code); the sender
types or scans it. No Bluetooth involved, and the two devices' operating systems don't
matter — no need to select the peer's OS. - This is what finally makes Apple-to-Apple transfers work. iPhone↔Mac transfers were
impossible in hotspot mode because neither device can host for the other. Join both to the
same network — including a hotspot you made manually beforehand — and it just works. This
also unblocks Android↔iOS, which had been broken by the iOS WiFi path (#131). - Wired connections are supported (#124). A desktop on ethernet can transfer with a phone
on WiFi, as long as they're on the same network — and a machine with no WiFi card at all
can now use Flying Carpet for the first time (#93). - Interface picker: a labeled dropdown showing each interface's IP, with unusable ones
hidden, for machines with several NICs. - No hotspot means no ad-hoc WiFi connection to drop partway through a large transfer (#130).
Rebuilt encryption
Every transfer, in both modes, now runs a
Noise_NNpsk0_25519_ChaChaPoly_SHA256 handshake (X25519 + ChaCha20-Poly1305 + SHA-256), with
the pre-shared key derived from your password by PBKDF2-HMAC-SHA256 at 600,000 iterations.
- Forward secrecy. Recording a transfer and cracking the password later no longer reveals
the files — each transfer has fresh ephemeral keys. - Metadata is encrypted too. Filenames, file sizes, and the file count used to travel in
the clear; they're now inside the encrypted channel along with the contents. - Tamper-evident. The plaintext version/mode preamble is bound into the Noise handshake as
the prologue, so modifying a single byte of it fails the handshake rather than going
unnoticed. This also closes the door on future downgrade attacks. - Replaces the previous SHA-256/AES-256-GCM per-chunk scheme; Noise is now the sole cipher.
- Discovery announcements are signed with a key derived from the stretched PSK, so no fast
hash of your password ever goes on the air — every offline guess costs a full 600k-iteration
PBKDF2. - Generated passwords are now 10 characters instead of 8 (~2⁵⁸ instead of ~2⁴⁷),
foreclosing a precomputed-table attack over the whole password space. - Full design writeup:
docs/shared-network-crypto.md. All three implementations (Rust,
Kotlin, Swift) are held together by shared known-answer test vectors.
Hotspot mode keeps WPA2 underneath, so it's now encrypted twice over.
macOS ↔ Linux Bluetooth now works
Hotspot transfers between a Mac and a Linux machine previously required manually pairing the
two in System Settings first, and often failed afterward with "Peer removed pairing
information".
Root cause: macOS advertises with a public address and dual-mode flags, and BlueZ's bearer
tiebreak prefers classic BR/EDR on a tie — so Linux was connecting over classic Bluetooth,
which macOS serves no GATT over. Linux now bonds over an LE socket first, which pins the
bearer to LE permanently, and keeps the bond for macOS peers so their rotating address stays
resolvable. Pairing also surfaces the 6-digit code in the app for confirmation (real MITM
protection), and declining now aborts the transfer cleanly instead of hanging.
Other fixes and improvements
Desktop (Windows/Linux)
- File selection now comes first everywhere: hotspot joiners pick files and then get
prompted for the password, instead of needing the host's password before the file dialog
would open. - The UI recovers if a transfer task panics or is aborted, instead of freezing (#118).
- The Windows firewall UAC prompt no longer appears on every transfer (#129). The check
for an existing rule passed the rule name tonetshwith literal quotes around it, so it
never matched and the rule was re-added every time. Both rules are also added under a
single elevated command now, so the one-time prompt is one prompt, not two. - Windows Bluetooth: recovers from GATT enumeration failures (
0x8000FFFF) against
already-paired iPhones — retries, then re-pairs once within the same transfer, rather than
failing the run. Advertising is now explicitly stopped after the credential exchange. - Linux: stale
flyingCarpet_*NetworkManager connections are pruned at startup and the
hotspot is torn down on window close (#51). - Linux Bluetooth: cached BlueZ entries are purged and discovery is LE-only, so a
previously-paired device (an audio device, say) can no longer be picked up as the transfer
peer and then fail with "Could not find service UUID on scanned device" (#106). - The WiFi Direct failure message now points at shared network mode (#115).
- Edge/WebView2 no longer offers previously typed transfer passwords in an autofill dropdown.
Android
- Fixed an intermittent iOS→Android hotspot failure ("Empty key" crash or a silent stall)
caused by a BLE credential-exchange race across two GATT connections. - Fixed a stuck hotspot flag that made repeat transfers hang with "hotspot already running";
GATT client and server are now properly closed between transfers. - Successful shared-network receives no longer end with a spurious
"Discovery error: StandaloneCoroutine was cancelled". - Output box auto-scrolls; the transfer log survives screen rotation (it previously could
overflow the Binder transaction limit and vanish); every line is mirrored to logcat under
the tagFlyingCarpet, so a full transfer log can finally be pulled with
adb logcat -s FlyingCarpetfor bug reports (#130). - Bluetooth pairing that's declined or fails now aborts the transfer instead of waiting
forever; failed GATT reads no longer propagate empty values as the peer's OS or password. - Missing Bluetooth permissions are distinguished from missing hardware, and the switch
stays usable so you can re-grant (#101). - Password-prompt dialog buttons are readable in dark mode.
Security hardening
- Received filenames are sanitized against path traversal on all five platforms before
touching the filesystem. - Header values from the peer (file count, filename length, chunk size) are bounds-checked
before they're used to size allocations. - Fixed a Windows WiFi-profile XML injection via the SSID/password fields.
- Dependency updates closing 6 Dependabot advisories, including CVE-2026-42184 in Tauri
(is_local_url()misclassifying remote URLs as trusted local origins on Windows/Android,
allowing a remote page to invoke local-only IPC commands). Also CVE-2026-25727 (time),
CVE-2026-25541 (bytes), and fixes inserde_withandrand.
Send Folder is consistent everywhere
Sending a folder now recreates that folder inside the destination the receiving device
chose, with the contents inside, on all five platforms. Previously only macOS did this —
everywhere else the folder's contents were dumped loose into the destination. Sending
individual files is unchanged: they still arrive flat.
This also fixes sending a folder that contains sub-folders from Android to Windows or Linux,
which used to fail outright, and two cases where the desktop app aborted a transfer:
selecting a folder whose top level holds only sub-folders, and dropping two folders (or
files from two different directories) at once.
The in-app instructions were also wrong about how to send a folder — they told you to drag it
onto the window, which Android and macOS have no handler for, and which is not something a
screen reader user can do (#122). Each platform's help text now describes the control it
actually has, and says what arrives on the other end.