deadrop 0.1.4

Zero-knowledge encrypted dead drop. One binary. One command. Gone.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
<p align="center">
  <img src="assets/deadrop-logo.png" width="220" alt="deadrop logo" />
</p>

<h1 align="center">deadrop</h1>

<p align="center">
  <b>Zero‑knowledge file drops that self‑destruct.</b><br/>
  One command. One link. Gone. Like it never happened.
</p>

<p align="center">
  <a href="https://crates.io/crates/deadrop"><img src="https://img.shields.io/crates/v/deadrop.svg?style=flat-square&color=00ff88" alt="crates.io" /></a>
  <a href="https://github.com/Karmanya03/Deadrop/releases"><img src="https://img.shields.io/github/v/release/Karmanya03/Deadrop?style=flat-square&color=00ff88" alt="release" /></a>
  <a href="https://github.com/Karmanya03/Deadrop/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-00ff88?style=flat-square" alt="license" /></a>
  <img src="https://img.shields.io/badge/encryption-XChaCha20--Poly1305-00ff88?style=flat-square" alt="encryption" />
  <img src="https://img.shields.io/badge/written_in-Rust_πŸ¦€-00ff88?style=flat-square" alt="rust" />
</p>

<p align="center">
  <img src="https://img.shields.io/badge/server_knows-nothing_🀷-ff4444?style=flat-square" alt="zero knowledge" />
  <img src="https://img.shields.io/badge/after_download-πŸ’₯_self_destructs-ff4444?style=flat-square" alt="self destruct" />
  <img src="https://img.shields.io/badge/dependencies-just_the_binary-blueviolet?style=flat-square" alt="single binary" />
</p>

---

## 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.

```
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚   You   β”‚                                          β”‚  Friend β”‚
    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                                          β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
         β”‚                                                    β”‚
         β”‚  ded ./secret-plans.pdf                            β”‚
         β”‚  ━━━━━━━━━━━━━━━━━━━━━━━                           β”‚
         β”‚                                                    β”‚
    β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”β”‚
    β”‚                  πŸ”’ Your Machine                        β”‚β”‚
    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚β”‚
    β”‚  β”‚ Encrypt  │───►│  Ciphertext  │───►│ HTTPS Server β”‚  β”‚β”‚
    β”‚  β”‚ (WASM)   β”‚    β”‚  (on disk)   β”‚    β”‚ (Axum+TLS)   β”‚  β”‚β”‚
    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚β”‚
    β”‚       πŸ”‘ 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

| Feature | Description |
|---|---|
| πŸ” **End‑to‑end encrypted** | XChaCha20‑Poly1305. The server never sees the key. Ever. |
| πŸ”— **Key in URL fragment** | The `#key` part never hits server logs, proxies, or HTTP headers |
| πŸ”’ **HTTPS by default** | Auto‑generated self‑signed TLS cert β€” encrypted on the wire, zero config |
| πŸ’₯ **Self‑destruct** | Expire by time, by download count, or both |
| πŸ“± **Works on phones** | Receiver only needs a browser. No app. No account. No signup. |
| πŸ“ **Send folders** | Directories auto‑pack to `.tar.gz` before encryption |
| ♾️ **Unlimited file size** | Streams from disk β€” your 50GB file won't eat your RAM |
| πŸ”‘ **Optional password** | Argon2id key derivation (64MB memory‑hard, GPU‑resistant) |
| πŸ“¦ **Single binary** | No runtime, no Docker, no config files. Just one executable. |
| πŸ“² **QR code** | Because typing URLs is for people who still use fax machines |
| πŸ“₯ **Receive mode** | `ded receive` β€” phone‑to‑PC uploads with browser encryption |

### Security Hardening

| Feature | Description |
|---|---|
| πŸ‘» **Fragment auto‑clear** | `#key` is stripped from the URL bar and browser history the instant the page loads |
| πŸ”’ **IP pinning** | Download is locked to the first IP that connects β€” anyone else gets HTTP 403 |
| πŸ›‘ **Security headers** | CSP, `X-Frame-Options: DENY`, `no-referrer`, `no-cache`, anti‑clickjack |
| ⏱ **Rate limiting** | 2 req/sec per IP with burst of 5 β€” stops brute‑force ID enumeration |
| 🎯 **16‑char drop IDs** | ~2⁢⁴ possible IDs β€” statistically impossible to guess |
| πŸ• **Constant‑time 404s** | Random delay on not-found responses prevents timing‑based ID enumeration |
| πŸ”₯ **Burn page** | Late visitors see "πŸ”₯ This drop was already downloaded and destroyed" instead of a generic 404 |
| ⏰ **Auto‑expire page** | If the tab stays open past expiry, the key is nuked from JS memory and the UI self‑destructs |
| 🧠 **Memory locking** | `mlock()` on Unix prevents the encryption key from being swapped to disk |
| πŸ—‘ **Zero‑write deletion** | Encrypted temp files are overwritten with zeros before `rm` β€” no forensic recovery |
| 🧹 **Key zeroization** | Encryption key is wiped from RAM (via `zeroize`) on drop, both server‑side and in‑browser |

