ANProto over ATProto -- using Bluesky PDSes to store ANProto messages and blobs
1# Handles vs. DIDs
2
3In the AT Protocol, users have two identifiers: a **Handle** and a **DID** (Decentralized Identifier).
4
5## Handle (`alice.bsky.social`)
6- **Human-readable**: Looks like a domain name.
7- **Mutable**: Users can change their handle at any time (e.g., `alice.bsky.social` -> `alice.com`).
8- **Usage**: Used for login input, display names, and mentions.
9- **NOT for Storage**: Never use the handle as the primary key in your database user table.
10
11## DID (`did:plc:z72...`)
12- **Machine-readable**: A unique string starting with `did:`.
13- **Immutable**: This creates a permanent identity for the user, regardless of handle changes.
14- **Usage**: Database primary keys, internal logic, and resolving data from the PDS (Personal Data Server).
15
16## The Resolution Flow
17
181. **User Input**: User types `alice.bsky.social`.
192. **Resolution**: The OAuth client (or a resolver) queries the network to find the DID associated with that handle.
203. **Authentication**: The OAuth flow proceeds using the DID.
214. **Storage**: Your app stores the DID.
225. **Display**: When showing the user, you resolve the DID back to their *current* handle (or cache it and update periodically).
23
24## Code Example
25
26When a user logs in:
27
28```typescript
29// src/index.ts logic
30const handle = req.body.handle; // "alice.bsky.social"
31
32// The client.authorize() method handles the resolution internally!
33const url = await client.authorize(handle, { ... });
34
35// On callback, we get the session which contains the DID
36const { session } = await client.callback(params);
37const userDid = session.did; // "did:plc:123..."
38```