chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
0
0
# 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/<cid>` 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.