deadrop 0.2.0

Zero-knowledge encrypted dead drop. One binary. One command. Gone.
Documentation

What is this?

Remember in spy movies when someone leaves a briefcase under a park bench, and someone else picks it up later? That's a dead drop.

This is that, but for files. And the briefcase is encrypted with military-grade cryptography. And the park bench self-destructs after pickup. And nobody β€” not even the bench β€” knows what's inside. And now the bench can hide on the dark web. πŸ§…

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   You   β”‚                                          β”‚  Friend β”‚
    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                                          β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
         β”‚                                                    β”‚
         β”‚  ded ./secret-plans.pdf                            β”‚
         β”‚  ━━━━━━━━━━━━━━━━━━━━━━━                           β”‚
         β”‚                                                    β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
    β”‚                  πŸ”’ Your Machine                        β”‚β”‚
    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚β”‚
    β”‚  β”‚ Encrypt  │───►│  Ciphertext  │───►│ HTTP Server  β”‚  β”‚β”‚
    β”‚  β”‚ (Rust)   β”‚    β”‚  (on disk)   β”‚    β”‚ (Axum)       β”‚  β”‚β”‚
    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚β”‚
    β”‚       πŸ”‘ Key goes in URL #fragment          β”‚          β”‚β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β”‚
         β”‚                                        β”‚           β”‚
         β”‚  πŸ“² Sends link via Signal / QR scan    β”‚           β”‚
         β”‚ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─►│           β”‚
         β”‚                                        β”‚           β”‚
         β”‚                                  β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”   β”‚
         β”‚                                  β”‚  Opens URL   β”‚β—„β”€β”€β”˜
         β”‚                                  β”‚  in browser  β”‚
         β”‚                                  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                                        β”‚
         β”‚                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚                          β”‚  πŸ“¦ Browser fetches blob  β”‚
         β”‚                          β”‚  πŸ”‘ Extracts #key         β”‚
         β”‚                          β”‚  ⚑ WASM decrypts locally  β”‚
         β”‚                          β”‚  πŸ’Ύ File downloads         β”‚
         β”‚                          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚                                        β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
    β”‚  πŸ’₯ Self-destruct triggered       β”‚         β”‚
    β”‚  πŸ”₯ Drop marked as burned         β”‚         β”‚
    β”‚  πŸ›‘ Server shuts down             β”‚         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
         β”‚                                        β”‚
         β–Ό                                        β–Ό
    What file? There was no file.           Got it. Thanks. πŸ‘

Features

Core β€” the stuff that makes your IT department nervous

Feature Description
πŸ” End‑to‑end encrypted XChaCha20‑Poly1305. The server never sees the key. Like a blind courier.
πŸ”— Key in URL fragment The #key part never hits server logs, proxies, or HTTP headers. It's a ghost.
πŸ’₯ Self‑destruct Expire by time, by download count, or both. This message will self‑destruct in...
πŸ“± Works on phones Receiver only needs a browser. No app. No account. No soul‑selling signup.
πŸ“ Send folders Directories auto‑pack to .tar.gz. Your entire homework/ folder, encrypted. πŸ“š
πŸ“¦ Multi‑file drops ded file1.txt file2.pdf photos/ β€” bundles everything into one encrypted drop.
πŸ“‹ Stdin / clipboard echo "secret" | ded - β€” pipe anything. Your terminal is the dead drop.
♾️ Unlimited file size Streams from disk β€” your 50GB file won't eat your RAM for breakfast.
πŸ”‘ Password protection Argon2id key derivation (64MB memory‑hard, GPU‑resistant). Receiver gets a password prompt in-browser β€” key is derived client-side. The server never sees the password OR the key. Fort Knox mode. 🏰
πŸ“² QR code Because typing URLs is for people who still use fax machines.
πŸ“₯ Receive mode ded receive β€” phone‑to‑PC uploads. Your phone becomes the dead drop.
πŸ§… Tor hidden service --tor β€” generates a .onion address. The dark web called, it wants its files.
πŸ“¦ Single binary No runtime, no Docker, no config files. Just one executable. Like a katana. πŸ—‘οΈ

