Introduction

DeadLetter

DeadLetter is a censorship-resistant, identity-driven dead-drop communication system.

It allows senders to deliver encrypted messages to recipients without knowing where they are, while recipients can safely fetch, decrypt and acknowledge messages using strong cryptography.

No accounts. No metadata. No central trust beyond cryptographic verification.

Core properties

  • Zero knowledge backend — servers never see plaintext
  • Post-quantum-safe hybrid crypto design (X25519 + Ed25519 + AES-GCM + HKDF)
  • Device trust pinning
  • ESP32 hardware dead-drop nodes
  • CLI inbox with offline decryption
  • Fully self-hostable

Quickstart

1. Download device setup tool

This tool is used to configure the physical ESP32 DeadLetter device with the correct Backend and Registry gateway addresses over USB.

macOS

curl http://YOUR_VPS_IP/dist/deadletter-setup-mac -o deadletter-setup
chmod +x deadletter-setup
sudo mv deadletter-setup /usr/local/bin/deadletter-setup

Linux

curl http://YOUR_VPS_IP/dist/deadletter-setup-linux -o deadletter-setup
chmod +x deadletter-setup
sudo mv deadletter-setup /usr/local/bin/deadletter-setup

Windows (PowerShell)

curl http://YOUR_VPS_IP/dist/deadletter-setup-windows.exe -OutFile deadletter-setup.exe

Run from the same directory:

.\deadletter-setup.exe --help

2. Verify checksum

Always verify the downloaded binary before running it.

Download checksums:

curl http://YOUR_VPS_IP/dist/SHA256SUMS.txt -o SHA256SUMS.txt

Verify:

sha256sum -c SHA256SUMS.txt 2>/dev/null | grep deadletter-setup

On macOS if sha256sum is missing:

shasum -a 256 deadletter-setup

Compare the hash with the value inside SHA256SUMS.txt.


3. Configure the ESP32 device

Plug in your DeadLetter ESP32 over USB and run:

deadletter-setup --registry REGISTRY_IP:8081 --backend BACKEND_IP:8080

This permanently stores the gateway addresses inside the device.

Architecture

Overview of the Dead Letter architecture.

Architecture

DeadLetter is a privacy-first, offline-capable dead-drop communication system built around three principles:

  1. No persistent sender identity
  2. Receiver-controlled trust
  3. Zero-knowledge infrastructure

The system is composed of four independent layers:


1. Device Layer (ESP32 DeadDrop)

The ESP32 device is responsible for:

  • Generating ephemeral encryption keys
  • Encrypting payloads using X25519 + AES-256-GCM
  • Never storing plaintext
  • Never learning recipient private keys

Each device only knows:

  • Its own device ID
  • The gateway addresses (registry + backend)
  • A pinned root trust fingerprint

2. Registry Layer (Trust Authority)

The registry acts as a minimal Certificate Authority.

Responsibilities:

  • Issues signed certificates binding:
    • handle
    • encryption public key
    • keyId
    • expiry
  • Signs certificates with a long-lived root signing key
  • Publishes the root public key at /keys/

Security properties:

  • Registry never sees messages
  • Compromise cannot decrypt past traffic
  • Clients verify all certificates locally

3. Backend Layer (Dead Drop Store)

The backend is a dumb encrypted mailbox.

Responsibilities:

  • Accepts encrypted envelopes via POST /post
  • Returns encrypted envelopes via GET /inbox/:handle
  • Deletes messages only after:
    • Signed delete request
    • Verified against registry certificate

Security properties:

  • Backend cannot decrypt messages
  • Backend cannot forge delete requests
  • Messages expire automatically (TTL purge)

4. Client CLI Layer

The CLI is the user's trust anchor.

Responsibilities:

  • Fetches and verifies registry root key
  • Verifies receiver certificates
  • Derives shared secrets locally
  • Decrypts messages
  • Signs delete acknowledgements

The CLI never transmits private keys.


Message Flow

ESP32 Sender → Encrypt → Backend Store → CLI Fetch → Decrypt → Signed Delete

Threat Model Summary

ComponentCan DecryptCan ImpersonateCan Delete
ESP32NoNoNo
RegistryNoYes (future)No
BackendNoNoNo
CLIYesYes (local)Yes

