A secure, zero-trust secret-sharing app built with Cloudflare Pages and KV. Features optional E2EE, server-side encryption, auto-expiry with timers, OTP verification, and QR code sharing. Includes a dark-themed UI and API for effortless secret management.
- UI and API: Create, retrieve, and remove secrets via a dark-themed web interface or programmatically.
- Zero Trust: Server never sees plaintext secrets; encryption is client-side.
- Encryption: Optional client-side AES-GCM (user password or auto-generated key) + server-side AES-GCM (session key encrypted with master key).
- Auto-Expiry: Options for 5 minutes, 10 minutes, 1 day (default), or 1 week with a countdown timer.
- Retrieval Limits: Configurable max attempts (1-10), with removal option and initial code existence check.
- Secret Link: 6-character short code + 4-digit OTP, with optional 4-character key for non-E2EE secrets.
- QR Code: Generated for easy sharing with clear instructions.
- Security: Autocomplete/autofill disabled for all fields.
- Styling: Dark theme using Tailwind CSS via CDN.
- A Cloudflare account with Pages and KV enabled.
- Node.js and npm installed locally for development.
wrangler
CLI installed (npm install -g wrangler
).
git clone <repository-url>
cd secret-sharing-app
npm install
- In the Cloudflare dashboard, go to Workers & Pages > KV.
- Create a namespace (e.g.,
secrets
). - Note the KV namespace ID.
- Update
wrangler.toml
with the ID:[[kv_namespaces]] binding = "SECRETS_KV" id = "<your-kv-namespace-id>"
- Generate a 256-bit key:
Example output:
openssl rand -base64 32
X7k9p2mQv8sL4nJ6tR3wY5zA1bC0dE2f
- For local dev, add to
.dev.vars
:echo "MASTER_SERVER_KEY=X7k9p2mQv8sL4nJ6tR3wY5zA1bC0dE2f" > .dev.vars
- For production, in Cloudflare Pages, go to Settings > Environment Variables > Secrets, add
MASTER_SERVER_KEY
.
- Link your repository to Cloudflare Pages via GitHub/GitLab.
- Or deploy manually:
npx wrangler pages deploy ./public --project-name secret-sharing-app
- Start a local dev server:
npx wrangler pages dev ./public --kv SECRETS_KV
- Open
http://localhost:8788
to test the app.
- Create a Secret: Visit
/index.html
, enter a secret, toggle E2EE, set expiry and attempts, and submit. Share the generated link/QR code and OTP. - Retrieve a Secret: Use the link or go to
/retrieve.html?code=<short-code>&key=<optional-key>
. Page checks code existence on load; input OTP and (if E2EE) password. View timer and removal option. - Remove a Secret: After retrieval, click "Remove Secret" to delete it from KV.
- Check logs in the terminal or add
console.log
in Functions andscript.js
.
- Edit
public/index.html
to change expiry options:<select id="expiry" class="w-full p-2 border border-gray-600 rounded bg-gray-700 text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"> <option value="300">5 Minutes</option> <option value="600">10 Minutes</option> <option value="86400" selected>1 Day</option> <option value="604800">1 Week</option> <!-- Add more options here --> </select>
- Modify Tailwind classes in
public/index.html
andpublic/retrieve.html
via the CDN script (e.g., toggle dark/light theme by removingdark
class from<html>
).
- In
functions/api/create.js
, adjust the short code length:const code = crypto.randomUUID().slice(0, 8); // Change 6 to desired length
- In
script.js
, adjust the offline key length:function generateOfflineKey() { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let key = ""; for (let i = 0; i = 8; i++) { // Change 4 to desired length key += chars.charAt(Math.floor(Math.random() * chars.length)); } return key; }
- Modify responses in
functions/api/create.js
,functions/api/retrieve.js
,functions/api/remove.js
, orfunctions/api/check.js
.
- Push changes to your linked Git repository, and Cloudflare Pages will auto-deploy.
- Or use:
npx wrangler pages deploy ./public --project-name secret-sharing-app
-
Create Secret:
curl -X POST https://<your-pages-domain>/api/create \ -H "Content-Type: application/json" \ -d '{"encrypted": "<base64-encrypted-secret>", "iv": "<base64-iv>", "expiry": 86400, "maxAttempts": 3, "isE2EE": true}'
Response:
{"success": true, "code": "abc123", "otp": "4567"}
-
Check Secret Existence:
curl https://<your-pages-domain>/api/check?code=abc123
Response:
{"success": true, "message": "Secret exists"}
-
Retrieve Secret:
curl https://<your-pages-domain>/api/retrieve?code=abc123&otp=4567
Response:
{"success": true, "encrypted": "<base64-encrypted-secret>", "iv": "<base64-iv>", "attemptsLeft": 2, "isE2EE": true, "expiry": 1641234567890}
-
Remove Secret:
curl -X DELETE https://<your-pages-domain>/api/remove?code=abc123
Response:
{"success": true}
- Ensure
MASTER_SERVER_KEY
is kept secret and rotated periodically. - Use HTTPS (automatic with Cloudflare).
- Autocomplete/autofill disabled for all fields to prevent browser caching.
- Monitor KV usage to avoid exceeding free tier limits (100K reads/day).
- The 4-character offline key is basic; consider increasing length for non-E2EE secrets.
- For high traffic, consider Cloudflare’s paid KV plans.
- Optimize QR code generation for performance if needed.
my-secret-app/
├── functions/
│ ├── api/
│ │ ├── create.js # API to create a secret
│ │ ├── retrieve.js # API to retrieve a secret
│ │ ├── remove.js # API to remove a secret
│ │ └── check.js # API to check secret existence
├── public/
│ ├── index.html # UI for creating secrets
│ ├── retrieve.html # UI for retrieving secrets
│ ├── script.js # Client-side logic
├── wrangler.toml # Cloudflare configuration
└── package.json # Dependencies
MIT License - feel free to use and modify!