Security Hardening β€” because paranoia is a feature

Feature Description
πŸ‘» Fragment auto‑clear #key is stripped from the URL bar and history the instant the page loads. Poof.
πŸ”’ IP pinning Download is locked to the first IP that connects β€” everyone else gets a 403 slap.
πŸ›‘ Security headers CSP, X-Frame-Options: DENY, no-referrer, no-cache. The whole paranoia buffet.
⏱ Rate limiting 2 req/sec per IP with burst of 5 β€” stops brute‑force nerds in their tracks.
🎯 16‑char drop IDs ~2⁢⁴ possible IDs β€” you'll win the lottery before guessing one.
πŸ• Constant‑time 404s Random delay on not-found β€” prevents timing‑based detective work.
πŸ”₯ Burn page Late visitors see "πŸ”₯ This drop was already downloaded and destroyed." Savage.
⏰ Auto‑expire page Tab stays open past expiry? Key nuked from JS memory. The UI self‑destructs.
🧠 Memory locking mlock() on Unix prevents the key from being swapped to disk. It lives in RAM or dies.
πŸ—‘ Zero‑write deletion Encrypted temp files get overwritten with zeros before rm. CSI can't touch this.
🧹 Key zeroization Key wiped from RAM (via zeroize) on drop, both server and browser. Clean exit.

Installation

πŸš€ One-line install (Linux/macOS)

curl -fsSL https://raw.githubusercontent.com/Karmanya03/Deadrop/main/install.sh | bash

Detects your OS & architecture automatically, downloads the right binary, and adds it to your PATH. Magic.

Download a binary

Grab the latest release for your platform from Releases.

Platform Binary Architecture
Windows ded-windows-x86_64.exe x86_64
Linux ded-linux-x86_64 x86_64 (musl, static)
Linux ded-linux-aarch64 ARM64 (Raspberry Pi, etc.)
macOS ded-macos-x86_64 Intel
macOS ded-macos-aarch64 Apple Silicon (M1/M2/M3/M4)

Quick install (Linux/macOS):

# Linux x86_64
curl -L https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-linux-x86_64 -o ded && chmod +x ded && sudo mv ded /usr/local/bin/

# macOS Apple Silicon
curl -L https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-macos-aarch64 -o ded && chmod +x ded && sudo mv ded /usr/local/bin/

Via cargo

cargo install deadrop

Build from source (for the trust‑no‑one crowd)

git clone https://github.com/Karmanya03/Deadrop.git
cd Deadrop
cargo build --release
# Binary at: target/release/ded

πŸ”„ Update

# Linux/macOS β€” same as install, overwrites the old binary
curl -fsSL https://raw.githubusercontent.com/Karmanya03/Deadrop/main/install.sh | bash

# Via cargo
cargo install deadrop --force

πŸ—‘ Uninstall

# If installed via script
rm -f ~/.local/bin/ded && echo "deadrop removed ☠"

# If installed to /usr/local/bin/
sudo rm -f /usr/local/bin/ded && echo "deadrop removed ☠"

# If installed via cargo
cargo uninstall deadrop

Usage

The basics β€” impress your friends in 3 seconds

# Send a file β€” that's it, that's the whole tool
ded secret.pdf

# Send a folder β€” auto-archives to .tar.gz
ded ./tax-returns-2025/

# Send multiple files β€” bundles into one drop
ded passwords.csv nudes.zip plans.pdf

# Pipe from stdin β€” your clipboard is a dead drop
echo "the password is swordfish" | ded -
cat ~/.ssh/id_rsa | ded -

Receive mode β€” your phone becomes the dead drop

# Open upload page on your LAN β€” scan QR from phone
ded receive

# Save to a specific folder
ded receive -o ~/Downloads/

# Custom port, no QR
ded receive -p 9090 --no-qr

