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.