A zero-dependency AT Protocol Personal Data Server written in JavaScript
atproto
pds
Changelog#
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog.
[Unreleased]#
[0.5.0] - 2026-01-08#
Added#
- Direct OAuth authorization without requiring Pushed Authorization Requests (PAR)
/oauth/authorizenow accepts direct query parameters (client_id, redirect_uri, code_challenge, etc.)- Creates authorization request record on-the-fly, same as PAR flow
- DPoP binding deferred to token exchange time for direct auth flows
- Matches official AT Protocol PDS behavior
Changed#
- AS metadata:
require_pushed_authorization_requestsnowfalse - Extracted
validateAuthorizationParameters()helper shared between PAR and direct auth
[0.4.0] - 2026-01-08#
Added#
- Foreign DID proxying via
atproto-proxyheaderparseAtprotoProxyHeader()parsesdid:web:api.bsky.app#bsky_appviewformatgetKnownServiceUrl()maps known service DIDs to URLsproxyToService()generic proxy utility with header forwarding- Repo endpoints (getRecord, listRecords, describeRepo) support explicit proxying
- Returns appropriate errors for malformed headers or unknown services
- Unit tests for proxy utilities
- E2E tests for foreign DID proxying behavior
Changed#
- Refactored
handleAppViewProxyto use sharedproxyToServiceutility
[0.3.0] - 2026-01-08#
Added#
- Granular OAuth scope enforcement on repo and blob endpoints
parseRepoScope()parsesrepo:collection?action=create&action=updateformatparseBlobScope()parsesblob:image/*format with MIME wildcardsScopePermissionsclass for checking repo/blob permissions- Enforced on createRecord, putRecord, deleteRecord, applyWrites, uploadBlob
- Consent page permissions table displaying scopes in human-readable format
- Identity-only: "wants to uniquely identify you" message
- Granular scopes: Table with Collection + Create/Update/Delete columns
- Full access: Warning banner for
transition:generic
parseScopesForDisplay()helper for consent page rendering- E2E tests for scope enforcement and consent page display
[0.2.0] - 2026-01-07#
Added#
- OAuth 2.0 authorization server with full AT Protocol support
- Discovery endpoints (AS metadata, protected resource, JWKS)
- Pushed Authorization Requests (PAR)
- Authorization endpoint with dark-themed consent UI
- Token endpoint (authorization_code + refresh_token grants)
- Token revocation (RFC 7009)
- DPoP proof validation and token binding
- PKCE with S256 code challenge
- Client metadata fetching and validation
- Loopback client support for development
- DPoP JTI tracking to prevent replay attacks
- Comprehensive OAuth e2e tests
Changed#
- BREAKING: Normalized SQL schema to snake_case convention
- Tables:
blob→blobs,record_blob→record_blobs - Columns:
mimeType→mime_type,createdAt→created_at,blobCid→blob_cid,recordUri→record_uri - Existing Durable Objects require storage reset
- Tables:
- Consolidated error responses to use
errorResponsehelper - Moved OAuth types to TYPES & CONSTANTS section
[0.1.0] - 2025-01-07#
Initial experimental release.
Added#
- Repo operations: createRecord, getRecord, putRecord, deleteRecord, applyWrites, listRecords
- Sync endpoints: getRepo (CAR export), subscribeRepos (WebSocket firehose), getLatestCommit
- Authentication: createSession, getSession, refreshSession with JWT tokens
- Blob storage: uploadBlob, getBlob, listBlobs with R2 backend
- MIME type sniffing (JPEG, PNG, GIF, WebP, MP4, AVIF, HEIC)
- Automatic orphaned blob cleanup via DO alarms
- Blob-record association tracking
- Identity: Handle resolution, PLC directory registration
- Federation: Relay notification (requestCrawl), AppView proxy for app.bsky.* endpoints
- Infrastructure:
- Merkle Search Tree (MST) for repo structure
- DAG-CBOR encoding with CID generation
- P-256 ECDSA signing via Web Crypto
- TypeScript checking via JSDoc annotations
- Setup script for key generation and PLC registration