POSSE stands for Publish (on your) Own Site, Syndicate Elsewhere. I first heard about it from Cory Doctorow.

I’m experimenting with automation to convert posts tagged shortform into Mastodon threads (I’m mathstodon.xyz/@j2kun). I’m using Hugo as a static site generator, with the source a (private) GitHub repository, and Netlify for deployments. After a deployment, Netlify calls a serverless function that hits the GitHub API with a POST request to trigger a GitHub action workflow.

The GitHub action workflow runs a Python script that does the main work, ultimately writing a file published_toots.txt that contains a mapping from shortform post URLs to Mastodon thread URLs. Then the GitHub action commits the changes to the repository, which will include mappings for any newly created Mastodon threads created by the action.

Finally, the Python script itself loads the mapping to determine which new posts (in a specific directory) to process, converts those new posts to a list of strings corresponding to the toots in the desired Mastodon thread, with minor alterations to make them compatible for Mastodon. Because the posts themselves are stored as Markdown files, I have to parse them (using marko), and traverse the parsed syntax tree to extract the content, links, etc. Currently I simply split each paragraph into its own toot. The script uses Mastodon.py to publish the thread to Mastodon, with appropriate reply ordering to make a thread.

The rest of the setup involves making sure all the permissions are set up correctly: GitHub actions has a Mastodon API key, Netlify has a GitHub API key, the GitHub action is allowed to be manually dispatched, and the GitHub action has write permissions to the repository contents for updating the flatfile database. Netlify’s CLI has a dev command for testing the serverless function locally, which was a pleasure to use and helps me internally justify the cost of using the service.

If all went well, you’re reading this on Mastodon, and it’s not reposted every time my blog delpoys. Give me a shout if you’s like a more detailed guide on how I did this. Unfortunately, it’s quite custom, and I don’t know of any off-the-shelf POSSE solutions for my setup, nor would I want to pay for one given how quickly I could get this working.