## Installation

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

```bash
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.

### Download a binary

Grab the latest release for your platform from [**Releases**](https://github.com/Karmanya03/Deadrop/releases).

| Platform | Binary | Architecture |
|---|---|---|
| **Windows** | [`ded-windows-x86_64.exe`]https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-windows-x86_64.exe | x86_64 |
| **Linux** | [`ded-linux-x86_64`]https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-linux-x86_64 | x86_64 (musl, static) |
| **Linux** | [`ded-linux-aarch64`]https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-linux-aarch64 | ARM64 (Raspberry Pi, etc.) |
| **macOS** | [`ded-macos-x86_64`]https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-macos-x86_64 | Intel |
| **macOS** | [`ded-macos-aarch64`]https://github.com/Karmanya03/Deadrop/releases/latest/download/ded-macos-aarch64 | Apple Silicon (M1/M2/M3/M4) |

**Quick install (Linux/macOS):**

```bash
# 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

```bash
cargo install deadrop
```

### Build from source

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

### πŸ”„ One-line update (Linux/macOS)

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

> Same as install β€” it overwrites the old binary with the latest release. Your PATH stays intact.

### πŸ—‘ One-line uninstall (Linux/macOS)

```bash
rm -f ~/.local/bin/ded && echo "deadrop removed ☠"
```

> If you installed to `/usr/local/bin/` instead:

```bash
sudo rm -f /usr/local/bin/ded && echo "deadrop removed ☠"
```

### πŸ—‘ Uninstall (cargo)

```bash
cargo uninstall deadrop
```

## Usage

### Send mode (default)

```bash
# Send a file
ded ./secret.pdf

# Send a folder
ded ./tax-returns-2025/

# That's it. That's the tool.
```

### Receive mode

```bash
# Receive a file from phone β†’ PC
ded receive

# Receive to a specific directory
ded receive -o ~/Downloads/

# Custom port
ded receive -p 9090
```

Opens a browser upload page on your LAN. Scan the QR from your phone, pick a file, and it's encrypted in-browser β†’ sent to your PC β†’ decrypted β†’ saved. One upload, then the server self-destructs.

### The spicy options

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

# Password-protected (because you're paranoid, and that's ok)
ded ./passwords.csv --pw "correct-horse-battery-staple"

# Custom port
ded ./file.txt -p 4200

# No QR code (you hate fun)
ded ./file.txt --no-qr

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

### What you see

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

  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  URL  https://192.168.1.42:8080/d/a3f9c1b2#xK9m β”‚
  β”‚                                                   β”‚
  β”‚  β”œβ”€ File       secret.pdf                         β”‚
  β”‚  β”œβ”€ Size       4.2 MB                             β”‚
  β”‚  β”œβ”€ Expires    10m                                β”‚
  β”‚  β”œβ”€ Downloads  1                                  β”‚
  β”‚  └─ Crypto     XChaCha20-Poly1305                 β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

  πŸ”’ Self-signed TLS β€” browser will show a warning (safe to proceed)

  β–ˆβ–€β–€β–€β–€β–€β–ˆ β–€β–€β–€β–ˆβ–„β–ˆ β–ˆβ–€β–€β–€β–€β–€β–ˆ     <- QR code appears here
  β–ˆ β–ˆβ–ˆβ–ˆ β–ˆ β–ˆβ–€β–ˆ β–€β–„  β–ˆ β–ˆβ–ˆβ–ˆ β–ˆ        scan with phone
  ...
