Skip to main content
Floe encrypts every transfer end to end and never puts your files on a server. Both of those are true and neither is the whole story. This page is the other half: the things Floe does not do, written plainly, so you can decide whether it fits what you are sending. Nothing here is a bug report. Each item is a design consequence, and each one says what you can do about it.

The short version

Most people meet three of these. Anyone who has your link or room code can receive your files, so send it only to the person it is for. On a direct connection the other person’s device learns your IP address, unless you turn on Hide my IP. A browser that receives can hold only so much, so receive very large transfers with Floe Desktop or the CLI, or in smaller batches on a phone. Floe’s server also introduces the two devices, and a link key checks that introduction on a link that carries one. The check has limits, and the next four sections say exactly where they are.

Security and privacy

What the link key checks

Floe’s signaling server passes along the short IDs the two devices use to agree on encryption keys. It cannot read your files and never sees one. But a server that wanted to could swap in IDs of its own, sit between the two peers, and decrypt everything. A link key is Floe’s check for that. A link from the web app, from floe 1.12 and later, or from Floe Desktop 1.1 and later carries one after the #: 128 random bits that no server receives. Once the two devices connect, each side that holds the link key proves it, with a proof tied to the certificates actually in use on that connection. A server that swapped the IDs does not hold the link key, and a proof made on one connection does not pass on another, so it cannot make a proof that passes. Encryption explains the IDs, and Transfer protocol has the exact proof. What each app does with the link key decides how far that goes: So a request link from Floe Desktop 1.1 and later is checked both ways, and one from Floe Desktop 1.0.x is checked neither way, because it holds no link key. A sender that only detects can lose the whole file. It cannot tell a server playing a receiver from a real receiver with no link key, such as someone who typed the room code, so it goes on. That server gets every byte, not only the file’s name and size. Its words can reach the sender’s screen. Its word that it received the files earns the sender’s Verified row or green check mark, and that is only that side’s word. The receiver is the one who notices. A receiving page that holds the link key stops after 30 seconds if the other side connects and says nothing, and says it could not confirm who sent the link. So a server that takes the file from the sender and keeps quiet is caught by the receiver, not by the sender, whose page says the files were sent. floe receive 1.12 and later and Receive in Floe Desktop 1.1 and later stop after the same 30 seconds, on their usual lines for a sender that never starts: connected, but no data arrived from the sender within 30s and “The sender never started sending”. A server that never connects the receiver at all leaves nothing to check, and the transfer just never starts. What you can do: when it matters, share the link and not the code, and ask your receiver to open it in a browser, floe receive 1.12 or later, or Floe Desktop 1.1 or later. With floe 1.12 or later, send with --link-only. Ask your receiver whether the files arrived, and if they read that the sender could not be confirmed, assume someone else has the files. A room code carries no link key, so a transfer joined by code has no check. floe send and Send in Floe Desktop print the code beside the link, so the person who chooses the code is choosing no check. The server also chooses the code’s text and the room a code resolves to. A link with no link key has no check either, and nothing on screen tells a checked link from an unchecked one. That covers a request link from Floe Desktop 1.0.x and every older share link. What you can do: give the link rather than the code when the check matters, and share from the web app, floe 1.12 or later, or Floe Desktop 1.1 or later, so that the link holds a link key. The proof says “holds the link”, not who. So the check cannot see:
  • Whoever serves the web app’s JavaScript. It holds every link key its pages open. On floe.one that is Vercel and the build, which the same operator publishes. On your own instance it is your web image and the edge in front of it. It is also anything else on the page, such as browser extensions and in-app browsers, and the docs host, which serves script on the same origin.
  • An edge that can rewrite the page. For browser peers, a one-domain edge or a proxy that inspects TLS can change the page as well as pass the signaling along.
  • A stored copy of the page’s code. A script on the same origin can plant an entry in the site’s stored copies. An answer from the network overwrites it, but the stored copy runs when the request for that file fails. See In the browser.
  • Anyone who held the whole link. Everything that held it can pass the check: chat and mail services, link scanners, browser history and its sync, clipboard history, and a photo of the QR code. So can a shell’s history, and the process list while floe receive "<link>" runs, which is how the line under the box in floe send 1.12 and later says to pass it. A server plus any of those goes unnoticed.
What you can do: where none of that is good enough, encrypt the file yourself before you send it. If you run your own instance, the signaling server and the web app are in your hands, so the party you trust is you. That changes nothing else on this list. On api.floe.one you are trusting the same operator who publishes the client, which is the ordinary situation for any hosted service.

