The cross-platform file transfer program that keeps your data private and secure
https://send.pfaff.dev/
- Kotlin 61.8%
- Go 16.5%
- JavaScript 14.7%
- Go Template 2.2%
- CSS 2.1%
- Other 2.7%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| android | ||
| assets | ||
| go-client | ||
| relay | ||
| screenshots | ||
| .gitignore | ||
| build.zig | ||
| build.zig.zon | ||
| constants.json | ||
| Containerfile | ||
| diceware-wordlist.json | ||
| go.mod | ||
| go.sum | ||
| justfile | ||
| mise.toml | ||
| protocol.svg | ||
| protocol.typ | ||
| README.md | ||
Secure Wormhole
The cross-platform file transfer program that keeps your data private and secure.
Building
Requirements:
- just (for server and command line client)
- Go 1.27 (for server and command line client)
- Zig 0.17.0 (used for fetching and verifying JavaScript dependencies)
- Android SDK (only for Android client)
To build the server and command line clients, assuming the relevant requirements are met, simply run just. You will then find secure-wormhole and secure-wormhole-relay binaries in the project root.
Usage
Web client
- Visit the URL of a relay server of your choice
- Select a file and optionally choose a password.
- Hit "Send"
- Share the generated URL with the receiver. Sharing over an ephemerally secure channel is critical if there is a realistic chance that your password could be guessed on the first try. Sharing the URL via QR code is a practical choice if not in visibility of a possible attacker.
- Share the password with the receiver over a secure channel. This should be easier than sharing the URL.
Command line client
Some general usage info may be obtained by running ./secure-wormhole --help, and similar with the send and recv subcommands.
- Input content for the
sendcommand is expected on fd 3 - Messages, prompts, etc. will be output on fd 2 (stderr)
- Responses to prompts for transfer URLs, passwords, and other questions are expected on fd 0 (stdin)
- Received content will be output on fd 1 (stdout)
- Temporary limitation: for the
sendcommand, only direct file inputs are supported (i.e.... 3< file-to-send, not... 3< <(echo "text to send")
How it works
- A random keypair and salt is generated client-side.
- The password and salt are used with Argon2id to derive a preshared key (PSK).
- The file is encrypted in 1 MiB chunks (each with an incrementing nonce) and streamed through a WebSocket relay.
- A shareable URL of the form
https://relay.example.com/d?t=<transfer-id>#salt=<salt>&fingerprint=<fingerprint>is displayed. - The relay never sees the salt, password, or plaintext.
- Receiver opens the link, enters the password, and the same key is derived. Encrypted chunks are decrypted on the fly and the original file is reconstructed.
Technical Details
Security Properties
- End-to-end encryption: The server only relays opaque binary blobs and ephemeral public keys.
- Ephemeral keys that are destroyed upon transfer completion ensure that a post-transfer attack on either endpoint is unable to compromise previously intercepted transfer traffic.
- HPKE provides hybrid public key encryption.
- HPKE's MODE_PSK with the aforementioned PSK authenticates the receiver
- X25519MLKEM768 (also known as X-Wing) provides hybrid post-quantum key exchange using safe curves.
- AES-GCM-256 provides authenticated encryption.
- Argon2id protects weak passwords
- HPKE's standard session construction prevents replay, reuse, and reordering of chunks
- Zero server storage: No file data is written to disk or kept in memory on the server after the transfer.