v1.0.0 · open source · public domain · written in rust

Leech it. Lock it.
Drop it. Leave a trace.Leave nothing.

Leech a torrent on a server. Encrypt it. Hand it to one client, peer to peer. Leave nothing behind.

$ brew install asheliahut/tap/deaddrop
deaddrop — two machines, zero open ports
🖥 server
  1. $ deaddrop serve 'magnet:?xt=urn:btih:9f3c…'
  2. leeching ubuntu-24.04.iso · upload disabled
  3. ████████████████████ 100% ↑ 0 B
  4. swarm session stopped (dht, trackers, peers)
  5. age-encrypted 1 file · plaintext deleted
  6. serving over iroh · single use
  7. stdout ▸ blobafk3…q7z AGE-SECRET-KEY-1QX…
  8. client finished · disconnected
  9. single use: shutting down. nothing left.
💻 client
  1. $ export DEADDROP_KEY=AGE-SECRET-KEY-1QX…
  2. $ deaddrop fetch blobafk3…q7z -o ./dl
  3. connecting · direct via hole punch
  4. ████████████████████ 6.1 GB BLAKE3 ✓
  5. decrypting 1 file
  6. ✓ ubuntu-24.04.iso

Output abridged for illustration.

// how it works

A dead drop in two commands.

A server downloads a torrent without seeding it, encrypts the result, then hands the files to one client over iroh: peer-to-peer QUIC with NAT traversal and relay fallback.

  1. 01

    Leech

    The server downloads the torrent with uploading disabled. Nothing is seeded, ever.

  2. 02

    Seal

    The swarm session stops. Each file is age-encrypted to a new key, and the plaintext is deleted.

  3. 03

    Drop

    A ticket and a key are printed. The client connects peer to peer over iroh and pulls the ciphertext.

  4. 04

    Vanish

    The client verifies and decrypts. The single-use server shuts down, or wipes the data and waits.

// features

Small tool. Sharp edges, on purpose.

Zero seeding

Built on librqbit's disable-upload. No incoming BitTorrent listener, and the swarm session is torn down before serving starts.

End-to-end encrypted

Every file is age-encrypted with a fresh per-torrent key that never touches the server's disk. File names are hidden too.

NAT-proof transfers

iroh punches through NATs and falls back to relays, so neither side needs an open port.

Verified delivery

Every chunk is checked against its BLAKE3 hash. Tampering or a wrong key fails loudly. No partial file is left under its real name.

Single use by default

One client, one download, then the server shuts down, or wipes the data and waits for the next drop.

Hot swaps

Point a running server at a new torrent with deaddrop reset. The old ticket dies, and the new one gets a new key.

Notifications

Push tickets to Discord or any webhook, with per-notifier control over who also gets the key.

Script friendly

<ticket> <key> on stdout, everything else on stderr. TOML config, env vars, shell completions.

// what the wire sees

Even the names are redacted.

Only ciphertext goes into the blob store and over iroh. Collection entries are named 0,1, 2… and the real file names live in an encrypted manifest. The key exists only in the server's memory.

What it doesn't hide: file sizes, file count, and the fact that a transfer happened. Plaintext exists on the server's disk while the torrent downloads. Read the threat model →

collectiondeaddrop-manifest.age 🔒
  • 0Season 1/E01.mkv
  • 1Season 1/E02.mkv
  • 2extras/notes.txt
  • 3cover.jpg

hover or tap to decrypt: needs AGE-SECRET-KEY-1…

// install

Brew it, grab a binary, or build it.

On macOS and Linux, Homebrew is the easiest way: it installs the prebuilt binary with shell completions set up. Prebuilt binaries for Linux (gnu + musl, x86_64 + aarch64), macOS (Intel + Apple Silicon) and Windows (x86_64 + ARM) are attached to every GitHub release, with SHA256SUMS.

# macOS and Linux, Intel + ARM
brew install asheliahut/tap/deaddrop

Then: quick start · config file ·shell completions

Read the docs. Then leave nothing behind.