From atproto-skills
This skill should be used when the user asks to "resolve a Bluesky handle", "look up a DID", "find a PDS endpoint", "discover an auth server", "work with ATProto identity", "query XRPC endpoints", "what is did:plc vs did:web", "get a user's profile", "build on AT Protocol", "create a Bluesky bot", "how does federation work", or mentions AT Protocol architecture, DID documents, handle resolution, PDS discovery, plc.directory, ATProto scopes, XRPC NSIDs, Bluesky API structure, data repositories, feed generators, labelers, or app views. Covers identity, discovery, data model, federation architecture, and API fundamentals — not OAuth flows (see atproto-oauth for authentication).
How this skill is triggered — by the user, by Claude, or both
Slash command
/atproto-skills:atproto-domainThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The AT Protocol (ATProto) is a decentralized social networking protocol. Bluesky (`bsky.social`) is the largest network built on it. This skill covers the identity layer, discovery mechanisms, data model, and protocol fundamentals that underpin all ATProto integrations.
The AT Protocol (ATProto) is a decentralized social networking protocol. Bluesky (bsky.social) is the largest network built on it. This skill covers the identity layer, discovery mechanisms, data model, and protocol fundamentals that underpin all ATProto integrations.
ATProto separates speech (permissive, distributed data layer) from reach (flexible aggregation for content discovery). The network has five service types:
| Service | Role | Example |
|---|---|---|
| PDS (Personal Data Server) | Hosts user repos, manages auth, stores private data | bsky.network hosts, or self-host via pds repo |
| Relay (Big Graph Service) | Crawls all PDSes, emits a unified firehose stream | bsky.network relay |
| App View | Consumes firehose, builds domain-specific indices and APIs | public.api.bsky.app for app.bsky lexicon |
| Feed Generator | Produces custom feed algorithms consumed by App Views | Third-party or self-hosted |
| Labeler | Applies moderation labels to content | Bluesky moderation service or custom |
Data flows: PDS → Relay (firehose) → App View → Client. The PDS is the user's agent in the network — it hosts their data and handles their requests. Anyone can run any of these services.
For detailed architecture information, see references/federation-architecture.md.
User data lives in signed repositories on the user's PDS.
app.bsky.feed.post)rkey (record key)at://{did}/{collection}/{rkey} — the canonical address for any recordLexicons are schemas (similar to JSON Schema) that define the XRPC methods and record types. They use reverse-DNS NSIDs:
com.atproto.* — core protocol operations (identity, repo, sync, server)app.bsky.* — Bluesky social app (feed, actor, graph, notification)New applications define their own lexicons and deploy corresponding App Views. The schema system enables interoperability — any client that understands a lexicon can work with any server implementing it. For the full type system, naming conventions, evolution rules, and tooling, see the atproto-lexicon skill.
ATProto identity has three layers: handles (human-readable), DIDs (persistent identifiers), and DID documents (service discovery).
Handle (alice.bsky.social)
→ DID (did:plc:abc123)
→ DID Document
→ PDS endpoint (https://morel.us-east.host.bsky.network)
→ Auth server, data, XRPC endpoints
alice.bsky.social, alice.com)@ prefix and lowercase before resolutionTwo DID methods in use:
did:plc — most common, resolved via plc.directory (e.g. https://plc.directory/did:plc:abc123)did:web — resolved via https://{domain}/.well-known/did.jsonDIDs are the stable, permanent identifier. Always store DIDs, not handles, as the canonical user reference.
Contain service endpoints and verification keys. The #atproto_pds service entry points to the user's Personal Data Server (PDS). Extract it with:
did_doc.service.find(s => s.id === "#atproto_pds").serviceEndpoint
For full DID document examples (did:plc and did:web), see references/did-document-examples.md.
Three methods, in priority order:
GET https://public.api.bsky.app/xrpc/com.atproto.identity.resolveHandle?handle={handle} — works everywhere, use as primary_atproto.{handle} via DNS-over-HTTPS — fails from Cloudflare Workers (cloudflare-dns.com unreachable from within Workers)GET https://{handle}/.well-known/atproto-did — fails for DNS-based custom domain handlesFor most implementations, method 1 is sufficient. Use methods 2-3 as fallbacks for self-hosted or offline-first scenarios.
From a DID document, extract the #atproto_pds service endpoint. This is the user's PDS.
Two-step discovery from the PDS:
GET {pds}/.well-known/oauth-protected-resource
→ { "authorization_servers": ["https://bsky.social"] }
GET {auth_server}/.well-known/oauth-authorization-server
→ { authorization_endpoint, token_endpoint, pushed_authorization_request_endpoint, ... }
Always discover dynamically — ATProto is decentralized. Self-hosted PDS instances use different auth servers. Never hardcode bsky.social as the auth server.
ATProto APIs use XRPC (Cross-Reference Procedure Call):
GET /xrpc/{nsid}?params (read operations)POST /xrpc/{nsid} with JSON body (write operations)com.atproto.identity.resolveHandle, app.bsky.feed.getTimelineThese work without authentication via public.api.bsky.app:
com.atproto.identity.resolveHandle — handle → DIDapp.bsky.actor.getProfile — public profile dataapp.bsky.feed.getAuthorFeed — public postsRequire OAuth tokens, sent to the user's PDS:
com.atproto.repo.createRecord — create posts, likes, followscom.atproto.repo.deleteRecord — delete recordsapp.bsky.feed.getTimeline — authenticated timelineapp.bsky.notification.listNotifications — notificationsATProto OAuth scopes control access levels:
atproto — mandatory for all sessionstransition:generic — broad PDS read/write (equivalent to legacy App Passwords; temporary until granular scopes ship)transition:chat.bsky — DM access (requires transition:generic)transition:email — account email accessThe transition: prefix indicates these are temporary scopes that will be replaced by fine-grained permissions.
bsky.social — ATProto is decentralized. Always discover the PDS and auth server dynamically from the user's DID.cloudflare-dns.com is unreachable from within Workers. Use the Bluesky API for handle resolution instead of DNS-over-HTTPS.@ strip — handles entered by users often include @ prefix. Always strip and lowercase.public.api.bsky.app) is for unauthenticated reads. Authenticated requests go to the user's PDS endpoint from their DID document.@atproto/api — npm install @atproto/apiatproto — pip install atprotoatproto.dartSDKs handle session management, token refresh, and request signing automatically.
Two approaches:
com.atproto.server.createSession with identifier + app password. Returns accessJwt (short-lived) and refreshJwt (long-lived). SDKs manage refresh automatically.App passwords are created in Bluesky Settings → App Passwords. They are scoped to the account but cannot access DMs without explicit scope.
Accounts can migrate between PDSes because identity is anchored to DIDs, not servers:
Data repos are signed Merkle trees, so integrity is verifiable independent of the hosting PDS.
references/did-document-examples.md — Full DID document examples for did:plc and did:web, verification method structures, and service endpoint formatsreferences/federation-architecture.md — Detailed federation architecture: PDS, Relay, App View roles, data flow, self-hostingnpx claudepluginhub zentered/atproto-skills --plugin atproto-skillsGuides building on AT Protocol (atproto/Bluesky): authoring Lexicons, app views, firehose consumption, DIDs/handles, repositories, records, XRPC endpoints, OAuth.
This skill should be used when the user is resolving, validating, parsing, or debugging AT Protocol identities in Rust, TypeScript, or Go — handles (domain-name usernames like `alice.bsky.social`) and DIDs (`did:plc:…`, `did:web:…`, `did:webvh:…`). Triggers on phrases like "resolve a handle", "why does my handle return 404", "DNS TXT `_atproto.`", "`/.well-known/atproto-did`", "bidirectional verification", "handle.invalid", "`alsoKnownAs` not matching", "DID document", "atproto_pds service endpoint", "signing key multikey", "my PDS location is wrong", "can't find PDS endpoint", "did:web vs did:plc", "com.atproto.identity.resolveHandle", "rotation key", "handle resolution mismatch". Also triggers on dependency/import names like `atproto-identity` (Rust crate), `@atproto/identity`, `@atproto-labs/handle-resolver`, `@atproto/syntax`, `ensureValidHandle`, `ensureValidDid`, `IdResolver`, `HandleResolver`, `DidResolver`, `github.com/bluesky-social/indigo/atproto/identity`, `indigo/atproto/syntax`, `syntax.ParseHandle`, `syntax.ParseDID`, `identity.DefaultDirectory`, `identity.BaseDirectory`, `LookupHandle`, `LookupDID`, `HickoryDnsResolver`, or lockfile names `Cargo.toml`, `package.json`, `pnpm-lock.yaml`, `go.mod`, `go.sum`. Covers handle syntax rules and reserved TLDs; DNS TXT + HTTPS well-known handle resolution (concurrent, with conflict handling); input normalization (`at://`, `@` prefixes); DID method selection (plc, web, webvh — webvh is validated by all three reference libraries but not resolved: none of them ship a log verifier, and all three reject webvh inputs at fetch time rather than silently falling back); DID document requirements (handle binding in `alsoKnownAs`, `Multikey` with `#atproto` suffix, `AtprotoPersonalDataServer` service); bidirectional verification; `handle.invalid` semantics; caching guidance. Use to implement handle/DID resolvers, client-side auth onboarding, AppView handle lookups, PDS handle-change flows, or diagnostic tooling. Does NOT cover did:plc operation log format, key rotation cryptography, OAuth (see `atproto-oauth`), record CID computation (see `atproto-cid`), repo/MST traversal (see `atproto-repository`), or parsing `at://` URIs *inside records* (see `atproto-lexicon` — this skill covers identity-side `at://` normalization only).
Ingests Bluesky/AT Protocol social graph and content: fetches paginated user posts, maps engagements (likes, reposts, replies, quotes), streams via Firehose.