There is no verification code to compare

A short verification code is the older defense against a server in the middle. It is worked out from both devices’ keys, and the two people read it to each other over a channel an attacker does not control, such as a phone call. If the codes match, nobody is in the middle. No client computes or displays that code today, in the web app, Floe Desktop or the CLI. A link key checks for a server in the middle with nobody reading anything, but only as far as What the link key checks says: a sender may only detect, and it shows who holds the link, not who they are. A transfer joined by room code, or by a link with no link key, has neither. What you can do: use a link that carries a link key, or encrypt the file yourself before you send it. A share link or a room code is the only thing that protects a transfer. Floe has no accounts and no password step. Whoever opens the link or enters the code first, while the sender is still waiting, takes the receiving seat and gets the files. The sending app does not ask the sender to approve them. A room code is three words from a list of 1,247, so it can be guessed. The server allows each network address 10 wrong codes a minute, and a self-hosted server can set its own number. A code stops working when a receiver joins, when the sender leaves, or after 10 minutes: see How long a code and a link last. A link carries a long random room id instead of words, which is far harder to guess, and it works for as long as its sender is still waiting. A link key does not change this: whoever holds the whole link holds the link key too, and passes the check. Once a sender and a receiver on two different networks have started connecting, a device on a third network is turned away for as long as the room exists. So a link that leaks later cannot be used from somewhere else to take the receiver’s place. Devices that share one network address count as one, so a sender and a receiver on the same network are not covered. See A room holds exactly two people. The link is only as private as the channel you send it through: a mail security service that rewrites links may see all of it. See The room secret stays out of every server log. What you can do: send a link or a code only over a channel you trust, to the one person it is for. If either may have reached someone else before your receiver joined, stop the send and start a new one, which gets a new link and a new code.

The other person learns your IP address

A direct connection is two devices talking to each other, which means each one knows where the other is. That is not a leak; it is how any peer-to-peer connection works, and a video call does the same thing. With a request link, that happens as soon as the sender starts the send and the two devices connect, before the person who made the link accepts or declines. Declining does not take it back. What you can do: Floe Desktop has a Hide my IP address switch that forces every transfer through the relay, so the other side sees the relay’s IP address instead of yours. The CLI’s --relay-only flag does the same. The web app’s Send page has no equivalent, but the page a sender opens from a request link has its own Hide my IP checkbox. The trade is speed and the 2 GB session cap.

The relay sees more than the signaling server does

When a direct path is impossible, traffic goes through a TURN relay. The relay cannot read anything: the data is encrypted before it arrives and decrypted only on the recipient’s device. But the relay is an endpoint that both devices connect to, so it observes both IP addresses and the timing and size of what it forwards. On floe.one that relay is Cloudflare’s Realtime TURN network, which is infrastructure Floe does not own, and its operator may log those facts about the transfer for its own security purposes. Cloudflare also sees each device’s IP address while a connection is being set up, on every transfer: see What the relay can see. What you can do: stay on a direct connection where you can, or self-host with your own coturn relay.

The public counter is a vanity metric

The total on the homepage is fed by receivers reporting a byte count after a transfer. It is protected by a per-address rate limit and a per-report size cap, and nothing else. Anyone can post a plausible number to it. It is an honest best-effort figure, not an audited one, and it is deliberately built that way: adding real integrity would mean identifying the reporter, which is exactly what the counter avoids. It carries no file names, no per-transfer records, and no link to anyone. See Aggregate statistics.

Sending and receiving

A browser receiver is limited by what the browser can hold

The web app keeps every file it receives inside the browser until you leave the page, so that you can still save it. What counts is therefore the size of the whole transfer, not of its largest file, and how much fits depends on the browser.
  • Chrome, Edge, and other browsers built on Chromium, Chrome’s engine, in a normal window: the tab holds no more than about 16 MB of a file at a time. The browser keeps some of what has arrived in memory, about 2 GB on a computer. It moves the rest into temporary files of its own on disk, up to a limit it sets from the size of the disk, and less when the disk is nearly full. Past that limit the transfer stops and says the browser ran out of room.
  • A private or Incognito window in those browsers: nothing goes to disk, so everything received in private windows shares the memory part alone, about 2 GB on a computer and much less on a phone. Past that the transfer stops the same way, and a normal window gets further.
  • Firefox, Safari, and every browser on iPhone and iPad: every file is held in memory. A large enough transfer runs the tab out of memory, and on a phone that ceiling arrives sooner than you would like. The transfer stops and says the browser ran out of memory.
