An end-to-end encrypted file and text transfer assistant for Cloudflare Workers. The Worker and Durable Object only coordinate anonymous room presence and relay encrypted WebSocket envelopes. File names, text, file bytes, MIME types, and transfer metadata are encrypted in the browser before anything is sent.
- Anonymous rooms with one manual room code, stored in URLs as
/r/:roomCode. - The room code is also the encryption password.
- Manual join form for entering only the room code.
- Click upload, drag and drop, and paste support for files.
- Plain text messages with the same client-side AES-GCM encryption path.
- Browser previews for images, video, audio, PDF, text-like files, and generic downloads.
- Cloudflare Durable Object room fanout over WebSockets.
- Temporary encrypted room history, so reconnecting recipients can receive recently sent files without asking the sender to resend.
npm install
npm run devOpen the Wrangler URL, usually http://localhost:8787. The app creates a room code automatically. Other browsers can either open the link or open the app and enter the room code manually.
Wrangler is installed as a project dev dependency. Before the first deploy from a local machine, authorize Wrangler with your Cloudflare account:
npx wrangler loginWrangler opens a Cloudflare OAuth page in your browser. Sign in, choose the Cloudflare account that should own the Worker, and approve the requested Wrangler permissions. If the browser does not open automatically, copy the authorization URL printed in the terminal and open it manually.
Confirm that Wrangler can see your Cloudflare identity and account membership:
npx wrangler whoamiIf your user belongs to multiple Cloudflare accounts, set the account explicitly before deploying:
export CLOUDFLARE_ACCOUNT_ID="<account-id>"For headless environments such as CI, use an API token instead of browser OAuth:
- In the Cloudflare dashboard, create a user API token from
My Profile > API Tokens, or an account-owned token fromManage Account > API Tokens. - Scope the token to the target account. For this
workers.devdeployment, the token needs Workers script edit access for the account, for exampleAccount > Workers Scripts > Edit. If your Cloudflare account requires account metadata lookup during deployment, also grantAccount > Account Settings > Read. - Store the token in the CI secret store, not in this repository.
- Expose the token and account ID to Wrangler when deploying:
export CLOUDFLARE_API_TOKEN="<api-token>"
export CLOUDFLARE_ACCOUNT_ID="<account-id>"
npm run deploynpm run deployWrangler uses wrangler.jsonc, which configures static assets, a ROOMS Durable Object binding, and a SQLite-backed Durable Object migration. The deployed Worker name is e2ee-transfer-assistant, and workers_dev is enabled so Wrangler prints a *.workers.dev URL after a successful deploy.
New rooms use a browser-generated room code. The browser derives a 256-bit AES-GCM key from that same room code with PBKDF2-SHA-256 before any transfer content is encrypted. There is no separate passcode or URL fragment secret. Every text message, file metadata object, and file chunk is encrypted with a fresh 96-bit IV before being sent through the WebSocket.
The Worker temporarily stores encrypted WebSocket envelopes for up to about one hour, capped by count and total ciphertext size. This lets a recipient reconnect or join shortly after a send and receive the encrypted file chunks from room history. The Worker still cannot decrypt transfer contents, but it can observe operational metadata such as IP-level request metadata, room IDs, connection timing, ciphertext sizes, peer counts, and the existence/order of encrypted chunks. Since the room code is also the password and is sent in the room URL, this simple mode is intended for quick private transfers between people who already trust the deployment. Anyone with the room code can join and decrypt recent room history.
This implementation is optimized for temporary transfer rather than permanent storage. If long-lived delivery is needed, add explicit encrypted blob storage with expiry controls.