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)
- deleteSession (logout)
- updateHandle
- importRepo
- OAuth
- Account management (createAccount, deleteAccount)
- Email verification
- Invite codes
- Admin/moderation
- Rate limiting
Prerequisites#
- Node.js 18+
- shfmt (optional, for
npm run format)brew install shfmt
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
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.