Back to Floe

Legal

Privacy policy

Last updated
See every change on GitHub

How the transfer works

When you send a file, we use WebRTC to open a connection between you and the recipient. Our signaling server introduces the two devices and then steps aside. In most cases, data then flows directly between the two devices, whether each one is a browser, Floe Desktop, or the CLI. When a direct path is not available, a TURN relay (on floe.one, Cloudflare's network) bridges the connection. Even through the relay, files remain encrypted and are never stored.

The encryption is WebRTC's own (DTLS), with keys that exist only on the two devices for that one transfer. The certificate fingerprints the two devices use to agree on those keys pass through our signaling server, so that server is trusted to introduce you honestly, and no Floe client shows a verification code to check it today. This is true of every WebRTC application; the full list of what Floe does not protect against is in Known limitations.

Peer-to-peer means the two devices connect to each other, so on a direct connection each learns the other's IP address, in the same way a video call does. A receiver page joins the room as soon as the link opens, with no click, and your browser begins exchanging its network addresses with the sender at that point. If you would rather not reveal your address to the other person, Floe Desktop's "Hide my IP address" setting and the CLI's --relay-only flag (or FLOE_RELAY_ONLY=1) route your side through the relay instead, so the other person sees the relay's address; relayed transfers are slower and capped at 2 GB. The web app has no equivalent: its "Network relay fallback" checkbox does the opposite, turning the relay off so only a direct connection is attempted.

Information we collect

Files

We do not collect or store any files.

Connection setup

To pair two devices, our signaling server receives the room id and relays the WebRTC connection messages (offers, answers, and ICE candidates, which carry each device's network addresses) between them. It forwards them from memory, unread, and keeps nothing once the room closes. When the sender uses the CLI or Floe Desktop, the server also maps a random three-word code to the room id for ten minutes.

Metadata

Filenames and sizes travel between the two devices over the encrypted data channel so the receiver can see what is arriving. They never reach our signaling server. On a relayed transfer they pass through the relay in the same encrypted stream as the file data, which the relay cannot read.

Byte total

When a transfer completes, the receiving side reports only the number of bytes it received. We add this to one shared, all-time counter of total bytes transferred, shown on our homepage and stored as a single number in a hosted Redis database (Upstash). The sender never reports. We do not store file names, file contents, or any link between this number and you.

You can opt out: uncheck "Contribute to global stats" on the receiver view in the browser or in Floe Desktop's Settings, or use --no-report (or set FLOE_NO_STATS=1) with the CLI. This switch covers the public counter and nothing else: on floe.one the site analytics described under Usage analytics record a transfer's outcome separately, and the desktop app and the CLI send no such event.

IP addresses

Our signaling server keeps your IP address in memory only long enough to rate-limit abuse (at most about two minutes after your last request), and the server itself writes no log of it. Like all web servers, the reverse proxy in front of it and the providers that host us may log connection request IP addresses for security and abuse prevention. We do not link any of this to your identity. Who else sees your IP address, and how to hide it from the other person, is under How the transfer works.

Third-party services

Floe uses third-party infrastructure providers for hosting and connection setup. The web app is hosted on Vercel and the signaling server runs on Microsoft Azure. Connection setup uses Cloudflare on every transfer, direct or relayed, and relayed file data passes through Cloudflare's TURN network; Relay server below says exactly what it sees. If your app cannot get relay credentials from our signaling server, because the server is unreachable, has rate limited you, or has none to hand out at that moment, it falls back to Google's public STUN servers (stun.l.google.com) to learn its own public address. Google then sees your IP address and nothing about the transfer; STUN carries no file data.

The running total behind the homepage counter is stored in a hosted Redis database (Upstash); the only thing ever written there is that single number, by our server, never by your device. Floe Desktop and the CLI contact GitHub only to check for or download updates: Floe Desktop's GitHub build once a day unless you turn the check off (Microsoft Store builds never check), and the CLI only when you run floe version, floe update, or floe update --check. Both are described in their own sections below. The documentation at floe.one/docs is served by Mintlify, a third-party documentation host: requests to those pages reach Mintlify, its pages load Mintlify's own scripts, and Mintlify may collect page-view data there.

For usage analytics we use only Umami, which is cookieless and does not track you across sites, and for error monitoring we use Sentry. Both apply to the floe.one website only and are described below; the desktop app and the CLI have neither, and a self-hosted copy of Floe ships with both turned off.

The link you share carries its room id in the URL fragment (the part after the #). Browsers never include the fragment in HTTP requests, so it stays out of hosting logs and referrer headers, and our analytics script is configured to drop it as well; what our error monitoring receives is described under Error monitoring. Links from the web app and Floe Desktop also carry a short random marker before the # (the ?s= part); it exists only so each link opens as a distinct page, it says nothing about you or the room, and links printed by the CLI do not carry one. Our signaling server receives the room id when your app joins the room to be paired with your peer, and, for a transfer started from the CLI or Floe Desktop, when it registers the three-word code that stands in for the link. Both are held in memory only: the room is dropped when the last device leaves it, and a code's mapping to its room id expires ten minutes after the code was made. Neither is written to a log.

Please refer to each provider's privacy policy regarding data handling: Vercel, Microsoft, Cloudflare, Mintlify, and the Sentry and Umami policies linked in the sections below.

Relay server

When a direct connection cannot be established, file data is routed through Cloudflare's TURN relay network (turn.cloudflare.com). The relay forwards encrypted packets it cannot decrypt: the keys exist only on the two devices. It does not store or inspect file contents. Relay sessions are limited to 2 GB per session.

Cloudflare is involved before the route is decided, not only when a relay is needed. To find a working path, your app asks Cloudflare's STUN server (stun.cloudflare.com) for its own public address and, unless you turned relay fallback off in the browser or passed --no-relay to the CLI, opens a standby relay allocation on turn.cloudflare.com. That happens on every transfer, whichever route wins, so Cloudflare sees your IP address and the timing of the connection attempt on every transfer. File data crosses Cloudflare only when the relay is the route that wins.

Before connecting, your app fetches relay credentials from our signaling server. On floe.one these are one shared credential set, minted from Cloudflare and valid for 24 hours, served to every client until we refresh it. They are not tied to you, your device, or an account.

The relay is a hop both devices connect to, so Cloudflare sees both IP addresses and the timing and amount of data it forwards, and may log that for its own security purposes. See Cloudflare's privacy policy for its handling of that data.

Error monitoring

The web app uses Sentry to monitor application errors and a 10% sample of page performance traces. This applies to the floe.one website only; the desktop app and the CLI contain no error monitoring. Sentry is configured to attach no cookies and no IP address to what it sends; each report does carry the page address, your browser's user agent, and the page that referred you. It may capture:

  • Error stack traces and browser metadata (browser version, OS, device type, language, and time zone)
  • Connection type (direct or relay), transfer progress, file count, and total size at the time of an error

Session replay is not enabled. Floe used to record a sample of browser sessions. A recording reported the page address, and on a receiver page that address contains the room link, so replay was removed rather than kept: the room link is the only thing protecting a transfer.

Sentry does not capture file names, file contents, or any personally identifiable information. The room link is stripped from every error report, breadcrumb, and performance trace before it is sent. Sentry Privacy Policy.

Usage analytics

The web app uses Umami Cloud, the hosted analytics service run by Umami's makers, to understand how the service is used. This applies to the floe.one website only; the desktop app and the CLI contain no analytics. Umami collects:

  • Transfer outcomes: for a completed transfer, the number of files, the total size in bytes, whether you were sending or receiving, and whether the connection was direct or relayed; for a failure, your role, one of five fixed labels (for example "ice-failed"), and, when you were sending, the file count and total size
  • Which download button you clicked on the download page, the homepage, or the navigation bar, and, when Floe opens inside another app's built-in browser (such as Facebook or Instagram), the name of that app and whether it was Android or iOS
  • Standard page view data: the page path, referring site, browser, operating system, device type, screen size, language, and approximate location (country, region, and city), derived from your IP address by Umami, which does not store the address

Umami does not use cookies and stores nothing on your device. It does not collect personally identifiable information: your IP address and browser signature are hashed with a salt that rotates on a schedule, so Umami can count a returning visitor for a while without keeping the address, and the hash is tied to floe.one, so it cannot follow you to other websites. File names and file contents are never recorded. It is configured to drop the URL fragment and the query string before reporting a page view, so the room link never reaches it. If your browser sends the Do Not Track signal, Umami records nothing at all for your visit. Umami Privacy Policy.

The web app on your device

The web app keeps a few small things in your browser, and none of them leave it. So that floe.one loads quickly and can open without a network, your browser caches the app's pages and build files, the same files every visitor downloads; that cache never holds file data or anything from our API. Your "Contribute to global stats" choice is remembered in this browser's local storage, and a timestamp is kept for the life of the tab to avoid reload loops after a new version is deployed.

Files you receive are held in the tab's memory until you download them; closing the tab discards anything you did not save, and nothing about a transfer's files is written to browser storage. While a transfer is running the app asks your browser to keep the screen awake and releases that when the transfer ends. The app writes to your clipboard only when you press Copy and never reads it, and it never asks for notification permission.

The desktop app

Floe Desktop runs the same peer-to-peer engine as the CLI and speaks the same protocol as the web app, as a Windows application. It talks to the same servers the web app does: it contacts our signaling server to pair you with your peer (and to register the three-word code it shows), fetches relay credentials, and then streams file data directly between devices, or through the relay described above when no direct path exists or when "Hide my IP address" is on. Two more requests happen only when you ask for them: the Test button in Settings contacts only the server address you typed, and if the Microsoft WebView2 runtime that draws the interface is missing, the app offers to download it from Microsoft before it starts. It contains no analytics, no error monitoring, and no telemetry.

It makes two optional requests, each with its own off switch: the same anonymous byte total described above, which you can turn off with "Contribute to global stats" in Settings, and a once-a-day update check. The update check (GitHub builds 0.2.3 and later) asks GitHub whether a newer release exists; it carries nothing about you beyond what any web request reveals (your IP address and a user agent), nothing about your files or transfers is in it, and nothing downloads or installs by itself. Turn off "Check for updates" in Settings (or set FLOE_NO_UPDATE_CHECK=1, the same variable the CLI honors) and no request is made at all. Microsoft Store builds never check; the Store updates them itself.

It also adds one privacy switch the web app does not have. On a direct connection the other device learns your IP address, as in any peer-to-peer connection. Turn on "Hide my IP address" in Settings and the app uses only the relay, so the other person sees the relay's address instead; relayed transfers are slower and capped at 2 GB.

Everything else it does stays on your device:

Clipboard

The app reads your clipboard only when you paste (Ctrl+V) to stage copied files or a screenshot for sending. It never reads the clipboard in the background.

Files

Received files are written to the folder you choose (your Downloads folder by default). Nothing is uploaded anywhere.

History

The app keeps a list of your last 50 transfers on your device: for each one, the direction, the file names (for a received transfer, the names as saved and the folder they went to), the file count, the total size, and the time. It never leaves your device. Remove one entry with Remove inside its row, or all of them with Clear in the History view. Reset in Settings and uninstalling the app both leave it in place.

Settings

Your preferences are stored locally in your user profile and are not removed automatically when the app is uninstalled.

Right-click menu

The GitHub build adds a "Send with Floe" entry to the File Explorer right-click menu. It is on by default: the first time the app runs it writes one per-user registry key (with a command subkey) that holds the app's location, and if you move the app it rewrites that key at the next start. Turn off "Show in right-click menu" in Settings and the key is removed; the installer's uninstall removes it too. The portable zip has no uninstaller, so turn the switch off before deleting that build. The Microsoft Store build does not offer this entry.

Notifications

The app shows standard Windows notifications when a transfer completes or fails.

When installed from the Microsoft Store, installation and automatic updates are handled by Microsoft; see Microsoft's privacy statement for what the Store itself collects.

The CLI

The floe command line tool runs the same peer-to-peer engine as Floe Desktop. floe send and floe receive contact our signaling server (api.floe.one, or the server you name with --server or FLOE_SERVER) to register or look up a short code, fetch relay credentials, and pair you with your peer, and then stream file data directly between devices, or through the relay described above when no direct path exists. The CLI contains no analytics, no error monitoring, and no telemetry, and it keeps no history or log of your transfers. It makes two optional requests, each with its own off switch:

Byte total

After a transfer completes, floe receive sends the number of bytes it received to the signaling server it used, so on a self-hosted server the count goes to that server and not to us. Pass --no-report, or set FLOE_NO_STATS=1, and no request is made. floe send never reports.

Update check

floe version asks GitHub whether a newer release exists when you run it, at most once a day (the answer is kept for 24 hours in a small file in your user configuration folder). The request carries nothing about you beyond what any web request reveals (your IP address and a generic user agent); nothing about your files or transfers is in it, and nothing downloads or installs by itself. Set FLOE_NO_UPDATE_CHECK=1 and no request is made. floe send and floe receive never check, and a build compiled from source never checks.

floe update is you asking for an update: it fetches the latest release, its checksum file, and the archive for your platform from GitHub, verifies the SHA-256 checksum, and replaces the binary. On an install managed by Homebrew, Scoop, or Winget it stops before any request and points you at your package manager instead. Received files go to the folder you pass with -o (the folder you ran the command in, by default), and --relay-only (or FLOE_RELAY_ONLY=1) routes your side through the relay so the other device sees the relay's address instead of yours.

Contact & reports

Questions about this policy, bug reports, and abuse reports go to our public issue tracker: github.com/jannskiee/floe/issues. Two things should not go there. If you have found a security vulnerability, report it privately through GitHub's Report a vulnerability form or the email address in our security policy, so the problem is not public before there is a fix. If an abuse report includes personal details about anyone, or copies of the content itself, send it through those same private channels instead of opening a public issue.

Because Floe is peer-to-peer, transferred content never sits on our servers: a direct transfer never touches them, and a relayed one passes through the relay only as encrypted packets it cannot read. We cannot see, store, or remove files that users send to each other. What we can do in response to a report is limited, and we would rather say so plainly. Our signaling server keeps no record of who joined which room, and the relay credentials it hands out are shared by every client rather than issued per person, so we cannot identify a particular sender after the fact or cut off their relay access alone. Per-address rate limits apply to everyone automatically, and that is the extent of what the service does on its own.

If you believe Floe is being used to send you illegal or harmful content, do not open further links or codes from that sender (a transfer only starts when you open one, and the command line asks before it accepts) and report the details as described above.