Part 1: Architecting a relay
Sharing content across multiple social media feeds can be a real pain. Social media links are fragile. Networks can ban you, or force you to remove your posts. They often use link shorteners, modifying the original link, which adds another possible point of failure. Social media is built for the moment, and it’s not built to make bridging networks easy. If you want to share in multiple places, or to keep a record of what you’re sharing, including the original, unmodified URL, that can be a challenge.
Posts without posting, a series
- Architecting a relay
- Writing the code
- Configuring Azure Functions with Terraform
- Setting up an iOS Shortcut
After Elon Musk’s takeover of Twitter, I decided to stop using the site day-to-day. Instead, I want to use it, and my relatively large following there, to act as a syndication engine, following the Indieweb principles of POSSE: Publish Own Site, Syndicate Elsewhere. I still want to share what I find interesting, and I want to share what I write and create. But as I migrate to the indieweb.social server on Mastodon, I want my sharing to be automated and mindless. My ideal workflow is to read an interesting article, press a button, type a message, and automatically share the link to the article with my comment everywhere I have an online presence, all without requiring any third-party apps.
For me currently, that means sharing in three places: Twitter, Mastodon, and the front page of this site. Before building this solution, I was using Zapier to serve as a relay for this content. However, Zapier’s free tier does not let you use webhooks, and the service does not have a Mastodon plug-in. This mean that to use REST functionality, including cross-posting to Mastodon, I had to subscribe to their cheapest tier, which at around $20 a month is not cheap. I wanted a cheaper solution, one I could manage entirely myself. Technically, I could have done all of this entirely with an iPhone Shortcut, if I had the Twitter app installed. Since I haven’t had the app on my phone in almost a year, this wouldn’t do. As I’ll explain in this post, I’ll still use an iPhone Shortcut, but I’m pushing most of the business logic to a serverless Azure Function App. This is a solution that is practically free, if you know how to do it. Lucky you, over the next few posts, I’m going to show you how.
In this post, I’m going to describe the architecture of how I set this up. I do not know how to build something similar with Android; I do not have access to an Android phone at the moment. I’m almost positive, however, that it can be done with similar ease.
To get started, here’s the tech I’m going to use.
- Microsoft Azure — For low-cost hosting, and because I use it to host the rest of my site
- Terraform — For setting up the Azure Functions App using Infrastructure-as-Code
- F# — Because I love the language and I want to practice it more
- VSCode — A fantastic development environment
- iOS Shortcuts — A built-in app on my iPhone to let me build the workflow for free
- Google Sheets — A simple, free, high-performance data store for efficient reads, infrequent writes, with an unbeatable SLA for the cost
- Github Actions — A simple, low-cost CI/CD environment
To get started, you’ll need:
- A Github account (free)
- An Azure account (costs vary and you need a credit card to set this up, but I pay ~$9 a month for almost all my hosting needs. The feature described in this post costs me $0.01 monthly)
- A Gmail account (free)
- A Google cloud account attached to that Gmail account (free, you might need a credit card here as well, but what I am discussing costs exactly $0 monthly)
The link sharing workflow begins when I read an interesting article on my phone. Using the default iOS share tray, I add a sharing option that collects the page URL, the page title, and opens a dialog for me to write a comment about the post. I’ll call this shortcut
Post Link. After writing a comment, I click Submit, and the shortcut packages everything into a small JSON payload and calls a serverless Azure Function App REST API authenticated with an authentication token, which gets passed along in a header.
The Function App, which I call
LinkSharer, takes the JSON payload, crafts a simple post, and performs a sequence of asynchronous API calls to put the link and the comment to various social media outlets. For my website, I also write another Function App called
LinkFetcher which collects the last
n links and displays them on my website.
Altogether, the workflow looks something like this.
The first question you might ask is, “Google Sheets? Really?” My answer is yes, really. For mostly-read, seldom-write, rarely-update workflows, Google Sheets is probably the lowest friction, lowest cost option you have for a durable backing store out there. Simply put, you’re not going to beat Google’s SLA with a free offering. You don’t have to worry about backups, it’s dirt-simple to set up, hand-editable, and it is highly-performant. Heck, it even comes with an out-of-the-box JSON API if you don’t want to write your own reader facade. Google Sheets has limitations, big ones. But for quick-and-dirty JAM stack development, there are worse options out there. Eventually, I will replace this with a proper database. But I’m not ready to spend the money yet and I want to focus on the rest of the feature work. So Google Sheets it is.
In terms of my choice of Azure, I’ve been on Azure for a couple of years now. It has limitations, but for personal use, it’s great. I spend $5-10 monthly for everything. That will increase when I set up a proper database, but most of the cost right now is the CDN. Essentially, for $5 monthly I can host my site and have incredible DDoS protection. It’s not bad. But my favorite feature of Azure is that I can deploy Azure Functions written in .Net, which means I can write F#. If you haven’t tried F#, it’s an amazing language. I’m not a very good functional programmer; if you ask me what a monad is, I’m going to lecture you how real mathematicians think Category Theory is a bunch of abstract useless gibberish, and I’m also not going to answer your question because I don’t actually know. I like F# because the community is averse to this kind of jargon, but also because the language is beautiful and fun to write. Honestly, I wish I could write it every day.
Enough asides. To get started on the above workflow, I’ll develop in three phases.
First, I’ll actually code up the Azure Function and run it locally. This will let me test everything before deployment. I’ll need the Azure Functions Core Tools and the .Net runtime installed for this. (As of this writing, I am using .Net 6.0, as .Net 7.0 still has some kinks when it comes to Azure Functions.)
In the second step, I’ll set up my infrastructure so that I can actually deploy the code to Azure Functions. This will involve setting up the function, and also setting up some Azure Key Vault secrets. There are a couple bits I haven’t figured out how to do, so it’s not pure infra-as-code here. Namely, it seems like Azure Function App Custom Domains aren’t working with Terraform as of this writing. In this step I’ll also set up a CI/CD workflow with Github actions to automate the deployment of the Azure Function.
Third, I’ll shift my focus to a low-code environment, and build a simple iOS shortcut to use the workflow.
The writeups for these posts I’ll be publishing in the following days. In the meanwhile, this post should give you an overview of the workflow and the architecture. It’s not complex, but some of what I describe might feel overly technical. If you want a simplified version of these posts at some point, let me know. I’m confident that the average person familiar with a computer can get this working for themselves with some help.
In the meantime, I’m really enjoying being able to share content with all of my followers without having to log into social media.