> ## Documentation Index
> Fetch the complete documentation index at: https://www.floe.one/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Known limitations

> An honest list of what Floe does not protect against: what the signaling server could do, what the relay sees, and why there is no verification code yet.

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 signaling server is trusted to introduce you honestly

Two devices agree on encryption keys by exchanging certificate fingerprints, and those
fingerprints travel through the signaling server during setup. The server cannot read your files
and never sees one. But a server that wanted to could hand each side a fingerprint it controls
instead of the other peer's, sit in the middle, and decrypt everything.

This is true of every WebRTC application, not just Floe. It is the reason the next item matters.

**What you can do:** [run your own instance](/docs/self-hosting/overview). A server you operate is a
server you do not have to trust in this sense. 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

The usual defense against the previous item is a short code, derived from both certificate
fingerprints, that the two people read to each other over a channel an attacker does not
control. If the codes match, nobody is in the middle.

Floe still carries the code that derives it, but nothing calls it. **No client computes or
displays it today.** It has been added and
removed more than once, and it is not in the web app, Floe Desktop, or the CLI as it ships now.
Until one of them shows it, there is no way to detect the attack described above from inside
Floe.

**What you can do:** for anything where that attack is part of your threat model, self-host, or
encrypt the file yourself before you send it.

## 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.

**What you can do:** Floe Desktop has a [Hide my IP address](/docs/desktop/settings#hide-my-ip-address)
switch that forces every transfer through the relay, so the other side sees the relay's address
instead of yours, and the CLI's `--relay-only` flag does the same. The web app has no
equivalent. 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 peers 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 your provider may log that metadata for
its own security purposes.

**What you can do:** stay on a direct connection where you can, or self-host with
[your own coturn relay](/docs/self-hosting/turn-relay).

## A browser receiver holds each file in memory

The web app keeps each incoming file in memory until that file is complete, then hands it to
your browser as a download. A large enough file exhausts the tab, and on a phone that ceiling
arrives sooner than you would like.

Zipping compounds it: **Download ZIP** holds every file at once before it writes the archive.

**What you can do:** receive multi-gigabyte transfers with [Floe Desktop](/docs/desktop/receiving) or
the [CLI](/docs/cli/receive), which write straight to disk and have no such ceiling. In the browser,
save files one at a time rather than zipping when the total is large.

## Floe does not hash your files

A completed file is checked against the size the sender announced, and a mismatch in either
direction discards it. That catches a truncated or over-run transfer, which is what actually
happens when a connection dies partway.

It is not a content checksum. Floe computes no hash of file contents anywhere, so the docs never
claim one. In practice the transport already authenticates every packet, so silent corruption in
flight is not the gap; the gap is that Floe cannot tell you the file on disk is byte-identical to
the one that left.

**What you can do:** compare hashes yourself when that guarantee matters. `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 a receiver running on Windows replaces
each of them with `_` before writing. 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. macOS and Linux receivers
keep both names as they were sent, because those characters are legal there.

**What you can do:** if the distinction matters, rename the files before sending, or send to
a macOS or Linux receiver. The progress line and the transfer history both show the name that
was actually written, so you can always tell what landed where.

## 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-IP 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](/docs/security-privacy#aggregate-statistics).

## A self-hosted server runs as a single process

Rooms, room codes, and the rate limiters all live in one process's memory. Running two replicas
behind a load balancer does not scale Floe out; it silently breaks pairing, because the two
peers can land on different replicas and never see each other.

**What you can do:** run one instance. A single small server handles a lot, since file data
never passes through it. If you need redundancy, use a failover pair rather than a load-balanced
set, and accept that in-flight transfers do not survive the switch.

## Reporting something that is not on this list

If you find a security issue in Floe, report it privately through
[GitHub Security Advisories](https://github.com/jannskiee/floe/security/advisories/new) or the
contact in [SECURITY.md](https://github.com/jannskiee/floe/blob/main/SECURITY.md). Please do not
open a public issue before there is a fix.


## Related topics

- [Frequently asked questions](/docs/faq.md)
- [Security and privacy](/docs/security-privacy.md)
- [Relay fallback when a direct connection is impossible](/docs/how-it-works/relay-connection.md)
- [How your files are encrypted](/docs/how-it-works/encryption.md)
- [Data-channel transfer protocol](/docs/reference/transfer-protocol.md)
