···
3
+
title: "did:plc Directory Server API"
4
-
title: DID PLC Directory API
6
+
name: "Protocol Team at Bluesky"
7
+
email: "protocol@blueskyweb.xyz"
8
+
url: "https://web.plc.directory"
10
+
DID PLC is a self-authenticating [DID](https://www.w3.org/TR/did-core/) which is strongly-consistent, recoverable, and allows for key rotation.
12
+
The central directory server receives and persists self-signed operation logs for each DID, starting with a "genesis operation" which defined the DID identifier itself. This document describes the HTTP API for interacting with the directory server to resolve DID Document, fetch audit logs, and submit signed operations.
14
+
The HTTP API is permissionless, but only valid (correctly signed) operations are accepted. Reasonable rate-limits are applied, but they should not interfer with account recovery in most situations.
- url: https://plc.directory
12
-
description: "Fetch DID Document"
21
+
description: "Resolve DID Document for the indicated DID"
22
+
operationId: ResolveDid
···
21
-
description: "Successfully returned DID Document"
31
+
description: "Success, returned DID Document"
24
-
$ref: '#/components/schemas/DidDocument'
35
+
$ref: '#/components/schemas/DidDocument'
26
-
$ref: '#/components/responses/404NotFound'
37
+
$ref: '#/components/responses/404DidNotFound'
39
+
$ref: '#/components/responses/410DidNotAvailable'
44
+
curl -s https://plc.directory/did:plc:pyc2ihzpelxtg4cdkfzbhcv4 | jq .
50
+
did = "did:plc:pyc2ihzpelxtg4cdkfzbhcv4"
51
+
resp = requests.get(f"https://plc.directory/{did}")
52
+
resp.raise_for_status()
55
+
description: "Create new PLC Operation for the indicated DID"
56
+
operationId: CreatePlcOp
68
+
$ref: '#/components/schemas/Operation'
71
+
description: "Success, operation validated and persisted"
72
+
# TODO: what is returned here?
74
+
$ref: '#/components/responses/400BadOperation'
76
+
$ref: '#/components/responses/404DidNotFound'
78
+
$ref: '#/components/responses/410DidNotAvailable'
81
+
description: "Get Current PLC Operation Chain"
82
+
operationId: GetPlcOpLog
91
+
description: "Success, retured operation log"
97
+
$ref: '#/components/schemas/Operation'
99
+
$ref: '#/components/responses/404DidNotFound'
102
+
description: "Get PLC Operation Audit Log"
103
+
operationId: GetPlcAuditLog
112
+
description: "Success, retured audit log"
118
+
$ref: '#/components/schemas/LogEntry'
120
+
$ref: '#/components/responses/404DidNotFound'
123
+
description: "Get Latest PLC Operation"
124
+
operationId: GetLastOp
133
+
description: "Success, returned latest operation"
137
+
$ref: '#/components/schemas/LogEntry'
139
+
$ref: '#/components/responses/404DidNotFound'
142
+
description: "Get Current PLC Data for the indicated DID"
143
+
operationId: GetPlcData
152
+
description: "Success, retured current PLC data"
153
+
# TODO: basically just an op, but missing some fields? sigh.
155
+
$ref: '#/components/responses/404DidNotFound'
157
+
$ref: '#/components/responses/410DidNotAvailable'
160
+
description: "Bulk fetch PLC Operations for all DIDs, with pagination, in JSON Lines format"
161
+
operationId: Export
174
+
description: "Return only operations after this indexed timestamp"
177
+
description: "Success, returned batch of operations"
179
+
application/jsonlines:
180
+
description: "Newline-delimited JSON file, with a separate JSON object on each line"
182
+
$ref: '#/components/schemas/LogEntry'
184
+
$ref: '#/components/responses/400BadRequest'
42
-
description: "DID not found"
189
+
description: "DID Not Found"
198
+
message: "DID not registered: did:plc:ewvi7nxzyoun6zhxrhs64oiz"
199
+
404DidNotAvailable:
200
+
description: "DID Not Available (Tombstone)"
···
209
+
message: "DID not available: did:plc:ewvi7nxzyoun6zhxrhs64oiz"
211
+
description: "Invalid PLC Operation"
220
+
message: "Invalid Signature"
222
+
description: "Bad Request"
231
+
message: "Invalid Query Parameter"
···
create: '#/components/schemas/LegacyCreateOp'
246
+
description: "Regular PLC operation. Can be a genesis operation (create DID, no 'prev' field), or a data update."
···
262
+
description: "Ordered set (no duplicates) of cryptographic public keys in did:key format"
265
+
description: "Map (object) of application-specific cryptographic public keys in did:key format"
270
+
description: "Ordered set (no duplicates) of aliases and names for this account, in the form of URIs"
273
+
description: "Map (object) of application-specific service endpoints for this account"
277
+
description: "Strong reference (hash) of preceeding operation for this DID, in string CID format. Null for genesis operation"
280
+
description: "Cryptographic signature of this object, with base64 string encoding"
282
+
type: "plc_operation"
285
+
type: "AtprotoPersonalDataServer"
286
+
endpoint: "https://bsky.social"
288
+
- "at://atproto.com"
290
+
- "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg"
291
+
- "did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
292
+
verificationMethods:
293
+
atproto: "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
297
+
description: "Special operation which deactives (revokes) the DID. This is permanent once the recovery window expires."
···
307
+
description: "Strong reference (hash) of preceeding operation for this DID, in string CID format"
310
+
description: "Cryptographic signature of this object, with base64 string encoding"
312
+
type: "plc_tombstone"
313
+
prev: "bafyreid6awsb6lzc54zxaq2roijyvpbjp5d6mii2xyztn55yli7htyjgqy"
314
+
sig: "41iJmrPRUTIi24HBduzgoavjOibAx2yFJ2p1d7zTN6ZmMgjSaTF8dJf0HtdU4EBNUBTWq33PZyh5tyb1bJq3Fw"
318
+
description: "Obsolete PLC genesis operations, which must still be supported to ensure all did:plc identifiers can be resolved correctly."
···
332
+
description: "atproto cryptographic public key in did:key format"
335
+
description: "PLC recovery cryptographic public key in did:key format"
338
+
description: "atproto handle as AT-URI (at://)"
341
+
description: "atproto_pds service endpoint URL"
345
+
description: "Strong reference (hash) of preceeding operation for this DID, in string CID format"
348
+
description: "Cryptographic signature of this object, with base64 string encoding"
351
+
signingKey: "did:key:zQ3shP5TBe1sQfSttXty15FAEHV1DZgcxRZNxvEWnPfLFwLxJ"
352
+
recoveryKey: "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg"
353
+
handle: "first-post.bsky.social"
354
+
service: "https://bsky.social"
356
+
sig: "yvN4nQYWTZTDl9nKSSyC5EC3nsF5g4S56OmRg9G6_-pM6FCItV2U2u14riiMGyHiCD86l6O-1xC5MPwf8vVsRw"
129
-
400: Not a valid operation
130
-
404: DID not registered
369
+
description: "DID that this operation applies to"
371
+
$ref: "#/components/schemas/Operation"
374
+
description: "Hash of the operation, in string CID format"
377
+
description: "Whether this operation is included in the current operation chain, or has been overridden"
381
+
description: "Timestamp when this operation was received by the directory server"
383
+
did: "did:plc:ewvi7nxzyoun6zhxrhs64oiz"
385
+
sig: "lza4at_jCtGo_TYgL5PC1ZNP7lhF4DV8H50LWHhvdHcB143x1wEwqZ43xvV36Pws6OOnJLJrkibEUFDFqkhIhg"
387
+
type: "plc_operation"
390
+
type: "AtprotoPersonalDataServer"
391
+
endpoint: "https://bsky.social"
393
+
- "at://atprotocol.bsky.social"
395
+
- "did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg"
396
+
- "did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
397
+
verificationMethods:
398
+
atproto: "did:key:zQ3shXjHeiBuRCKmM36cuYnm7YEMzhGnCmCyW92sRJ9pribSF"
399
+
cid: "bafyreibfvkh3n6odvdpwj54j4xxdsgnn4zo5utbyf7z7nfbyikhtygzjcq"
401
+
createdAt: "2023-04-26T06:19:25.508Z"
410
+
example: "did:plc:ewvi7nxzyoun6zhxrhs64oiz"
413
+
description: "Ordered set (no duplicates) of aliases and names for this account, in the form of URIs"
416
+
example: "at://atproto.com"
417
+
verificationMethods:
425
+
- publicKeyMultibase
433
+
publicKeyMultibase:
437
+
type: "EcdsaSecp256k1VerificationKey2019"
438
+
controller: "did:plc:ewvi7nxzyoun6zhxrhs64oiz"
439
+
publicKeyMultibase: "zQYEBzXeuTM9UR3rfvNag6L3RNAs5pQZyYPsomTsgQhsxLdEgCrPTLgFna8yqCnxPpNT7DBk6Ym3dgPKNu86vt9GR"
457
+
type: "AtprotoPersonalDataServer"
458
+
serviceEndpoint: "https://bsky.social"