// GAUNTLET

Ten attacks. Zero mercy.

Meet Eve. The harness mints her a real identity, a real client, and full visibility of the relay — then she attacks ten specific claims, one probe each. Pass means the claim held under attack. Exit code 0 means all ten held.

$

642 lines · in-process FastAPI TestClient — no ports, no network · 10 probes · exit 0 iff all pass · --json · --probe <name>

alice
sender

A real identity in a throwaway vault. Sends every message under attack conditions.

bob
recipient

Scans, derives, decrypts. If Bob can't read what Alice sent after the attack, the probe fails loudly.

eve
the eavesdropper

A real minted identity, not a mock. She reads every relay endpoint, replays tokens, flips bits, and forces duress unlocks.

simulated display — results match actual harness output · run locally to verify

Falsifiable, not just asserted.

[what]
What is the Gauntlet?

Ten adversarial probes that fire at DRIFT's core privacy invariants — relay blindness, stealth unlinkability, forward secrecy, sealed sender opacity, and more. Each probe spins up the relay in-process and attacks one specific claim. Pass means the claim held under attack. The harness is open source and runs in under 30 seconds.

view the harness →
[why]
Why does this exist?

Privacy tools that only assert claims are not enough. The Gauntlet makes DRIFT's claims falsifiable — any researcher can run it, add probes, or break it. A failed probe is not hidden; it becomes a bug report. The dashboard shows what a clean run looks like. The script lets you verify it yourself.

read the design doc →
[scope]
Honest limits

The Gauntlet tests protocol-layer invariants in an isolated in-process environment. It does not test your OS, your hardware, your network, or your threat model. A full pass does not mean DRIFT is production-ready — it means the specific claims tested held under automated adversarial conditions. Read the threat model for the full picture.

view threat model →

What Eve tried. Why it failed.

[PASS]relay-blindness

tried: scraped every relay endpoint for identity material.

failed because: 4 endpoints expose zero sender or recipient fields — there is nothing stored to scrape.

4 relay endpoints expose zero identity material · 21ms
[PASS]stealth-unlinkability

tried: link ten envelopes to one recipient and derive the next address in the sequence.

failed because: all ten addresses are distinct, and derivation requires the private scan key Eve doesn't have.

10/10 addresses distinct, non-derivable without scan key · 10ms
[PASS]forward-secrecy

tried: recover a deleted message key after the ratchet advanced, to decrypt captured ciphertext.

failed because: the key no longer exists anywhere — decryption dies with InvalidTag.

deleted key unrecoverable from advanced chain (InvalidTag) · 0ms
[PASS]burn-replay

tried: capture a burn request and replay it.

failed because: burn tokens are single-use — the first burn returns 200, the identical replay is rejected with 409.

first burn 200, identical replay 409 · 5ms
[PASS]sealed-sender-opacity

tried: read sender identity out of the fields the relay actually stores.

failed because: the stored fields carry no sender identity — the linkable ratchet header is sealed inside the ciphertext.

stored fields ['_id', '_relay_ts', 'addr', 'ct', 'to', 'ts'] carry no sender identity · 3ms
[PASS]witness-chain-integrity

tried: tamper with a published blindness certificate.

failed because: the Ed25519 signature and hash chain reject a single flipped bit — the whole chain fails verification.

4 certs verify clean; tamper rejected · 6ms
[PASS]forged-message-rejection

tried: bit-flip ciphertext into a forgery and poison the conversation state.

failed because: the AEAD throws InvalidTag, the ratchet survives untouched, and both real messages still decrypt.

InvalidTag on tamper; ratchet survived, both msgs decrypt · 3ms
[PASS]panic-isolation

tried: force a duress unlock and pivot from the decoy to the real keys.

failed because: the duress passphrase yields only the decoy vault — the real keys are structurally unreachable from it.

duress unlock yields decoy; real keys unreachable · 2ms
[PASS]otpk-consumption

tried: replay a consumed one-time prekey to run a second handshake as Alice's contact.

failed because: each one-time prekey is handed out and deleted exactly once — the replay is rejected.

OTPK 924191019 consumed once, replay rejected · 22ms
[PASS]fmd-rate

tried: use the relay's delivery hints to spot which messages are real.

failed because: false positives track the dial exactly — at full noise, every message looks like a match and the signal drowns.

rates 0/0.1/1.0 → 0/61/500 of 500 · 4160ms

Where this goes: the Gauntlet is the seed of a "Drift Protocol Compatible" conformance suite — a second implementation that passes all ten probes can legitimately claim interop. It is a test harness, not a bug bounty. Found something it misses? Write the probe that proves it and open an issue.