{
  "path": "/3mgimvh7ygc2j",
  "site": "at://did:plc:ulg2bzgrgs7ddjjlmhtegk3v/site.standard.publication/3mgbpqoas222x",
  "tags": [
    "pds",
    "atproto",
    "rivet",
    "actor model",
    "how to"
  ],
  "$type": "site.standard.document",
  "title": "How to Make a PDS - Part 1",
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "id": "019cbbc4-6c21-744d-8310-84c957225a53",
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.bskyPost",
              "postRef": {
                "cid": "bafyreicl7mru6yyp5x35hked6oovu7gcou5vafgmc3glswtghakf45kwfe",
                "uri": "at://did:plc:ulg2bzgrgs7ddjjlmhtegk3v/app.bsky.feed.post/3mgdcsuhvak2q"
              },
              "clientHost": "bsky.app"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.bskyPost",
              "postRef": {
                "cid": "bafyreib7axd47nafm4r77prurugv5ijqm5ehwh3mxoomjwngulr7qu3jwa",
                "uri": "at://did:plc:ulg2bzgrgs7ddjjlmhtegk3v/app.bsky.feed.post/3mgdcsuhz5k2q"
              },
              "clientHost": "bsky.app"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "This won't be exactly what I imagined, but it'll be something."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.blockquote",
              "facets": [
                {
                  "index": {
                    "byteEnd": 5,
                    "byteStart": 0
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#bold"
                    }
                  ]
                }
              ],
              "plaintext": "Note: It's actually quite possible I never finish this. So while it will be someting it may not be anything more than what you find below. We'll see."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 80,
                    "byteStart": 62
                  },
                  "features": [
                    {
                      "uri": "https://github.com/samuelgoto/micropod",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Recently I ran into someone who made a personal, experimental PDS implementation."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.bskyPost",
              "postRef": {
                "cid": "bafyreighwtrvv5tjr6jm7qyi5w2d4d47tvv6wwyf3azaxnabw5autkiwnq",
                "uri": "at://did:plc:bftbdgcbe4zclvpkboe4hleq/app.bsky.feed.post/3mfq6drale22u"
              },
              "clientHost": "bsky.app"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "That seems like a pretty good sign as far as how possible it is to make one."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 58,
                    "byteStart": 46
                  },
                  "features": [
                    {
                      "did": "did:plc:cyqufxsezk33hqulcilckna6",
                      "$type": "pub.leaflet.richtext.facet#didMention"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 93,
                    "byteStart": 88
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "On top of my general interest in the idea, at @roomy.space we recently realized that we might end up essentially making our own PDS with extra features for communities, privacy & realtime messaging. We're finding we agree with more of ATProto's design decisions than we thought we might."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 68,
                    "byteStart": 63
                  },
                  "features": [
                    {
                      "uri": "https://rivet.dev/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Anyway to learn the ropes I'm going to try to make a PDS using Rivet, and awesome, distributed actor framework. In the interest of the blog series, I figured I'd journal about it as I tried it out, too."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Now the goal here isn't to make a PDS from scratch. I don't want to implement an MST or anything else really that I don't have to. So we're going to try and re-use ATProto's reference implementations of everything we can, while maintaining full controll over the networking, storage, and overall design of the PDS ourselves."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I've never done this before, so again this is not so much a tutorial as a journal, but it might be interesting. Let's get started!"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Step 1 - Read the Docs"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 54,
                    "byteStart": 43
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/atp",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 82,
                    "byteStart": 74
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/atp",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "First let's head to the ATProto docs site, atproto.com. In particular the overview of the specs section looks like a good place to start."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 8,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/atp",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Overview: I'm pretty familiar with most of ATProto, and after reading the overview, there wasn't anything new to me, so I moved on."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 10,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/data-model",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 35,
                          "byteStart": 29
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#italic"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Data Model: Some good info I mostly already knew. Might need to come back to some details during implementation, but libraries will be handling most of this I think."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 7,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/lexicon",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Lexicon: Barely skimmed this 'cause I've read it before."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 12,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/cryptography",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Cryptography: Skimmed it. Will use libraries mostly for this."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 8,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/account",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Accounts: Read through this. It had some good info on accounts and their statuses and how the network is expected to respond to active or inactive accounts."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 10,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/repository",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Repository: Pretty meaty section worth reading through. I skimmed some of the MST implementation details, but got a good overview of how the repository is implemented."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 11,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/blob",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Media Blobs: Easy to read-through. Has good considerations about security and blob handling."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 6,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/label",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Labels: These are interesting, but I only skimmed the page for now. I don't think I need to worry about labels just yet."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 4,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/xrpc",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "XRPC: Good to know most of the stuff in here."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 5,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/oauth",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Oauth: The infamous OAuth. I read most of this, but skimmed over some of the details. I'm going to try and get a library to do this for me if at all possible."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 11,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/permission",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Permissions: Skipping for now, I've mostly read it before."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 12,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/event-stream",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Event Stream: Skipped through most of this, but got the general idea."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 4,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/sync",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Sync: Very interesting. I found it cool that you can efficiently sync and verify ATProto repos. That could be really good for live, incremental backups of your PDS."
                  },
                  "children": []
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 66,
                          "byteStart": 63
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/did",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 74,
                          "byteStart": 68
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/handle",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 80,
                          "byteStart": 76
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/nsid",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 86,
                          "byteStart": 82
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/tid",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 98,
                          "byteStart": 88
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/record-key",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 105,
                          "byteStart": 100
                        },
                        "features": [
                          {
                            "uri": "https://atproto.com/specs/at-uri-scheme",
                            "$type": "pub.leaflet.richtext.facet#link"
                          }
                        ]
                      }
                    ],
                    "plaintext": "I'm pretty familiar with these so I just skipped this for now: DID, Handle, NSID, TSID, Record Key, at://."
                  },
                  "children": []
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "And there! I've sort of read the ATProto spec! Honestly it's not that bad. Most of it reads easy enough. I can wait to get into the details on some of it until later. It's kind of a mild, pleasant surprise. I didn't think it would be that bad, but I was suprised how lean the protocol actually is."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Now I've got to figure out what to work on first."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Interlude - More on Rivet"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 46,
                    "byteStart": 41
                  },
                  "features": [
                    {
                      "uri": "https://rivet.dev",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 94,
                    "byteStart": 82
                  },
                  "features": [
                    {
                      "did": "did:plc:cyqufxsezk33hqulcilckna6",
                      "$type": "pub.leaflet.richtext.facet#didMention"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 185,
                    "byteStart": 174
                  },
                  "features": [
                    {
                      "uri": "https://en.wikipedia.org/wiki/Actor_model",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "I just wanted to drop a quick note about Rivet here and why I'm trying it out. In @roomy.space we're realizing that the very event-driven nature of it fits very well into an actor model."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "As we've looked into doing more integrations with other ATProto apps, the ability to make new integrations and server-powered functionality on top of Roomy quickly has shown it's importance."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Rivet seems like a very powerful framework for making applications using an actor model, and it looks like it'll be fun to try out, so I'm playing with it for this week-end project."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 18,
                    "byteStart": 12
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 132,
                    "byteStart": 128
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "Rivet looks really cool to me, but I want to try it out to see if it's as cool as I think it is. I hear a lot of developers who love how easy cloudflare makes things, and yet feel afraid of Cloudflare as being the internet's off-switch if everybody uses it. Rivet is also interesting because it seems like an Open Source alternative to Cloudflare workers."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 45,
                    "byteStart": 38
                  },
                  "features": [
                    {
                      "uri": "https://railway.com/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Random side note, I've also found the Railway cloud since it was mentioned in the Rivet deployment options, and I'm really liking it so far."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "What Next?"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "OK, so I've read the docs, and I need to pick what to work on first."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 118,
                    "byteStart": 113
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "I think OAuth is probably one of the worste things to have to get working. Since it's one of the first steps for using the PDS for anything, I think I'll start on that. Hopefully I can re-use an existing authorization server library or something."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": ""
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "That's for the next post!"
            }
          }
        ]
      }
    ]
  },
  "description": "So you want to make a PDS? Or maybe you want to watch some poor fool try? In this series I might make a PDS, or not.",
  "publishedAt": "2026-03-07T20:07:02.687Z"
}