I've started building an ATproto app. And since I'm a sicko who loves reading documentation, I'm simultaneously diving into docs to understand how the ecosystem works; currently, I'm on The AT Stack.
Overall, I think I have a pretty good understanding:
PDSes are data storage
Relays are event queues
Taps are essentially event listeners with backfilling
App Views handle materialized views (or, precomputed results of feeds)
Shoutout to Martin Kleppmann's Designing Data-Intensive Applications for explaining what this means
Cool, cool, cool. All good.
Except, this one brief and simple phrase borked my understanding:
did:plc is a self-authenticating Decentralized Identifier
Okay... I know what the word self means, right? When an action is preceded by the word "self," it means no other entity was relied upon to execute that action. Self-governing, self-replicating, self-contained, self-funded - it's just me, myself, and I.
But according to the docs:
PLC operations are stored in a directory (currently run at plc.directory) which is a centralized service that validates operations, stores them, and serves those operations...
So:
- 1.
What does
selfmean in this instance? - 2.
And, what exactly is being authenticated?
After some more digging and a lengthy conversation with Gemini, I think I have an understanding.
Here's my breakdown of how ATproto DIDs work, and what we mean when we say they are self-authenticating:
First: What are DIDs?
DIDs are IDs. But they're also hashes of the initial operation that resulted in the DID itself!
Let's look at my own for specifics:
did:plc:o6hf54rkiurn3wmai7dr33dcThe last clause of my DID, o6hf54rkiurn3wmai7dr33dc, is actually a hash of the original operation that created this very same DID.
How it works:
The operation to create my DID is:
{
"prev": null,
"type": "plc\_operation",
"services": {
"atproto_pds": {
"type": "AtprotoPersonalDataServer",
"endpoint": "https://polypore.us-west.host.bsky.network"
}
},
"alsoKnownAs": [
"at://kat-kime.bsky.social"
],
"rotationKeys": [
"did:key:zQ3shhCGUqDKjStzuDxPkTxN6ujddP4RkEKJJouJGRRkaLGbg",
"did:key:zQ3shpKnbdPx3g3CmPf5cRVTPe1HtSwVn5ish3wSnDPQCbLJK"
]
"verificationMethods": {
"atproto": "did:key:zQ3shhX2aH6szJj9NrCLQL3hBAYqZsnSMhs2swRYDnAtN1Th2"
}This object was serialized into bytes using DAG-CBOR
DAG-CBOR enforces a specific ordering of keys to ensure consistent output in all serializations
The Bluesky client used a private key to sign the bytes, resulting in the key/value pair:
sig: "8KE6tXu-S4zyN3uu1SBjJqIWQcZPWkWnR4T5ZLnOa28EvqjrMlxHG0hUND1BxOOVeeFJcsU_7ucm1tc7-QLkOQ"This was added to the operation, thus signing it
The signed operation was, again, serialized into DAG-CBOR bytes
A SHA-256 hash was run on the bytes and the output was encoded into base32, resulting in the string:
o6hf54rkiurn3wmai7dr33dcuqghspg36o5npejf4osl6ji4hrbqThe first 24 characters were extracted and appended to the did:plc prefix, resulting in my final DID:
did:plc:o6hf54rkiurn3wmai7dr33dcFinally, this DID was pushed to the PLC directory to store an append-only log of the creation operation, as well as all future operations performed on the DID.
So, my DID is an ID.
But it's also a hash of the original operation that created the DID! And, if you visit plc.directory/did:plc:o6hf54rkiurn3wmai7dr33dc/log/audit it's a pointer to the log that stores a history of all operations performed on the DID.
(Here's a quick Python program you can run to verify this yourself)
Second: Why do we call DIDs self-authenticating?
Okay, so here's the thing -
It's not the actual DID that does the authenticating. As we saw previously, my DID is only a truncation of the final base32 encoding of the original hashed operation. And the encoding itself only represents the initial genesis operation.
So when we say a DID is self-authenticating, what we're actually saying is that this identifier points to an append-only ledger, similar to how a git hash points to a code commit. And it's this ledger that contains all the content needed to verify its own validity.
How the ledger works:
Each log in the ledger has a prev pointer, pointing to the previous state of the ledger (except the initial operation, which has a prev pointer of null)
This state is represented as a content identifier, or CID, which is created by:
- 1.
Serializing the operation into DAG-CBOR bytes
- 2.
Running a SHA-256 hash
- 3.
Wrapping the hash into a container that includes:
- 1.
The CID version
- 2.
The DAG-CBOR codec identifier
- 3.
The hash
- 4.
Encoding the CID wrapper into base32
Because each entry in the ledger points to a hash of the previous state of the ledger, you can iterate through each entry and verify its contents all the way back to the original genesis operation. If a single byte of the content has changed, the hashes will not match.
So, this is what we mean when we say a DID is self-authenticating:
The DID points to an append-only ledger, where each entry contains the previous state of the ledger. You don't need any outside authority to verify the contents of the ledger; just run a program and do it yourself.