🔮 VaporDrop

Self-Hosted Digital Dead Drop on the Tor Network

Try It Now with Tor Browser http://mwgnkmbbgziasslmbqwo5qaeuxpftjbe63y6li62vijk3lpd23a66oyd.onion/

The Concept: Digital Dead Drop

During the Cold War, agents and informants used "dead drops" — predetermined physical locations to leave messages without ever meeting. A loose brick in a wall, a hollow tree, a secret compartment under a park bench.

VaporDrop brings this concept into the digital age. It's an anonymous exchange point on the Tor network where two people can deposit and retrieve messages and files without knowing each other, without registering, without leaving traces.

The key difference? You can host it yourself. The server is yours, the data is under your control, and when you shut down the Docker container, everything vanishes.

How Access Works

Forget traditional usernames and passwords. VaporDrop uses a mnemonic system called Brain Key.

Six Words, One Identity

To log in, you need six words of at least four characters each. These words are processed through PBKDF2 with 100,000 iterations to generate a seed, which in turn generates a P-256 cryptographic key pair. The same combination of words always produces the same identity.

There's no "forgot password" because there's no user database. Your six words are your identity.

Choosing Memorable Words

The system doesn't require complicated words. You can use:

The important thing is that they're words you can reconstruct mentally without having to write them down anywhere.

The Numeric ID

Once logged in, the system assigns you a Numeric ID in the format 12345678-90. This is the identifier you share with whoever needs to contact you via message. It's derived from your public key, so the same six words will always produce the same Numeric ID.

Messages and Files: Two Modes

📨

Text Messages

Just the recipient's Numeric ID. The message is AES-256-GCM encrypted in the browser before sending.

📁

File Transfer

Up to 1GB, encrypted chunk by chunk. Requires exchange of complete public keys (130 hex characters).

File Transfer Flow

Key Exchange

The recipient copies their public key and communicates it to the sender

Local Encryption

The sender pastes the key, drags the file — it gets encrypted in the browser

Chunk Upload

Encrypted chunks (1MB each) are uploaded to the server

Download and Decrypt

The recipient downloads, decrypts locally, and the server deletes the chunks

End-to-End Encryption

All cryptographic code runs in the browser. The server never sees cleartext data.

Cryptographic Stack

ComponentAlgorithmNotes
Key ExchangeECDH P-25665 bytes = 130 hex
Symmetric EncryptionAES-256-GCMRandom 12-byte IV
Brain Key DerivationPBKDF2-SHA256100,000 iterations
Onion Key DerivationArgon2id64MB RAM, 3 iter
Onion Service KeyEd25519Deterministic
RAM ProtectionMemGuardPage locking

Active Backend Protections

The Destroy Button

In the dashboard you'll find a red button: 🔥 Destroy.

It instantly deletes:

Note: this does not delete messages already deposited on the server for you. Those remain until automatic expiration (7 days) or until you retrieve them.

Data Lifetime on Server

Messages and files have a maximum lifetime of 7 days.

After one week from deposit, an automatic garbage collector permanently deletes them. This choice balances two needs: giving the recipient enough time to retrieve the data, and not accumulating material indefinitely on the server.

For different requirements, you can modify the constants in the source code and deploy your own customized instance.

Self-Hosting with Docker

VaporDrop is distributed as a Docker container. It works on any system: Linux, Windows, macOS.

Requirements

Installation

# Create project directory
mkdir vapordrop && cd vapordrop

# Create structure
mkdir -p static file_storage

# Copy files: main.go, index.html, Dockerfile, docker-compose.yml, go.mod

# Set the secret key (generates the .onion address)
export VAPOR_KEY="your-very-long-secret-phrase"

# Start
docker compose up -d --build

# Find your .onion address in logs
docker compose logs -f

Windows/macOS with Docker Desktop

Install Docker Desktop, launch it, open a terminal and follow the same commands. Docker Desktop automatically manages the underlying Linux VM.

The VAPOR_KEY Variable

VAPOR_KEY is the secret phrase that deterministically generates your service's .onion address.

Warning: If you lose the phrase, you lose the address. If someone knows it, they can impersonate your service. Treat it like a master password.

Dockerfile (Multi-Stage Hardened)

# STAGE 1: Build
FROM golang:1.22-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /build
COPY go.mod go.sum* ./
RUN go mod download 2>/dev/null || true
COPY main.go .
RUN [ -f go.mod ] || go mod init vapordrop
RUN go mod tidy
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
    -ldflags="-s -w -extldflags '-static'" \
    -trimpath -o vapordrop main.go

