@dwk/atproto-pds
v1.0.0-beta.2
Published
Edge-native AT Protocol Personal Data Server: XRPC surface, signed Merkle Search Tree repository, CAR sync, did:web identity. Ships the per-account Durable Object.
Maintainers
Readme
@dwk/atproto-pds
Edge-native AT Protocol Personal Data Server: XRPC surface, a signed Merkle Search Tree repository, CAR sync, and
did:webidentity. Endpoint package + Durable Object.
Part of the @dwk IndieWeb + Solid cohort. See the
package specification for the full
requirements — including why this package is the cohort's strategic outlier.
A self-hosted AT Protocol Personal Data
Server (PDS) rooted at the user's own domain: the home of one account's
repository in the Bluesky network. It is the package embodiment of
“there are no instances in atproto” —
identity is a did:web the user controls and hosting is a swappable service
entry in that DID document, so the repository is a portable, signed,
content-addressed structure rather than an identity-defining silo. It mirrors the
architecture proven in @dwk/solid-pod: a stateless
front door over a per-account Durable Object that is the single authority
for the repository signing key, the MST, and the signed commit chain.
Unlike the rest of the cohort it shares neither @dwk/store
(the repository is an MST/blockstore, not key → { rdf | blob }) nor
@dwk/rdf (records are lexicon-typed DAG-CBOR, not RDF), so
its storage core — DAG-CBOR, CIDv1, the MST, CAR, and commit signing — is
self-contained and built directly on WebCrypto.
What it covers
- Identity at your own domain —
/.well-known/atproto-did(handle → DID) and/.well-known/did.json(adid:webdocument advertising the repository signing key as aMultikeyand the PDS as anAtprotoPersonalDataServerservice). - XRPC surface (
/xrpc/<nsid>):com.atproto.server.*—createSession,getSession,refreshSession,describeServer.com.atproto.repo.*—createRecord,putRecord,deleteRecord,getRecord,listRecords,describeRepo,uploadBlob.com.atproto.sync.*—getRepo(CAR export),getLatestCommit,getBlob,listRepos.com.atproto.identity.resolveHandle.
- A signed, portable repository — records are DAG-CBOR blocks in a
deterministic Merkle Search Tree; each write produces a new commit signed with
the account's repository key (compact, low-S) and chained through
prev.getRepoexports the whole thing as a CARv1 whose root commit verifies against the key in the DID document. - Blobs stream to R2, addressed by their raw-codec CID.
Design decisions
- P-256 signing by default, secp256k1 (K-256) opt-in. Both are valid per AT
Protocol's cryptography spec. WebCrypto supports P-256 natively, so it is the
dependency-free default (published as a
did:key, multicodec 0x1200). SetsigningCurve: "secp256k1"for the network-preferred curve real Bluesky accounts use — signed via@noble/curves(deterministic, low-S) and published with multicodec 0xe7. The curve is fixed at repository genesis. did:webby default,did:plcopt-in (in progress).did:webkeeps identity on the user's own origin with no external PLC directory. SetdidMethod: "plc"for adid:plcaccount: the DO mints a DO-custodied rotation key, self-signs a genesis operation, derives itsdid:plc, and serves it consistently. The PLC directory client (plc-directory.ts) submits the genesis op (POST /:did) and resolves DIDs (GET /:did); setplcDirectoryUrlto register a fresh account on the network at creation — done via a Durable Object alarm with exponential-backoff retry, never blocking init (default unset — nothing reaches the network unless asked).- Single-account scope. One account per
baseUrl, authenticated by a configured password. The firehose is future work.
Usage
import { createAtprotoPds, AtprotoRepoObject } from "@dwk/atproto-pds";
const pds = createAtprotoPds({
baseUrl: "https://alice.example.com",
password: env.ACCOUNT_PASSWORD, // secret binding
jwtSecret: env.SESSION_SECRET, // secret binding
});
export default {
fetch: (request, env, ctx) => pds(request, env, ctx),
};
export { AtprotoRepoObject };The composed Worker must bind the REPO Durable Object namespace
(AtprotoRepoObject) and the BLOBS R2 bucket; a missing binding fails loudly
at startup.
License
ISC
