The short version
Your files go only to the other person’s device, encrypted end to end. Floe’s servers, and the services floe.one runs on, can see that devices connect and from which network addresses, but never a file. The only thing the server stores for good is one shared byte total, which says nothing about you and which you can opt out of.What the signaling server can see
The server atapi.floe.one exists so two devices can find each other. It never touches the file.
What it does:
- Put two devices in a room and decide which one sends. For a request link, the Floe Desktop that made it is the one that receives.
- Pass the connection-setup messages between them (WebRTC offers, answers and ICE candidates, which carry each device’s network addresses). It checks the size and shape of each one, forwards it, and keeps no copy.
- Hand out relay credentials, the temporary user name and password a device needs to use the relay, when a relay is configured. Each set is valid for 24 hours and is not tied to you or to an account. On floe.one a new set is minted from Cloudflare every few minutes and served to every client that asks in the meantime.
- Map a three-word room code to a room id when a sender asks for one, for 10 minutes at most. See How long a code and a link last.
- Check the web address each connection says it comes from (its Origin) against an allow-list. The first one it refuses on each connection path since its last restart goes to its log, with the host that connection asked for, and no other Origin does.
- Keep a single running total of bytes transferred, for the counter on the homepage.
- Receive, store, or inspect file data. File names and sizes travel between the two devices on the encrypted channel, and the only size the server is ever told is the byte total under Aggregate statistics.
- Receive a link key. An app joins a room with the room id alone.
- Keep a record of who joined which room after the room has closed.
- Log what you transfer, your file names, your IP address, a room id, or who you sent to.
What it holds, and for how long
Everything in this table lives in the server’s memory only, so a restart forgets all of it. On floe.one, neither the server nor the proxy in front of it writes any of it to a log. The byte total under Aggregate statistics is the one thing the server stores for good.
A keyed hash is a scrambled stand-in for an address. The server can tell two stand-ins apart but
cannot turn one back into the address, and after a restart they mean nothing. Technically it is an
HMAC, a hash made with a secret key, under a secret the server draws when it starts and never
stores. It covers a whole IPv4 address, or the first half of an IPv6 address (its /64), which
normally names one network rather than one device.
The room seal is what lets the server turn away a device on a third network, as
A room holds exactly two people
describes. The request-link limits themselves are listed under
Turn on request links.
What the relay can see
When a direct path is impossible, traffic goes through a TURN relay. On floe.one that is Cloudflare’s Realtime TURN network; a self-hosted instance can run its own coturn instead. The relay sees encrypted packets, plus the metadata that being the middle hop entails: both IP addresses, and the timing and size of what it forwards. The relay’s operator may log that for its own security purposes: on floe.one that is Cloudflare, not your own internet provider. The relay does not see the contents of those packets. Files are encrypted before they reach it and decrypted only on the recipient’s device. On floe.one, Cloudflare sees each device’s IP address and connection timing on every transfer, direct ones included. That is because the relay’s operator is contacted before the route is decided. While a connection is being set up, every device asks the STUN server for its public address. Unless relay fallback is off, it also reserves a standby path through the relay, a TURN allocation. File data crosses the relay only when the relay path is the one that wins. Routing through a relay also changes who learns your IP address. On a direct connection the two devices learn each other’s, which is how any peer-to-peer connection works. If you would rather not reveal yours, Floe Desktop’s Hide my IP address forces the relay path so the other person sees the relay instead, and the CLI’s--relay-only flag does
the same. Either way the transfer is then capped at 2 GB, as
File size limits explains. The web app has no equivalent, but the page a
sender opens from a request link has its own
Hide my IP (relay only, 2 GB per drop) checkbox.
With a request link, each side learns the other’s IP address as soon as the sender connects,
before anyone accepts or declines, unless that side has Hide my IP on. See
Who sees your IP address if you made the link,
and What each side can see if you are sending
through one.
The room secret stays out of every server log
A share link keeps its room id and its link key in the URL fragment, after the#, never in the query string, the part after a
? that servers do receive. A link from the web app or from Floe Desktop 1.1 and later looks like
https://floe.one/?s=a1b2c3d4#k=<link key>&room=<room id>, and the web app starts it with the
address you opened the site at. One from floe 1.12 and later looks like
https://floe.one/#k=<link key>&room=<room id>. A link from an older Floe has no k= and no link
key. The ?s= value is a random per-link marker carrying no information; it exists only so each
link is a distinct page, which stops a device that already has Floe open from reusing a stale tab.
Browsers never send the fragment to a server. So neither the room id nor the link key is in the web
host’s access logs, and Floe’s analytics is configured to leave the fragment out. Neither is in the
Referer header, the address a browser passes on to the next site when you click a link that leaves
Floe. A script running on the page can still read the fragment, which is why floe.one
runs its own copy of the analytics script.
The signaling server is the one server that does learn the room id, because devices join the room by
it. A sender in the CLI or Floe Desktop also sends it when it asks for a room code. The server holds
the room id in memory while the room is open and writes it to no log. No server receives the link
key: it stays in the link, and in the memory of the app that read it. What the link key checks, and
what it cannot see, is under
Known limitations.
floe receive, floe send --to and Floe Desktop also refuse a pasted floe.one link while they
are set to use another server, and a link from another Floe server while they use floe.one by
default. They stop before contacting any server, so in those two cases the room id never leaves
the device.
A request link from Floe Desktop 1.1 and later looks like
https://floe.one/r/<link id>#<room id>.<link key>, and one from Floe Desktop 1.0.x like
https://floe.one/r/<link id>#<room id>, with no link key. The part after the # is the request
link’s secret, and it stays out of logs the same way. The link id before it is not secret. It sits
in the address path, so it reaches the web host’s request logs. It exists so that a report, or a
block on one address, can name one link without the room id. The signaling server never receives the
link id. On that page, Sentry strips the link id from the address before a report is sent, Umami does
not load at all, and the browser sends no referrer.
A link is only as private as the way you send it. Mail security services that rewrite links may
see the whole link, the part after the # included, and whatever held the whole link can
pass the check.
Aggregate statistics
The homepage carries a public, all-time count of bytes moved across every Floe user. To feed it, the receiving side reports how many bytes it received after a transfer completes. Floe Desktop also reports a request link drop that stops after it was accepted, with the bytes of the files it saved. The sender never reports. All three receivers contribute, and the total itself is only ever displayed in the browser. What is recorded: one cumulative integer. Nothing else. What is not recorded: file names, contents, per-transfer rows, who sent or received, or any link to your identity or IP address. The reported figure is a size, added to a running total. A transfer of only empty files reports nothing. Every receiving surface can turn the report off, and turning it off means the byte count is never sent, rather than sent and ignored.- Browser: uncheck Contribute to global stats on the receiver view, where it is shown until the first file arrives. The choice is saved in that browser and applies to every later transfer in it.
- Floe Desktop: turn off Contribute to global stats in Settings. It is saved with your other settings.
- CLI: pass
--no-reporttofloe receivefor one transfer, or setFLOE_NO_STATS=1in your environment to opt out of every transfer run from it.
Error monitoring and analytics
Sentry and Umami run on the floe.one website only. The CLI and Floe Desktop contain no analytics,
no error monitoring, and no telemetry.
floe update, or Test in
Floe Desktop’s Settings), the CLI and Floe Desktop make two kinds of request on their own. Each has
an off switch: the byte-count report above, and an update check.
The update check runs once a day in Floe Desktop (GitHub builds 0.2.3 and later). The CLI does it
only when you run floe version, at most once a day, and never during a transfer. It asks GitHub
whether a newer release exists. The request carries nothing about you beyond what any web request
reveals, and nothing downloads or installs by itself. Turn off Check for updates in the
desktop Settings, or set FLOE_NO_UPDATE_CHECK=1 for
either surface, and no request is made. Microsoft Store builds never check, because the Store
updates them.
On the floe.one website:
-
Sentry records application errors and a 10% sample of page performance traces. It is
configured to attach no cookies and no IP address.
- Each report carries the page address, the browser’s user agent (its name and version), language and time zone, and the referring page.
- An error report, or a performance trace that falls in the 10% sample, may add the connection type, whether relay fallback was on, the transfer’s progress, the file count, and the total size.
- An error report, or a performance trace in the 10% sample, also carries a short trail of what happened just before it. The trail lists clicks, page changes, the addresses of network requests, and console lines, with the room link removed from each. A click is recorded without the label of what was clicked, because the labels of the Remove and Download buttons name a file.
- A report of a failed receive may also add the device’s approximate memory.
- A link key check that does not pass sends one warning,
Link key check failed. It says which side it was, how the check read the certificate, the outcome, and whether the page was set to stop on it. Each is a fixed label, with no link, no link key and nothing the other device sent.
- Session replay is not enabled and will not be added. A replay records the page address, and on a receiver page that address contains the room link, which is the only thing protecting the transfer.
-
Umami records cookieless, anonymous page views and a few events:
- Page views carry the page path, the referring site, the browser, operating system, device type, screen size, language, and a location down to city. The location is worked out from the IP address, which Umami says it does not keep.
- Transfer outcomes: file count, total bytes, whether you were sending or receiving, direct or
relay, and success or failure with one of 10 fixed reason labels. The tenth,
unverified, is a transfer that a link key check stopped. - Which download button was clicked, and the name of an in-app browser the site was opened in, with whether it was Android or iOS.
Other services floe.one relies on
Five more companies can see a request, and none of them sees a file.
An app falls back to Google’s STUN servers when the signaling server is unreachable, or is limiting
requests from your network.
A self-hosted instance uses neither of the first two. Until you configure a relay it uses Google’s
STUN servers on every connection, because the server then hands every client those two Google
STUN addresses. With a relay configured, only the fallback in the table does. See
TURN relay.
One more service never sees a request from you. The total behind the homepage counter is stored as
a single number in a Redis database at Upstash, written by Floe’s server and never by your device.
See the full Privacy Policy for the site-wide detail. It shows the
month it was last updated, and its See every change on GitHub link lists every change made to
it.
What stays on your device
Nothing in this section is sent anywhere. It is what a shared or borrowed computer still holds after a transfer, and how to remove it.In the browser
Local storage, session storage and Cache Storage are places where a browser keeps a site’s data on your device. Clearing the site’s data in your browser’s settings empties all three.
The browser uses those stored copies only when the request for that page or build file fails, and a
page or build file it fetches replaces its stored copy. A request can fail while the device is
online. Something else running on the same origin could have written a copy of its own, and a failed
request then runs it: see
What the link key cannot see. That
cache never holds a share link, a request link’s page, an answer from the server, or these docs. The web app sets no cookies, uses no IndexedDB,
writes to your clipboard only when you press a copy button and never reads it, and never asks for
notification permission. How much a browser
can hold while it receives is under
A browser receiver is limited by what the browser can hold.
In Floe Desktop
A registry key is an entry in Windows’ own settings database, and
HKCU is the part of it that
belongs to your user account. A request link itself, its link key and its secret token are held in
memory only and are never written to disk. Received files stay where they were saved until you delete them, and on a drive that
supports it Floe marks each one as a download: see
Received files are marked as downloads.
Uninstall lists what removing the app leaves behind.
On the command line
The CLI keeps no history, no log, and no settings file. It leaves two things:- The update check’s last answer.
floe versionsaves it infloe/update-check.jsoninside your user config folder and reuses it for 24 hours. See The update check. - The files you received, in the folder you named with
-o(the folder you ran the command in, by default). Each file is written as a.partbeside its final name and renamed when it is complete, and a receive that fails or is interrupted deletes the unfinished.part. One can remain after a killed process, a power loss, or a finished file that could not be renamed: see floe receive. On Windows each finished file carries the same download mark as in Floe Desktop.
floe keeps no record of a link, but your shell can. A link you pass to floe receive or
floe send --to is on the command line, link key included, so your shell keeps it in its history.
While the command runs, other users of the same computer can see it in the list of running processes.
Browser hardening
In short, no other website can show floe.one inside its own page. The rest of this section is the technical detail, and you can skip it. floe.one sends a Content-Security-Policy withframe-ancestors 'none', repeated as
X-Frame-Options: DENY for older browsers, so no other site can put Floe in an iframe.
That matters more here than on a typical site. A receiver page joins its room automatically on
load and then answers the sender’s offer with its own network addresses. An invisible embedded
copy pointed at an attacker’s room could otherwise disclose a visitor’s local and public IP
addresses without a single click.
The same headers also:
- block plugin content and
<base>rewriting - restrict form submissions to the site itself
- send
X-Content-Type-Options: nosniff - set
Referrer-Policy: strict-origin-when-cross-origin - deny camera, microphone, geolocation, payment, and USB access outright
- opt the site out of ad interest cohorts
Referrer-Policy: no-referrer instead, because its link id is in the
address path. The signaling server sends the standard hardening headers too.
On a self-hosted instance
A self-hosted server runs the same code, so everything above about what the signaling server sees and holds applies to yours, with you as the operator. The parts that name Cloudflare, Sentry, or Umami describe floe.one’s own deployment. Most of the per-address limits are yours to set: see Configuration. The signaling server never receives file data and a relay only ever forwards encrypted packets, so running your own instance gives you the same encryption and puts the infrastructure under your control. It also puts the introduction in your own hands, which is the part encryption alone cannot cover. That is not the whole of it. Your web image, and any edge in front of it, can read the link keys its pages open, and a link key is not checked on a room code or by an older app. See What the link key checks. See Self-hosting overview to run one.Reporting abuse
Floe cannot see, store or remove the files people send each other. Its server keeps no record of who joined which room once the room has closed. Relay credentials are shared by everyone who asks within the same few minutes, so Floe cannot identify a sender afterward or cut off one person’s relay access. If someone sent you something harmful:- Do not open more links or codes from that sender. A transfer starts only when you open one, and
floe receiveasks before it accepts files. - Report it on Floe’s public issue tracker. When the report includes personal details about anyone, or the content itself, send it privately instead, through the channels under Reporting a vulnerability.
- For a request link, use Report this link on its page: see
Report a link. Never post a whole request link in
public, because the part after the
#lets anyone use it.