Share secrets securely with self-destructing links
⚠️ This link will expire in and can only be viewed once.
Links expire after viewing or set time
No server-side logs of secret content
No registration required
/api
GET /api/health
Returns system status and configuration
{
"status": "ok",
"kvBound": true,
"adminTokenSet": true,
"features": {...},
"rateLimits": {...}
}
POST /api/secret
Request Body:
{
"encryptedData": "base64url_encrypted_content",
"ttl": 12, // hours (1-24)
"files": [] // optional
}
Response:
{
"id": "uuid-v4",
"expiresIn": 12
}
⚠️ Data must be client-side encrypted with AES-256-GCM before sending
GET /api/secret/{id}/metadata
Response:
{
"exists": true,
"viewed": false,
"hasFiles": false,
"fileCount": 0
}
POST /api/secret/{id}/view
Response:
{
"encryptedData": "base64url_encrypted_content",
"files": []
}
⚠️ Secret is deleted immediately after retrieval. Client must decrypt with key from URL fragment.
GET /api/admin/stats
Headers: X-Admin-Token: your_token
Response:
{
"totalSecrets": 5,
"secrets": [{"id": "uuid"}, ...]
}
Configurable via environment variables
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);