```

### What the receiver sees

A clean, dark download page in their browser. Click **"Download & Decrypt"** β†’ file decrypts locally in their browser via WebAssembly β†’ downloads to their device. The server never touches the plaintext.

## Flags Cheat Sheet

### Send mode

| Flag | Short | Default | What it does |
|---|---|---|---|
| `--port` | `-p` | `8080` | Port to listen on |
| `--expire` | `-e` | `1h` | Auto‑expire (`30s`, `10m`, `1h`, `7d`) |
| `--downloads` | `-n` | `1` | Max downloads before self‑destruct (0 = ∞) |
| `--pw` | β€” | None | Require password (Argon2id derived) |
| `--bind` | `-b` | `0.0.0.0` | Bind address |
| `--no-qr` | β€” | `false` | Hide QR code |

### Receive mode

| Flag | Short | Default | What it does |
|---|---|---|---|
| `--port` | `-p` | `8080` | Port to listen on |
| `--output` | `-o` | `.` | Directory to save received files |
| `--bind` | `-b` | `0.0.0.0` | Bind address |
| `--no-qr` | β€” | `false` | Hide QR code |

## How It Works

### Send flow

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

### 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 tried.

## 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          HTTPS (TLS 1.3) + security headers β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 2 β”‚ Zero-knowledge   Key in URL #fragment only          β•‘
  ╠══════════β•ͺ══════════════════════════════════════════════════════╣
  β•‘  Layer 1 β”‚ Encryption       XChaCha20-Poly1305 (256-bit, AEAD) β•‘
  β•šβ•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
```

### 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 |
| Network eavesdropping | HTTPS with auto‑generated TLS cert (rustls) |
| 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 |
| GPU attacks on passwords | Argon2id with 64MB memory cost |
| 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 |
| Late visitor confusion | Burn page β€” "already downloaded and destroyed" |

#### ❌ NOT protected against

- Someone who has the full URL with the `#key` (that IS the key)
- 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

## Technical Details

| Component | Choice | Why |
|---|---|---|
| Encryption | XChaCha20‑Poly1305 | 256‑bit, extended nonce, AEAD. Used by WireGuard, Cloudflare, etc. |
| KDF | Argon2id | Memory‑hard, GPU‑resistant. Winner of the Password Hashing Competition |
| TLS | rustls + rcgen | Auto‑generated self‑signed cert per session. No OpenSSL dependency. |
| Chunk size | 64KB | Balances streaming performance vs. auth tag overhead |
| Server | Axum (Rust) | Async, zero-copy, no garbage collector |
| Rate limiter | tower_governor | Token bucket per IP β€” prevents brute force |
| Browser crypto | WebAssembly | Same Rust code compiled to WASM, runs in-browser at 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 |

## Memory Usage

| File Size | Server RAM (Sender) | Browser RAM (Receiver) |
|---|---|---|
| 1 MB | ~5 MB | ~5 MB |
| 100 MB | ~5 MB | ~200 MB |
| 1 GB | ~5 MB | ~2 GB (desktop) |
| 10 GB | ~5 MB | Desktop only (streaming) |

The server uses constant memory regardless of file size. It streams encrypted chunks from disk.

## 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 love 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.

**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 operator see my files?**
A: No. The encryption key is in the URL fragment which never reaches the server. The server only holds meaningless encrypted bytes.

**Q: What if someone else tries to download with the link?**
A: They can't. The download is IP-pinned to the first device that connects. A second IP gets blocked with HTTP 403.

**Q: What if I visit a dead link?**
A: If the file was already downloaded, you'll see a burn page: "πŸ”₯ This drop was already downloaded and destroyed." If it expired, you get a standard not-found message.

**Q: Why does the browser show a certificate warning?**
A: Deadrop auto-generates a self-signed TLS certificate for HTTPS. It's fully encrypted β€” your browser just doesn't recognize the cert authority. Click "Proceed" / "Advanced β†’ Continue" and you're good.

**Q: Why Rust?**
A: Because we wanted the binary to be fast, safe, and have zero dependencies. Also because we enjoy fighting the borrow checker on Friday nights.

## Contributing

PRs welcome. Here's what's on the radar:

- [x] ~~Built‑in HTTPS (rustls + auto‑generated certs)~~
- [x] ~~`ded receive` mode (pull instead of push)~~
- [ ] Receiver‑side streaming decryption for huge files on mobile
- [ ] Clipboard mode (`echo "secret" | ded -`)
- [ ] Tor hidden service mode
- [ ] Multi‑file drops
- [ ] Web UI drag-and-drop improvements

## License

MIT β€” do whatever you want. Just don't blame us if your dead drop gets intercepted by actual spies.

---

<p align="center">
  <sub>Built with πŸ¦€ and paranoia.</sub>
</p>