Scan the QR from your phone β†’ pick a file β†’ encrypted in-browser β†’ sent to your PC β†’ decrypted β†’ saved. One upload, then πŸ’₯ server self-destructs.

Password mode β€” true zero‑knowledge πŸ”‘

# Share a file with a password
ded secret.pdf --pw "correct-horse-battery-staple"

How it works:

  1. Server encrypts the file with a key derived from your password via Argon2id (64MB, 3 iterations)
  2. The URL contains the salt (not the key) β€” so the link alone can't decrypt anything
  3. Receiver opens the link β†’ sees a πŸ”‘ password prompt β†’ enters the password
  4. Browser derives the same key via Argon2id in WASM (same params, runs client-side)
  5. File decrypts locally. Server never sees the password or the key. Ever.

πŸ’‘ Pro tip: Send the link over Slack, tell them the password on a phone call. Two channels = maximum paranoia.

The spicy options 🌢️

# Self-destruct after 1 download, expire in 10 minutes
ded evidence.zip -n 1 -e 10m

# 30-second self-destruct. Blink and it's gone.
ded confession.txt -e 30s

# Go full Mission Impossible
ded plans.pdf -n 1 -e 30s --pw "this-message-will-self-destruct"

# Go full dark web spy πŸ•΅οΈ
ded whistleblower-docs.pdf --tor

# Receive via Tor β€” your phone uploads through the shadow realm
ded receive --tor -o ~/secrets/

What you see

     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—
     β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—
     β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—  β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•
     β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•  β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘   β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β•β•
     β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘  β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ•‘
     β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β•β•šβ•β•  β•šβ•β•β•šβ•β•β•β•β•β• β•šβ•β•  β•šβ•β• β•šβ•β•β•β•β•β• β•šβ•β•
          ⚑ zero-knowledge encrypted file sharing ⚑

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  URL  http://192.168.1.42:8080/d/a3f9c1b2#xK9m  β”‚
  β”‚                                                   β”‚
  β”‚  β”œβ”€ File       secret.pdf                         β”‚
  β”‚  β”œβ”€ Size       4.2 MB                             β”‚
  β”‚  β”œβ”€ Expires    10m                                β”‚
  β”‚  β”œβ”€ Downloads  1                                  β”‚
  β”‚  β”œβ”€ πŸ”‘ Password  yes (Argon2id)                   β”‚
  β”‚  └─ Crypto     XChaCha20-Poly1305                 β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  πŸ§… Tor: http://abc...xyz.onion/d/a3f9c1b2#pw:...   ← (with --tor)

  β–ˆβ–€β–€β–€β–€β–€β–ˆ β–€β–€β–€β–ˆβ–„β–ˆ β–ˆβ–€β–€β–€β–€β–€β–ˆ     <- QR code appears here
  β–ˆ β–ˆβ–ˆβ–ˆ β–ˆ β–ˆβ–€β–ˆ β–€β–„  β–ˆ β–ˆβ–ˆβ–ˆ β–ˆ        scan with phone πŸ“±
  ...

What the receiver sees (password-protected drop)

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  DEADROP  encrypted dead drop            β”‚
  β”‚                                          β”‚
  β”‚  πŸ“„ File       secret.pdf                β”‚
  β”‚  πŸ“¦ Size       4.2 MB                    β”‚
  β”‚  ⏰ Expires    59m                       β”‚
  β”‚  πŸ” Encryption XChaCha20-Poly1305        β”‚
  β”‚                                          β”‚
  β”‚  πŸ”‘ This drop requires a password        β”‚
  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
  β”‚  β”‚ Enter password...               β–ˆβ”‚    β”‚
  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
  β”‚                                          β”‚
  β”‚  [ πŸ”“ Unlock & Download ]               β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

After entering the correct password, Argon2id runs in WASM (~2-5 seconds on the derivation), then the file decrypts and downloads. Wrong password? Decryption fails gracefully β€” try again.

πŸš€ Demo Commands β€” Try Every Feature

