# Deploy: Cloudflare Workers The Worker keeps the repo and the shared account data in a Durable Object with SQLite storage, and blobs in R2. ## Prerequisites - A Cloudflare account, signed in with `npx wrangler login` The Workers Free plan carries Durable Objects with the SQLite storage backend, which is the backend `examples/cloudflare/wrangler.toml` declares through `new_sqlite_classes`. R2 has a free tier of its own. Check the current limits for each against your traffic. Wrangler is a dev dependency of this repository, so every command below uses `npx`. Drop the prefix if you installed it globally. ## Steps 1. **Create the R2 bucket** ```bash npx wrangler r2 bucket create pds-blobs ``` 2. **Set the secrets** ```bash npx wrangler secret put PDS_PASSWORD npx wrangler secret put JWT_SECRET ``` 3. **Deploy** ```bash cd examples/cloudflare npx wrangler deploy ``` 4. **Register the DID** ```bash npm run setup -- --pds https://your-worker.workers.dev ``` ## Permissioned data Uncomment the `[vars]` block in `examples/cloudflare/wrangler.toml` before you deploy. See [permissioned data](permissioned-data.md). ## Your own Worker The package exports the fetch handler and the Durable Object class. Point `wrangler.toml` at `@pdsjs/cloudflare`, or re-export from your own entrypoint: ```javascript export { default, PDSDurableObject } from '@pdsjs/cloudflare' ``` Your `wrangler.toml` needs the same bindings as `examples/cloudflare/wrangler.toml`: | Binding | Kind | Holds | |---------|------|-------| | `PDS` | Durable Object, `PDSDurableObject` | The repo and the account data, in Durable Object SQLite | | `BLOBS` | R2 bucket | Blobs | | `BACKUPS` | R2 bucket, optional | Scheduled backups | | `EMAIL` | Send Email, optional | Account verification mail | The Durable Object needs the `new_sqlite_classes` migration. Keep `BACKUPS` in a separate bucket from `BLOBS`. The retention policy deletes freely inside it. ## Email Account verification mail (confirming an address, authorizing a PLC operation) goes out through the Email Service `EMAIL` binding: ```toml [[send_email]] name = "EMAIL" ``` The From address must use a domain onboarded for Email Sending: ```bash npx wrangler email sending enable example.com ``` Set `PDS_EMAIL_FROM` to the sender, or leave it out to default to `no-reply@HOSTNAME`. A workers.dev hostname cannot be onboarded, so a custom domain is required. With the binding and a sender in place the account page offers "Email for recovery"; without them it stores an address that stays unconfirmed. See [configuration](configuration.md#mail). ## Static sites Set `PDS_EXPERIMENTAL_SITE_DOMAIN` and route the domain's traffic to the Worker. Two zone routes cover every site, present and future: ```toml [[routes]] pattern = "example.com/*" zone_name = "example.com" [[routes]] pattern = "*.example.com/*" zone_name = "example.com" ``` DNS needs two proxied records in the zone: one for the apex and a wildcard (`*`, CNAME to the apex works). With both in place, deploying a site record is the whole release: `blog` is live at `blog.example.com` with no further configuration. `www` with no `www` site deployed redirects to the apex. - **Routes only intercept proxied traffic.** A DNS-only (grey cloud) record bypasses the Worker silently: the route deploys cleanly, and requests keep going to whatever the record points at. Check the proxy status first when a routed hostname serves the wrong thing. - A hostname that must not reach the Worker (a legacy host on the same zone) stays out by keeping its record DNS-only. - Per-site `custom_domain` entries also work, but wrangler only ever attaches them: removing one from `wrangler.toml` and redeploying does not detach it. Delete them in the dashboard or over the API. - Two zone features rewrite site HTML in flight: HTML post-processing strips the ETag, and Browser Cache TTL raises `max-age`, so a redeploy can look stuck for hours. Set Browser Cache TTL to "Respect Existing Headers", or scope a Configuration Rule to the site hostnames. - With the zone's image transformations enabled ("Transform via URL"), the `/.blobs/` route composes with `/cdn-cgi/image/` for resized variants of repo images. The blobs are immutable, so transformed variants cache indefinitely. ## Operating notes The Durable Object holds the `subscribeRepos` WebSockets. A hard restart of the object stops federation, and it stays stopped until those sockets close. Close them gracefully instead.