After I wrote my first blog post here, I decided to see if I could set up standard.site integration for it. However, I didn’t want to use a pre-existing platform, and I thought that maybe because I already had a static site and a Bluesky account, I could hack something together myself? Turns out I could! Emphasis on the hack, though – because I don’t like to use a lot of external services I don’t understand for this blog (all of the HTML and even the Atom feed is hand-edited, which is an awful idea), none of this is automated. It’s a lot of terminal commands, copy-and-pasting, and questionable security decisions. As such, I couldn’t find any tutorials for how to do this, so I’m writing down my process here so I don’t forget it.
First of all, you’ll need to log in. For this, you’ll need to create an App Password. You might want to write this into a file, because you won’t be able to look at it again, and it’d be better if it weren’t in your shell history. Then, make a POST request to the com.atproto.server.createSession endpoint with a JSON body. With HTTPie, that looks like:
http post \
https://bsky.social/xrpc/com.atproto.server.createSession \
identifier=<your account handle> \
password=<app password>
The JSON body you’re sending looks like this:
{
"identifier": "<your account handle>",
"password": "<app password>"
}
You should get back a chunk of JSON data. You’re looking for the accessJwt
field. This is your auth token; you probably want to copy that down as well.
Now, it’s time to upload your publication record. This represents your blog and makes it discoverable. If you look at the schema, you can see all the fields you need to fill in. It’s just a blob of JSON. (Watch your trailing commas.) Here’s the publication for this blog:
{
"$type": "site.standard.publication",
"url": "https://dignifiedalpaca.codeberg.page",
"name": "dignifiedalpaca’s blog",
"description": "Overwrought and overthought.",
"basicTheme": {
"$type": "site.standard.theme.basic",
"background": {
"$type": "site.standard.theme.color#rgb",
"r": 255,
"g": 251,
"b": 246
},
"foreground": {
"$type": "site.standard.theme.color#rgb",
"r": 26,
"g": 26,
"b": 26
},
"accent": {
"$type": "site.standard.theme.color#rgb",
"r": 16,
"g": 105,
"b": 153
},
"accentForeground": {
"$type": "site.standard.theme.color#rgb",
"r": 255,
"g": 251,
"b": 246
}
},
"preferences": {
"showInDiscover": true
}
}
You’ll want to set showInDiscover if, well, you want people to discover your site by having it shown to them. Now it’s time to upload the record. With HTTPie, that should look something like this:
http post "https://bsky.social/xrpc/com.atproto.repo.createRecord" \
Authorization:"Bearer <contents of accessJwt>" \
repo=<your account handle> \
collection=site.standard.publication \
record:=@<path to your JSON record>
Again, you’ll get back some JSON data. This time, you want the uri field.
In order to prove you control the website your publication record links to, you need to link back to the publication from your website. In order to do this, you can just create a file on your website at .well-known/site.standard.publication. The contents of that file should just be the contents of the uri field from the response, without quotes. Next, you can also link to your publication on your website’s pages, by adding a <link> tag to the <head> of your HTML files. The rel of that <link> should be site.standard.publication, and its href should be the uri field. You can look at the source of this page to see how I did it. By this point, you’ve set everything up; you should only have to do this once. Next, you’ll be publishing your first post (or rather, document).
To upload a post, you need another record. This time, it’s a document record. Again, you can see the fields in the schema. Remember your $type fields! The bskyPostRef field is a particularly interesting one: it lets you reference a Bluesky post about your blog post. This is a strongRef, meaning it contains a URI linking to the post as well as a hash of its contents. In order to get one of these, you just need to make a GET request (I used curl) to a URL like this: https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=<your account handle>&collection=app.bsky.feed.post&rkey=<the last part of the post URL>. Once you’ve written out the document record, upload it like this:
http post "https://bsky.social/xrpc/com.atproto.repo.createRecord" \
Authorization:"Bearer <contents of accessJwt>" \
repo=<your account handle> \
collection=site.standard.document \
record:=@<path to your JSON record>
That’ll give you another JSON blob with a uri field. You can also verify this document with a <link> tag in the referenced blog post’s <head>. This time, the rel should be site.standard.document, and the href should be the contents of the uri field returned when you published the document.
And that’s that! Your site and posts should now be indexed and discoverable.