Run one at a time β€” each starts a server. Ctrl+C to stop, then run the next.

# Feature Command What happens
1 πŸ“„ Single file ded secret.pdf Encrypts β†’ serves link β†’ browser decrypts β†’ πŸ’₯
2 πŸ“ Folder ded ./my-folder/ Archives β†’ encrypts β†’ serves .tar.gz
3 πŸ“¦ Multi-file ded file1.txt file2.pdf pics/ Bundles all β†’ one encrypted archive
4 πŸ“‹ Stdin pipe echo "swordfish" | ded - Reads stdin β†’ drops as clipboard.txt
5 ⏱ Custom expiry ded file.txt -e 5m Auto-expires after 5 minutes
6 πŸ”’ Download limit ded file.txt -n 3 Self-destructs after 3 downloads
7 🚫 No QR ded file.txt --no-qr URL only, no QR code (you hate fun)
8 πŸ”‘ Password ded file.txt --pw "hunter2" Receiver gets password prompt. Argon2id in-browser.
9 🌐 Custom port ded file.txt -p 9090 Listens on port 9090
10 🀯 Full paranoia ded file.txt -n 1 -e 30s --pw "yolo" 1 download, 30s, password. Gone.
11 πŸ“₯ Receive mode ded receive -o ~/Downloads/ Upload page β†’ phone sends file to PC
12 πŸ“₯πŸ”§ Receive custom ded receive -p 9999 --no-qr Custom port receive, no QR
13 πŸ§… Tor send ded secret.pdf --tor Generates .onion URL. Dark web drop.
14 πŸ§…πŸ“₯ Tor receive ded receive --tor -o ~/secrets/ Tor receive. Max stealth.
15 πŸ”’ IP pinning test ded file.txt -n 2 Download on PC β†’ try on phone β†’ 403 blocked
16 ⏰ Auto-expiry test ded file.txt -e 30s Wait 30s β†’ open URL β†’ "Drop not found"

⚑ Flags Cheat Sheet

ded [send] β€” Send mode (the default)

send is optional β€” ded file.txt and ded send file.txt are identical.

Flag Short Default What it does Vibe
<PATH>... β€” β€” File(s), folder(s), or - for stdin 🎯 The stuff you're dropping
--port -p 8080 Port to listen on πŸšͺ Pick your door
--expire -e 1h Auto‑expire duration (30s, 10m, 1h, 7d) ⏰ The countdown timer
--downloads -n 1 Max downloads before self‑destruct (0 = ∞) πŸ’£ How many pickups
--pw β€” None Password-protect drop (Argon2id, 64MB memory‑hard). Receiver gets a password prompt in-browser. 🏰 Fort Knox mode
--bind -b 0.0.0.0 Bind address 🌐 Which interface
--no-qr β€” false Suppress QR code 😐 You hate fun
--tor β€” false Enable Tor hidden service πŸ§… Dark web activated

ded receive β€” Receive mode

Flag Short Default What it does Vibe
--port -p 8080 Port to listen on πŸšͺ Pick your door
--output -o . Save received files here πŸ“‚ Where the loot goes
--bind -b 0.0.0.0 Bind address 🌐 Which interface
--no-qr β€” false Suppress QR code 😐 Still no fun
--tor β€” false Enable Tor hidden service πŸ§… Receive from the shadow realm

How It Works