Compromising any single layer is insufficient to break privacy.

Security Model

DeadLetter is designed as a trust-minimized dead drop messaging system.
No single component can read or forge messages, even if fully compromised.

This document describes the threat model, trust boundaries, and cryptographic guarantees.


Threat Model

We assume the following adversaries may exist:

ActorCapabilities
Malicious Backend OperatorFull read/write access to message storage and transport
Malicious Registry OperatorCan attempt to forge certificates
Network AttackerFull MITM on all traffic
Device ThiefGains physical access to ESP32 or client computer
Passive ObserverCan log IPs, timing, and metadata

We do not assume any server is honest.


Core Guarantees

DeadLetter provides:

  • End‑to‑end encryption — only sender and receiver can read messages
  • Forward secrecy — compromise of long‑term keys does not reveal past messages
  • No backend trust — backend stores opaque blobs only
  • No registry trust — all registry data is signed and verified by clients
  • Offline‑safe messaging — sender and receiver never need to be online simultaneously

Key Types

Each identity owns two keypairs:

KeyAlgorithmPurpose
Encryption keyX25519Message encryption
Signing keyEd25519Certificate verification & deletion authorization

Certificate Chain

  1. Registry has a root signing key.
  2. Each user certificate is signed by this root.
  3. Clients verify:
root_pub -> verifies -> user_cert -> authorizes -> encryption_pub

The backend never sees private keys.


Message Encryption

Every message uses a new ephemeral keypair.

sender_ephemeral_priv + receiver_enc_pub
        ↓ X25519
     shared_secret
        ↓ HKDF
     session_key
        ↓ AES‑256‑GCM
     ciphertext

Salt:

SHA256(ephemeral_pub || receiver_pub)

Message Deletion Authorization

When a user deletes a message, the client signs:

"<message_id>:delete"

using its Ed25519 signing key.

The backend verifies this signature against the registry certificate.
No one else can delete your messages.


Metadata Leakage

DeadLetter intentionally minimizes metadata but cannot eliminate:

  • IP addresses (unless Tor used)
  • Timing correlations
  • Message sizes

Trust Boundaries

ComponentTrusted?
Sender deviceYes
Receiver deviceYes
BackendNo
RegistryNo
NetworkNo

Security Properties Summary

PropertyStatus
Confidentiality✅ End‑to‑end
Integrity✅ Signed + authenticated
Authenticity✅ Certificate chain
Forward Secrecy✅ Ephemeral keys
Server Compromise Safety✅ Backend & registry are untrusted
Offline Delivery✅ Dead drop model

DeadLetter is built on the assumption that servers will eventually be hostile.
Your security lives entirely on your devices — and that is exactly the point.

Trust Model

DeadLetter is built around cryptographic trust pinning instead of central user accounts.
No part of the system ever requires trusting the backend with plaintext data.


Root of Trust

The system has a single Root Signing Key.

This key:

  • Lives only on the registry server
  • Signs all user certificates
  • Is fetched once by clients and ESP devices
  • Is pinned locally and never auto‑updated

If the root key changes, all clients must explicitly clear trust.

This makes silent compromise of the registry impossible.


Certificate Structure

Each user has a certificate:

{
  "handle": "alice",
  "encPub": "<base64 X25519 public key>",
  "keyId": "<random>",
  "expiresAt": 1735689600
}

This certificate is signed by the root key using Ed25519.


Certificate Verification Flow

When a client or ESP wants to send or read messages:

  1. Fetch /keys/ from registry
  2. Receive:
    • root_pub_b64
    • signing algorithm
  3. Pin the root key locally
  4. Fetch /keys/<handle>
  5. Verify signature using pinned root key
  6. Reject any unsigned or invalid certificate

ESP Trust Pinning

Each ESP device stores:

ItemStored In
Root public key fingerprintNVS
Registry gateway addressNVS
Backend gateway addressNVS

This prevents:

  • Evil‑twin registry attacks
  • Gateway redirection
  • Silent man‑in‑the‑middle attacks

Fingerprint Verification

On first boot the ESP prints:

Root Trust Fingerprint:
aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99