When a transfer stops this way, the files that had already arrived stay in the list and can still be saved. Zipping adds to it in every browser. Download ZIP writes the archive a piece at a time, but the archive is a second copy of the files and about as large. In Chrome or Edge in a normal window it is kept where received files are, so the limit above counts both. In a private window, in Firefox, in Safari and on iPhone and iPad it needs as much memory again. An archive of 4 GB or more, or of more than 65,534 files, is refused before any file is read. On a phone, the browser holds less than on a computer. How much depends on the phone, its free memory and its browser, and Floe cannot tell beforehand whether a large transfer will fit. The figures above were measured on computers, so on a phone the only way to know is to try. A 3 GB video, for example, may arrive whole or may stop partway with the out-of-memory message. If the connection is relayed, a transfer over 2 GB is refused before anything moves: see File size limits. While it runs, keep the Floe page open and in front on the phone until the file is saved. A phone that locks, or a switch to another app, can pause the page and end the transfer. What you can do: receive multi-gigabyte transfers with Floe Desktop or the CLI, which write straight to disk and have no such ceiling. In the browser, use a normal Chrome or Edge window, and save files one at a time rather than zipping when the total is large. When the receiver is a phone, Floe Desktop and the CLI do not run there, so the phone’s browser has to hold the files:
  • Send several large files as separate transfers, a few at a time. The receiver saves each batch, then opens the next link, which starts the page empty.
  • A single file too large for the phone has to go to a computer instead, through Floe Desktop or the CLI. Copy it to the phone from there with a cable or the phone’s own file app. Or make it smaller first, for example by exporting the video at a lower quality.
  • Put both devices on the same Wi-Fi network. The connection is then usually direct, so the 2 GB relay cap does not apply.

A transfer that stops cannot be resumed

Floe has no resume. A transfer can stop partway because a connection dropped or one side closed its app. The files that had finished are then kept, and everything else has to be sent again in a new transfer, including the file that was moving. What is kept depends on the receiver. Floe Desktop and floe receive leave the finished files in the save folder and discard the file that was arriving: see If a transfer stops partway. The web app keeps the finished files in its list until you leave the page, so save them before you reload. What you can do: keep both devices awake and online until the transfer ends. After a stop, ask which files arrived and send only the rest.

A device that sleeps ends the transfer

Closing a laptop’s lid usually puts it to sleep, and a sleeping device ends the transfer as surely as closing the app. The browser page asks to keep the screen on, and Floe Desktop asks Windows to stay awake, but closing the lid or choosing Sleep still wins. What each side of a browser transfer sees once the sleeping device wakes: Floe Desktop usually reads “The connection dropped before the transfer finished” on either side. Do not count on the old link: after a sleep it can stop working without a message on the sender’s page. Start a new transfer instead:
1

Save what arrived

The receiver saves every file that arrived. In a browser, do it before leaving the page.
2

Send the rest in a new transfer

The sender reloads the page, adds only the files that did not arrive, and shares the new link. In Floe Desktop or the CLI, send those files again.
3

Keep both devices awake

Keep both devices awake and online until it ends. On a laptop, keep the lid open and plug it in.

There are no accounts

Floe has no accounts and no sign-in, so three things do not exist:
  • a list of what you sent that follows you between devices
  • a way to take a file back once it has arrived
  • a way to watch or stop a transfer from another device
Floe Desktop keeps a History of its last 50 transfers on that PC only. The web app and the CLI keep none. What you can do: treat a file as out of your hands once the other side has it, and keep your own note of what you sent if you need one.

Saving received files

The SHA-256 check covers the transfer, not your disk afterward

Floe checks every file it receives against the size the sender announced and, when the sender provides one, against the sender’s SHA-256 of the file. It deletes a file that fails either. File checks has the whole account. Two things the check does not do:
  • It covers the file as it arrived, so it cannot notice damage that happens on your disk afterward. Floe Desktop and floe receive take the SHA-256 while they write the file and never read it back. The web app checks the copy it holds, before your browser saves it.
  • A file that comes without a SHA-256, or whose check did not finish in the time allowed, is checked against its size and nothing more. That file is kept, and the transfer finishes without the green check mark or the Verified row. See When there is no mark.