Send flow

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Sender  β”‚          β”‚   Server (your PC) β”‚          β”‚ Receiver β”‚
  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
        β”‚                         β”‚                           β”‚
        β”‚  1. Generate random     β”‚                           β”‚
        β”‚     256-bit key         β”‚                           β”‚
        β”‚     (or derive from pw) β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  2. Encrypt file        β”‚                           β”‚
        β”‚     XChaCha20-Poly1305  β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  3. Store ciphertext ──►│                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  4. Key β†’ URL #fragment β”‚                           β”‚
        β”‚     (or salt if --pw)   β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  5. Share link ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─►│
        β”‚     (Signal, QR, etc.)  β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         │◄── 6. Open link ──────────│
        β”‚                         β”‚                           β”‚
        β”‚                         │─── 7. Serve encrypted ──►│
        β”‚                         β”‚       blob (HTTP)         β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚    8. Browser extracts    β”‚
        β”‚                         β”‚       #key (or prompts    β”‚
        β”‚                         β”‚        for password)      β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚    9. WASM decrypts       β”‚
        β”‚                         β”‚       locally in browser  β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚   10. File downloads      β”‚
        β”‚                         β”‚       to device           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
        β”‚  β”‚  πŸ’₯ Self-destruct β”‚ πŸ”₯ Burned β”‚ πŸ›‘ Off β”‚        β”‚
        β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
        β–Ό                                                     β–Ό

Password flow (zero-knowledge)

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Sender  β”‚          β”‚   Server (your PC) β”‚          β”‚ Receiver β”‚
  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
        β”‚                         β”‚                           β”‚
        β”‚  ded file --pw "pass"   β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  1. Argon2id(pass,salt) β”‚                           β”‚
        β”‚     β†’ 256-bit key       β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  2. Encrypt with key    β”‚                           β”‚
        β”‚  3. URL = #pw:<salt>    β”‚                           β”‚
        β”‚     (NOT the key!)      β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  5. Share link ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─►│
        β”‚  6. Tell password  ─ ─ ─│─ ─ ─ ─ (phone call) ─ ─►│
        β”‚                         β”‚                           β”‚
        β”‚                         │◄── 7. Open link ──────────│
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚    8. Browser shows πŸ”‘    β”‚
        β”‚                         β”‚       password prompt     β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚    9. Receiver types pw   β”‚
        β”‚                         β”‚   10. WASM: Argon2id      β”‚
        β”‚                         β”‚       (pw,salt) β†’ key     β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         │◄──11. Fetch blob ─────────│
        β”‚                         │──►12. Return ciphertext──►│
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚   13. WASM decrypts       β”‚
        β”‚                         β”‚   14. File downloads      β”‚
        β”‚                         β”‚                           β”‚
        β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”‚
        β”‚    β”‚  Server never saw: password or key  β”‚         β”‚
        β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β”‚
        β–Ό                                                     β–Ό

Receive flow

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Receiver β”‚          β”‚   Server (your PC) β”‚          β”‚  Phone   β”‚
  β”‚   (PC)   β”‚          β”‚                    β”‚          β”‚ (sender) β”‚
  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
        β”‚                         β”‚                           β”‚
        β”‚  ded receive            β”‚                           β”‚
        β”‚  ━━━━━━━━━━━━           β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  1. Generate key ──────►│                           β”‚
        β”‚  2. Key β†’ QR code       β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         │◄── 3. Scan QR, open ─────│
        β”‚                         β”‚       upload page         β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         β”‚    4. Pick file           β”‚
        β”‚                         β”‚    5. WASM encrypts       β”‚
        β”‚                         β”‚       in-browser          β”‚
        β”‚                         β”‚                           β”‚
        β”‚                         │◄── 6. Upload ciphertext ─│
        β”‚                         β”‚                           β”‚
        β”‚  7. Server decrypts  ◄──│                           β”‚
        β”‚  8. Saves to disk       β”‚                           β”‚
        β”‚                         β”‚                           β”‚
        β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚
        β”‚  β”‚  βœ… Saved β”‚ πŸ’₯ Self-destruct β”‚ πŸ›‘ Off β”‚          β”‚
        β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚
        β–Ό                                                     β–Ό

The critical insight: the #fragment in a URL is never sent to the server. Not in HTTP requests, not in logs, not in referrer headers. The server literally cannot learn the key even if it wanted to. It's like trying to read a letter through a sealed envelope. While blindfolded. In the dark.

