Projects
Here are some of the things I've been working on! You can see some bigger, long-standing projects I've got at the bottom or a running log of the smaller things I've been doing laterly.
Smaller Things
-
Royal Mail label formatter (2026-02-20)
- Made a client-side JS tool to put Royal Mail shipping labels nicely in the middle of envelope-sized PDF documents for ease of printing.
- Link: https://akpain.net/tools/labelFormatter
-
IPoPS (2025-10-25)
- During HackNotts 2025, me and Matt made IPoPS - Internet Protocol over Postal Services
- This entailed writing a TUN driver to siphon off IP data, encode it into a printable format and then print it, as well as an interface to scan the printed sheets and feed them back into the other driver in order to facilitate IP communcation over postal services
- request -> print -> post -> scan -> receive -> profit (maybe)

-
Made a tiny ESI server (2025-08-09)
- There are a couple of bits of dynamic content on this website, and I've wanted to have them generated by the server instead of using Javascript on the client side for a long time.
- A recent attempt was made to do this with [Varnish](https://varnish-cache.org] and ESI, and it was kinda fine, but the process of configuring and deploying Varnish was not pleasant and I wasn't convinced it worked properly anyway.
- So, instead of using that, I built a tiny little ESI proxy using Go, which works like a charm, does exactly what I want it to do and nothing else, and was a nice learning opportunity about working with XML and buffered I/O in Go.
-
Started a new deployment repository (2025-05-19)
- Started a new repository for managing DNS records and servers using DNSControl and Ansible as part of a currently-secret project
- It's very nice to be able to take the lessons I've learnt doing this for my own site and stuff and being able to use them from the get-go and not have to deal with old cruft
- The playbooks! They work so well!!
-
Popepedia (2025-05-08)
- After noticing that the new Pope's Wikipedia page got quite popular after the announcement of his election, I decided it might be funny to see what that looks like in graph format.
- It looks like this.
- A crude collection of Python scripts, Cron jobs and Caddy reverse proxy rules held together with string produced a live-updating set of graphs that refresh once ever 5 minutes and I quite enjoyed it while it was happening.
- Now that things have quietened down now (as of the 10th), I've stopped the scraper and made the data static.
- This is probably a good thing because the code I wrote for it is shocking.
Click to see the rest of the log...
-
Setup a Windows mini-PC for photo editing (2025-04-10)
- Since I do a fair bit of photography, I end up needing to do a fair bit of photo editing too, which kinda sucks because of the state of creative software on Linux.
- Sorry GIMP, you're great but you just don't compare to professional tools.
- Since upgrading
wall-eto a larger ThinkCentre, I've had a spare ThinkCentre M73 Tiny kicking around. I nicked a 128GB SSD off of Charlie, installed Windows 11 on it (surprisingly without hitch, despite the machine being a 4th-gen i5 with no form of TPM whatever) and loaded VueScan and Affinity Photo onto it. - The computer fits nicely on my existing desk by hiding under my laptop stand.
- This also helps get around its lack of WiFi card - I run it with it plugged into my laptop via ethernet. NetworkManager is configured to share the laptop's WiFi connection over ethernet to the mini-PC and it might be one of my favourite uses of technology ever.
- I've since scanned and edited a whole roll of film on it, and while it's not the most performant thing in the world, the experience was immediately better than trying to do the same on Linux.
- Since I do a fair bit of photography, I end up needing to do a fair bit of photo editing too, which kinda sucks because of the state of creative software on Linux.
-
Reworked small projects (2025-04-09)
- Tweaked this page to be loaded from a YAML file during build and to put everything but the first five entries in the list in a collapsible box
- This uses a fun little dataloader class that I wrote that allows the (controlled) loading of arbitrary YAML from the disk in Jinja templates
- A couple small tweaks to society-voting (2025-04-07)
-
Wrote some VBA in Excel (2025-04-04)
- Hacked an Excel spreadsheet around to make a quick little tool to use at work
- Part of my job involves copying some data out of a Sharepoint database, formatting it and sending it on, which up until now was done manually.
- This is trivial to automate with a Python script, but then there was interest from the rest of the team in using it - none of whom are particularly technically inclined (plus getting them to download and run a "random executable", ie. a Python interpreter, seems like a bit sketch)
- So I rewrote it all in VBA and threw it in an Excel macro-enabled spreadsheet alongside a quick UI using some text boxes and buttons.
- This was the best option considering the existing constraints around (not) sharing personal data and running random executables and also because it fitted fairly well in with out existing workflows.
- Overall, not a horrendous experience. Fun to do something a bit different for a change.
-
Hackathon Game (2025-03-23)
- Made a little game using PyGame at a hackathon - it's kinda like a cross between Papa's Pizzeria and Papers Please
- Everything was built from scratch in 24 hours, including all the pixel art (thanks Nathan!!)
- Repo

-
Small website tweaks (2025-03-19)
- Moved dependency management to
uvand updated the Dockerfile to use that accordingly - Replaced a very outdated dependency on
pyinotifyin favour ofinotify(this stopped me upgrading past Python 3.11 because they removed an APIpynotifyneeded in 3.12) - Upgraded the generator's Python version to 3.14 (from 3.10 lol)
- Moved dependency management to
-
Added a backlight block to cdmbar (2025-03-05)
cdmbaris my DIY i3wm status bar.
-
Small contributions to MusicBrainz (2025-02-26)
- MusicBrainz is basically Wikipedia for music.
- As well as correcting some data for other albums, I created a record for a new release I purchased
-
Setting up Navidrome and beginning to build out my music collection (2025-02-21)
- Lately I've been wanting to start building up my own library of music that I own and control and is of better quality than what you'd find on streaming services
- I picked Navidrome to manage that collection, and provide a nice API to use it on clients across my devices
- So far, so good! It's running on the newly-upgraded
wall-eand I'm using Copyparty to upload files to be ingested into it along with Feishin and Symfonium as clients. - Listening to lossless FLACs compared to mashed streaming audio is like being enveloped in a warm embrace of rich sound that you never realised you were missing
-
Moving
wall-eto new hardware (2025-02-15)wall-eis my home server that I use to run Gitlab and some other file storage services. Historically, this has been run on a ThinkCentre M73 Tiny and that's been all well and good, but I was starting to need an upgrade on the 8GB of RAM and 256GB of storage.- I decided to upgrade to a ThinkCentre M710s and toss a terabyte of spinning rust in it - plus I now have actual DIMM slots to add more RAM and I get a slightly newer and faster CPU (we're now rocking a Intel i5-7400 @ 3.5GHz!) so big wins all round.
- The only hitches I had were:
- Taking about an hour to work out that I needed to switch the BIOS to use legacy boot because that's how the boot SSD that I was physically moving over was partitioned
- Needing to edit
/etc/network/interfacesto adjust for the new ethernet adapter name before I could connect to the internet
-
NowPlaying (2025-02-14)
- NowPlaying is a teeny tiny web service to fetch your latest Spotify song and expose it as a basic API endpoint
- This is used to power the "the last song I listened to on Spotify" thing on my homepage
-
Switching Walrss to use index-based feed filtering (and other improvements) (2025-02-11)
- Historically, Walrss has filtered feed items in the digests it generates using the time of each item, selecting only items that have been published since the last run.
- This is unreliable since feed items do not always have an accurate date or any date at all - instead, it's best to use the feed item ID to remember what has been seen before and only send the new stuff down the wire, which is what Walrss now does.
-
Moving all my services to use Postfix as an MTA (2025-02-09)
- I run quite a few services that send email, each of which has historically been set up to reach out to SMTP services like Fastmail and Sendgrid to send their mail.
- This meant each service had its own API key and required a slightly annoying configuration process each time
- Each of my servers now runs an instance of Postfix in a Docker container that accepts incoming mail from other containers and forwards it on to Fastmail.
- Now, as opposed to having 8 different SMTP configurations littered about across multiple servers and email providers, I have 2 configurations, one per server, both pointing at Fastmail. Easy!
- The log ends here!
Bigger Things
- Advent of Code solutions
- For years 2020 to 2023 inclusive
- Completed an average of 45 stars per year
- society-voting
- Project for my uni's computer science society to streamline committee elections
- Walrss
- A RSS/Atom/JSONFeed email digest generator

- RailMiles
- A DIY train journey tracker
- See also this blog post (though the app has been substantially updated since writing)

- cdmbar
- A status bar for the i3 window manager