chadtmiller.com/pds.js
An atproto PDS in JavaScript, for Cloudflare Workers and Node
Contributing
Setup
git clone https://tangled.org/chadtmiller.com/pds.js
cd pds.js && pnpm install
npm run build:ui
Install with pnpm. The workspace dependencies use the workspace:* protocol, and
npm install fails on it with EUNSUPPORTEDPROTOCOL. The npm run scripts work
normally once pnpm has installed the tree.
Run npm run build:ui first on a fresh clone. @pdsjs/account-ui exports a
gitignored bundle, and without it the node and cloudflare entrypoints do not
resolve.
Local development
With just and Docker installed, one command
starts the whole stack:
just dev # infra, PDS, and a seeded account
just dev-down # stop the PDS and remove the docker infra
just dev starts the docker infra (local PLC, relay, Caddy) and runs the PDS on
port 2471. It then registers a mock account with a few records. Open
http://localhost:2471/account and sign in with the printed credentials.
just dev serves a prebuilt account interface. There is no hot reload. After you
change interface code, run npm run build:ui and refresh the page.
For live interface work, run just dev-live. It starts the same stack plus a
Vite dev server with hot module reload on port 5173. Sign in at
http://localhost:5173/account, and your edits reload in the browser. The PDS also
restarts on backend edits. Port 2471 and port 5173 are different origins, so you
sign in separately on each.
Run just to see all recipes. just logs tails the PDS log.
Setup without <code>just</code>
# Start the local PLC and relay (needs Docker)
docker compose up -d
# Start the PDS
npm run dev:node
# Register with the local PLC, in another terminal
npm run setup -- --pds https://localhost:3443 --plc-url http://localhost:2582 --relay-url http://localhost:2470
The commit gate
npm run ci
That runs build:ui, biome, tsc --noEmit, and test:coverage. CI runs the
same command. Run it before you commit.
The full gate is heavy: the git integration tests spawn the real git binary
against an in-process PDS, and the coverage floors across 195 test files take a
few minutes. You do not need it for every local edit. CI enforces it on every
push, so iterate with the targeted commands below and run the full gate only
before you commit.
The individual steps:
npm run check # biome lint + format check
npm run format # auto-format
npm run lint # lint only
npm run typecheck # tsc --noEmit
npm test # unit tests
npm run test:watch # unit tests, watching
Coverage floors are in vitest.config.js, per area rather than global. A glob
key aggregates every file it matches, so give each module its own entry.
End-to-end tests
The e2e suite is not part of the gate. It needs docker compose.
docker compose up -d
npm run test:e2e:node
npm run test:e2e:deno
npm run test:e2e:cloudflare
The suite binds port 2471, so a just dev stack blocks it.
npm run test:reference tests permissioned data against the reference
implementation. See docs/permissioned-data.md.
Conventions
CLAUDE.md holds the conventions this repository enforces by review
rather than by tooling: what earns a comment, how to phrase one, no any in
library type annotations, and no em dashes in user-facing copy.
docs/architecture.md explains the ports and the packages.
docs/pds-decomposition.md tracks the handler module
work in core/src/pds.js. Read it before you take the next cluster.
Changesets
npm run changeset
Describe the change relative to the previous release. That is where "what changed" belongs, rather than in a code comment.
Project structure
packages/
core/ # platform-agnostic core
spaces/ # permissioned data (optional)
lexicon-resolver/ # lexicon resolution and record validation
account-ui/ # account interface
node/ # Node.js adapter
deno/ # Deno adapter
cloudflare/ # Cloudflare adapter
readonly/ # read-only server for CAR-backed repositories
storage-sqlite/ # SQLite storage adapter
blobs-*/ # blob storage adapters
examples/
node/ # Node.js example
deno/ # Deno example
cloudflare/ # Cloudflare example
runclub/ # permissioned data demo, two PDSes and a syncing app
docker/ # container entrypoint and the local dev stack
scripts/ # setup, seeding, and reference tests