Why deniable encryption matters
Standard encryption protects bytes from people who don't have the key. It does not stop leaked ciphertext from announcing that real secret material is present. That second problem is the one deniable encryption is built to address.
The bytes leave more often than people think
Standard encryption assumes you hold the key and the bytes stay in places you control. The model breaks the moment the ciphertext itself moves into a place you no longer control. That happens often:
- A cloud provider gets breached and dumps a backup table to a forum.
- A laptop walks off after a conference. The disk is encrypted, but the encrypted backup file you synced last night is now sitting on the thief's drive.
- An
.envfile with a real API key gets pushed to a public repo. GitHub's secret scanner pages someone five seconds later, but the file is already in the public log. - An AI agent loaded with credentials gets prompt-injected into reading and exfiltrating its own context.
- A SaaS data export, requested for legitimate reasons, lives forever in someone's downloads folder.
In every one of those cases, encryption did its job: the bytes are still ciphertext. But that is all it does. The attacker holds a file that either opens with the right key or doesn't, and the file itself confirms there is a real secret inside worth the effort of getting that key. The ciphertext is honest to a fault. It admits something is there.
The third dimension
Deniable encryption adds the dimension that's been missing. The same ciphertext decrypts to completely different content depending on which control file you feed back in (the two passwords stay the same on both decrypts). One control file gives the real plaintext. Another gives a plausible decoy. Both decryptions are valid. Neither is provably "the real one" from the bytes themselves.
The maths is well studied. Anderson described the construction in 1996, Canetti formalised it in 1997. What didn't exist for most of that time was a usable implementation that ran in a browser, shipped as an SDK, and didn't ask you to mount a hidden volume.
The construction at deny.sh/encrypt uses Argon2id key derivation, AES-256-CTR for encryption, and XOR composition to bind multiple plausible plaintexts to one ciphertext. SDKs in TypeScript, Rust, Go, and Python are open source under Apache 2.0; the hosted application layer is open source under AGPL-3.0. All verifiable in your browser at deny.sh/verify: 22 checks across eight categories, including chi-squared, entropy, KAT vectors, fuzz rounds, and multiple deniable messages. Independent third-party cryptographic audit is on the roadmap; firm and scope will be announced once engaged.
Where it actually applies
The honest framing is that this is for at-rest data that needs to fail closed when it leaks. Concrete cases:
Developer secrets. API keys, OAuth tokens, signing keys, and the contents of .env files. Cloud backups and repo history are the leak path; deniable encryption means the leak surrenders a decoy.
AI agents holding credentials. Prompt injection is now a routine attack. An agent that only ever sees a decoy control file can be tricked into reading its full context out loud and the real key still doesn't appear. The real one stays separated, decrypted only when the agent finishes a task and a different process unwraps it.
Cloud-backup defence in depth. Treat the cloud breach as when, not if. Encrypt your backups so the encrypted blob, even after a full provider compromise, opens to a decoy.
Pre-positioned decoys for known scenarios. Devices crossing borders, laptops attending sensitive meetings, M&A deal-room machines. Bounded threat model, scriptable workflow.
Regulated at-rest data. Healthcare PHI, deal-room secrets under privilege, family-office records. Breach safe-harbours already exist for encrypted-at-rest data; deniability simplifies the post-breach narrative because the leaked bytes don't even reveal real content under analysis.
Where it doesn't apply
This part matters more than the marketing. Deniable encryption isn't magic, and it isn't a defence against an attacker who already knows you use it. Someone watching you, holding your subscription records, or otherwise aware that you store things deniably will not accept the first decoy and leave. They will keep asking, and a second password produces a second plausible file, and so on, and at no point does the cipher tell them to stop. That is a problem for physical security and opsec, not for encryption.
It's also no substitute for simply not holding the file. If a secret is dangerous enough that nobody can know it exists, the correct move is to not store it at all. deny.sh is for the far more ordinary case: bytes that have to live somewhere, that you want to surrender a decoy when they leak.
And the believability is on you. The decoy has to make sense in context. An .env file with one obviously-fake API key and nothing else won't survive a second glance; a full file with a working-looking database URL, a real-format Stripe test key, and a couple of stale tokens will. The cryptography guarantees the bytes can't betray which file was real. Whether the decoy is convincing is your call, not the maths.
The point
Standard encryption gives you one decryption. Deniable encryption gives you two. When a backup leaks, when a device walks off, when an agent gets prompted out of a secret, the file decrypts to your decoy. The real content never appears in the breach.
One ciphertext. Multiple plausible plaintexts.
No account. No tracking. Everything in your browser.
Try the encryptor