Documentation

Build with secrets.
Keep them out of chat.

Give your engineers tools for local encryption, believable decoys and monitored alerts. Start with a test message, then choose the pieces your application needs.

For team leads planning a pilot and engineers ready to build it.

Choose your starting point

Three ways to start.

  1. 01 / BROWSER

    See real and decoy outputs

    Try a made-up message in the free browser tool. Save the encrypted file and both control files. Open the same encrypted file with each control file to see the difference. No account needed.

    Try the browser tool ↗
  2. 02 / LOCAL CODE

    Build in your own app

    Add a local SDK to your application. Encryption runs in your environment, without an API key. Start with the Node.js example below, or choose Python, Go or Rust in the SDK guide.

    Run the example ↓
  3. 03 / HOSTED API

    Connect to deny.sh

    Register for an API key and make a test request. Use hosted encryption, decoy generation, encrypted storage or monitoring as needed. Hosted encryption receives your message and passwords during processing.

    Make your first API call ↗
A working first step

One encrypted file. Two outputs.

Install deny-sh with npm install deny-sh. Save this as example.mjs and run node example.mjs. Demo passwords only; use secure credential handling for real data.

In the code, controlData is the control file: the separate file that chooses which message opens. generateDeniableControl makes a second control file that opens your chosen decoy from the same encrypted data, with the same two passwords.

import { encrypt, decrypt, generateControlData,
  generateDeniableControl } from 'deny-sh';

// Demo passwords only. Run as an ES module (.mjs).
const password1 = 'demo-pass-one';
const password2 = 'demo-pass-two';
const message = new TextEncoder().encode('Test secret: blue door');
const controlData = generateControlData(message.length + 4);
const { ciphertext } = await encrypt(message, {
  password1, password2, controlData
});
const { controlData: decoyControl } = await generateDeniableControl(
  ciphertext, password1, password2,
  new TextEncoder().encode('Test note: buy milk')
);
for (const control of [controlData, decoyControl]) {
  const { plaintext } = await decrypt(ciphertext, {
    password1, password2, controlData: control
  });
  console.log(new TextDecoder().decode(plaintext));
}
// Test secret: blue door
// Test note: buy milk

Both passwords are used together. The control file selects the real message or your chosen fake. Keep the real control file separate from the encrypted data. More languages and examples ↗

Find your reference

Go straight to the task.

Limits and scope

Local browser and SDK encryption keep messages and passwords in your environment; hosted encryption processes them on our service. A decoy is an alternative message, not a guarantee against coercion or evidence from elsewhere. Monitoring observes matching inputs at supported deny.sh endpoints, not offline decrypts or a fake credential rejected by another provider.

The construction, source code and test vectors are public, so your team can review them now; an independent cryptographic audit is planned. Before production, review the trust centre, threat model and deployment checklist. See pricing for current plans and allowances.