A single-account AT Protocol Personal Data Server written in JavaScript. Runs on Node.js, Deno, or Cloudflare Workers. Federates with the live network.
Work in progress. Experimental. Probably not production-ready yet, though the author's account, @chadtmiller.com, runs on it.
The official atproto PDS is a multi-tenant server built for running thousands of accounts. pds.js is built around a different premise: one server, one identity, yours.
Each one is off by default. Configuration has the setting that turns each on. They are exempt from semver and may change shape or disappear in any release, including a patch.
$ git clone https://tangled.org/chadtmiller.com/pds.js $ cd pds.js && pnpm install $ just dev
The workspace uses the workspace:* protocol, so install with pnpm. just dev needs just and Docker. It starts a local PLC, relay, and Caddy, runs the PDS, and registers a mock account with a few records. Open localhost:2471/account and sign in with the printed credentials. just dev-down tears it all down.
The fastest path is start.pdsjs.dev, a browser wizard that deploys to your own Cloudflare account. It generates your signing key and secrets client-side. Nothing sensitive leaves your tab.
For manual deployments:
| Target | Guide |
|---|---|
| Docker | deploy-docker.md |
| Node.js | deploy-node.md |
| Cloudflare Workers | deploy-cloudflare.md |
| Deno | deploy-deno.md |
Every deployment needs TLS in front of it, a public hostname, and one run of npm run setup to register the DID with the PLC directory.
Sign in at /account. The PDS serves every page itself, with no external dashboard.
Four working apps live in examples/apps/. Each one discovers whose repo it serves from its own origin, so any of them runs on any pds.js account that installs it.
| App | What it does |
|---|---|
| photos.mjs | Photo galleries from social.grain.* records. Justified layouts, lightbox, map, terrain headers, network favourite counts |
| roasts.mjs | Coffee roast log with a live timer, weight-loss and development metrics against a Sweet Maria's roast card, notes and photos |
| drop.mjs | Image host. Drag, paste, or pick a file and the public /.blobs/<cid> URL copies itself |
| videos.mjs | Video gallery from Drive. Plays same-origin with seeking, read-only; uploads happen in Drive |
$ pdsjs-site install at://you.example.com/dev.pdsjs.app.manifest/photos
pds.js covers the com.atproto.repo.*, com.atproto.sync.*, com.atproto.server.*, and com.atproto.identity.* namespaces. That includes record writes, the firehose over subscribeRepos, streaming blob upload and ranged download, handle resolution and rename, and account migration in and out. It signs commits with a did:plc identity and proxies app.bsky.* to an AppView with service auth.
Sessions come from passwords, app passwords, passkeys, or OAuth 2.0 with PKCE and DPoP-bound tokens.
There are no com.atproto.admin.* endpoints and no invite codes. A single-account server has nobody to administer. The endpoint comparison lists every endpoint on both sides.
| Document | Contents |
|---|---|
| Configuration | Every environment variable |
| Architecture | Ports, adapters, packages, library usage |
| Building an app | Sites, manifests, installs, how to update |
| Permissioned data | Spaces proposal, the run club example |
| Endpoint comparison | Coverage against the official atproto PDS |
| Scope comparison | OAuth scopes against the reference implementation |
| Contributing | Local dev setup, tests, commit gate |
Source at tangled.org/chadtmiller.com/pds.js. Packages on npm under @pdsjs. MIT licensed.
This page is a record in an atproto repo, served by the pds.js instance that holds it. The mark follows the unofficial JavaScript logo by Chris Williams, which is public domain.
connecting to pds.pdsjs.dev