01deep dive
How it works
1
Encrypt client-side
In your browser. Relay never sees plaintext.
2
Transit via Ghost Pipe
Padded to 5 MB, hashed into the CT log.
3
One-time download
Recipient decrypts locally. Blob erased from RAM.
4
Cryptographic proof
Merkle root updated. Delivery proven.
Sender → Ghost Pipe Relay → Receiver
┌───────────────────┐ ┌───────────────────────────┐ ┌───────────────────┐ │ file.pdf │ │ RAM only — no disk writes │ │ file.pdf │ │ │ │ burn-on-read │ │ ▲ │ │ │ │ │ 5 MB fixed padding │ │ │ │ │ ▼ │ │ hash → Merkle CT log │ │ │ │ │ encrypt(ML-KEM) │ ───► │ blob destroyed │ ────► │ decrypt(ML-KEM) │ │ X-Api-Key header │ │ on read │ │ X-Api-Key header │ └───────────────────┘ └───────────────────────────┘ └───────────────────┘
What the relay never sees
plaintext · encryption keys · filenames · recipient identity
What the relay does see
fixed-size 5 MB ciphertext blobs · blob hashes · API key identifiers
wire format v1 — PQHB header
HEADER (10 bytes)
MAGIC 0x50 0x51 0x48 0x42 'PQHB'
VERSION 0x01
KEM_ID uint16 be 0x0002 = ML-KEM-768
SIG_ID uint16 be 0x0002 = ML-DSA-65 (0 = anonymous)
FLAGS 0x00 reserved
KEY ENCAPSULATION
CT_KEM_LEN uint32 be
CT_KEM N bytes Kyber ciphertext
SENDER_PUB uint32-prefixed bytes
SIGNATURE (omitted if SIG_ID == 0)
SIG_LEN uint32 be
SIGNATURE N bytes Dilithium over header||kem||pub||nonce||ct
PAYLOAD
NONCE 12 bytes AES-256-GCM nonce
CT_LEN uint32 be
CIPHERTEXT N bytes AAD = header[0:10] || chunk_index_be32
PADDING to {4KB, 64KB, 512KB, 5MB}relay invariants
- · Redis
save ""+appendonly no— zero disk persistence - · Blob TTL enforced by
PEXPIRE; burn via atomicGETDEL - · RFC 6962 Merkle tree, SHA3-256 node hash, ML-DSA-65 signed STH
- ·
HTTP 415for unknown KEM/SIG IDs ·HTTP 413for > 5 MB - · API key prefix
pgp_orplk_· Argon2id verification