Integrating my website with ATProto

I've been really excited about ATProto lately, and decided to integrate my personal website (the one you're looking at now) with the network in a couple ways.

Publishing standard.site records on my PDS

The first is by publishing standard.site records on my PDS for each of my /notes entries. This is an emerging standard for how long-form blog content can be shared on the ATProto network in an interoperable manner, and I wanted in on the action.

While there are some fantastic platforms supporting the standard already (leaflet, pckt, offprint), I like having total control over my space on the web, and building all the functionality into it myself.

There's also sequoia, a CLI for publishing standard.site records from a static site, which could have worked nicely. But again, I wanted to integrate it into my workflow myself and have full control over the details of that integration.

How it works

In short, I have a deploy command which builds the site, pushes to Cloudflare pages, then syncs the standard.site records into my PDS. One for my publication, and one for each article.

The entrypoint is a script which uses the source files for each of my articles to know what standard.site records to publish.

The publication information is driven by some site-level config. If an article has a standardSiteRkey property in the frontmatter, it's included in what gets published. I have a small script for generating new tids to use as these rkeys.

When the sync runs, it looks to see if an existing record with that rkey exists already or not. If it does, it will update that record, otherwise it creates one new.

I also keep a contentHash property on the site.standard.document records, derived from the content of the source file (not the rendered page). If this hasn't changed, then no update to the record occurs. If it does change, we update the record, including the updatedAt field.

One thing I'm not accounting for there yet is path changes, since those are not part of the metadata, but I do my best not to ever break my links anyways, and would put up some redirects if that did happen.

Dealing with the content field

I made the decision to not include the content or textContent fields in my site.standard.document records. The content field is a union type, meaning it can follow the format of a variety of different lexicons. For example, leaflet implements their own block based content system and corresponding lexicons. But that content can only be rendered properly on leaflet itself (sure, someone could implement a separate renderer, but I digress).

I could have included the content as markdown, but to my knowledge there is no proper shared markdown lexicon, so I would be making something up.

Ultimately the main reason I decided against including these fields was because the site.standard.document records primarily serve, for me, as pointers to my content for discoverability. I don't want my content rendered on another site, and would rather have visitors come to my site to read and interact with it.

To that end, I made sure each of my pages has solid description and tag fields, and called that Good Enough.

Bluesky comments

The other addition I made to my website was embedding bluesky comments on it! I'm using bluesky fairly heavily now, and again, am excited about this new phase of the open social web. So I wanted to enable and encourage some level of engagement with my writing.

I have some javascript running client side now that fetches any post on bluesky that links to the current page and renders them on the page itself. If it finds one by me (@seth.computer), it renders it first, and shows those replies. Every other post shows up below that.

To find the posts, I'm using constellation, an incredibly useful service for querying data from the ATProto firehose. This whole project would not have been so simple if it wasn't for this service.

Admittedly, I had claude write this code. I understood how it needed to work and the tedium of writing it all out just didn't feel valuable. I knew I wanted bluesky post embedding on my website, knew I could pull the data from constellation, and that was enough for me.

Next?!

This is certainly not the end of more fun social web integrations for my website. I've been very actively using Margin for bookmarking and annotating websites. I intend to create a /reading page that shows select bookmarks and my highlights on them. I'm also considering what including Margin annotations on my website directly could look like (without needing the margin extension), but I wouldn't want it to interfere with the extension itself.

Either way, plenty more opportunities to pursue here. ATProto has me more excited about the potential of the internet than I've been in a very long time. Couple that with agentic engineering enabling me to pursue more of these projects with my limited spare time, and I have a recipe for a lot of fun and exploration.