🤫 Shh - Secure Secrets

Share secrets securely with self-destructing links

Create a Secret

0 / 1024 characters
🔥

Self-Destructing

Links expire after viewing or set time

🔐

Secure

No server-side logs of secret content

Fast & Simple

No registration required

📚 API Documentation

Base URL

/api

Health Check 🌐 PUBLIC

GET /api/health

Returns system status and configuration

💻 Bash Example:

curl https://shh.example.com/api/health

Create Secret 🌐 PUBLIC

POST /api/secret

💻 Bash Example (Encrypt & Create):

#!/bin/bash
# 1. Generate encryption key (32 bytes = 256 bits)
KEY=$(openssl rand -base64 32 | tr -d '=')

# 2. Encrypt your secret
SECRET="My secret message"
IV=$(openssl rand -base64 12 | tr -d '=')
ENCRYPTED=$(echo -n "$SECRET" | \
  openssl enc -aes-256-gcm -K $(echo -n "$KEY" | base64 -d | xxd -p) \
  -iv $(echo -n "$IV" | base64 -d | xxd -p) | base64 | tr -d '=')

# 3. Combine IV + encrypted (base64url format)
PAYLOAD="${IV}${ENCRYPTED}" 

# 4. Create secret
RESULT=$(curl -s -X POST https://shh.example.com/api/secret \
  -H "Content-Type: application/json" \
  -d "{\"encryptedData\":\"$PAYLOAD\",\"ttl\":12}")

# 5. Extract secret ID
ID=$(echo $RESULT | jq -r '.id')

# 6. Build shareable URL
echo "Share this URL: https://shh.example.com/view/$ID#$KEY"

⚠️ Note: Client-side encryption via browser is recommended for security

Check Secret Metadata 🌐 PUBLIC

GET /api/secret/{id}/metadata

💻 Bash Example:

curl https://shh.example.com/api/secret/abc-123-def-456/metadata

View & Decrypt Secret 🌐 PUBLIC

POST /api/secret/{id}/view

💻 Bash Example (View & Decrypt):

#!/bin/bash
# Extract ID and key from URL
URL="https://shh.example.com/view/abc-123#xY9zK2..."
ID=$(echo $URL | cut -d'/' -f5 | cut -d'#' -f1)
KEY=$(echo $URL | cut -d'#' -f2)

# Fetch encrypted secret (one-time use!)
RESPONSE=$(curl -s -X POST https://shh.example.com/api/secret/$ID/view)
ENCRYPTED=$(echo $RESPONSE | jq -r '.encryptedData')

# Decrypt (extract IV and ciphertext)
IV=$(echo -n "$ENCRYPTED" | head -c 16 | base64 -d)
CIPHER=$(echo -n "$ENCRYPTED" | tail -c +17 | base64 -d)

# Decrypt with AES-256-GCM
echo -n "$CIPHER" | openssl enc -d -aes-256-gcm \
  -K $(echo -n "$KEY" | base64 -d | xxd -p) \
  -iv $(echo -n "$IV" | xxd -p)

⚠️ Secret is deleted after viewing - can only be retrieved once

Create Secret with File 🔧 FEATURE DISABLED

File uploads are currently disabled. To enable, set FILE_UPLOADS_ENABLED: true in worker code.

💻 Example when enabled:

{
  "encryptedData": "base64url_encrypted_text",
  "ttl": 12,
  "files": [
    {
      "name": "document.pdf",
      "type": "application/pdf",
      "size": 1024000,
      "data": "base64url_encrypted_file_content"
    }
  ]
}

Max file size: 10MB. Files are also encrypted client-side.

Admin Stats 🔐 ADMIN ONLY

GET /api/admin/stats

⚠️ Requires admin authentication header

💻 Bash Example:

curl -H "X-Admin-Token: your_admin_token_here" \
  https://shh.example.com/api/admin/stats

Admin Purge All 🔐 ADMIN ONLY

POST /api/admin/purge

⚠️ Requires admin authentication header

💻 Bash Example:

curl -X POST -H "X-Admin-Token: your_admin_token_here" \
  https://shh.example.com/api/admin/purge

Admin Delete Single Secret 🔐 ADMIN ONLY

DELETE /api/admin/secret/{id}

⚠️ Requires admin authentication header

💻 Bash Example:

curl -X DELETE -H "X-Admin-Token: your_admin_token_here" \
  https://shh.example.com/api/admin/secret/abc-123-def-456

Rate Limits

  • Create: 10 secrets per IP per hour
  • View: 50 views per IP per hour
  • Admin: 5 actions per hour

Configurable via environment variables

🔐 Security Notes

  • All secrets are encrypted client-side with AES-256-GCM
  • Server never sees plaintext (zero-knowledge architecture)
  • Encryption keys are in URL fragments (never sent to server)
  • One-time use: secrets are deleted immediately after viewing
  • Rate limiting prevents abuse
  • Admin tokens use timing-safe comparison
  • Anonymous users cannot list secret IDs - only admins can
  • Unauthenticated API access - only allows retrieving specific secrets with valid ID

💻 Client-Side Decryption Example

How to decrypt a secret programmatically:

// Extract ID and key from URL
const url = "https://shh.example.com/view/{id}#{key}";
const [path, fragment] = url.split('#');
const secretId = path.split('/').pop();
const encryptionKey = fragment;

// Fetch encrypted secret
const res = await fetch(`/api/secret/${secretId}/view`, {
  method: 'POST'
});
const { encryptedData } = await res.json();

// Import key from base64url
const keyBytes = Uint8Array.from(
  atob(encryptionKey.replace(/-/g,'+').replace(/_/g,'/')),
  c => c.charCodeAt(0)
);
const key = await crypto.subtle.importKey(
  'raw', keyBytes, {name: 'AES-GCM'}, false, ['decrypt']
);

// Decrypt
const combined = Uint8Array.from(
  atob(encryptedData.replace(/-/g,'+').replace(/_/g,'/')),
  c => c.charCodeAt(0)
);
const iv = combined.slice(0, 12);
const ciphertext = combined.slice(12);

const plaintext = await crypto.subtle.decrypt(
  {name: 'AES-GCM', iv}, key, ciphertext
);
const secret = new TextDecoder().decode(plaintext);
console.log('Secret:', secret);