Cloudflare Worker #
A lexicon authority as a single Worker script. No bindings, no secrets, no origin.
corpora build (your machine, with the key) -> dist/
wrangler deploy (any machine, no key) -> 330+ cities
Deploy #
$ npm install
$ CORPORA_SIGNING_KEY=$(pass corpora/signing-key) \
npx corpora build --lexicons ./lexicons --out ./dist --did did:web:lexicon.example.com
$ npx wrangler deploy
Two commands, and only the first one touches the key. wrangler deploy uploads a script whose
only interesting content is the dist/ you just built. Nothing in the deploy step needs the
signing key, and nothing in the deployed Worker holds it.
Before the first deploy, edit wrangler.jsonc: set name, set routes[0].pattern to your
hostname, and set compatibility_date to today's date.
What the entry point is #
import { createWorker } from 'corpora/worker'
import data from './dist/data.js'
export default createWorker(data)
dist/data.js is a generated ESM module of base64 payloads. corpora/worker wraps
corpora/serve, which is a switch statement over those payloads. Neither imports anything, so
wrangler bundles them without a single polyfill or module-resolution flag. That is the payoff of
keeping @atproto/repo in the offline build — see
PRIOR_ART for what the alternative
costs.
Do not use Workers static assets #
wrangler.jsonc has no assets block on purpose.
Static assets are lazily populated: fetched from central storage on first request and cached at
the nearest location, with tiered caching on a miss. That is caching with fallback to an origin,
which is the exact topology this project exists to remove. Worker scripts replicate instead,
propagating globally in under a second. Static assets also fix Content-Type from the file
extension at upload time and _headers cannot override it, so .car files would be served with
a MIME type you cannot correct. See
ARCHITECTURE.
The payload is a fraction of the bundle limit, so embedding it in the script costs nothing.
Check the bundle size #
$ npx wrangler deploy --dry-run --outdir .wrangler/bundled
Total Upload: 259.61 KiB / gzip: 47.23 KiB
The gzip: figure is the one that counts against the limit: 3 MB on the Workers Free plan,
10 MB on Paid, plus a 64 MB uncompressed ceiling on both. A real namespace lands two orders of
magnitude below that. Watch the reported startup_time_ms too — the whole script's global scope,
including the base64 decode in data.js, must execute within 1 second.
Routing and DNS #
routes with custom_domain: true makes wrangler create the DNS record and issue the
certificate, so there is no DNS step. It requires the zone to be in the same Cloudflare account
and the hostname to have no existing CNAME record.
did:web resolution fetches https://<host>/.well-known/did.json with redirects treated as
errors, so the Worker must own the hostname the DID names — not a name that redirects to it.
Serve the authority from a dedicated subdomain and this is automatic.
Verify #
$ npx corpora verify --origin https://lexicon.example.com
A deploy that succeeds is not a deploy that resolves. See DEPLOYING.
Cost #
Every request is a Worker invocation returning bytes already in memory: no subrequests, no
storage reads, single-digit milliseconds of CPU. A lexicon authority's traffic is Auth Servers
resolving on a cold cache, which is not much traffic. Uncommenting the cache block in
wrangler.jsonc removes even the invocation for repeat requests.