๐Ÿคซ Shh - Secure Secrets

Share secrets securely with self-destructing links

Create a Secret

0 / 1024 characters

If set, the recipient must know this passphrase to decrypt the secret. It is never sent to the server.

๐Ÿ”ฅ

Self-Destructing

Links expire after viewing or set time

๐Ÿ”

Zero-Knowledge E2EE

Server never sees plaintext โ€” encrypted in your browser

โšก

Fast & Simple

No registration required

๐Ÿ“š API Documentation

Base URL

/api

Health Check ๐ŸŒ PUBLIC

GET /api/health

Returns system status, feature flags, and configuration.

curl https://your-site.pages.dev/api/health

Create Secret ๐ŸŒ PUBLIC

POST /api/secret

๐Ÿ’ป Bash example โ€” encrypt then create:

#!/bin/bash
# 1. Generate a random 256-bit key
KEY_B64=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=\n')

# 2. Generate a random 12-byte IV
IV_HEX=$(openssl rand -hex 12)

# 3. Encrypt with AES-256-GCM
SECRET="My secret message"
ENCRYPTED=$(printf '%s' "$SECRET" \
  | openssl enc -aes-256-gcm -nosalt -nopad \
    -K $(echo "$KEY_B64" | tr '-_' '+/' | base64 -d | xxd -p -c 256) \
    -iv "$IV_HEX" 2>/dev/null | base64 | tr '+/' '-_' | tr -d '=\n')

# 4. Combine IV + ciphertext (matches browser format)
IV_B64=$(echo "$IV_HEX" | xxd -r -p | base64 | tr '+/' '-_' | tr -d '=\n')
PAYLOAD="${IV_B64}${ENCRYPTED}"

# 5. POST to API
RESULT=$(curl -s -X POST https://your-site.pages.dev/api/secret \
  -H "Content-Type: application/json" \
  -d "{\"encryptedData\":\"$PAYLOAD\",\"ttl\":12,\"maxViews\":1}")

ID=$(echo "$RESULT" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
echo "Share: https://your-site.pages.dev/view/$ID#$KEY_B64"

โš ๏ธ Browser-based creation is strongly recommended โ€” the Web Crypto API handles GCM authentication tags correctly. The bash example above is for automation/testing only.

Check Secret Metadata ๐ŸŒ PUBLIC

GET /api/secret/{id}/metadata

Returns: exists, viewed, hasFiles, fileCount, viewsRemaining, maxViews, expiresAt, passphraseProtected

๐Ÿ’ป Bash example:

curl -s https://your-site.pages.dev/api/secret/{id}/metadata | jq .

View & Decrypt Secret ๐ŸŒ PUBLIC

POST /api/secret/{id}/view

Returns encrypted ciphertext. Decrements view count; deletes on last view.

๐Ÿ’ป Bash example โ€” fetch and decrypt:

#!/bin/bash
# Full URL: https://your-site.pages.dev/view/{id}#{key}
URL="https://your-site.pages.dev/view/abc-123-def#xY9zK2mP..."
ID=$(echo "$URL" | sed 's|.*/view/||' | cut -d'#' -f1)
KEY_B64=$(echo "$URL" | cut -d'#' -f2)

# Fetch encrypted payload (โš ๏ธ this consumes a view)
RESPONSE=$(curl -s -X POST "https://your-site.pages.dev/api/secret/$ID/view")
PAYLOAD=$(echo "$RESPONSE" | grep -o '"encryptedData":"[^"]*"' | cut -d'"' -f4)

# Split IV (first 16 base64url chars = 12 bytes) from ciphertext
IV_B64=$(echo "$PAYLOAD" | head -c 16)
CT_B64=$(echo "$PAYLOAD" | tail -c +17)

IV_HEX=$(echo "$IV_B64" | tr '-_' '+/' | base64 -d | xxd -p -c 256)
KEY_HEX=$(echo "$KEY_B64" | tr '-_' '+/' | base64 -d | xxd -p -c 256)

echo "$CT_B64" | tr '-_' '+/' | base64 -d \
  | openssl enc -d -aes-256-gcm -nosalt -nopad -K "$KEY_HEX" -iv "$IV_HEX"

โš ๏ธ Each POST to /view counts as one view and cannot be undone.

File Attachments checking...

Loading feature status from server...

๐Ÿ’ป Bash example โ€” encrypt a file and attach to a secret:

#!/bin/bash
# 1. Generate key and IV
KEY_B64=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=\n')
IV_HEX=$(openssl rand -hex 12)
KEY_HEX=$(echo "$KEY_B64" | tr '-_' '+/' | base64 -d | xxd -p -c 256)
IV_B64=$(echo "$IV_HEX" | xxd -r -p | base64 | tr '+/' '-_' | tr -d '=\n')

# 2. Encrypt the secret text
SECRET="My secret message"
TEXT_CT=$(printf '%s' "$SECRET" \
  | openssl enc -aes-256-gcm -nosalt -nopad -K "$KEY_HEX" -iv "$IV_HEX" 2>/dev/null \
  | base64 | tr '+/' '-_' | tr -d '=\n')
TEXT_PAYLOAD="${IV_B64}${TEXT_CT}"

# 3. Encrypt the file using the same key
FILE="/path/to/document.pdf"
FILE_IV_HEX=$(openssl rand -hex 12)
FILE_IV_B64=$(echo "$FILE_IV_HEX" | xxd -r -p | base64 | tr '+/' '-_' | tr -d '=\n')
FILE_CT=$(openssl enc -aes-256-gcm -nosalt -nopad -K "$KEY_HEX" -iv "$FILE_IV_HEX" \
  -in "$FILE" 2>/dev/null | base64 | tr '+/' '-_' | tr -d '=\n')
FILE_PAYLOAD="${FILE_IV_B64}${FILE_CT}"

FILENAME=$(basename "$FILE")
FILESIZE=$(wc -c < "$FILE" | tr -d ' ')
MIMETYPE="application/pdf"

# 4. POST secret + encrypted file
RESULT=$(curl -s -X POST https://your-site.pages.dev/api/secret \
  -H "Content-Type: application/json" \
  -d "{
    \"encryptedData\": \"$TEXT_PAYLOAD\",
    \"ttl\": 12,
    \"maxViews\": 1,
    \"files\": [{
      \"name\": \"$FILENAME\",
      \"type\": \"$MIMETYPE\",
      \"size\": $FILESIZE,
      \"data\": \"$FILE_PAYLOAD\"
    }]
  }")

