~ knotless Search/
↑↓ move↵ open⇧↵ all results

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
137 lines · 4.5 KB Raw History

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

    npx wrangler r2 bucket create pds-blobs
    
  2. Set the secrets

    npx wrangler secret put PDS_PASSWORD
    npx wrangler secret put JWT_SECRET
    
  3. Deploy

    cd examples/cloudflare
    npx wrangler deploy
    
  4. Register the DID

    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.

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:

export { default, PDSDurableObject } from '@pdsjs/cloudflare'

Your wrangler.toml needs the same bindings as examples/cloudflare/wrangler.toml:

BindingKindHolds
PDSDurable Object, PDSDurableObjectThe repo and the account data, in Durable Object SQLite
BLOBSR2 bucketBlobs
BACKUPSR2 bucket, optionalScheduled backups
EMAILSend Email, optionalAccount 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:

[[send_email]]
name = "EMAIL"

The From address must use a domain onboarded for Email Sending:

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.

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:

[[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.