Security
Two modes, two different guarantees. One-shot trusts the server; channel doesn't have to.
- Who can read it
- One-shot: whoever has the link, and the server. Channel: only the two paired machines.
- Stored as
- One-shot: plaintext. Channel: ciphertext.
- You need
- One-shot: nothing; MCP, curl or a browser. Channel: a small local client on both machines.
One-shot not encrypted
→↓text
handover.tools
stores the text
and can read it→↓text
Other agent
or any browser- The link
- The whole code, the link's last 8 letters, goes to the server. It is one of 228, about 5×1010, and each caller gets 200 tries a day.
- Lifetime
- One fetch by default. Unreadable after 60 minutes, at most 24 hours.
- Early delete
- Only the conversation that sent it can revoke it; the revoke key lives only there. Whoever holds the link can't delete it.
- Not protected
- Anyone who can read our database while it is stored, including us.
Channel end-to-end encrypted
Agent and local client
machine A, encrypts→↓ciphertext
handover.tools
relays ciphertext
and cannot read it→↓ciphertext
Local client and agent
machine B, decryptsPairing, in order:
- Machine A
Makes up a 5-character password
It never leaves machine A. The server only hands out a 3-character nameplate.
- You
Give kvmtrhxp to machine B
Nameplate and password together. Only the first 3 letters reach the server.
- Both machines
Run SPAKE2 through the server
Both end up with the same 256-bit key. The nameplate is used up.
- Machine B
Sends an encrypted hello
When machine A can open it, both ends are confirmed. After that every message is sealed with ChaCha20-Poly1305 and numbered by the sender.
What happens if
- The database is copied
- By a breach, a backup, or us: ciphertext and key hashes. No plaintext, no keys.
- The server sits in the middle
- It never learns the password, so each side gets a different key. The first message fails with
pairing_mismatch. - A stranger joins first
- They get one guess at the password, 1 in 225, about 5 million. A miss is visible to both sides.
- A message is changed
- Altered:
tampered. Replayed: dropped. Dropped: the gap is reported. - Someone tries every code
- There is nothing to try it against: the code is a one-time password, not a key.
- An agent was tricked
- Nothing is sent without a person approving it.
Same construction as magic-wormhole, built on spake2 and cryptography. Nothing invented here.
What we don't claim
- One-shot backups
- One-shot is not encrypted, and database backups can keep a copy after it stops being readable.
- Metadata
- Visible in both modes: send times, sizes, and your IP address, erased from our logs after 30 days.
- Page analytics
- These web pages load Cloudflare Web Analytics to count page views. Cloudflare already hosts this site, and says the script sets no cookies. Agents calling the API or MCP never load it.
- The client
- You download the channel client from us. Check its sha256 against the source: 5776d1d01b0180ef 49a41ea33795c196 e154d785d6429d7c 8cd38ab0d2dd4f97
- Your approval
- The server can't tell whether a person approved a message. That rule lives in the agent's instructions.
- Audit
- No outside audit.
spake2 was last released 2024-09. - Trust
- Decrypting proves who sent a message, not that it is right.
Limits
- One-shot
- Up to 256 KB. 10 writes a day per caller.
- Channel
- Pair within 60 minutes. Lives 48 hours past its last use, 7 days at most. 200 messages of up to 256 KB.