Comparison 01 of 10 · Utilities
Courier, set beside FilePizza and PairDrop
A browser-to-browser file sender with no signalling server at all. Both rivals are peer to peer too, and both are easier to use; the checklist shows where, and what Courier buys with the effort.
- Under test
- labs.llc/courier/
- Measured
- 27 Sep 2026, 20:00–20:03 UTC, local copy of build 537
- 1 labs.llc ahead
- 5 a rival ahead
- 0 level
- 2 unconfirmed
1What it is, and who it is for
Courier moves a file from one browser to another over a WebRTC data channel. Nothing is uploaded and no server keeps a copy. The browsers are introduced by two short codes, one each way, which the two people pass over a channel they already trust: a text, an email, a call.
When the last chunk arrives, both ends have hashed the file with SHA-256 and the receiver sends its digest back as a receipt. It is built for one person handing a file, or a handful, to one other person while both screens are open, with no account on either side.

2How it works
All of it runs in the page. We searched the transfer script, courier/assets/js/courier.js, for fetch(, XMLHttpRequest and sendBeacon and found none. The sender's browser builds an RTCPeerConnection that asks one public STUN server — Google's stun.l.google.com:19302 — for its outside address, or asks nobody at all when SAME NETWORK is ticked (lines 160–161).
The page waits up to four seconds for connection candidates rather than trickling them, so one code each way is the whole handshake (149–153). Each session description is compressed with the browser's CompressionStream and printed as a code beginning LC1S. (offer) or LC1R. (reply) (122–141).
The file then travels in 64 KB messages with back-pressure: sending pauses at 8 MB buffered and resumes at 1 MB (172–174, 225). WebCrypto can only hash a complete buffer, so the page carries its own streaming SHA-256 (28–30) and refuses to start unless it turns “abc” into the published digest (92–98). The folder holds no relay and no TURN server.
3The test
Times are UTC on 27 September 2026, against the local copy of labs.llc build 537. We had no second device in the loop, so the log says plainly what was not run.
| UTC | What | What came back |
|---|---|---|
| 20:00:05 | The page | HTTP 200, 49,854 bytes of HTML. |
| 20:03 | Network calls in courier.js | 0 matches for fetch, XMLHttpRequest or sendBeacon. The only outside contact is the STUN server in the connection settings. |
| 20:03 | The hand-written SHA-256 | Lines 27–91 run under macOS jsc: the empty string and the 448-bit two-block FIPS 180 vector both hashed correctly, also when fed in two pieces. |
| 20:01:36 | Share card (share.php) | HTTP 200, a 14,461-byte PNG for link previews. It plays no part in a transfer. |
| — | A real two-device transfer | Not run. No live pairing was timed; the in-tab demonstration is evidenced from its code (446–451), not from a stopwatch. |
4The checklist
Eight checks a person choosing a peer-to-peer sender would ask about. Courier leads on one outright. On five, a rival is plainly ahead. Two turn on details the rivals' pages did not settle.
- Yes: does it
- Partly: partly — read the note
- No: does not
- Not checked: not checked: no claim either way
- Not applicable: does not apply
| Check | labs.llc · under testCourier | rivalFilePizza | rivalPairDrop |
|---|---|---|---|
| No service-run server in the connectionlabs.llc ahead | Yes1 | No2 | No3 |
| Recipient only has to open a linkRival ahead | No | Yes | Partly4 |
| Still connects when a direct path is blockedRival ahead | No5 | Partly6 | Yes7 |
| Hash checked at both ends, with a receiptUnconfirmed | Yes | Not checked | Not checked8 |
| A mode with no outside contact at allUnconfirmed | Yes9 | Not checked | Not checked |
| Several people download one shareRival ahead | No | Yes | Not checked |
| Large files stream to diskRival ahead | No10 | Yes11 | Not checked |
| Devices remember each otherRival ahead | No12 | Not checked | Yes13 |
- 1Courier's only outside contact is one optional STUN request; its introduction is two hand-carried codes (courier.js:122–141, 160–161).
- 2FilePizza's README names PeerJS for WebRTC signalling.
- 3PairDrop's README describes WebSockets and its own TURN server.
- 4Devices on the same network find each other by themselves; elsewhere, pairing is by a 6-digit code, a QR code or a 5-letter public room (README).
- 5No TURN relay, by design: the transfer fails and says the network would not allow a direct pair (courier.js:228–230).
- 6The README calls its TURN server optional.
- 7“auto-connected via the PairDrop TURN server” (README).
- 8Neither rival's front page or README, as fetched, mentions a hash check; we claim nothing either way.
- 9SAME NETWORK sets an empty server list (courier.js:160).
- 10The receiver holds every chunk in memory and builds one Blob at the end (courier.js:368, 388).
- 11Streaming downloads through a Service Worker (README).
- 12Two fresh codes every time; both people must be present.
- 13“Paired devices always find each other via shared secrets” (README).
Does better
FilePizza
- A share link instead of two codes carried by hand.
- Several people can download the same share.
- Streaming downloads through a Service Worker, and several files delivered as one zip.
- An optional TURN server and an optional password.
Does better
PairDrop
- Finds devices on the same network with nothing to copy or paste.
- Paired devices find each other again later.
- Falls back to its own TURN server behind awkward NATs.
- Sends text as well as files, and installs as a PWA.
Goes further
Courier
- No signalling server of any kind: offer and answer are codes the people carry (courier.js:122–141).
- A receipt: the sender sees “DOES NOT match” if the digests differ (300–307, 377).
- SAME NETWORK skips even the STUN request (160–161).
- An in-tab demonstration that runs 256 KB through the real code path (446–451).
5Where Courier falls short
- No TURN fallback: on networks that forbid a direct pair, some corporate and carrier NATs among them, the transfer fails.
- Both people must be there at once and swap two codes by hand; no link works later.
- Nothing streams to disk, so the receiving device's memory bounds the file size.
- One sender to one receiver per pairing.
- The page says the code “self-tests against the published FIPS vectors”, plural; the self-test checks one (“abc”, courier.js:95–97). The others passed when we ran them separately, but the sentence overstates the check.
- “Then this page contacts nothing at all” is true of the transfer. The page itself still loads the site's Google Analytics tag and the estate's shared scripts (courier/index.html:206–213, 762–773).
6The verdict, by the checklist
Choose Courier when the file must not touch anyone's server and you want proof it arrived whole; choose PairDrop or FilePizza when convenience matters more.
Courier loses more rows than it wins, by deliberate design. The rows it wins are about trust: nobody's server in the introduction, a digest compared at both ends, and a switch that keeps everything on your own network. For a non-technical recipient or a strict firewall, a rival will get the file there with less fuss.
Try Courier on labs.llc