{
  "path": "/3m3zys47zgctb",
  "site": "at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.publication/3m3x4bgbsh22k",
  "tags": [
    "website"
  ],
  "$type": "site.standard.document",
  "title": "Link Rot and Eight Iterations of ewancroft.uk",
  "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": "I’ve somehow managed to create my own little link rot ecosystem. Eight versions of ewancroft.uk, each leaving a trail of dead URLs like breadcrumbs that have gone stale."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "The Hugo Years and the Great Migration"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The biggest changes came during my Hugo phase – versions 2 through 4, roughly from late January to early December 2024. I loved Hugo back then: clean, fast, minimal fuss. But moving to my current custom data-display site shifted the entire file structure under my feet."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "It wasn’t a massive content library – maybe 20 links – but every single one instantly went obsolete. The sitemap got a full makeover, old permalinks disappeared, and anyone who had bookmarked posts ended up staring at 404s."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Digital Entropy (and why stats are dodgy)"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Link rot isn’t new, and a few stats get quoted a lot. The issue? Not all of them are reliable."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Early Studies"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 33,
                    "byteStart": 24
                  },
                  "features": [
                    {
                      "uri": "https://en.wikipedia.org/wiki/Link_rot",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "One oft-cited line from Wikipedia says a 2003 study found about one link in every 200 breaks each week, suggesting a link “half-life” of 138 weeks. Makes sense mathematically, but the original study is hard to track down – it mostly circulates as a secondary citation. More folklore than fact."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Recent Data"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 61,
                    "byteStart": 44
                  },
                  "features": [
                    {
                      "uri": "https://ahrefs.com/blog/link-rot-study/",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 136,
                    "byteStart": 124
                  },
                  "features": [
                    {
                      "uri": "https://www.pewresearch.org/wp-content/uploads/sites/20/2024/05/pl_2024.05.17_link-rot_report.pdf",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Recent research paints a starker picture. A 2024 Ahrefs study sampled links created since 2013 and found around 66.5% dead. Pew Research found about 25% of pages from 2013–2023 were inaccessible by 2024. Credible studies, but still just a slice of reality. Online, entropy reigns: links decay unless we intervene."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "The Personal Cost"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The tricky part isn’t technical – redirects and compatibility layers exist. It’s the realisation that I’d broken the implicit promise of permalinks. People expect URLs to be permanent. Sharing a link shouldn’t mean it disappears in months."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I could have set up 301 redirects, or kept a compatibility layer. Instead, I did what most devs do: got excited about something new and ignored the old stuff."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Stability, Version 2.0"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 63,
                    "byteStart": 32
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/record-key",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "Now, blog posts are anchored to AT Protocol record keys (rkeys) – stable identifiers in a repository collection. That’s why URLs look like this:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "plaintext": "ewancroft.uk/blog/3lylpbqlw4c2h"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "No titles, no slugs, no fragile folder structures. Ugly? Maybe. Stable? Definitely. Even if I rebuild the frontend or redo the architecture, the rkey sticks to the post. Identity is decoupled from presentation."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "It’s not immortal – if my DID disappears or the AT Protocol collapses, the links vanish. But compared to the handmade, slug-based URLs I was constantly breaking, it’s a major improvement. The weak point has moved closer to the protocol layer, making links more resilient to my own digital restlessness."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Slugs as a Surface Layer"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 30,
                    "byteStart": 23
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 64,
                    "byteStart": 50
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "What if slugs didn’t replace the rkey, but just resolved to it? Keep the rkey as the canonical identifier, but let a human-friendly alias point to it."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Inspiration from AT Protocol Handles"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 24,
                    "byteStart": 5
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/handle",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 64,
                    "byteStart": 52
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 120,
                    "byteStart": 88
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 210,
                    "byteStart": 187
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                }
              ],
              "plaintext": "Like AT Protocol handles: your domain handle (e.g., ewancroft.uk) resolves to your DID (did:plc:ofrbh253gwicbkc5nktqepol for me), which is the canonical identity. Verified via DNS TXT or .well-known/atproto-did. Once resolved, the DID is the truth."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Slug → rkey Example"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "plaintext": "ewancroft.uk/blog/wolf-king-vs-wereworld\n    ↳ resolves to ewancroft.uk/blog/3lylpbqlw4c2h"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 22,
                    "byteStart": 9
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 40,
                    "byteStart": 30
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/record-key",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 65,
                    "byteStart": 44
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#code"
                    }
                  ]
                },
                {
                  "index": {
                    "byteEnd": 155,
                    "byteStart": 70
                  },
                  "features": [
                    {
                      "$type": "pub.leaflet.richtext.facet#italic"
                    }
                  ]
                }
              ],
              "plaintext": "The post 3lylpbqlw4c2h is the record key in com.whtwnd.blog.entry for “Wolf King vs. Wereworld: My Completely Unqualified Take on Netflix's Adaptation”. Slugs are cosmetic – readable and SEO-friendly – rkeys are the truth."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 3,
              "plaintext": "Resolver in Practice"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Each blog entry has its title, so a resolver can slugify it, compare it to the incoming slug, and redirect to the rkey if needed:"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.code",
              "language": "ts",
              "plaintext": "function slugify(title) {\n  return title.toLowerCase().replace(/[^a-z0-9]+/g, \"-\");\n}\n\nfunction resolveRkeyFromSlug(slug, posts) {\n  for (let post of posts) {\n    if (slugify(post.title) === slug) return post.rkey;\n  }\n  return null;\n}"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Rkeys remain canonical. Slugs can change, titles can change, but the rkey doesn’t move."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "How AT Protocol Works"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.unorderedList",
              "children": [
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 17,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 65,
                          "byteStart": 42
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Handles → DIDs: Verified via DNS TXT or .well-known/atproto-did. DID = canonical identity."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 22,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "DIDs → Repositories: Each DID owns a repository (posts, profiles, lists)."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 12,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 71,
                          "byteStart": 50
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Collections: Records live inside collections like com.whtwnd.blog.entry."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 12,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Record Keys: Each record gets a stable rkey."
                  }
                },
                {
                  "content": {
                    "$type": "pub.leaflet.blocks.text",
                    "facets": [
                      {
                        "index": {
                          "byteEnd": 11,
                          "byteStart": 0
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#bold"
                          }
                        ]
                      },
                      {
                        "index": {
                          "byteEnd": 43,
                          "byteStart": 12
                        },
                        "features": [
                          {
                            "$type": "pub.leaflet.richtext.facet#code"
                          }
                        ]
                      }
                    ],
                    "plaintext": "Permalinks: ewancroft.uk/blog/3lylpbqlw4c2h maps to DID → repo → collection → rkey."
                  }
                }
              ]
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Slugs sit on top as sugar; rkeys are the bedrock."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "The Irony of Citation"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Writing about link rot while citing external sources is a bit ironic. The Wikipedia article might vanish tomorrow. Ahrefs’ study? One redesign away from a dead link. Pew Research? Subject to institutional web policies."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Even rkey-based posts aren’t immune. They last as long as the protocol ecosystem does. Digital permanence is always a bet against entropy."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Lessons Learned"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "I’ve become more deliberate. The new setup is simple, shallow, and identifiers don’t rely on mutable text. I’m thinking more about 301 redirects, archiving, and recording migrations."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "The rkey system has given me a frame of reference. I can’t freeze the web, but I can design my corner so changes cause less damage. Not immortality, but resilience."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.header",
              "level": 2,
              "plaintext": "Entropy vs Immortality"
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "facets": [
                {
                  "index": {
                    "byteEnd": 145,
                    "byteStart": 128
                  },
                  "features": [
                    {
                      "uri": "https://atproto.com/specs/record-key",
                      "$type": "pub.leaflet.richtext.facet#link"
                    }
                  ]
                }
              ],
              "plaintext": "The web doesn’t last forever. Domains expire, protocols age, even institutions let archives rot. Entropy wins eventually. But AT Protocol rkeys slow decay, strengthen content, and push back against fragility."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Immortality online is a myth. Durability? That’s achievable if we design for it."
            }
          },
          {
            "$type": "pub.leaflet.pages.linearDocument#block",
            "block": {
              "$type": "pub.leaflet.blocks.text",
              "plaintext": "Eight versions down – who knows how many more to go. At least the next iteration of ewancroft.uk will stand on stronger foundations, with a few more stubborn URLs that refuse to die."
            }
          }
        ]
      }
    ]
  },
  "publishedAt": "2025-09-14T15:39:48.761Z"
}