How this actually works
Last updated 2026-09-07 · written for anyone who wants more than "trust us"
The app and its privacy page keep things simple on purpose — most people just want to know it's safe, not how. This page is for the rest: the real mechanics, in plain terms, plus the honest limits of what any of this can actually promise.
The pairing handshake
When you pair with someone — in person via QR code, or remotely via a link — both devices generate a fresh key pair (ECDH on curve P-256, if you want the exact primitive) and exchange the public halves. Neither of you ever sends a private key anywhere; each side combines its own private key with the other's public key to arrive at the same shared secret independently. That shared secret never touches our servers, even in encrypted form — it's computed locally on each device and never transmitted at all.
In-person pairing (scanning each other's code) never sends this handshake over the internet at all, so there's nothing for a relay to intercept or tamper with. Remote pairing (sending a link) does relay the handshake through our servers, which is why the app offers a safety number afterward — a short code both devices generate from the shared secret. Read it out loud to each other (or scan it): if it matches, no one swapped keys in the middle. If someone had intercepted and substituted their own key mid-handshake, the safety numbers would not match.
Why one stolen key doesn't matter
The shared secret from pairing isn't used to encrypt messages directly. Instead, it seeds a ratchet: every single message derives its own one-time encryption key (AES-GCM) from a constantly-advancing chain (via HKDF, if you want the exact construction), and each step immediately discards the material used to derive the previous key. This is the same family of technique used in Signal's protocol.
Practically, this means: if a single message's key were ever somehow recovered, it would decrypt exactly that one message — not the conversation before it, and not the conversation after it. There's no master key sitting anywhere that unlocks everything at once.
Padding & hiding message type
Before sealing, message content is padded up to one of several fixed size buckets, so the exact length of what you wrote isn't visible from the outside — only a rough size range. Even what kind of message it is (text vs. photo vs. voice note) is sealed along with the content itself, not left visible as metadata.
Why neither of you can "prove" a message
Messages are authenticated with a symmetric key both of you hold — not signed with either person's private identity key. That's a deliberate choice: it means neither of you can later prove to a third party which of you actually wrote a specific message, even by handing over your keys and the plaintext. Either side could technically have forged it after the fact. The intent is real conversations without an accidental permanent, provable record either of you could be held to later.
What this can't hide
Being specific about the limits matters as much as being specific about the protections:
- Timing and rough size. Whoever operates the storage this pairing runs through (us) can see roughly when messages are sent and how large they are — padding narrows this to a size range, not the content itself, and never what's inside.
- Which messages came from the same person. Each side is tagged with an opaque value instead of a role label, but it's the same value throughout a pairing — so a relay can tell "these came from the same side" without knowing who that side actually is.
- A message count on your lock screen. If you turn on arrival notifications, your device shows how many messages arrived — never their content, but the count itself appears on the lock screen, where someone holding your phone can read it. It is off until you turn it on, and the tab title and icon carry the same count if you'd rather not involve the operating system at all. Arrival signals only work while the page is alive: this app stops checking for new messages when you switch to another browser tab, so nothing can reach you with the tab closed or in the background.
- Who can tell a message is new. New arrivals are noticed while the page is open, and your own sends never count as unread. There is no server-side read receipt, so we cannot tell whether you have read anything, and the operator learns no more from the badge than they already do from the ciphertext arriving.
- Someone who can read this browser's storage directly. Your passcode stops anyone opening the app on your device, and the private key is encrypted at rest underneath it. But the keys that decrypt messages you have already received are kept by the browser so your history can be displayed, and no web app can hide them from code running on the same origin. A malicious extension, malware, or anyone with developer tools open on an unlocked device is outside what a passcode protects — on an unlocked device, assume anything on screen can be copied. Clearing site data removes that key material for good, which is also why we cannot recover a conversation for you.
- Not post-quantum. The key exchange is classical elliptic-curve cryptography, not a post-quantum algorithm. Traffic recorded today could theoretically be decrypted by a sufficiently powerful future quantum computer — the same caveat that applies to most encrypted traffic on the internet today, not something unique to this app.
- Screenshots and screen recording. No browser API lets a web page detect or prevent either. The app covers its own screen the instant it's backgrounded, so an OS app-switcher preview can't show your conversation — that's the one real, honest thing a web page can do here. It is not screenshot prevention.
- Your IP address, from the hosting layer. Like any website, our hosting/CDN provider (Cloudflare) sees the IP address of every request as a basic function of how the internet routes traffic. This isn't something the app's own code logs or has access to, but it's a real, separate layer from the message encryption above, worth being upfront about.
What the server actually does
The backend (a small Cloudflare Worker + KV store) only ever holds encrypted blobs it cannot read — the decryption key never leaves your device. A few other things worth knowing:
- Messages auto-expire from storage only if you set a disappearing-messages period — they follow whatever you picked, for both of you. With no period set, a message stays on the server until it burns after delivery, or until you delete the conversation.
- Write requests (creating a room, sending a message) are rate-limited per IP address to prevent abuse, using a counter that resets every 60 seconds.
- Every response is sent with
Cache-Control: no-store, so nothing here is meant to be cached anywhere along the way.
- Oversized requests are rejected before any storage work happens at all.
Known limitations, stated plainly
- The encryption has not had external/independent review. This is the top outstanding item. It's a standard-looking construction, built and reviewed only by whoever's worked on this code — that is not the same thing as a security audit.
- Joining a room isn't perfectly atomic. The storage layer (Cloudflare KV) has no atomic read-modify-write, so there's a small theoretical race if two people hit the exact same join link in the exact same instant. For a two-person pairing tool this is a low-probability edge case, left as a documented limitation rather than patched with an untested fix.
Verifying this yourself
The source isn't publicly browsable, but it's genuinely available — email [email protected] and you'll get real access to the actual repository, not a brush-off. If you want to check any claim on this page against the real implementation rather than take it on faith, that's what it's for.
← Back to Only Us Two