What you can do: compare hashes yourself when you need proof about the file as it sits on disk. sha256sum on Linux, shasum -a 256 on macOS, Get-FileHash on Windows.

Windows cannot keep two names that differ only by a reserved character

Windows refuses < > : " | ? * in a file name, so Floe Desktop and floe receive on Windows replace each of them with _ before writing. A name that starts like a drive, such as P:L 2025.xlsx, loses its first two characters instead and is saved as L 2025.xlsx: see Names Windows cannot represent are adjusted. Names that were distinct on the sending machine can then land on the same name. A Linux folder holding report:v1.pdf and report?v1.pdf arrives as report_v1.pdf and report_v1 (1).pdf, and nothing on disk says which was which. The numbered suffix follows the order the files arrived in, which is the order they were sent. Nothing is overwritten and nothing is lost. The receiver never replaces an existing file, so the second one is saved beside the first rather than on top of it. floe receive on macOS and Linux keeps both names as they were sent, because those characters are legal there. The web app replaces the same characters on every operating system, because a ZIP it builds may be opened on Windows. What you can do: if the distinction matters, rename the files before sending, or send to floe receive on macOS or Linux. The progress line of floe receive and the History in Floe Desktop both show the name that was actually written, so you can always tell what landed where.

Very deep or very long paths are refused

Before they write a file, Floe Desktop and floe receive refuse one whose path inside the transfer is too deep or too long, or that starts at a drive or a root. The receive stops there and tells the sender why. Files saved before it are kept, and no setting or flag turns the limits off. The numbers are in Limits. What you can do: zip deeply nested folders before sending them. See what the desktop app shows and what floe receive prints.

Floe does not scan received files

Floe does not scan received files for malware. On Windows, Floe Desktop and floe receive mark each file as downloaded, the way a browser does, so SmartScreen and Office’s Protected View, which opens a downloaded document read-only, treat it like any download. The mark needs a drive that can keep it, and not every drive does. See Received files are marked as downloads. What you can do: open files you did not expect with the same care as any download, and save to an NTFS drive, the format of a Windows PC’s own drive, when the mark matters. A drop through a request link also renames file types that File Explorer opens by itself, such as shortcuts; see Renamed files. A request link is a live transfer, not a mailbox. Files move only while Floe Desktop is open on your PC and the PC is awake and online, and only while the sender’s page, or their floe send --to, stays open. There is no resume: if the connection drops, the file that was moving starts over through a new link. A drop that runs for hours can also be cut short by Windows restarting for updates outside your active hours. What you can do: keep the PC plugged in and awake for a long drop, and set Windows Update’s active hours to cover it. A sender in a browser keeps the tab in front, and pins it for a long drop. See Request links. Floe Desktop 1.1 and later closes a request link once 5 senders on it have failed the link key check, and the next sender reads that the link has ended. So a hostile server can end your link by seating 5 failing senders. That costs you the link and nothing else, and it is denial of service such a server already had: it could refuse to introduce anyone. The 5 is not a rate limit. A sender that says nothing or leaves does not count toward it, so a server can also keep your Floe connecting for as long as the link lasts without ever reaching it. The limits on how often senders can connect are under Limits. What you can do: make another link. If it keeps happening on one server, it is worth asking who runs that server.

Windows decides where a notification appears

Floe Desktop sends standard Windows notifications, and Windows decides how they show: on which screen, in which corner, for how long, and whether Focus or Do Not Disturb hides them. Floe cannot pick a screen or a corner. A notification also stays in Notification Center after the request it announced has ended, or after you have answered it, until you clear it. Clicking a notification does not take you to the request: switch to Floe to answer it. What you can do: change how they show in Windows. In Floe’s Settings, click Open under Windows notification settings, then find Floe in the list (Floe Desktop for the Microsoft Store build). Turn notifications off there, or in Floe, if you would rather not have them. A request still flashes the taskbar button, the window title reads (1) Floe, and a notice appears inside Floe. See With notifications off.

Running your own server

A self-hosted server runs as a single process

Rooms, room codes, request-link reservations, and the rate limiters all live in one process’s memory, so two replicas behind a load balancer break pairing instead of scaling Floe out. Run one instance: see Run one instance, not several.

Reporting something that is not on this list

If you find a security issue in Floe, report it privately through GitHub Security Advisories or the contact in SECURITY.md. Please do not open a public issue before there is a fix. To report someone who sent you something harmful, see Reporting abuse.
Last modified on November 3, 2026