We have a Standard Site!
Continuing in the spirit of over-engineering my blog, we now have Standard Site integration configured. What even is a Standard Site do you ask? I’ll attempt to steer clear of the normal atproto pilled explanation I see people give, and try a plain English one.
The over-simplified answer is that it’s RSS for Bluesky. This answer is wrong, but not wrong enough that it’s not useful. Standard Site is a standardised way of storing your blog posts in the same place as your Bluesky skeets. This enables syndication and discovery.
With a single app you can read posts from multiple different blogs (or publications as Standard Site defines them). An example of this is Standard Reader which, like an RSS reader, allows you to discover, read and subscribe to posts from multiple blogs hosted on different platforms.
Standard Site is a standard that three blogging platforms; Leaflet, Offprint and pckt aka p(a)ck(e)t; came together to define.
The great thing about the Atmosphere (aka the wider ecosystem that Bluesky helped create) is that anyone can adopt the standards. So individuals (like me) can add Standard Site support, and my posts can appear in any app that chooses to show them.
This common approach allows Standard Reader to show other people’s posts without having to scrape or integrate with their platform directly, effectively re-publishing but with attribution. It allows pckt to show a feed of posts from other platforms, though they just link to the posts as opposed to re-publishing the posts themselves.
It’s permissionless syndication. When you implement Standard Site for your blog, you accept you have no control over who may re-publish your posts. The Standard Site standard doesn’t capture the license of a post, and it is a grey area. Probably one that should be addressed. As an author today you should write under the assumption of a CC BY license.
This is just the current state. There is a proposal to add ‘Permissioned Data’ to the standards the Atmosphere works with. This will allow private data with gated access. For Standard Site this means you will be able to put posts behind a gate like a paywall or similar.
The technical bits
Getting past the over-simplified explanation of Standard Site, how have I integrated it? In my previous post I talked about how I created a CLI to deploy the site to AWS. I have extended that CLI to add Standard Site support. It has two workflows, one to set up the integration including authentication and another to add it to the publishing flow.
The integration workflow is a one-off workflow that sets up publishing. There are four steps to it.
-
Create an ES256 key pair. The private key is stored in AWS Secrets Manager, and the public key is published on this site at
/oauth/jwks.json. This key pair is used for OAuth authentication with my Personal Data Server (PDS) host (aka the place my data is stored). Alongside the public key, there is client metadata stored in/oauth/client-metadata.json. This makes my website an OAuth client that can authenticate with any PDS via a modified OAuth protocol that the Atmosphere uses.This is done by running a single
blog pds keygencommand. -
Publish the OAuth configuration. The next step is to publish the client metadata and public key to the website. This is relatively simple, as the required JSON files are put into the
public/folder that Astro uses to publish static data. This data is meant to be public, and won’t change often. Then it’s just about going through the CI process I have set up to push changes live. -
Authenticate with my PDS host. I currently use Eurosky.social to host my PDS, and they only support the Atmosphere OAuth protocol for authentication. I’ve set up my website as an OAuth client, so I just need to run
blog pds login --identifier <handle-or-did>to start the login process. I supply my handle (iamstan.dev) and the CLI makes an authorization request to my PDS host, signed using the private key from step one, and prints a URL that points at that request.I follow that URL, and it redirects me to Eurosky where I authenticate. After authentication I am redirected back to my site via callback URL (
/oauth/callback/) with a code in the querystring. That callback URL is rendered by an Astro component that doesn’t do much other than tell me to copy the callback URL (with code) back into the CLI, which is in a waiting state. The CLI takes that URL, extracts the code in the querystring, then calls the PDS host to exchange the code for tokens and a session, which are then stored in AWS Secrets Manager. -
Initialise the PDS. Now that I have authenticated with the PDS host, I need to add a publication record to my PDS, telling the PDS details about my publication. I do this by running
blog pds init. It creates the publication record in my PDS, and returns an Atmosphere URI that points to the publication.The CLI then adds that URI to two places. The first is the
public/.well-knownfolder, in a file calledsite.standard.publication. When published this will end up inhttps://iamstan.dev/.well-known/site.standard.publication, and can be used for discovery and to link the website to the PDS.The second place is in the
src/data/folder to be used by Astro, in a file calledatproto.json. This is used by Astro when generating the static files, to include a reference back in an HTML<link rel="site.standard.document" href="at://…">tag in the header of any post published to the PDS.Now you just trigger the publish process again to get this all live.
That’s the initialisation done. The CLI does a lot of the heavy lifting, and I just needed to do some copying, pasting, and pushing commits to GitHub for it to be configured.
From here we just need to add the CLI into the publishing pipeline. After every time new content is published the CI workflow triggers the ops CLI, via post-deploy hook, to do a PDS sync with the command blog pds sync. The CLI will fetch the PDS token and use it to add any new post to the PDS, keeping the website in sync with the PDS. It also rotates the token, which keeps the process headless with no need to reauthenticate.
How did I come up with this workflow? I didn’t. Our little buddy Claude Code running Fable did. I asked it to do a research project to come up with a few suggestions on the best way to implement publishing to a PDS, with a host that only supports OAuth. It presented three options. I had a short conversation with Fable to go through the tradeoffs, and chose what is now the implemented route.
It picked up the CLI that had been implemented and one of the recommendations suggested building on top of it. This is the recommendation we went with.
The combination of the permissionless Atmosphere and agentic development means we can just do things these days.