Tor flow πŸ§…

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  Sender  │────►│  ded --tor  │────►│  Tor Network  │────►│ Receiver β”‚
  β”‚          β”‚     β”‚             β”‚     β”‚  (.onion)     β”‚     β”‚ (Tor     β”‚
  β”‚          β”‚     β”‚  Generates  β”‚     β”‚               β”‚     β”‚  Browser)β”‚
  β”‚          β”‚     β”‚  .onion URL β”‚     β”‚  3 relays     β”‚     β”‚          β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                                               β”‚
                                        No IP. No trace.       β”‚
                                        Just encrypted bytes.  β”‚
                                                               β–Ό
                                                          File decrypts
                                                          in browser πŸ”“

Security Architecture

Defense in Depth

  ╔══════════════════════════════════════════════════════════════════╗
  β•‘  Layer 7 β”‚ Self-destruct    One download β†’ burn β†’ server off    β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 6 β”‚ Browser          Fragment auto-clear + auto-expire   β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 5 β”‚ Anti-forensics   mlock() + zeroize + zero-write     β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 4 β”‚ Access control   IP pinning + rate limit + 64-bit IDβ•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 3 β”‚ Network          HTTP + security headers             β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 2 β”‚ Zero-knowledge   Key in URL #fragment only          β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 1 β”‚ Encryption       XChaCha20-Poly1305 (256-bit, AEAD) β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 0 β”‚ Anonymity        Tor hidden service (.onion) πŸ§…     β•‘
  β•šβ•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Threat Model

βœ… Protected against

Threat How
Server operator learning file contents Zero‑knowledge β€” key never reaches server
Man‑in‑the‑middle reading the key Key lives in #fragment, never transmitted over HTTP
Someone intercepting the URL (with --pw) URL contains salt, not key. They still need the password.
Network eavesdropping Encryption at application layer (XChaCha20-Poly1305)
Server logs leaking the key Fragments aren't logged by any HTTP server or proxy
Brute force on encryption XChaCha20-Poly1305 with 256‑bit keys. See you in 10²⁰ years.
GPU attacks on passwords Argon2id with 64MB memory cost. Your 4090 weeps.
Drop ID enumeration 16‑char IDs (~2⁢⁴) + rate limiting + constant‑time 404s
URL bar shoulder surfing Fragment stripped from URL bar on page load
Browser history forensics history.replaceState() removes the #key
Key persisting in RAM zeroize on Rust side, key = null on JS side
Key swapped to disk (Unix) mlock() pins key memory pages
Encrypted file recovery Zero‑overwrite before deletion
Clickjacking / iframe embedding X-Frame-Options: DENY + frame-ancestors 'none'
XSS injection Content Security Policy β€” scripts only from 'self'
Stale tab leaking key Auto‑expire nukes key from memory when drop expires
IP tracking --tor hides both sender and receiver behind .onion
Late visitor confusion Burn page β€” "already downloaded and destroyed"

