Developers
Command line and API
The command line tool speaks the same encrypted protocol as the browser, so a terminal can send to a phone and a browser can send to a server.
Send from the terminal
npx ferry-send send report.pdf photos/The tool prints a QR code, a link and a 6-digit code. Open any of them on the other device. Folders are sent with their structure. The command exits when every file has arrived.
Receive in the terminal
npx ferry-send receive 482107
npx ferry-send receive "https://ferry.sholajegede.com/room/…#k=…" --out ~/DownloadsWhen you join with a 6-digit code, the terminal shows a security code. The sender confirms the same code on their screen before the transfer starts.
Options
--out <folder>sets where received files are written. The default is the current folder.--keepkeeps a send open for more receivers after the first one finishes.--yesadmits devices that join with the 6-digit code without asking. Use it only on a network you trust.--serverand--sitepoint the tool at your own deployment.
HTTP API
The API creates and inspects transfers. File data never passes through it. Devices still connect to each other directly and encrypt everything they send.
Create a transfer
POST https://ferry.sholajegede.com/v1/rooms
Content-Type: application/json
{
"roomId": "<16 random bytes, base64url>",
"joinTokenHash": "<sha-256 hex of your join token>",
"name": "Build server"
}The response has what a client needs to host the transfer:
{
"roomId": "p1Qm…",
"code": "482107",
"expiresAt": 1791547200000,
"deviceId": "…",
"deviceSecret": "…"
}Create the link secret yourself: 32 random bytes in base64url. Derive the join token from it with HKDF-SHA-256, using 32 zero bytes as the salt and ferry/join/<roomId> as the info string, and send the SHA-256 of the base64url token. The link to share is https://ferry.sholajegede.com/room/<roomId>#k=<link secret>. The secret stays in the part after the # sign, which is never sent to a server.
Check a transfer
GET https://ferry.sholajegede.com/v1/rooms/<roomId>
{ "roomId": "p1Qm…", "status": "open", "expiresAt": 1791547200000, "devices": 2 }How the encryption works
- Each pair of devices runs an ECDH P-256 key exchange. The joining device commits to its key first, so a short code is enough to detect an attacker in the middle.
- Keys are derived with HKDF-SHA-256. When the receiver joins with the full link, the secret in the link is mixed in, and the server never sees that secret.
- Every piece of a file is sealed with AES-256-GCM. Each piece carries its file and its position, so pieces cannot be swapped, replayed or cut short without being detected.
- Connection setup messages that pass through the server are sealed with a separate key from the same exchange.