ID=$(echo "$RESULT" | grep -o '"id":"[^"]*"' | cut -d'"' -f4)
echo "Share: https://your-site.pages.dev/view/$ID#$KEY_B64"

โš ๏ธ Browser-based creation is recommended. The bash approach omits the AES-GCM authentication tag that OpenSSL produces โ€” the recipient's browser will reject files encrypted this way. Use the web UI for real file transfers; this example shows the payload structure only.

Admin Stats ๐Ÿ” ADMIN ONLY

GET /api/admin/stats

curl -H "X-Admin-Token: your_token" https://your-site.pages.dev/api/admin/stats

Admin Purge All ๐Ÿ” ADMIN ONLY

POST /api/admin/purge

curl -X POST -H "X-Admin-Token: your_token" https://your-site.pages.dev/api/admin/purge

Admin Delete Single Secret ๐Ÿ” ADMIN ONLY

DELETE /api/admin/secret/{id}

curl -X DELETE -H "X-Admin-Token: your_token" https://your-site.pages.dev/api/admin/secret/{id}

Rate Limits

Create: 10/IP/hour ยท View: 50/IP/hour ยท Admin: 5/hour

Configurable via RATE_LIMIT_CREATE, RATE_LIMIT_VIEW, RATE_LIMIT_ADMIN env vars.

๐Ÿ” Security Notes

  • All secrets encrypted client-side with AES-256-GCM before leaving your browser
  • Encryption key travels in the URL fragment (#key) โ€” never sent to the server
  • Optional passphrase: key is PBKDF2-derived and wrapped via AES-KW, entirely client-side
  • Server stores only ciphertext โ€” zero-knowledge architecture
  • Secrets destroyed on last view (or at TTL expiry)
  • Admin token comparison is timing-safe (no length-leak side-channel)

๐Ÿ’ป Client-Side Decryption (JavaScript)

How to decrypt a secret programmatically in the browser or Node.js:

// Full URL: https://your-site.pages.dev/view/{id}#{key}
const url       = window.location.href;
const secretId  = url.split('/view/')[1].split('#')[0];
const keyB64url = url.split('#')[1];

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

// Fetch encrypted payload (โš ๏ธ consumes a view)
const res   = await fetch(`/api/secret/${secretId}/view`, { method: 'POST' });
const { encryptedData } = await res.json();

// Decode base64url โ†’ bytes, split IV (12 bytes) + ciphertext
const b64    = encryptedData.replace(/-/g, '+').replace(/_/g, '/');
const bytes  = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
const iv     = bytes.slice(0, 12);
const cipher = bytes.slice(12);

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