❌ NOT protected against

  • Someone who has the full URL with the #key (for non-password drops, that IS the key β€” guard it)
  • Malware on sender/receiver device (keyloggers, screen capture)
  • Your friend screenshotting the file and posting it on Twitter
  • Rubber hose cryptanalysis (look it up, it's not pretty πŸ”¨)
  • Time travelers
  • Your mom looking over your shoulder
  • The NSA (just kidding... unless? πŸ‘€)

Technical Details

Component Choice Why
Encryption XChaCha20‑Poly1305 256‑bit, extended nonce, AEAD. Used by WireGuard, Cloudflare
KDF Argon2id Memory‑hard, GPU‑resistant. 64MB cost, 3 iterations. Winner of Password Hashing Competition
Browser KDF Argon2id (WASM) Same Rust argon2 crate compiled to WASM β€” identical params, runs client-side
Chunk size 64KB Balances streaming performance vs. auth tag overhead
Server Axum (Rust) Async, zero-copy, no garbage collector. Blazingly fastβ„’
Rate limiter tower_governor Token bucket per IP β€” stops brute force
Browser crypto WebAssembly Same Rust code compiled to WASM, near-native speed
Nonce derivation base XOR chunk_index Per-chunk unique nonces without storing them
Binary embedding rust-embed HTML, CSS, JS, WASM all baked into the single binary
Memory safety mlock + zeroize Key never hits swap, wiped from RAM on drop
Anonymity Tor hidden service .onion address via local tor daemon
Archive tar + flate2 Folder/multi-file bundling with gzip compression

Memory Usage

File Size Server RAM Browser RAM Notes
1 MB ~5 MB ~5 MB Smol file, smol memory
100 MB ~5 MB ~200 MB Still chill
1 GB ~5 MB ~2 GB Desktop territory
10 GB ~5 MB Desktop only Streaming mode. Server doesn't care.

The server uses constant memory regardless of file size. It streams encrypted chunks from disk. Your 50GB Linux ISO is treated the same as a 1KB text file (memory-wise).

FAQ

Q: Is this legal? A: It's a file sharing tool with encryption. Like Signal, or HTTPS, or putting a letter in an envelope. What you put inside is your business.

Q: Can I use this at work? A: Your IT department will either promote you or fire you. No in-between.

Q: Why not just use Google Drive? A: Google Drive knows your files. Deadrop doesn't. That's the whole point. Also Google Drive doesn't self-destruct. Boring.

Q: What happens if I lose the URL? A: The file is gone. That's... the feature. It's a dead drop, not Google Photos.

Q: Can the server see my files? A: No. The encryption key is in the URL fragment which never reaches the server. The server holds meaningless encrypted bytes. It's like asking if your mailbox can read your letters.

Q: What about password-protected drops? A: Even better. The URL only has the salt β€” the server never sees the password or the key. The receiver's browser derives the key locally via Argon2id in WASM. True zero-knowledge. The server is literally clueless.

Q: What if someone intercepts my password drop URL? A: Without the password, the URL is useless. It only contains a random salt. They'd need to brute-force Argon2id (64MB memory Γ— 3 iterations per guess). Good luck with that.

Q: What if someone else tries the link? A: They can't. IP pinning locks the download to the first device. Second IP gets 403'd into oblivion.

Q: What if I visit a dead link? A: Already downloaded β†’ "πŸ”₯ This drop was already downloaded and destroyed." Expired β†’ "Drop not found." Either way, it's gone. Like your ex's texts.

Q: Why does --tor take so long? A: Tor needs ~30-60 seconds to generate a .onion address and establish circuits through 3 relays. Patience. Good anonymity takes time.

Q: Can I send multiple files? A: Yes! ded file1.txt file2.pdf folder/ bundles everything into one encrypted .tar.gz archive automatically.

Q: Can I pipe from stdin? A: echo "the password is swordfish" | ded - β€” works like a charm. Serves it as clipboard.txt.

Q: Why Rust? A: Because we wanted the binary to be fast, safe, and have zero runtime dependencies. Also because we enjoy fighting the borrow checker at 3 AM. It builds character.

Contributing

PRs welcome. Here's the roadmap:

  • End-to-end encryption (XChaCha20-Poly1305)
  • QR code generation
  • Self-destruct by time & download count
  • IP pinning
  • Folder support (.tar.gz)
  • ded receive mode (phone β†’ PC)
  • Multi-file drops
  • Stdin / clipboard mode
  • Tor hidden service
  • Password protection (Argon2id)
  • In-browser password prompt with client-side Argon2id
  • Receiver‑side streaming decryption for huge files on mobile
  • Web UI drag-and-drop improvements
  • Resume interrupted downloads
  • Multi-recipient drops (different keys per recipient)

Star History

If you've read this far, you're legally obligated to star the repo. It's in the fine print.

⭐ Star this repo β€” it makes the self-destruct mechanism work better. (Not really, but it makes us happy.)

License

MIT β€” do whatever you want. Just don't blame us if your dead drop gets intercepted by actual spies. Or if your friend screenshots the file. Or if time travelers get involved.