This value is unique per installation.
Never trust the example above – always compare the fingerprint shown on your own ESP device with the one printed by your CLI.

This must be compared with the fingerprint shown by the CLI.

Only if both match is trust established.

Where this happens

This process is fully described in:

→ Device Setup → CLI Usage → Trust Commands

The sender ESP device is always the authority for fingerprint verification.


Trust Revocation

Trust is only reset when:

  • The user runs the setup CLI again
  • Or manually clears NVS
  • Or reflashes firmware

There is no remote trust reset endpoint.


Why This Matters

This model guarantees:

ThreatOutcome
Backend compromisedCannot read messages
Registry compromisedCannot forge certificates
Network MITMDetected by pinned root key
Rogue ESPCannot impersonate other devices

Trust Summary

DeadLetter does not trust infrastructure.

It only trusts:

  • Locally pinned keys
  • Cryptographic verification
  • Physical access when provisioning

Device Setup

How to set up and configure devices.

Device Setup

This guide walks you through setting up a physical DeadLetter ESP32 device (the sender).
The device is responsible for encrypting messages and pinning the registry trust anchor.

You will configure the device using the deadletter-setup CLI tool over USB.


Requirements

  • ESP32 DeadLetter device flashed with firmware
  • USB cable
  • One computer running macOS, Linux, or Windows
  • Access to your Registry and Backend gateway IP addresses

Step 1 — Download Device Setup Tool

Download the correct binary for your platform from the DeadLetter download server:

macOS

curl http://YOUR_VPS_IP/dist/deadletter-setup-mac -o deadletter-setup
chmod +x deadletter-setup
sudo mv deadletter-setup /usr/local/bin/

Linux

curl http://YOUR_VPS_IP/dist/deadletter-setup-linux -o deadletter-setup
chmod +x deadletter-setup
sudo mv deadletter-setup /usr/local/bin/

Windows (PowerShell)

curl http://YOUR_VPS_IP/dist/deadletter-setup-windows.exe -o deadletter-setup.exe

Step 2 — Verify Checksum (Critical)

Always verify the checksum before executing:

curl http://YOUR_VPS_IP/dist/SHA256SUMS.txt
sha256sum deadletter-setup

Ensure the output matches the entry in SHA256SUMS.txt.


Step 3 — Plug In the ESP Device

Connect the ESP32 to your computer using USB.

The setup tool will automatically detect available serial ports.


Step 4 — Configure Registry & Backend

Run:

deadletter-setup --registry REGISTRY_IP:PORT --backend BACKEND_IP:PORT

Example:

deadletter-setup --registry 192.168.1.200:8081 --backend 192.168.1.200:8080

If multiple devices are connected you will be prompted to select one.


Step 5 — Trust Fingerprint Ceremony

During setup the device prints a Root Trust Fingerprint:

Root Trust Fingerprint:
aa:bb:cc:dd:ee:ff:...

This fingerprint must be verified out-of-band with the receiver.

Do NOT copy fingerprints from documentation or screenshots.

This is the moment your trust anchor is pinned permanently.


Step 6 — Completion

If successful you will see:

✓ Configuration Complete - Device Rebooting

The device is now ready to securely send DeadLetter messages.


Troubleshooting

IssueFix
No device detectedCheck USB cable and drivers
Permission denied (Linux)sudo usermod -a -G dialout $USER then relogin
Wrong gateway savedRe-run setup tool
Fingerprint mismatchAbort setup immediately

The ESP device is now cryptographically bound to your registry. All trust flows from this step.

CLI Usage (Receiver)

The DeadLetter receiver CLI is a native Rust binary.
It runs without any runtime dependencies once installed.

⚠️ Prebuilt binaries are not yet published. They will be made available for download soon. Until then, this document describes the intended workflow.


Overview

The receiver CLI is used by the message recipient to:

  • Initialize a local identity (keys + handle)
  • Fetch and verify trust anchors from the registry
  • Download, decrypt and manage inbox messages
  • Save attached files
  • Acknowledge and delete messages securely

All cryptography and trust validation happens locally.


Planned Installation

Soon you will be able to download a platform-specific binary:

PlatformBinary
macOSdeadletter
Linuxdeadletter
Windowsdeadletter.exe

Example (macOS / Linux):

curl http://<download-host>/deadletter -o deadletter
chmod +x deadletter
sudo mv deadletter /usr/local/bin/

Initialize Identity

Each receiver has a handle and a local keypair stored in:

~/.deadletter/<handle>/

Create a new identity:

deadletter init alice

This generates:

  • enc_private.key – encryption private key
  • enc_public.key – encryption public key
  • sig_private.key – signing key

These never leave your machine.


Fetch Inbox

To fetch and decrypt messages:

deadletter inbox alice

Flow:

  1. Fetch trust anchor from registry /keys/
  2. Fetch and verify certificate for alice
  3. Fetch inbox from backend
  4. Display message list
  5. Decrypt selected message locally
  6. Optionally delete message from server

Trust & Verification

The receiver can manually inspect and verify the current trust anchor (root signing key) used by the registry.

deadletter trust

This command will:

  1. Fetch the current root signing key from the registry /keys/
  2. Display the root public key fingerprint
  3. Warn if the trust anchor has changed since last verification

Example output (example only — your fingerprint will be different):

Root Trust Fingerprint: aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99

⚠️ This fingerprint is only an example.

The sender (ESP32 device) and the receiver (CLI user) must exchange and verify their displayed fingerprints using a trusted out‑of‑band channel (e.g. in person, secure chat, phone call).

Never trust the registry blindly — always confirm that both sides see the same root trust fingerprint to detect any possible man‑in‑the‑middle attack.


Reading Messages

After running inbox you will see:

📬 2 message(s)

  1 770c6253-a430-4e 2025-12-30T12:24:37Z
  2 a6120b86-57e3-43 2025-12-30T12:18:01Z

Select msg (q=quit):

Select a number to decrypt.


File Attachments

If the message contains a file payload, it will be written to the current working directory:

📎 File saved: ./report.jpg (image/jpeg, 34812 bytes)

Deleting Messages

After viewing a message:

Delete message? (y/n):

If confirmed, the CLI signs a deletion request using your signing key and sends it to the backend.

Deletion is idempotent – even if the backend already purged the message, your local inbox will stay consistent.


Important Notes

  • The CLI is offline-safe – all cryptography happens locally.
  • No plaintext ever reaches the backend.
  • Messages cannot be decrypted without the recipient private key.
  • This tool is strictly for receivers, not ESP devices.

Coming Soon

  • Public binary downloads
  • Checksum verification instructions
  • Automated installers for macOS, Linux and Windows

Backend Service

The Backend is the message transport layer in DeadLetter. It never sees plaintext and has no ability to decrypt messages. Its sole responsibility is to accept encrypted envelopes from senders and deliver them to the correct recipient inbox.


Responsibilities

The backend handles:

  • Receiving encrypted message envelopes from ESP32 sender devices
  • Storing envelopes temporarily until fetched by the receiver
  • Serving inbox contents to receivers
  • Verifying delete acknowledgements
  • Purging expired messages automatically

At no point does the backend have access to message plaintext or encryption keys.


Data Model

Each stored message is an envelope:

FieldDescription
idUnique message identifier
ephemeral_pubSender ephemeral X25519 public key (base64)
ivAES‑GCM IV (base64)
ciphertextEncrypted payload (base64)
tagAES‑GCM authentication tag (base64)
receivedAtServer timestamp

Endpoints

Fetch inbox

GET /inbox/{handle}

Returns all encrypted envelopes for the specified handle.


Delete message (acknowledge)

POST /ack-delete

Body:

{
  "id": "<message_id>",
  "handle": "<handle>",
  "sig": "<base64-ed25519-signature>"
}

The backend verifies the signature against the recipient certificate before deleting the message.


Security Properties

  • Backend never holds encryption keys.
  • Messages are end‑to‑end encrypted between ESP sender and CLI receiver.
  • Delete requests are signed with the receiver’s private signing key.
  • Backend is safe to run over Tor or clearnet.

Expiration

Messages are stored with a TTL and are purged automatically even if never fetched.

This ensures the backend never becomes a long‑term message archive.


Threat Model

