An anonymous static HTML sharing service built on Cloudflare Workers and D1.
- Upload a single
.htmlfile up to 512KB with an alias and optional metadata. - Shares are accessible at
/s/<alias>. - Alias availability is checked before upload and enforced with a unique D1 index.
- Default expiration is 7 days; choose 1-30 days. Expired records are removed by a daily UTC 20:00 cron job and lazily on read.
- D1 keeps up to 2000 shares. When full, creation returns a "storage full" response.
- Backdoor convention: entering
zenshare/<name>as the alias stores<name>as a permanent share and the same availability check applies. - Optional password protection: files are encrypted in the browser with PBKDF2 and AES-256-GCM; the server never stores passwords.
- Reader pages render content in a sandboxed iframe with meta info, HTML download, PDF export, and a share button.
- Responsive layout, light/dark theme, and Chinese/English UI.
src/index.js Worker routes, API, cron cleanup
public/ Upload and reader page assets
migrations/ D1 migration
scripts/smoke-test.mjs Local smoke test (encryption + API round trip)
wrangler.toml Worker / D1 / cron configuration
npm install
npm run db:local
npm run devThe local server runs at http://127.0.0.1:8787.
Run the smoke test:
npm run smokeThe smoke test creates a password-protected share and verifies upload, read, decryption, wrong-password rejection, and alias duplicate detection.
The D1 binding is defined in wrangler.toml. Do not publish credentials or database identifiers in public documentation.
Create the schema in the remote D1 database:
wrangler login
npm run db:remoteThe Worker also runs idempotent CREATE TABLE IF NOT EXISTS statements on first access, so a missing migration will not break the service.
npm run deployThe Worker name, routes, and cron trigger are defined in wrangler.toml.
GET /api/alias-check?alias=demoReturns availability, the normalized alias, and whether the backdoor prefix was used.
POST /api/share
Content-Type: application/json{
"alias": "demo",
"title": "Report title",
"description": "Description",
"author": "Author",
"tags": ["report", "demo"],
"expires_days": 7,
"password_protected": true,
"content": "base64(ciphertext or plaintext)",
"salt": "base64(16 bytes)",
"iv": "base64(12 bytes)"
}expires_days defaults to 7 and must be between 1 and 30. When password_protected is true, salt and iv are required.
- Password-protected shares are zero-knowledge: the key is derived in the reader's browser and the server only stores ciphertext, salt, and IV.
- Unprotected shares are stored as plaintext.
- Reader pages render uploaded HTML inside an iframe without
allow-same-origin, so uploaded scripts cannot access the Zenshare origin. - Permanent shares are not editable or deletable through the UI; remove them directly in D1 if needed.
MIT. See LICENSE.