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

chadtmiller.com/pds.js

An atproto PDS in JavaScript, for Cloudflare Workers and Node

0
0
main
Go to filet
Code
README.md

@pdsjs/start

The setup wizard behind start.pdsjs.dev. A visitor signs in with Cloudflare and the wizard deploys pds.js into their own account: one Worker, one R2 bucket, a workers.dev hostname as the handle. The signing key, the server password and the JWT secret are generated in the visitor's browser tab and go from the tab to the visitor's own worker.

The wizard is a static site, served from the pdsjs.dev PDS through the site lexicons like any other site. There is no wizard worker. The page calls its dependencies directly:

  • The OAuth token exchange goes to dash.cloudflare.com. It is a form POST, which browsers send without a preflight, and the response carries CORS for the origins registered on the OAuth client.
  • PLC registration and the relay crawl request go to plc.directory and bsky.network, which serve open CORS.
  • The worker artifact comes from the release site named in config.json (artifacts.pdsjs.dev, filled by .knotless/workflows/release-worker.yml on each version tag). Site files serve with open CORS.
  • Cloudflare API calls are the one thing a browser cannot do: api.cloudflare.com answers no CORS preflight. They ride through the serving PDS's /cf-api/ passthrough (PDS_EXPERIMENTAL_CF_API_PROXY in @pdsjs/cloudflare), same-origin because the same worker serves the site.

Operator setup

  1. Register an OAuth client in the Cloudflare dashboard under Manage account, then OAuth clients. Grant type: authorization code with PKCE, no client secret. Redirect URL: https://<host>/callback. Add the site's origin to Allowed CORS Origins; without it the token exchange fails in the browser. Scopes: account settings read, workers scripts write, workers R2 write; plus zone read and DNS write for the custom-domain picker. Without the zone scopes the picker offers workers.dev alone.
  2. Set PDS_EXPERIMENTAL_CF_API_PROXY = "true" on the PDS worker that serves the site domain.
  3. Build and publish:
npm run build:start
cd packages/start
PDS_URL=https://pds.pdsjs.dev PDS_IDENTIFIER=pdsjs.dev PDS_PASSWORD=... \
  OAUTH_CLIENT_ID=... OAUTH_SCOPES="account-settings.read workers-scripts.write workers-r2.write zone.read dns.write" \
  npm run publish-site

The record's fallback serves the page for /callback and /update, the app's client-side routes. config.json is generated by the publish script; the page falls back to sane defaults for everything but the client id.

A public OAuth client needs domain ownership verification for the redirect URL's domain, via a DNS TXT record. Public visibility is permanent once set.

What the visitor's account needs

  • R2 enabled. Enabling it takes one dashboard step and a billing profile; the wizard links there when the bucket creation fails.
  • A workers.dev subdomain. The wizard links to the dashboard when none is set.

Domains

The configure screen offers the account's active zones beside workers.dev. With a zone chosen, the server answers at pds.<zone> (a worker custom domain; Cloudflare creates the record and the certificate) and the handle is the zone apex, proven by a _atproto TXT record the wizard writes. The domain's site, if any, stays untouched: handle resolution reads the TXT record and never the apex. The workers.dev hostname stays enabled and carries the bootstrap, since a fresh certificate can take minutes.

Not yet in the wizard

  • Domain registration through the Registrar API (beta; needs a billing profile and a registrant contact on the account).
  • Updates for custom-domain servers; /update derives the worker name from a workers.dev hostname only.
  • Scheduled backups. The deployed worker supports them; see the root wrangler.toml for the S3 settings.