The backend is assumed to be:

  • Curious
  • Potentially compromised
  • Observable

Even in this worst case:

  • No plaintext is exposed
  • Tampering is detected via AES‑GCM
  • Deletions require cryptographic proof of ownership

Summary

The backend is a blind courier.

It moves opaque cryptographic envelopes between parties, but cannot read, forge, or impersonate any participant.

Registry Service

The Registry is the cryptographic root of trust in DeadLetter.
It does not store messages. Its sole responsibility is to bind human‑readable handles to encryption public keys and to sign those bindings using a long‑lived root signing key.

All clients (CLI + ESP32 firmware) trust the registry only via its root public key fingerprint.


Responsibilities

The registry provides three core guarantees:

  1. Handle ownership

    • A handle (e.g. alice) is bound to exactly one encryption public key.
    • Only the holder of the corresponding signing key may update or revoke it.
  2. Certificate issuance

    • The registry signs a canonical certificate object:
    {
      "handle": "alice",
      "encPub": "<base64-x25519-public-key>",
      "keyId": "v1",
      "expiresAt": 1735689600
    }
    
  3. Trust anchor distribution

    • The root signing public key is distributed through /keys/
    • Clients verify all certificates using this root key.

Endpoints

MethodPathDescription
GET/keys/Returns the current root signing public key
GET/keys/:handleReturns signed certificate for a handle
POST/registerRegister or rotate a handle certificate
POST/challengeRequest registration challenge
POST/verifyVerify signed challenge response

Root Key Endpoint

GET /keys/

Example response:

{
  "root_pub_b64": "MCowBQYDK2VwAyEA7qZ9...",
  "algorithm": "ed25519",
  "issued_at": 1735611120
}

Clients store the fingerprint of this key locally and warn if it ever changes.


Certificate Fetch

GET /keys/alice

Example:

{
  "cert": {
    "handle": "alice",
    "encPub": "YzN4...",
    "keyId": "v1",
    "expiresAt": 1735689600
  },
  "sig": "b64-ed25519-signature"
}

The CLI verifies:

SHA256( canonical(cert) ) verified by root public key

Registration Flow

  1. Client requests a challenge
  2. Registry returns a random nonce
  3. Client signs nonce using their signing key
  4. Registry verifies and stores the new certificate
  5. Registry persists to registry.store.json

This prevents unauthorized handle hijacking.


Local Persistence

The registry stores all handle mappings in:

dl/packages/registry/registry.store.json

The file is loaded at startup and written atomically after each update.


Security Properties

ThreatMitigation
MITM during registrationChallenge‑response signing
Registry database tamperingRoot‑signed certificates invalidate forged entries
Handle hijackOnly holder of signing key can rotate
Silent key swapCLI warns if trust anchor fingerprint changes

What the Registry Never Sees

  • Plaintext messages
  • Encryption private keys
  • Message metadata beyond handle → pubkey mapping

The registry cannot read, decrypt, or enumerate any inbox contents.


Operational Advice

  • Back up registry.store.json frequently.
  • Never rotate the root key unless all clients are prepared to re‑trust.
  • Host registry behind Tor gateway if anonymity is required.

Tor Gateway

The Tor Gateway is a small always-on computer (for example a Raspberry Pi or VPS) that bridges the ESP32 devices to the Tor network.

The ESP32 itself cannot run Tor. It does not have enough memory, storage, or a full TCP/IP stack capable of handling Tor’s circuit building, directory fetches, and encryption layers.
For this reason every DeadLetter device must talk to a trusted gateway which then forwards traffic into Tor.


Why a Gateway is Required

The ESP32 firmware is intentionally minimal:

  • No Tor binary on the device
  • No SOCKS implementation
  • No directory consensus parsing
  • No onion address resolution

Instead, the ESP32 only performs:

  • Key generation
  • Encryption / decryption
  • Trust fingerprint display
  • Plain HTTP communication to the gateway

All Tor-specific logic lives on the gateway.


Gateway Responsibilities

The gateway is responsible for:

  • Running Tor in client mode
  • Exposing a local HTTP service for the ESP32
  • Forwarding all traffic through Tor using a SOCKS proxy
  • Resolving .onion registry and backend addresses
  • Acting as a privacy boundary between the device and the public internet

