Zero-knowledge, one-time secret sharing powered by Cloudflare Workers.
Secrets are encrypted in your browser before being stored. The decryption key never leaves your device. Notes self-destruct after being read.
- Zero-knowledge encryption -- AES-256-GCM, client-side. The server only stores opaque encrypted bytes.
- Burn after reading -- non-password notes are destroyed the moment they're retrieved.
- Password protection -- optional second factor with 3 decryption attempts before auto-burn.
- Configurable TTL -- 1 hour, 24 hours, 1 week (default), or 30 days.
- Multiple views -- allow 1-5 views before burn (non-password notes).
- Title and burn message -- optional context for recipients and custom post-destruction message.
- IP restriction -- limit note access to specific IPv4/IPv6 addresses or CIDR ranges.
- Dark/light theme -- follows system preference with manual toggle.
- Usage stats -- note creation and burn counters at
/api/stats. - API-first -- JSON API with OpenAPI docs at
/api/docs, plusPOST /for easy curl piping. - CLI tools -- shell script and Rust binary for scripted secret sharing.
Visit secure.gremlin.group, paste your secret, and share the link.
Requires python3 with the cryptography package and curl.
# Download the script
curl -o gremlin-secret.sh https://secure.gremlin.group/cli.sh
chmod +x gremlin-secret.sh
# Share a secret
echo "my-api-key-123" | ./gremlin-secret.sh
# With a password (via env var)
GREMLIN_SECRET_PASSWORD="hunter2" echo "secret" | ./gremlin-secret.sh
# Custom TTL (1 hour)
echo "expires soon" | ./gremlin-secret.sh --ttl 3600Download the latest binary from Releases, or build from source:
cd cli/gremlin-secret
cargo build --release# Share a secret
echo "my-api-key-123" | gremlin-secret
# With a password (prompted interactively)
echo "secret" | gremlin-secret --password
# Custom TTL and base URL
echo "secret" | gremlin-secret --ttl 3600 --base-url https://secure.example.comInteractive documentation is available at secure.gremlin.group/api/docs.
curl -X POST https://secure.gremlin.group/api/notes \
-H "Content-Type: application/json" \
-d '{"c":"<base64-ciphertext>","iv":"<base64-iv>","pw":false,"ttl":604800}'Response: {"id":"<uuid>"}
The POST / endpoint is an alias that accepts the same payload.
curl https://secure.gremlin.group/api/notes/<id>Response: {"c":"...","iv":"...","pw":false}
For non-password notes, the note is permanently deleted on this call. For password-protected notes, you get 3 attempts before auto-burn. Call DELETE /api/notes/<id> to confirm burn after successful decryption.
curl https://secure.gremlin.group/api/docs.jsonAll encryption happens client-side using the Web Crypto API.
| Parameter | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key size | 256 bits (32 bytes) |
| IV size | 96 bits (12 bytes) |
| Auth tag | 128 bits, appended to ciphertext |
| Password derivation | PBKDF2-SHA-256, 600,000 iterations |
| Key encoding | Base64url (URL fragment) |
The decryption key is stored in the URL fragment (#), which browsers never send to the server.
# Install dependencies
bun install
# Run locally
bunx wrangler dev
# Run tests
bunx vitest run
# Type check
bunx tsc --noEmit# Create KV namespace
bunx wrangler kv namespace create NOTES
# Update wrangler.toml with the namespace ID, then deploy
bunx wrangler deploySet up WAF rate limiting rules to protect against brute-force attacks:
CLOUDFLARE_API_TOKEN=xxx CLOUDFLARE_ZONE_ID=xxx sh scripts/setup-rate-limits.shRequires an API token with "Zone WAF Write" permission. Creates three rules:
- GET /api/notes/*: Block after 10 failed lookups (404) per minute per IP for 5 minutes
- DELETE /api/notes/*: Block after 5 failed deletes (403/404) per minute per IP for 10 minutes
- POST /api/notes: Block after 10 creates per minute per IP for 5 minutes
| Variable | Default | Description |
|---|---|---|
BASE_URL |
https://secure.gremlin.group |
Public URL for link generation |
MAX_NOTE_SIZE |
1048576 |
Maximum note size in bytes (1MB) |
DEFAULT_TTL |
604800 |
Default TTL in seconds (1 week) |
Proprietary. Copyright Gremlin LTD.