A zero-dependency AT Protocol Personal Data Server written in JavaScript
atproto pds
JavaScript 100.0%
87 1 4

Clone this repository

https://tangled.org/chadtmiller.com/pds.js
git@tangled.org:chadtmiller.com/pds.js

For self-hosted knots, clone URLs may differ based on your setup.

README.md

pds.js#

A zero-dependency AT Protocol Personal Data Server written in JavaScript, running on Cloudflare Workers with Durable Objects. Let's see how far we can get with just Web APIs.

鈿狅笍 Work in progress - This is experimental. You probably shouldn't use this yet.

Status#

  • Repo operations (createRecord, getRecord, putRecord, deleteRecord, applyWrites, listRecords)
  • Sync endpoints (getRepo, getRecord, subscribeRepos, listRepos, getLatestCommit)
  • Auth (createSession, getSession, refreshSession)
  • Handle resolution (resolveHandle)
  • AppView proxy (app.bsky.* forwarding with service auth)
  • Relay notification (requestCrawl)
  • Single or multi-user (each DID gets isolated storage, no self-service signup yet)
  • Blob storage (uploadBlob, getBlob, listBlobs)
  • OAuth 2.0 (PAR, authorization code + PKCE, DPoP-bound tokens, refresh, revoke)
  • deleteSession (logout)
  • updateHandle
  • importRepo
  • Account management (createAccount, deleteAccount)
  • Email verification
  • Invite codes
  • Admin/moderation
  • Rate limiting

See endpoint comparison for detailed coverage vs the official atproto PDS.

Prerequisites#

  • Node.js 18+

Quick Start#

npm install

# Create local dev config
cp .env.example .dev.vars
# Edit .dev.vars with your values

# Run locally
npm run dev

Configuration#

For local development, create .dev.vars:

PDS_PASSWORD=your-password  # Used for legacy auth and OAuth consent
JWT_SECRET=your-secret
RELAY_HOST=https://bsky.network  # optional

For production, use Cloudflare secrets:

wrangler secret put PDS_PASSWORD
wrangler secret put JWT_SECRET
wrangler secret put RELAY_HOST  # optional

Blob Storage#

Blobs (images, videos) are stored in Cloudflare R2. Create the bucket before deploying:

npx wrangler r2 bucket create pds-blobs

The binding is already configured in wrangler.toml. Supported formats: JPEG, PNG, GIF, WebP, MP4. Max size: 50MB. Orphaned blobs are automatically cleaned up after 24 hours.

Testing#

npm test          # Unit tests
npm run test:e2e  # E2E tests (starts local server)

Deploy#

wrangler deploy

Initialize#

After deployment, run the setup script to register with PLC and initialize:

npm run setup -- --pds https://your-pds.workers.dev

This generates keys, registers your DID with the PLC directory, initializes the PDS, and saves credentials. Handle defaults to the worker hostname.