The flow is:

ESP32  →  Gateway (HTTP)  →  Tor (SOCKS)  →  Onion Registry / Backend

Example Setup: Raspberry Pi Gateway

A Raspberry Pi running Debian is sufficient.

Install Tor:

sudo apt update
sudo apt install tor socat

Verify Tor is running:

sudo ss -lntp | grep 9050

You should see Tor listening on 127.0.0.1:9050.


Forwarding via socat

The gateway exposes a local port that forwards traffic into Tor:

socat TCP-LISTEN:8080,reuseaddr,fork SOCKS4A:127.0.0.1:REGISTRY_ONION.onion:80,socksport=9050

This means:

  • ESP32 connects to http://gateway-ip:8080
  • socat forwards the traffic through Tor to the hidden service

You can run multiple listeners for backend and registry.


Current Limitation

At the moment the gateway hardcodes the onion addresses inside the systemd service files.

This means:

  • Onion URLs cannot yet be updated dynamically
  • Changing backend or registry onion requires editing the gateway config and restarting services

This is a known limitation and will be improved in future versions.


Trust Boundary

The gateway is trusted for transport only, not for message confidentiality.

  • Messages are always end-to-end encrypted between sender and receiver
  • The gateway cannot decrypt message payloads
  • Fingerprint verification still happens on the ESP32 device itself

If a gateway is compromised, the attacker can observe traffic patterns but cannot read message contents.

Troubleshooting

This page lists the most common problems when running DeadLetter and how to fix them.


Device setup tool does not detect my ESP32

Symptoms

  • No devices detected
  • The setup tool lists zero serial ports.

Fix

  • Make sure the ESP32 is connected with a data USB cable.
  • Install USB‑UART drivers:
    • macOS / Linux: CP210x or CH340 drivers if your board requires them.
    • Windows: Install Silicon Labs CP210x or CH340 drivers.
  • On Linux, ensure you are in the dialout group:
sudo usermod -a -G dialout $USER
# log out and back in

Permission denied opening serial port

Symptoms

  • Permission denied: /dev/ttyUSB0

Fix

sudo usermod -a -G dialout $USER

Log out and log back in.


Setup tool says “Registry gateway is NOT reachable”

Symptoms

  • Device prints: Registry gateway is NOT reachable!

Cause The ESP32 cannot reach the configured Tor gateway on the local network.

Fix

  • Ensure the Tor gateway (e.g. Raspberry Pi) is powered on.
  • Verify gateway IP + port are correct:
    deadletter-setup -r <gateway-ip:port> -b <gateway-ip:port>
    
  • Check the gateway firewall allows inbound connections.

Device keeps printing Registry gateway: UNSET

Cause Gateway IP was not saved to NVS or device rebooted before storing.

Fix Run device setup again and wait until it prints Registry gateway set before disconnecting USB.


Inbox shows messages but decryption fails

Symptoms

  • Decryption failed - invalid key or corrupted data

Fix

  • Make sure the correct identity directory exists: ~/.deadletter/<handle>/
  • Ensure enc_private.key and enc_public.key belong to the same identity used by the sender.
  • Run:
    deadletter trust
    
    and verify the fingerprint matches what the sender sees on their ESP32.

File downloads are saved as unreadable garbage

Cause Base64 payload corrupted or wrong key used.

Fix

  • Ensure sender and receiver verified the same trust fingerprint.
  • Retry deadletter inbox <handle>.

deadletter trust reports trust anchor changed

Symptoms

  • Warning about trust anchor mismatch.

Fix Stop immediately. Compare fingerprints with your sender through a secure channel. Only proceed if you are 100% sure the new fingerprint is legitimate.


deadletter command not found

Cause Binary is not in PATH.

Fix

sudo mv deadletter /usr/local/bin/
chmod +x /usr/local/bin/deadletter

Setup tool times out waiting for device ready

Fix

  • Unplug and reconnect the ESP32.
  • Wait until it prints Type 'help' before running setup again.

Still broken?

Enable verbose output:

deadletter-setup -v -r <gateway> -b <backend>

Attach the output when asking for support.