PDS Implementation TODOs#
Lewis' corrected big boy todofile
Server Infrastructure & Proxying#
- Health Check
- Implement
GET /healthendpoint (returns "OK"). - Implement
GET /xrpc/_healthendpoint (returns "OK").
- Implement
- Server Description
- Implement
com.atproto.server.describeServer(returns available user domains).
- Implement
- XRPC Proxying
- Implement strict forwarding for all
app.bsky.*andchat.bsky.*requests to an appview. - Forward auth headers correctly.
- Handle appview errors/timeouts gracefully.
- Implement strict forwarding for all
Authentication & Account Management (com.atproto.server)#
- Account Creation
- Implement
com.atproto.server.createAccount. - Validate handle format (reject invalid characters).
- Create DID for new user (PLC directory).
- Initialize user repository (Root commit).
- Return access JWT and DID.
- Create DID for new user (did:web).
- Implement
- Session Management
- Implement
com.atproto.server.createSession(Login). - Implement
com.atproto.server.getSession. - Implement
com.atproto.server.refreshSession. - Implement
com.atproto.server.deleteSession(Logout). - Implement
com.atproto.server.activateAccount. - Implement
com.atproto.server.checkAccountStatus. - Implement
com.atproto.server.createAppPassword. - Implement
com.atproto.server.createInviteCode. - Implement
com.atproto.server.createInviteCodes. - Implement
com.atproto.server.deactivateAccount. - Implement
com.atproto.server.deleteAccount(user-initiated, requires password + email token). - Implement
com.atproto.server.getAccountInviteCodes. - Implement
com.atproto.server.getServiceAuth(Cross-service auth). - Implement
com.atproto.server.listAppPasswords. - Implement
com.atproto.server.requestAccountDelete. - Implement
com.atproto.server.requestEmailConfirmation/requestEmailUpdate. - Implement
com.atproto.server.requestPasswordReset/resetPassword. - Implement
com.atproto.server.reserveSigningKey. - Implement
com.atproto.server.revokeAppPassword. - Implement
com.atproto.server.updateEmail. - Implement
com.atproto.server.confirmEmail.
- Implement
Repository Operations (com.atproto.repo)#
- Record CRUD
- Implement
com.atproto.repo.createRecord.- Generate
rkey(TID) if not provided. - Handle MST (Merkle Search Tree) insertion.
- Trigger Firehose Event.
- Generate
- Implement
com.atproto.repo.putRecord. - Implement
com.atproto.repo.getRecord. - Implement
com.atproto.repo.deleteRecord. - Implement
com.atproto.repo.listRecords. - Implement
com.atproto.repo.describeRepo. - Implement
com.atproto.repo.applyWrites(Batch writes). - Implement
com.atproto.repo.importRepo(Migration). - Implement
com.atproto.repo.listMissingBlobs.
- Implement
- Blob Management
- Implement
com.atproto.repo.uploadBlob.- Store blob (S3).
- return
blobref (CID + MimeType).
- Implement
Sync & Federation (com.atproto.sync)#
- The Firehose (WebSocket)
- Implement
com.atproto.sync.subscribeRepos.- Broadcast real-time commit events.
- Handle cursor replay (backfill).
- Implement
- Bulk Export
- Implement
com.atproto.sync.getRepo(Return full CAR file of repo). - Implement
com.atproto.sync.getBlocks(Return specific blocks via CIDs). - Implement
com.atproto.sync.getLatestCommit. - Implement
com.atproto.sync.getRecord(Sync version, distinct from repo.getRecord). - Implement
com.atproto.sync.getRepoStatus. - Implement
com.atproto.sync.listRepos. - Implement
com.atproto.sync.notifyOfUpdate.
- Implement
- Blob Sync
- Implement
com.atproto.sync.getBlob. - Implement
com.atproto.sync.listBlobs.
- Implement
- Crawler Interaction
- Implement
com.atproto.sync.requestCrawl(Notify relays to index us).
- Implement
Identity (com.atproto.identity)#
- Resolution
- Implement
com.atproto.identity.resolveHandle(Can be internal or proxy to PLC). - Implement
com.atproto.identity.updateHandle. - Implement
com.atproto.identity.submitPlcOperation/signPlcOperation/requestPlcOperationSignature. - Implement
com.atproto.identity.getRecommendedDidCredentials. - Implement
/.well-known/did.json(Depends on supporting did:web).
- Implement
Admin Management (com.atproto.admin)#
- Implement
com.atproto.admin.deleteAccount. - Implement
com.atproto.admin.disableAccountInvites. - Implement
com.atproto.admin.disableInviteCodes. - Implement
com.atproto.admin.enableAccountInvites. - Implement
com.atproto.admin.getAccountInfo/getAccountInfos. - Implement
com.atproto.admin.getInviteCodes. - Implement
com.atproto.admin.getSubjectStatus. - Implement
com.atproto.admin.sendEmail. - Implement
com.atproto.admin.updateAccountEmail. - Implement
com.atproto.admin.updateAccountHandle. - Implement
com.atproto.admin.updateAccountPassword. - Implement
com.atproto.admin.updateSubjectStatus.
Moderation (com.atproto.moderation)#
- Implement
com.atproto.moderation.createReport.
Temp Namespace (com.atproto.temp)#
- Implement
com.atproto.temp.checkSignupQueue(signup queue status for gated signups).
OAuth 2.1 Support#
Full OAuth 2.1 provider for ATProto native app authentication.
- OAuth Provider Core
- Implement
/.well-known/oauth-protected-resourcemetadata endpoint. - Implement
/.well-known/oauth-authorization-servermetadata endpoint. - Implement
/oauth/authorizeauthorization endpoint (headless JSON mode). - Implement
/oauth/parPushed Authorization Request endpoint. - Implement
/oauth/tokentoken endpoint (authorization_code + refresh_token grants). - Implement
/oauth/jwksJSON Web Key Set endpoint. - Implement
/oauth/revoketoken revocation endpoint. - Implement
/oauth/introspecttoken introspection endpoint.
- Implement
- OAuth Database Tables
- Device table for tracking authorized devices.
- Authorization request table.
- Authorized client table.
- Token table for OAuth tokens.
- Used refresh token table (replay protection).
- DPoP JTI tracking table.
- DPoP (Demonstrating Proof-of-Possession) support.
- Client metadata fetching and validation.
- PKCE (S256) enforcement.
- OAuth token verification extractor for protected resources.
- Authorization UI templates (currently headless-only, returns JSON for programmatic flows).
- Implement
private_key_jwtsignature verification (currently rejects with clear error).
OAuth Security Notes#
I've tried to ensure that this codebase is not vulnerable to the following:
- Constant-time comparison for signature verification (prevents timing attacks)
- HMAC-SHA256 for access token signing with configurable secret
- Production secrets require 32+ character minimum
- DPoP JTI replay protection via database
- DPoP nonce validation with HMAC-based timestamps (5 min validity)
- Refresh token rotation with reuse detection (revokes token family on reuse)
- PKCE S256 enforced (plain not allowed)
- Authorization code single-use enforcement
- URL encoding for redirect parameters (prevents injection)
- All database queries use parameterized statements (no SQL injection)
- Deactivated/taken-down accounts blocked from OAuth authorization
- Client ID validation on token exchange (defense-in-depth against cross-client attacks)
Auth Notes#
- Algorithm choice: Using ES256K (secp256k1 ECDSA) with per-user keys. Ref PDS uses HS256 (HMAC) with single server key. Our approach provides better key isolation but differs from reference implementation.
- Support the ref PDS HS256 system too.
- Token storage: Now storing only token JTIs in session_tokens table (defense in depth against DB breaches). Refresh token family tracking enables detection of token reuse attacks.
- Key encryption: User signing keys encrypted at rest using AES-256-GCM with keys derived via HKDF from MASTER_KEY environment variable. Migration-safe: supports both encrypted (version 1) and plaintext (version 0) keys.
PDS-Level App Endpoints#
These endpoints need to be implemented at the PDS level (not just proxied to appview).
Actor (app.bsky.actor)#
- Implement
app.bsky.actor.getPreferences(user preferences storage). - Implement
app.bsky.actor.putPreferences(update user preferences). - Implement
app.bsky.actor.getProfile(PDS-level with proxy fallback). - Implement
app.bsky.actor.getProfiles(PDS-level with proxy fallback).
Feed (app.bsky.feed)#
These are implemented at PDS level to enable local-first reads:
- Implement
app.bsky.feed.getTimeline(PDS-level with proxy). - Implement
app.bsky.feed.getAuthorFeed(PDS-level with proxy). - Implement
app.bsky.feed.getActorLikes(PDS-level with proxy). - Implement
app.bsky.feed.getPostThread(PDS-level with proxy). - Implement
app.bsky.feed.getFeed(PDS-level with proxy).
Notification (app.bsky.notification)#
- Implement
app.bsky.notification.registerPush(push notification registration).
Deprecated Sync Endpoints (for compatibility)#
- Implement
com.atproto.sync.getCheckout(deprecated, still needed for compatibility). - Implement
com.atproto.sync.getHead(deprecated, still needed for compatibility).
Misc HTTP Endpoints#
- Implement
/robots.txtendpoint.
Record Schema Validation#
- Handle this generically.
Preference Storage#
User preferences (for app.bsky.actor.getPreferences/putPreferences):
- Create preferences table for storing user app preferences.
- Implement
app.bsky.actor.getPreferenceshandler (read from postgres, proxy fallback). - Implement
app.bsky.actor.putPreferenceshandler (write to postgres).
Infrastructure & Core Components#
- Sequencer (Event Log)
- Implement a
Sequencer(backed byrepo_seqtable). - Implement event formatting (
commit,handle,identity,account). - Implement database polling / event emission mechanism.
- Implement cursor-based event replay (
requestSeqRange).
- Implement a
- Repo Storage & Consistency (in postgres)
- Implement
RepoStoragefor postgres (replaces per-user SQLite).- Read/Write IPLD blocks to
blockstable (global deduplication). - Manage Repo Root in
repostable.
- Read/Write IPLD blocks to
- Implement Atomic Repo Transactions.
- Ensure
blockswrite,repo_rootupdate,recordsindex update, andsequencerevent are committed in a single transaction.
- Ensure
- Implement concurrency control (row-level locking on
repostable) to prevent concurrent writes to the same repo.
- Implement
- DID Cache
- Implement caching layer for DID resolution (Redis or in-memory).
- Handle cache invalidation/expiry.
- Background Jobs
- Implement
Crawlersservice (debounce notifications to relays).
- Implement
- Notification Service
- Queue-based notification system with database table
- Background worker polling for pending notifications
- Extensible sender trait for multiple channels
- Email sender via OS sendmail/msmtp
- Discord bot sender
- Telegram bot sender
- Signal bot sender
- Helper functions for common notification types (welcome, password reset, email verification, etc.)
- Respect user's
preferred_notification_channelsetting for non-email-specific notifications
- Image Processing
- Implement image resize/formatting pipeline (for blob uploads).
- IPLD & MST
- Implement Merkle Search Tree logic for repo signing.
- Implement CAR (Content Addressable Archive) encoding/decoding.
- Validation
- DID PLC Operations (Sign rotation keys).
- Fix any remaining TODOs in the code, everywhere, full stop.
Web Management UI#
A single-page web app for account management. The frontend (JS framework) calls existing ATProto XRPC endpoints - no server-side rendering or bespoke HTML form handlers.
Architecture#
- Static SPA served from PDS (or separate static host)
- Frontend authenticates via OAuth 2.1 flow (same as any ATProto client)
- All operations use standard XRPC endpoints (existing + new PDS-specific ones below)
- No server-side sessions or CSRF - pure API client
PDS-Specific XRPC Endpoints (new)#
Absolutely subject to change, "bspds" isn't even the real name of this pds thus far :D Anyway... endpoints for PDS settings not covered by standard ATProto:
-
com.bspds.account.getNotificationPrefs- get preferred channel, verified channels -
com.bspds.account.updateNotificationPrefs- set preferred channel -
com.bspds.account.getNotificationHistory- list past notifications -
com.bspds.account.verifyChannel- initiate verification for Discord/Telegram/Signal -
com.bspds.account.confirmChannelVerification- confirm with code -
com.bspds.admin.getServerStats- user count, storage usage, etc.
Frontend Views#
Uses existing ATProto endpoints where possible:
User Dashboard
- Account overview (uses
com.atproto.server.getSession,com.atproto.admin.getAccountInfo) - Active sessions view (needs new endpoint or extend existing)
- App passwords (uses
com.atproto.server.listAppPasswords,createAppPassword,revokeAppPassword) - Invite codes (uses
com.atproto.server.getAccountInviteCodes,createInviteCode)
Notification Preferences
- Channel selector (uses
com.bspds.account.*endpoints above) - Verification flows for Discord/Telegram/Signal
- Notification history view
Account Settings
- Email change (uses
com.atproto.server.requestEmailUpdate,updateEmail) - Password change (uses
com.atproto.server.requestPasswordReset,resetPassword) - Handle change (uses
com.atproto.identity.updateHandle) - Account deletion (uses
com.atproto.server.requestAccountDelete,deleteAccount) - Data export (uses
com.atproto.sync.getRepo)
Admin Dashboard (privileged users only)
- User list (uses
com.atproto.admin.getAccountInfoswith pagination) - User detail/actions (uses
com.atproto.admin.*endpoints) - Invite management (uses
com.atproto.admin.getInviteCodes,disableInviteCodes) - Server stats (uses
com.bspds.admin.getServerStats)