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%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-20 17:12:21 -04:00
android Update dependencies 2026-10-20 17:12:21 -04:00
assets Add Android icon 2026-06-14 15:12:52 -04:00
go-client Speed up transfers with a reduced chunk size, and fix issues with truncated transfers due to browser WebSocketStream prioritizing close messages over existing queued messages 2026-10-16 15:37:24 -04:00
relay Update dependencies 2026-10-20 17:12:21 -04:00
screenshots Update README and add protocol.typ 2026-10-13 11:41:12 -04:00
.gitignore Speed up transfers with a reduced chunk size, and fix issues with truncated transfers due to browser WebSocketStream prioritizing close messages over existing queued messages 2026-10-16 15:37:24 -04:00
build.zig Update build process, Zig is back 2026-05-31 04:21:14 -04:00
build.zig.zon Update build process, Zig is back 2026-05-31 04:21:14 -04:00
constants.json Speed up transfers with a reduced chunk size, and fix issues with truncated transfers due to browser WebSocketStream prioritizing close messages over existing queued messages 2026-10-16 15:37:24 -04:00
Containerfile Speed up transfers with a reduced chunk size, and fix issues with truncated transfers due to browser WebSocketStream prioritizing close messages over existing queued messages 2026-10-16 15:37:24 -04:00
diceware-wordlist.json Rewrite relay /ws handler, reduce id len to 8 bytes, add send functionality to go-client 2026-10-12 01:07:29 -04:00
go.mod Update dependencies 2026-10-20 17:12:21 -04:00
go.sum Update dependencies 2026-10-20 17:12:21 -04:00
justfile Speed up transfers with a reduced chunk size, and fix issues with truncated transfers due to browser WebSocketStream prioritizing close messages over existing queued messages 2026-10-16 15:37:24 -04:00
mise.toml Use HPKE with ephemeral keypairs to secure transmissions against a theoretical "store now, intercept key later" attack 2026-05-31 04:04:23 -04:00
protocol.svg Update protocol 2026-10-15 13:34:35 -04:00
protocol.typ Update protocol 2026-10-15 13:34:35 -04:00
README.md Fix backwards fd numbers for stdin and stdout in README (whoopsie) 2026-10-17 23:22:29 -04:00

Secure Wormhole

The cross-platform file transfer program that keeps your data private and secure.

The send page

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

  1. Visit the URL of a relay server of your choice
  2. Select a file and optionally choose a password.
  3. Hit "Send"
  4. 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.
  5. 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 send command 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 send command, 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.

Protocol

Protocol