# STAGE 2: Runtime
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    tor ca-certificates && rm -rf /var/lib/apt/lists/*
RUN groupadd -g 1000 vapor && \
    useradd -r -s /bin/false -d /app -u 1000 -g 1000 vapor
WORKDIR /app
COPY --from=builder /build/vapordrop /app/vapordrop
COPY static/ /app/static/
RUN chown -R vapor:vapor /app && chmod 500 /app/vapordrop
USER vapor
ENTRYPOINT ["/app/vapordrop"]

docker-compose.yml (Hardened)

services:
  vapordrop:
    build: .
    container_name: vapordrop
    volumes:
      - ./static:/app/static:ro
      - ./file_storage:/app/file_storage
    environment:
      - VAPOR_KEY=${VAPOR_KEY:?VAPOR_KEY required}
    
    # Security
    cap_add:
      - IPC_LOCK          # For MemGuard
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    read_only: true
    
    # RAM-only temp dirs
    tmpfs:
      - /tmp:size=256M,mode=1777,uid=1000,gid=1000
      - /run:size=64M,mode=1777
    
    deploy:
      resources:
        limits:
          memory: 512M
    
    restart: unless-stopped

First Boot

In the logs you'll see:

⚙️  Key derivation with Argon2id (~64MB RAM, ~1 sec)...
⚙️  Starting VaporDrop Node...
⚙️  Creating Onion Service v3...
═══════════════════════════════════════════════════════
✅ VAPORDROP ONLINE
🔗 http://abc123xyz456...onion
═══════════════════════════════════════════════════════

Project Structure

vapordrop/
├── main.go              # Go backend (~700 lines)
├── go.mod               # Dependencies: memguard, bine, crypto
├── go.sum
├── Dockerfile           # Multi-stage hardened
├── docker-compose.yml   # With security options
├── static/
│   └── index.html       # Complete frontend (~500 lines)
└── file_storage/        # Chunk directory (auto-created)

API Endpoints

EndpointMethodFunction
/api/registerPOSTRegister Numeric ID ↔ Public Key
/api/resolve/{id}GETResolve ID to Public Key(s)
/api/sendPOSTDeposit encrypted message
/api/fetchPOSTRetrieve messages (and delete)
/api/file/initPOSTStart file transfer
/api/file/chunk/{id}/{n}POSTUpload encrypted chunk
/api/file/pending/{pk}GETList pending files
/api/file/download/{id}/{n}GETDownload chunk
/api/file/complete/{id}POSTConfirm download (delete)
/api/statsGETPublic statistics

Customization

Modifiable constants in main.go:

const (
    MessageTTL       = 7 * 24 * time.Hour    // Message duration
    FileTTL          = 7 * 24 * time.Hour    // File duration
    MaxFileSize      = 1 << 30               // 1GB max
    FileChunkSize    = 1 << 20               // 1MB per chunk
    MaxPendingFiles  = 1000                  // Pending files
    MaxIdentities    = 100000                // Registered identities
    RateLimitWindow  = 1 * time.Minute       // Rate limit window
    MaxRequestsPerIP = 60                    // Requests per IP
)

FAQ

What happens if I forget my six words?
You lose access to that identity. There's no recovery. Messages deposited for you will remain on the server until expiration, but you won't be able to read them.
Can I use the same words on different nodes?
Yes. The same six words generate the same identity everywhere. You can access from the public node or from your private node with the same credentials.
Can the server administrator read my messages?
No. Encryption happens in the browser before sending. The server receives and stores only encrypted blobs.
How do I verify the code is secure?
The frontend is a single inspectable HTML file. Open your browser's developer tools and verify that cryptographic calls happen locally before any fetch to the server.
Why P-256 and not X25519?
Web Crypto API in browsers natively supports P-256. For non-NIST curves, external JavaScript libraries would be needed, increasing the attack surface.

Conclusion

VaporDrop isn't for everyone. It doesn't have Telegram's animated emojis, WhatsApp's blue checkmarks, or Signal's groups with hundreds of people.

It's a minimal tool for a specific purpose: allowing two people to exchange information in an ephemeral, anonymous, and verifiable way.

If you need a communication channel that leaves no traces, requires no trust in third parties, and that you can host yourself — VaporDrop is an option.

Try VaporDrop

Six words. No account. No trace.