Another week, another set of open source work to do. Earlier in the week was the bulk of my atproto work, then I took it pretty easy after Wednesday focusing on learning some GBA development with rust. Next week might be a bit similar as well, I'm a bit tired and need a little break. But who knows, may get a second wind come Monday and knock out a lot of stuff! But that's the future, let's take a look at what I did this past week.
What days did I work on what?
May only care about certain things. So this is a bit of a guide on which days I talk about what if you wanted to skim to those parts
🧵 Stitch Counter(side quest) - Sunday, Monday, Wednesday, Saturday
❓ Missing Blobs - Monday, Tuesday, Wednesday
😘 Keep it Simple Software(opinion) - Wednesday
📄 pdsadmin Web UI Spec Draft - Thursday
👾 GBA Development(learning) - Thursday, Friday
Sunday, September 14th, 2025
Stitch Counter
I love tangled.sh. It has easily become one of my favorite parts of the atmosphere. Both @icyphox.sh and @oppi.li have done a fantastic job with it and every week bring great new features.
I have been playing around with the idea of creating a tangled.sh trending bot so I decided to give it a try with Stitch Counter.
I created the project from my Rust atrium template. Although I did butchered it some. This is a project I wrote to solve a problem I had, so it's not the cleanest code. Some other fun tidbits.
Uses rocketman as the JetStream listener
Uses constellation 🌌 for counting the stars on a repo
Uses slingshot 🎯 to grab the record for the repo so I don't have to resolve the did doc
chromiumoxide for a headless chrome to render the preview image, which is just the HTML card for trending on the tangled timeline
Currently the way it finds if something is "trending" is if a repo got x stars during y hours it will make a post about it as long as it hasn't in z hours. Which at the time of writing this it's 2 stars within 4 hours. All of those are set via env variables and the stars and when a post was made is kept in a sqlite DB. Expecting to tweak those thresholds, but will probably leave it there for now so it should make a few posts a day.
Pretty happy with how it has turned out so far!
Monday, September 15th, 2025
Stitch Counter
I ran Stitch Counter on my laptop for most of the day and it has been working pretty well! The plan is to eventually put it in a docker container and run from a Raspberry Pi at my house. Just testing right now to make sure thresholds work as expected and no other bugs. I had some concerns since it depended on headless chrome for building those card images in the post. Seems like an awful pretty big dependency to get that. Along with not sure how well it would run as a long term process, error handling, etc.
Was pretty worried about it till @chadtmiller.com had replied to a post and pointed out that thanks to his work with @slices.network he had an endpoint already creating nice open graph images of a tangled.sh repo and said I could use that! I am super appreciative to him for letting me know and allowing me to use that resource. I was happy to drop headless chrome and his preview images do look really nice. Hoping to move Stitch Counter to the rpi sometime this week.
haetae.tngl.sh/fanfic-atproto experimenting with making decentralized fanfic archives on atproto. github mirror: https://github.com/haetae-bit/fanfic-atproto ⭐️ 13
Missing Blobs
We had A LOT of people decide to migrate to a new PDS last week. Because of this a few users were met with rate limiting during the migration process no matter the migration tool they used. Still not sure if it was fully rate limiting, or timeouts on the PDS side, or possibly a PDS was sharing the rate limit across the board (taking a guess here). Either way, we ended up with a sizable chunk of users who were missing some of their blobs all at once. We noticed it once the CDN cache for profile pictures for a few users would expire and they were asking for help a few days after the migration. Very thankful that we just deactivate old accounts on the Bluesky hosted PDS instead of deleting on migrations. Thanks to that it means we can grab the missing blobs from their old repo.
So I started working on missing.pdsmoover.com a tool for users to check if their account is missing blobs, and if so to import them from their previous PDS. It’s a pretty simple tool very similar to pdsmoover.com and pdsmoover.com/turnoff.html. Just a simple alpine.js website to help those users out.
Some tidbits
I found out that you do not have to re activate the previous account to export the blobs like I expected. Being authenticated worked. Was super happy to find that out and not have to do any juggling of activating and deactivating the old account
Logins into your current PDS and checks to see it you are missing blobs
Grabs the previous PDS you were on via the PLC log. Or has an advance button to input the PDS url if you know it, or the tool has trouble finding it
Uses your did as the identifier when logging into your old PDS since we know it from the first login. So just need your old password to login into the old account.
Also we have a new variant of the PDS MOOver cow!
Tuesday, September 16th, 2025
Missing Blobs and counting them
So like most things, the real world is never as nice and neat as your test environment. The PDS has a /xrpc/com.atproto.server.checkAccountStatus
endpoint for use during migrations. Two properties are useful for blobs. expectedBlobs
for getting how many blobs your account expects you to have, as in how many atproto records have a blobRef
. importedBlobs
is how many blobs that have been imported(uploaded) to your account. I found during testing that those would always match up pretty well, but in real life not so much. My best guess is that improtedBlobs
shows temp uploads that have not cleared yet, so could not always trust that to know if users had missing blobs. Add that along with some of these users migrated over a week ago that count could really be off. Decided to switch to using /xrpc/com.atproto.repo.listMissingBlobs
as the official check of if a user is missing blobs or not. I just do a check with a limit of 10, If it returns an empty array the user is good to go.
Keep It Simple Software
You've probably noticed that the pdsmoover.com tools are all pretty simple. Not a lot of moving parts, not always the most user friendly. etc. Well, I thought I would talk about that a bit. It's not complete laziness, although that is a bit of it. It takes a lot of energy to create great software for free, and I'll be the first to tell you that. Not being spiteful, just honest. I am extremely blessed to be able to do that and help others. It brings me a lot of joy to be able to help others out and make a change in the world for the better. Even if it's a small one. But there is a price to the work.
I keep these tools simple with the idea of less moving parts, less logic, and less just general UI moving around means less to break and easier to test. For example pdsmoover.com is just one long form you fill out. Doesn't really hold your hand or do a ton of checks like, is it actually a PDS, is that handle available, selectable domain endings, does it require an invite code, and etc. Instead, it fails and give yous an error if one of those edge cases are met. All of that is accessible, and there are other migration tools out there like atpairport.com and tektite.cc that do a fantastic job of integrating that which ends up providing a much cleaner user experience.
Well why don't I integrate that into PDS MOOver? Well, when I created the tool I took a look around at everything offered and the one tool I used with the most success and everyone recommended was the goat cli. But it wasn't without it's faults (minor and still a goat of a cli tool). 1, a higher level of entry with it being a cli tool. 2, a lot of times the PLC token you entered at the start would be expired by the time it uploaded all your data to the new account. I thought, what if there was a similar tool like goat, but it was more accessible to end users. goat proved that a simple approach would work perfectly for account migrations in the atmosphere.
So I set out to check these boxes for a new tool
Simple logic
Everything to happen client side
Fail early if there's an issue
Some retry logic
Would request a PLC token after the time consuming uploads are done
A pun
And that's how pdsmoover.com came to be! It's not the best tool out there, it's not perfect, but that was never the goal. The goal was to make something that would work the majority of the time for the majority of users. It has caused a bit more work on support, but overall I've been very happy with the result and have seen fewer than expected edge cases of it not working for users.
Wednesday, September 17th, 2025
Missing Blobs
I talked a bit yesterday about the different tactics of deciding how to decide if the user's account was missing blobs. I decided to go all in on the listMissingBlobs endpoint and just hide from the UI the counts everywhere but at the very end of the process. I'm hoping this makes it a bit easier on users and give a clearer "Yes you are missing blobs" or "No you are not missing blobs". Especially at the end. I saw a lot of users getting the try again even tho they had all the blobs, just the imported vs expected was off so it was always saying they should try again.
Re worded some of the status updates for missing.pdsmoover.com Found in real world accounts that expected blobs and imported would not always match up as expected. Was causing some confusion. So now uses listMissingBlobs as the check if any are missing and hide some of the stats counts from the ui
Login check now calls listMissingBlobs. If array is not empty tells them are missing blobs
Calls a 1,000 limit on the first list call. Uses that count for the "Migrating blobs: x/y" status update. If the user has over 1,000 missing it updates that second count. Was getting the missing count from the expected - imported and was off a lot of the time for users
At the end I did leave the count if they were still missing the blobs. I figured a screenshot from the user would be handy for troubleshooting if we needed to
Stitch Counter Docker Image
Not much to say here other than @stitch.selfhosted.social is now up 24/7 and safely on a raspberry pi in my laundry room and I "unhide" the tangled.org repo. Aka changed the description from "Don't worry about it". Can find the code here.
tangled.sh trending is now running 24/7 and running on a raspberry pi!
This profile is now fully up! The current rules are if a repo gets 2 stars within 4 hours this account will post the repo. If it has been posted already it has a 24 hour cool down. Will monitor and see how spam-y that gets, but I figured more was better.
stitch_counter by @baileytownsend.dev
tangled.sh tending bluesky account
Thursday, September 18th, 2025
Not a ton of "work" happened today. Mostly just bebopped around.
pdsadmin UI
One of the goals I have for PDS gatekeeper is to host a pdsadmin UI screen that users can sign into via their atproto account and manage the PDS instead of relying on the admin password only. I wrote up a rough draft of my idea of how that would work.
GBA Development
It's been a minute since I've done any no_std rust and I also recently found out that agbrs has completed their online book on getting started. I figured it was time to check it out again.
About 2 years ago when I first heard about agb their book had about 1 or 2 chapters on building a pong clone on the GBA. I had read through it and somewhat finished it out to learn agb. Can see that attempt here. I have to say it was such an amazing feeling to see the GBA boot screen right before something I wrote.
Since then I've came back to agb with varying success various times, as seen below.
Decided to go be frustrated at something else for a bit. Not a pico, but is still no_std rust. I updated my suika GBA game to the latest agb version. Forgot how buggy the game is. May take another stab at it and just do all the fruit as square to help with physics
The GBA is hands down my all-time favorite console. I have not written a complete game yet, but here are some pictures and videos of a few I’ve tinkered with. Seeing the GBA start up animation before something I made was like a dream come true. All are written in Rust 🦀 using the agb crate
I'm taking it slow coming back. Feels like a lot in the crate has changed, but may just be I haven't looked at the code seriously in a while. I'm working through the new-ish agb book. Then will probably make a smallish game with the goal of completing it fully. I'm thinking a flappy bird clone. Nice and easy to do (famous last words).
Friday, September 19th, 2025
A chill day. Didn't do any open source per say, but I did continue learning more GBA development stuff
GBA Development
Not a ton to say other than I finished up the pong example in the agbrs book. It was a lot of fun! Was a good refresher on everything and got to learn some new things like using Rect for collisions. I can't remember if it was there before or not, but last time I messed around with agbrs I was doing the collisions checks on my own. Was nice to have a helper method for that.
Finished up the pong example in the agbrs book. Could use some improvement (like the CPU is a bit op). But that's alright, I got a good refresher going through it. Time to move on to making my own small GBA game agbrs.dev/book/pong/00...
Saturday, September 20th, 2025
Stitch Counter
When I wrote Stitch Counter I didn't really add a ton of error handling (whoops). So due to the minor constellation outage (ty fig for all you do with that). Then finding out it was possible for some images for repos to not be rendered there was an outage for a while for the bot and the bot missed a few new trending posts. If the bot could not find one or the other it would just not send the post. No biggie. Just made those optional and if the bot can't find them then the show goes on without them being added! I also switched to the community maintained JetStream (ty again fig), while also giving the ability to set a custom one from the env. Can check out that work in this commit.
And that was my week! Not sure what next week holds, not making any plans with the hope to take it a bit easier. But we’ll see! Thanks for reading, see ya next Sunday-ish!