{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreie4qnkcxtzcr67rj3vgjx3wkai6onrqnl3dpt43ww6wemc6z7c7xm",
    "commit": {
      "cid": "bafyreiduhla2fiqsn3iqhqlqi6gs7ntgvr52zg64h7etjh24c5uzkeo6aa",
      "rev": "3mogc44fbbm22"
    },
    "uri": "at://did:plc:dadhhalkfcq3gucaq25hjqon/app.bsky.feed.post/3mogc44baek25",
    "validationStatus": "valid"
  },
  "content": {
    "$type": "pub.leaflet.content",
    "pages": [
      {
        "$type": "pub.leaflet.pages.linearDocument",
        "blocks": [
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Previously on Journey to a distributed PDS, I discovered that when one node generates the DPoP nonce that a client uses as part of a request and another node then receives that as part of a request to validate, it would fail because node one would have generated it differently than node 2. This was due to Cocoon using an incremental counter on a timer which would get used to generate and then validate a nonce. Each node would have a different counter and so they would both calculate different values."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I had a couple ideas on how to solve this, but wasn't really feeling them and thankfully Devin suggested a really good idea."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.bskyPost",
              "clientHost": "bsky.app",
              "postRef": {
                "cid": "bafyreibsyijztic6t3xeqwhzhovx3k4m366jqymxbi4zcsy3a5ogv245vm",
                "uri": "at://did:plc:l3rouwludahu3ui3bt66mfvj/app.bsky.feed.post/3mmajpjicak25"
              }
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Once I saw this I knew immediately that this would probably be the best solution and I was annoyed that I didn't think of it myself. "
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "So off I went to implement it and it turned out to be pretty damn easy to do and it worked perfectly."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.iframe",
              "height": 360,
              "url": "https://tangled.org/willdot.net/distributed-pds/pulls/4/round/0"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "There's the PR that I used and it's relatively straight forward. "
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Some things to note are:"
            }
          },
          {
            "$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",
                    "plaintext": "A new env which contains the nonce secret (so that all nodes can generate the same nonce). Previously this was just a randomly generated value that was then stored to a local file so that it was consistent across restarts. No harm in making it an env."
                  }
                },
                {
                  "$type": "pub.leaflet.blocks.unorderedList#listItem",
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "plaintext": "A new nonce generator that instead of using an incremental counter, used a rounded time value. Currently it's hardcoded to 15 minute intervals because I need to do some testing on how small I can make that interval. The smaller the interval, the less time that a nonce value is valid for which tightens up the secureness of the value."
                  }
                }
              ]
            }
          },
          {
            "$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 been using it like this for a few weeks and haven't had any issues so far. The past few days I've attempted to test with a small interval and managed to get it down to 1 minute which is what Cocoon was using for it's rotation before, so I expect I'll PR that soon."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I was really impressed how this solution turned out because I was actually dreading getting into the OAuth trenches to figure it out."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Side note:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#link",
                      "uri": "https://tangled.org/willdot.net/distributed-pds"
                    }
                  ],
                  "index": {
                    "byteEnd": 60,
                    "byteStart": 45
                  }
                }
              ],
              "plaintext": "I created a new fork of Cocoon and called it distributed-pds because I wanted to have everything all in one place and not clutter up the fork I had for Cocoon which I like to contribute from time to time. Feel free to give it a star and follow the progress. I'm currently in the middle of adding some documentation on how to run it in distributed mode."
            }
          }
        ],
        "id": "019ed14e-c8d4-7111-a677-d917c9718e21"
      }
    ]
  },
  "description": "",
  "path": "/3mogc3tvd4s25",
  "publishedAt": "2026-06-16T17:10:00.790Z",
  "site": "at://did:plc:dadhhalkfcq3gucaq25hjqon/site.standard.publication/3lzrigh3cl22p",
  "tags": [],
  "title": "Journey to a distributed PDS: TOTP auth"
}