<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://catskull.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://catskull.net/" rel="alternate" type="text/html" /><updated>2026-09-14T22:27:18+00:00</updated><id>https://catskull.net/feed.xml</id><title type="html">catskull.net</title><subtitle>mostly harmless
</subtitle><author><name>catskull</name></author><entry><title type="html">Adding 4 more 2.5GbE interfaces to the GMKtec NucBox G9</title><link href="https://catskull.net/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9.html" rel="alternate" type="text/html" title="Adding 4 more 2.5GbE interfaces to the GMKtec NucBox G9" /><published>2026-08-22T17:33:32+00:00</published><updated>2026-08-22T17:33:32+00:00</updated><id>https://catskull.net/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9</id><content type="html" xml:base="https://catskull.net/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9.html"><![CDATA[<p>TL;DR: I added 4x Intel I226-V 2.5GbE network cards to my NucBox G9. You can do it too. <a href="https://www.printables.com/model/1816330-gmktec-nucbox-g9-4x-nvme-nic" target="_blank">Download the files and print them yourself.↗</a></p>

<h2 id="backstory">Backstory</h2>

<p>A few months ago, <a href="/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9.html">I blogged about using an Intel n150 based mini PC as my home router running OpenWrt</a>. Since then, I’m happy to report that it has been perfectly reliable<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</p>

<p>However, the NucBox G9 <a href="https://www.jeffgeerling.com/blog/2025/almost-perfect-mini-nas-my-mini-rack/" target="_blank">runs notoriously hot↗</a> with NVMe drives installed. Even without any hot NVMe drives installed, I’d describe the overall thermal situation as “not great, not terrible”. I haven’t had any specific issues since OpenWrt is about as light of a load as I could possibly run on it. My unit stays at about 50°C, which is wonderful. What worried me is the 64GB of onboard eMMC storage.</p>

<p>As far as I understand, eMMC is almost exactly a glorified SD card soldered to the board. And if my experience with SD cards, specifically my experience booting computers off of them, is any indication, it would not be very reliable. Adding to my fear was one user’s experience of the eMMC failing after 6 months. Of course I could always boot it off an NVMe or SATA m.2 SSD, or even a USB stick. But the router is the main artery of my entire network and trying to scramble downloading and flashing an OS while my network is down sounds like a horror story I’d like to avoid.</p>

<p>The NucBox is also a victim of its own success for me too. I really love it and it is perfect for my home router. The issue is that these mini PC manufacturers have an approximately 2-week product lifecycle and the G9 is over a year old at this point. In other words - it’s ancient history. There were a handful available on eBay back in May when I first purchased it, but since then the supply has completely dried up. There are replacement models available but thanks to the RAMpocalypse, the prices are outrageous and none of them are an exact drop in replacement for the N150 G9 and it’s Intel NICs.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Great price!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/ebay.png" alt="Screenshot of the eBay purchase for the G9" />
	
	</div>
</figure>

<p>I decided to try and get ahead of any potential hardware failures and procure a spare G9. I set up an eBay alert and mostly forgot about it. One morning I woke up to an email saying a G9 had been listed for $150 buy it now with free shipping. Even less than the $180 I paid for my original unit! A miracle! Sure, it had been “used”, the seller’s description said they’d been running it as an NVMe NAS but it just wasn’t sustainable, especially now that NVMe drives have also succumbed to the AI apocalypse. For my purposes, it was perfect.</p>

<h2 id="adding-the-nics">Adding the NICs</h2>

<p>Once I had the replacement unit in hand, the first thing I did was guillotine the horrific <a href="/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9.html#:~:text=It%20comes%20with%20a%20USB%2DC%20power%20adapter%20that%20outputs%20a%20constant%2019%20volts%20always.%20That%20is%20a%20huge%20spec%20violation%20and%20would%20very%20likely%20result%20in%20ruining%20whatever%20you%20plug%20it%20into%20that%20isn’t%20the%20NucBoxG9.">standards violating USB-C power adapter</a>.</p>

<p>Then, I turned my attention to my dream of adding a few more 2.5GbE NICs to it. Why? As I mentioned previously, redundant ISP connections are something I might possibly do in the future, but if I’m being honest it was mostly just because I thought it would be cool. At $25 a pop with two day shipping, they fell into this extremely dangerous territory where your project becomes a series of impulse purchases and the sunk cost fallacy snowballs with each one. I just thought it would be cool, okay?</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>What a curious bit of kit<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/adapter.jpg" alt="Amazon product photo of the m.2 Intel NIC" />
	
	</div>
</figure>

<p>I bought two m.2 B+M key I226-V adapters and plugged them in, ethernet ports dangling off the back. I fired up a fresh copy of Alpine, my Linux of choice, and was pleased to see they “just worked”. I did some rudimentary throughput tests between NICs and was pleased to see I was getting more or less the full 2.5Gb throughput.</p>

<p>The next step was figuring out some kind of mounting solution. My 3D design skills are about a 0.1/10, so the project stopped there for a few weeks. I left everything on my desk, and several times a day I’d just stare at the gear, fiddle with it in my hands, and just kind of wonder about how it all could work.</p>

<p>The G9 is essentially an aluminum center rail with plastic top and bottom plates. The screws are phillips and fully exposed. Very inviting for my iFixit screwdriver kit. The m.2 slots are on the bottom of the kit and that’s the first panel that needs to come off to disassemble the unit. Once I removed the bottom panel, I realized that the geometry was actually fairly simple and some kind of replacement bottom panel wouldn’t be insane to design to bolt on just like the OEM bottom panel. The mounting screws for the m.2 drives are integrated into the OEM bottom panel, which would complicate things, but still, it seemed simple. Yet still out of reach to me. The mere thought of downloading Fusion and logging in fills me with dread.</p>

<h2 id="claude-cad">Claude CAD</h2>

<p>Then, one day as I pondered the whole idea some more, I realized surely someone would have designed a bottom shell for it. It’s niche, but the fact that it is all but unusable with the stock bottom case and cooling creates quite a bit of motivation for fellow nerds to do something about it. One google search later, I came to <a href="https://www.printables.com/model/1277978-gmktec-nucbox-g9-modkit" target="_blank">an excellent kit on the Printables site↗</a> from user “sleeeeeepy”. It did exactly what I had envisioned in my head. A full replacement for the bottom shell with variations for some different fan sizes that would blow directly onto the NVMe drives. It was about 90% of what I needed to realize my dream!</p>

<p>So close, yet still so far. I still wasn’t willing to download Fusion. The print files were distributed as 3MF which is a format I was not familiar with. At work they give me a Claude account so I’ve gotten pretty good at being stupid enough to let it help me with things. Enough so that I reactivated my personal Claude account. So I turned to Claude to give me an idiot’s guide to what a 3MF is. Obviously it’s file format that describes 3D models. I was informed it was literally a zip file with some XML inside, like any good file format is. I mean, if there’s one thing an LLM is good at it’s parsing and creating text files, and at the end of the day XML is just text. So I wondered, could Claude modify the 3D file for me? I had heard not to trust an LLM to do math, and this would be quite a bit of math. I was shocked that it went through it like sliced butter. It burned a lot of tokens as it worked, but it never broke anything. I was measuring things with my digital calipers and feeding dimensions into Claude, working one step at a time through the modifications.</p>

<p>First I added 4 holes for the jacks to sit in. Then I added a mounting rail so I could screw the jack PCBs down with some heat set threaded inserts that are common in 3D print projects. Then I had it add places to mount two 40mm Noctua fans. Once I thought I had it good, I had Claude slice off the front of the shell so I could do a test print to verify the jacks would fit.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>I need some space<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/clearnce issues.png" alt="the first front test print showing clearance issues" />
	
	</div>
</figure>

<p>There was a small issue with clearance for some of the through-hole components, so I had Claude redesign to give more clearance. Another test print and it seemed it was totally good to go. A few more impulse purchases sent threaded inserts, fans, and two more NICs my direction.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>We have clearance, Clarence<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/clearance fixed.png" alt="clearance issues fixed and front test print with two jacks mounted into the g9 case" />
	
	</div>
</figure>

<p>The next day I realized that there probably wasn’t enough clearance to actually get the threaded inserts in place without also melting the front face. That was a simple fix, I had Claude just remove that entire mounting rail from the case so it could be separately printed, inserts added, then glued into place.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/rail separated.png" alt="PCB mounting rail separated next to the original test front panel with the rail printed in place" />
	
	</div>
</figure>

<p>I also switched from PLA to PETG because I hear that PETG will do better long term in hot-ish environments like a computer. Once I verified the mounting rail looked good, I fired off the big print for the bulk of the enclosure. I think it took about 8 hours and I’m not going to publicly admit how many of those hours I spent just staring at the printer do its thing. Am I the only one who is mesmerized by watching a 3D printer print? My kids even joined in for some of it, we stared at it together and it was fun.</p>

<h2 id="long-term-prusa-mk3-review">Long term Prusa MK3 review</h2>

<p>As an aside, due to my extreme lack of 3D modelling skills, my 3D printing journey has been fairly crude. Almost exactly 8 years ago at the time of writing I bought myself a Prusa i3 MK3 kit. It was pretty expensive, I think about $750. At that time I was running Catskull Electronics and had money coming in I needed to reinvest in the business which usually took the form of cool tools I could use to make my life easier as I assembled thousands of circuit boards by hand in my basement. I had purchased a few of the cheap Chinese Creality printers but they never really worked for me. There’s this funny dilemma a lot of 3D printer enthusiasts end up in where the 3D printer becomes the hobby, not so much 3D printing. It makes sense to me, tinkering with a cool machine is a lot more fun than printing kind of poor quality random crap, especially when you don’t have the CAD skills to design anything useful. That was the trap I desperately wanted to escape when I bought the Prusa. Assembly took me nearly a full 10 hours I think, and it printed like absolute garbage. It was mildly devastating. At some point I realized my issue was that the bed was not level enough. I found a simple mod where I could use some nylon locking nuts on the bed screws and an Octoprint plugin to allow me to visualize the level of the bed. Running the plugin gave me a visual map of the hills and valleys on the bed so I could tighten and loosen the nuts to compensate. And once it was dialed in, they should be locked in place and never really move. Once I did that, the 3D printer went from a hobby to a <em>tool</em>. I could just print things, reliably. I did actually get my money’s worth out of it as well. At that time, I was hand assembling surface mount circuit boards with a stainless steel stencil and solder paste. I did them all to order, so very small batches, maybe 3-10 at a time. Normally the way you do this is to tape down a few PCBs around the one you’re going to work on to lock it into place, then you tape down the stencil on top. It’s imprecise and time consuming when you’re just trying to build the things as quickly as you can. I built up enough Fusion skills to print a rectangle with a cutout to match the size of the PCB. I even had rails to hold the stencil in place. I could snap the PCB in place, lay the stencil on top, and my little jig would hold them in place for me to apply the solder paste. As stupid as it sounds, that actually saved me probably <em>hours</em> of time I really didn’t have. Since I stopped doing Catskull Electronics, the Prusa has sat in my closet 99% of the time, only pulling it out maybe once or twice a year when I need some random boring part. I’ve done literally no maintenance to it and I don’t really treat it very gently. It sits in my closet, I pull it out and set it on my floor, print something, then put it back. I don’t even keep the filament in a climate controlled area, but I also live in the desert so humidity is not a problem in the slightest. I keep a spool of black PLA and orange PETG, chosen for no specific reason, both of which are years old at this point and everything still “just works”. So my 8 year review of the Prusa MK3 is to use nylon nuts for the bed, get it level, and then it’s perfect. Whenever I get it out my kids ask me to print them impossible things and it’s hard to explain that it can only really reliably do glorified boxes. I <em>could</em> probably get it to do some more complex geometry if I really wanted to, but I just don’t have the appetite to have a print fail 10 hours in just for some dumb thing you could buy from Amazon for $5 that would be way better quality. It’s only worth it for very custom and simple things like a replacement bottom shell for a NucBox G9!</p>

<h2 id="final-assembly">Final assembly</h2>

<p>I installed the threaded inserts and then glued the mounting rail into place. I have a friend who’s a high school shop teacher and I traded him a computer for a literal <em>lifetime supply</em> of CA aka “Super” glue and activator. I find the industrial stuff just works better and is easier to apply than the tiny things you get at the store. The proof is in the pudding, I glued it into place without getting <em>any</em> on my hands! Glued into place, I screwed in the ethernet jack PCBs and was very satisfied to see them solidly in place.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Nicely glued!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/rail glued.jpeg" alt="PCB rail with threaded inserts glued into case shell" />
	
	</div>
</figure>

<p>The last tricky step was adapting the Noctua fans to the 4 pin SH1.0 JST connectors the stock fans use. I’m pretty slick with a soldering iron if I do say so myself, but soldering two bare wires together is still hard. I’d way rather solder a wire to a PCB than a wire to another wire. I even slightly tore down a Noctua fan to see if I could desolder the built in wires and solder the new connector directly to the PCB, but that’s not possible. In the end, I got it without too much trouble. I even verified the PWM and RPM sensors work. Technically you could get away with only using the 5V and GND pins, the fan would just run at full speed all the time, which for as quiet as the Noctua fans are, wouldn’t be a problem. I was just slightly mindful of the fact that running a fan at 100% all the time might reduce its lifespan from 100 years to only 50.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Wired up!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/fans and jacks mounted.png" alt="bottom case with all 4 jack PCBs mounted and both noctua fans mounted with cables adapted" />
	
	</div>
</figure>

<p>The NICs have a cable that connects from the m.2 PCB to the jack pcb and it was slightly challenging to get all the cables and fan wires coiled nicely in a way that wouldn’t interfere with the fans spinning, but I got it.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Looking nice!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/four nics.png" alt="All 4 m.2 adapter in place with heat sinks" />
	
	</div>
</figure>

<p>sleeeeeepy’s original design included some feet that would press fit into the screw holes on the bottom of the case to give some clearance for the fans to get fresh air. For my use case, I would be mounting the box on the wall of my network cabinet with some command strips, so I had Claude whip up two foot rails to give the fans plenty of room to breathe and accommodate the command strip. Once I had that in place, it was done!</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>I got two left feet<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/mounting feet.png" alt="mounting rails pressed into bottom case" />
	
	</div>
</figure>

<p>If you’re a maker too, you can probably imagine the sense of accomplishment of seeing a project you’ve been dreaming of for months come to fruition perfectly. It was clean. It’s still possible to take the whole thing apart. The hardware is all held in with screws, not glue. It’s also reversible, if I wanted to sell one of the NucBoxes I can reattach the stock bottom plate.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>All done!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/adding-4-more-25gbe-interfaces-to-the-gmktec-nucbox-g9/done.png" alt="the g9 with custom bottom case and 4 extra network jacks and fans" />
	
	</div>
</figure>

<h2 id="software-redundancy">Software redundancy</h2>

<p>My goal with the entire project was to have a hot spare ready to go in a moment’s notice. I want to be able to pull the WAN, LAN, and power cable from one box, put them in the new box, and be back up in less than a minute. I mulled over some possible options to keep a spare ready to go, or at the very least able to get ready as quickly as possible.</p>

<p>Initially, I had the live router backing up it’s config files to my NAS nightly. The plan would be to reflash fresh OpenWrt and then apply the configs to it. That was better than nothing, but still didn’t feel like it would be very fast in the case of an emergency such as the router dying in the middle of a work day. And now that I had an actual spare on hand, I could do a live test.</p>

<p>What I decided to do instead was have the live router do a complete backup of the filesystem with <code class="language-plaintext highlighter-rouge">dd</code>, then <code class="language-plaintext highlighter-rouge">gzip</code> it and store it on my NAS. Because OpenWrt only uses about 128MB of the disk space, and that 128MB compresses very nicely down to just about 17MB, this was a very reasonable option and also the most bullet proof. I have the script doing daily backups and I store daily backups for the past week, weekly backups for the last month, and monthly backups for the last year. I also have the backup folder rsync’ed to some off site storage as well.</p>

<p>From there, I installed Alpine onto a thumb drive and set up a script that auto runs on boot that will pull the latest backup from the NAS and flash it to the internal eMMC drive. This whole process takes about 2 minutes from cold boot into Alpine to cold boot into the freshly backed up OpenWrt. The BIOS will boot from the USB drive first, so any time it boots it clones itself to the live router. Then if I need to swap boxes, I just pull the USB drive out and it’s ready to go.</p>

<p>There’s a small detail I’ll skip over in order to make sure the WAN port is always the same physical jack location on the hardware. In Linux, interface names (eth0) are assigned in the order the interface is detected, so they’re generally stable on the same system but not necessarily deterministic between OS installs and hardware. There’s a small OpenWrt boot script that assigns WAN to one of two MAC addresses (one for each box). The other interfaces are all LAN.</p>

<h2 id="closing-thoughts">Closing thoughts</h2>

<p>It’s been a fun project to work on. For whatever reason, having a rock solid home network is just a hobby I find interesting. When I work from home, stable internet is as much a part of my livelihood as my ability to see and type. I’ll sleep just a little bit better knowing I have a great disaster recovery plan if my cool little x86 OpenWrt router ever bites the dust.</p>

<p>If I did it again, I might consider using the one m.2 slot in the NucBox that is able to take an m.2 SATA SSD as the boot drive and removing the built in m.2 A+E key WiFi card and putting 3x B+M key NICs and one A+E NIC. I’m a little torn there, since the ability to use the NucBox to also broadcast a WiFi network or possibly connect to a mobile hotspot for WAN failover is mildly appealing.</p>

<p>If you want to try this yourself, you can find the files and bill of materials on the <a href="https://www.printables.com/model/1816330-gmktec-nucbox-g9-4x-nvme-nic" target="_blank">Printables page↗</a>.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Except the one time after an OpenWrt upgrade when the WAN only negotiated 100MbE on reboot and I didn’t notice for a week. Restarting the interface solved the issue. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>catskull</name></author><category term="diy" /><category term="home network" /><category term="openwrt" /><category term="3d printing" /><summary type="html"><![CDATA[TL;DR: I added 4x Intel I226-V 2.5GbE network cards to my NucBox G9. You can do it too. Download the files and print them yourself.↗]]></summary></entry><entry><title type="html">Is your browser up to date?</title><link href="https://catskull.net/is-your-browser-up-to-date.html" rel="alternate" type="text/html" title="Is your browser up to date?" /><published>2026-07-16T23:35:11+00:00</published><updated>2026-07-16T23:35:11+00:00</updated><id>https://catskull.net/is-your-browser-up-to-date</id><content type="html" xml:base="https://catskull.net/is-your-browser-up-to-date.html"><![CDATA[<p>Check it right now. Is it up to date?</p>

<p>One of my personal interests is monitoring people’s browser update status. We have regular company calls where people will share their screen and all of the major browsers make it extremely obvious if the browser needs to be updated. Chrome especially makes it entertaining to monitor just <em>how</em> outdated it is. I wish I had a screenshot but I don’t. In the top right corner there will be a little badge that will be green/yellow/red depending on how outdated it is.</p>

<p>I’ve also casually asked friends and coworkers if their browser is updated and virtually none of them had an updated browser at the time of asking. This is across not only Chrome users, but Firefox, Brave, and whatever other hipster Chromium upstarts people like to use as well.</p>

<p>Interestingly, Sam initially claimed his Safari was out of date, but then realized it <em>was</em> updated somehow without him even knowing how or why. It seems that Safari is unique in that Apple ships Safari updates as OS updates and with the default update behavior, macOS automatically updates reliably. At least anecdotally.</p>

<p>I can’t say I know for sure why seemingly every running instance of Chrome is perpetually outdated, but I have a few hunches.</p>

<p>First, Google ships Chrome updates <em>very frequently</em>. As in, like, every other day. <a href="https://chromereleases.googleblog.com/search/label/Desktop%20Update">Google maintains a blog detailing the updates for various versions of Chrome</a>. It looks like they shipped an update <strong>today</strong>- July 16, 2026. Included in it are <strong>three</strong> critical CVE patches and <strong>four</strong> high patches. The previous update was literally <strong>two days ago</strong> on July 14. It included <strong>two</strong> criticals and <strong>twelve</strong> highs. For what it’s worth, <a href="https://www.mozilla.org/en-US/security/advisories/">Firefox publishes security advisories</a> and the most recent critical vulnerability was patched on July 14 as well. The page is a bit hard for me to parse, but it appears the last critical patch was back in May of this year. That isn’t to say that Firefox is inherently more secure than Chrome, just that for whatever reason Firefox ships fewer critical fixes. Reasons might include Firefox actually being more secure, fewer people looking for vulnerabilities, or Firefox fixing fewer of the vulnerabilities that are reported to them. I don’t actually have any knowledge or insight into those specifics, I just want to make it clear I’m not calling out Chrome or Firefox here specifically. It should be assumed that major vulnerabilities exist in all software at any point.</p>

<p>Second, Chrome does not automatically update. It automatically downloads the updates, but the user is required to manually exit and relaunch Chrome. The UI makes this fairly simple to do, but it seems that for whatever reason, users simply don’t do this. People keep a few tabs open at all times for whatever web apps they need for their work and don’t want to close them ever. Fear of losing data? I don’t know. It feels a bit ironic to me that in the day of everything being a cloud service, users are afraid of losing their data. We’re so far gone past the Windows 95/98 days of yore where a full system reboot was required at least once a day, if not more frequently. I think if Chrome actually relaunched itself automatically the amount of up to date Chrome instances would go to 100% overnight. I’m not sure why it doesn’t do this, perhaps there is some operating system technical constraint, perhaps Google simply doesn’t want to interfere with people’s lives. And it’s not like Firefox (or other Chromium based browsers) do this any better. Is it just the industry standard way of updating a browser?</p>

<p>Third, Chrome seems about equally likely to ship a <a href="https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/">4GB AI model</a> as it is to fix critical vulnerabilities. Because Google is an advertising company, their primary motivation will always be profit at whatever cost they can get away with. I’m not sure what the average Chrome user’s sentiment is towards Google, but considering they’re willing to manually install Chrome on their operating system, they must have some level of trust in Google software. Still, I wonder if this is part of the hesitation. They’re not sure what random changes might have shipped in Chrome and they don’t want to disrupt their workflow. That’s a fair standpoint, in my opinion. I think it also applies to the other browsers as well, not just Chrome.</p>

<p><a href="https://en.wikipedia.org/wiki/No_Silver_Bullet">There are no silver bullets</a>, especially in technology. Everything has tradeoffs. On one hand, I believe that the modern web browser is the most sophisticated and complex software that has ever existed. It’s quite literally miraculous. On the other hand, this means the potential pool of exploitable users is larger than it’s ever been. There’s more incentive for bad actors than there ever has been, period. It’s a <a href="https://en.wikipedia.org/wiki/Tragedy_of_the_commons">tragedy of the commons</a>.</p>

<p>However, I will call out Chrome specifically here. Interestingly, Wikimedia publishes their own browser usage share stat. Cloudflare also reports this metric. The <a href="https://en.wikipedia.org/wiki/Usage_share_of_web_browsers#Summary_tables">Wikipedia page on “Usage share of web browsers”</a> shows that Wikimedia reports 53.9% of their traffic in the month of July 2026 coming from Chrome. Cloudflare reports 68% for the same period. The next closest is Safari at 25.1%/19% from Wikimedia and Cloudflare respectively. Should this mean that Google shares twice the responsibility for keeping their users safe as Apple does?</p>

<p>The modern day Common Vulnerabilities and Exposures (CVE) system is a double edged sword. In the ideal process, vulnerabilities are discovered, reported, fixed, then disclosed to the public. Sometimes if the organization responsible for the patch doesn’t respond, the vulnerability will be disclosed before a patch is released, but in the case of the major browser companies, thankfully that rarely happens. The issue is not that vulnerabilities are not patched. The issue is that once the vulnerability is fixed, it is disclosed. At that point it’s no longer a <a href="https://en.wikipedia.org/wiki/Zero-day_vulnerability">“0-day” (unpatched) vulnerability</a> and instead becomes a “1-day” or “N-day” vulnerability. These patched vulnerabilities continue to be actively exploited because not everyone updates their software! In fact, it brings the vulnerability from elite hacking group status to more pedestrian levels of effort because the problem and the fix are well documented. The best example of this I have is myself.</p>

<p>Last year, <a href="/porting-openwrt-to-the-cudy-r700.html">I wanted to port mainline OpenWRT to the Cudy R700</a>. The software shipped by Cudy is a fork/skin on top of OpenWRT. However, it’s locked down and updates have to be signed by Cudy so a port was not possible without Cudy’s blessing. I eventually got them to sign a mainline OpenWRT image that removed the update signature check, but in the meantime I attempted to simply exploit the software. Among other things, I researched patched OpenWRT CVEs that were newer than Cudy’s firmware to see if I could use one of them to exploit the router and gain root access to install mainline OpenWRT <em>without</em> Cudy’s blessing. I was unsuccessful in that endeavor, but to me it highlights the issue. Bottom-feeder hackers like myself can easily research patched CVEs to target unpatched devices. While a consumer router is a fairly small attack vector, extrapolate that process to the millions of browser users! Combined with the ubiquity of NPM packages and the tendency for them to be exploited, the attack surface is possibly the largest it’s ever been. I am not malicious, but there are many who are!</p>

<p>I don’t have the solution here. But I’m also not a company with a market capitalization in the <em>trillions</em>. It does seem to me that Apple’s update strategy is more effective at getting critical security updates out to more users in a seamless way. Can Google really not figure out a way to automatically update the browser without requiring user intervention? Surely there must be a solution. If I was going to recommend something to a non-technical user such as my Mom, I’d probably just tell them to use Safari on some Apple hardware that was still getting software updates. But that’s also not a perfect solution and has a lot of its own issues.</p>

<p>Is <em>your</em> browser up to date? What browser do you use? If it’s not updated, why? I’m genuinely curious!</p>]]></content><author><name>catskull</name></author><category term="rant" /><summary type="html"><![CDATA[Check it right now. Is it up to date?]]></summary></entry><entry><title type="html">Using a GameCube controller on macOS (with rumble!)</title><link href="https://catskull.net/macos-gamecube-controller.html" rel="alternate" type="text/html" title="Using a GameCube controller on macOS (with rumble!)" /><published>2026-07-10T17:14:02+00:00</published><updated>2026-07-10T17:14:02+00:00</updated><id>https://catskull.net/macos-gamecube-controller</id><content type="html" xml:base="https://catskull.net/macos-gamecube-controller.html"><![CDATA[<h2 id="tldr">TL;DR:</h2>
<p>I made a GameCube to USB controller adapter that works on macOS including rumble. <a href="https://github.com/loopj/libjoybus/pull/35">You can build your own.</a></p>

<h2 id="intro">Intro</h2>

<p>Because I’ve had to move like 15 times in my life, I have a strong fear of owning too much crap. In times past, I had a fairly extensive game collection, but it’s been heavily pared down to just Switch, (3)DS, my childhood Xbox and GameCube games, and some random Game Boy and other retro console games that are physically odd or unique in some way such as the Satellaview cartridge and Japanese Tomagotchi games.</p>

<p>Here’s a video where I talk about some of the random stuff in my collection:</p>

<lite-youtube videoid="hiYSxv-ohq8" style="background-image: url('https://i.ytimg.com/vi/hiYSxv-ohq8/hqdefault.jpg');" title="Odd and Uncommon Retro Items">
  <a href="https://youtube.com/watch?v=hiYSxv-ohq8" class="lty-playbtn" title="Play Video">
    <span class="lyt-visually-hidden">Play Video: Odd and Uncommon Retro Items</span>
  </a>
</lite-youtube>
<script defer="" src="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.css" rel="stylesheet" />

<p>However, one item I still keep on hand is <em>controllers</em>. Authentic input methods really capture the experience of playing the games for me. I can’t quite capture the feeling of kneeling at the family room CRT, but I can capture the feeling of holding an N64 controller while I smash <a href="/every-game-I-beat-in-2025.html#beetle-adventure-racing"><em>Beetle Adventure Racing</em></a>.</p>

<h2 id="the-retro-gaming-nook">The Retro Gaming Nook</h2>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Shining Force Gaiden: Final Conflict<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/macos-gamecube-controller/nook.jpeg" alt="A monitor showing a retro video game being played" />
	
	</div>
</figure>

<p>Because I work from home, I’m at my computer desk all day. When I want to play a game after work, I want to sit <em>anywhere else</em>. I also have a bunch of kids and generally try to make myself extremely available to them for any whim or need, but sometimes Dad just needs a few minutes to himself where he’s <em>not</em> working, you know? A few years ago I assembled a pretty sweet little “retro gaming nook” in my office with a super comfy recliner. I traded my friend Jared an extra Analogue Pocket for his M1 Mac Mini. I thrifted a 19” Samsung SyncMaster 930B 4:3 LCD monitor for $15, some Logitech speakers for $5, and a new-in-box Belkin SurgeMaster power center power strip/monitor stand thing for $10. I like the SurgeMaster because it’s beige and has physical switches on the front to turn on and off the monitor, speakers, and computer. The SyncMaster has VESA mounting holes on the back, so I bought a Mac Mini VESA mounting bracket and stuck it on the back of the monitor, out of sight. I also got a nice <a href="https://www.benfei.com/products/benfei-usb-c-to-dvi-6-feet-cable-usb-type-c-to-dvi-cable-thunderbolt-3-4-compatible-with-iphone-15-pro-max-macbook-pro-air-2023-ipad-pro-imac-s23-xps-17-surface-book-3-and-more?variant=43730093047999">USB-C to DVI cable made by “Benfei”</a> which both works and looks excellent at the SyncMaster’s 1280x1024 native resolution. I especially love that it does 75Hz refresh rate, which just feels like I’m a pro gamer as opposed to the now-standard 60Hz. For input I use the legendary Logitech K400 Wireless Touch Keyboard, or I just connect to the mini over remote desktop on my MacBook. It works great because the mini isn’t ever used for normal computer things, the only time it’s not playing a game is when I’m installing or configuring a game. When I’m done playing, I just flip the switches on the SurgeMaster to turn off the monitor and speakers but leave the computer running so I can easily pick back up where I left off. It’s a bit wild to me how much use I’ve gotten out of this small setup and how authentic the whole experience feels.</p>

<p>Due to this setup and my desire to use authentic inputs with it, I have two main constraints: First, figuring out a way to get the random old controllers working on my computer to play my freshly dumped backups. Second, the fact that I only own computers with macOS. You wouldn’t think macOS would be so limiting when it comes to controllers, but Apple seems to like to totally reinvent the wheel every 5 years when it comes to how they deal with drivers, so most of the commercially available products simply don’t work on macOS. Theoretically my life would be easier if I used some form of SteamOS or Windows, but in practice I’m just a lot more comfortable with macOS, even with its limitations.</p>

<h2 id="input-methods">Input Methods</h2>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Meet the Family<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/macos-gamecube-controller/controllers.jpeg" alt="All of the controllers I describe in this section together." />
	
	</div>
</figure>

<p>For most wireless controllers, I like to use the Mayflash USB adapters. They have a bunch, with very similar names and features and I’m not really sure what the differences are. Specifically I own the “Magic-NS Lite” and the “Magic NS 2”. The only reason I bought the “NS 2” is because it supports the DualShock 3. As far as I understand it, these adapters have a built in Bluetooth receiver that pairs directly to the controller, and then on the USB side the host system sees whichever controller you have the adapter set to. For example, if you set the adapter to “Switch Pro”, you can pair a Sony DualSense to it, plug it into your Switch, and the console just sees a Switch Pro controller connected over USB. I prefer using these adapters for wireless controllers even if macOS can pair to the controller directly because I feel like there’s less latency. It seems like for some reason, controllers over Bluetooth natively on macOS have pretty bad latency. These adapters solve that problem completely.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Mayflash USB Adapters<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/macos-gamecube-controller/mayflashusb.jpeg" alt="The two mayflash wireless usb adapters." />
	
	</div>
</figure>

<p>For PlayStation stuff, the DualSense (PS5) controller essentially has first party support on Apple platforms - they even <a href="https://www.apple.com/shop/product/hsg32zm/a/dualsense™-wireless-controller">sell it in the Apple Store</a>. Since Sony hasn’t updated the button layout since the first <a href="https://en.wikipedia.org/wiki/DualShock">DualShock came out in 1997</a>, the DualSense works for anything PlayStation. It also works very well for Apple Arcade games and other official Apple Store games such as <em>Death Stranding</em> on macOS, iOS, and Apple TV. I do keep a DualShock 3 and a PSOne DualShock around.</p>

<p>For Genesis, I have one of the <a href="https://www.retro-bit.com/controllers/genesis/#console">officially licensed Retro-Bit controllers</a> which feels great. I got it back in the Catskull Electronics days when I was doing a lot of benchtop Genesis testing, so I shortened the cord on it from 10 feet to like 3. To get it connected over USB, I found an Arduino sketch I flashed to a Pro Micro (Leonardo) and wired and taped up a DB9 connector. I had both on hand, and it works great for what I need.</p>

<p>For NES and Game Boy (when I’m not playing on my Analogue Pocket), I have an A/V Famicom “dogbone” controller which is almost exactly the same as the controller that came with the top loader NES here in the US, but it has a way shorter cable. I also have an original NES controller for when I want to punish my hands. At some point I ended up with a controller port out of a broken NES, so I wired that up to an Arduino Pro Micro as well and it works great.</p>

<p>For Super Nintendo, I have a Super Famicom controller. Like the A/V Famicom controller, it’s about identical to the US SNES controller but the cord is shorter and the buttons are colored and not purple. I have an <a href="https://www.waveshare.com/rp2040-zero.htm">RP2040-zero</a> board running <a href="https://gp2040-ce.info/add-ons/snes-input">GP2040-CE’s SNES Input add-on</a>. I didn’t have a female controller port on hand, so I just stuck some wires into the controller connector and into a breadboard the RP2040-zero sits on. I should probably fix it. I also have an <a href="https://www.8bitdo.com/sn30-pro-g-classic-or-sn30-pro-sn/">8BitDo SF30 Pro</a> that I use quite often with basically everything. I like having a couple of extra buttons available to map to save states and fast forward which is how I personally enjoy unforgiving retro games when I don’t have the time to deal with evil random number generators and overall just kind of suck at video games in general. I’ve owned it for the better part of 10 years and I’ve gotten a ton of use out of it.</p>

<p>For Dreamcast and Xbox, I actually prefer to play on original hardware. I think there’s a 3-way tie for my favorite console between those two and the Nintendo DS. I have a custom Dreamcast I tried to make look like an OG Xbox with a black shell and a green power LED. Since the Dreamcast can do VGA output natively, I connect it directly to the monitor and speakers. I also built a <a href="http://usb4maple.wikidot.com">“USB4MAPLE”</a> adapter which allows me to use my Mayflash adapters on real Dreamcast hardware. During the pandemic I fully recapped an OG Xbox, installed a modchip and hard drive, and a sweet HDMI output mod so it actually stays connected to our main living room TV since my kids like to play it. On the odd chance I emulate either of them, I have an Xbox One Bluetooth controller that seems to work great on macOS. The Xbox controller also works very well for various Steam games on macOS.</p>

<p>For DS/3DS I <a href="/nintendo-announces-3ds-successor-3ds-plus.html">play on original hardware</a>. For GB/GBA I usually play on my Analogue Pocket, but sometimes I’ll use my retro gaming nook with the SF30 Pro. For Switch, I actually have an OLED Switch and sometimes play it docked to the family TV, but it’s been mostly commandeered by my kids, so I’ll usually play with my Switch Lite since I can keep it clean and nice. I have an ergonomic grip for it which I prefer to playing with joycons which are just too thin for me to use comfortably. I did buy a CRKD Nitro Deck but the rumble is actually hilariously bad and overall the product just feels cheap. For my kids, it’s a miracle since it projects the ports and offers a lot of protection over the raw switch + joycons. Man, those joycons are <em>really</em> not designed to hold up against kids. Even if you don’t abuse them, I think all 4 of ours have pretty bad stick drift.</p>

<p>For Nintendo 64, which has a very very unique and difficult to fully replicate controller, I use the official <a href="https://www.nintendo.com/us/store/products/nintendo-64-controller">Nintendo Switch Online controller</a>. It’s great. Built in rumble, fixed joystick, and it pairs perfectly with macOS. I just wish they’d come out with some funtastic color variants!</p>

<p>That just leaves one key console that has potentially the most iconic controller of all time, one that is still sought after, modded, and used by casual and esports players alike. Of course I’m talking about the Nintendo GameCube controller.</p>

<h2 id="options-for-using-a-gamecube-controller-on-macos-in-2026">Options for using a GameCube controller on macOS in 2026</h2>

<p>There are a lot of options for using a GameCube controller on modern platforms in 2026. Very few of them work at all on macOS, and the ones that do don’t work very well.</p>

<p>Probably the single most attractive option at this point is the <a href="https://www.nintendo.com/us/store/products/nintendo-switch-2-nintendo-gamecube-controller-120833/">Nintendo Switch Online GameCube Controller</a>. It seems to be essentially perfect. However, unlike the Nintendo 64 controller, it cannot be paired or used with macOS out of the box. There’s a <a href="https://github.com/RyanCopley/NSO-GameCube-Controller-Pairing-App">python based pairing app</a> but it doesn’t support rumble, which is of <em>vital</em> importance to me when using authentic inputs. <a href="https://github.com/isaacs-12/nso-gc-bridge">Another application</a> seems to support rumble on macOS, but it also looks a bit hacky to get working. I concluded that for me right now, the official Switch GameCube controller isn’t an option. I’m tempted to order one just to have on hand, but I’m frugal and $70 for a paperweight just cannot be justified. I actually emailed Mayflash support asking if they might add support for this with their adapters but they replied that their R&amp;D team was preoccupied with other projects.</p>

<p>I thought about just going for original hardware. I still have my childhood GameCube with the Digital A/V port. I used to have a <a href="https://insurrectionindustries.com/product/carby-v2/">Carby HDMI adapter</a> but my kids broke it, and even still I’d have to use some kind of HDMI to DVI adapter to get it working with my retro gaming nook. I also have a Wii with GameCube controller ports, but the story there is the same as the GameCube.</p>

<p>While I have quite a few GameCube controllers in various stages of decay due to years of use and abuse, a few years ago I had the incredible foresight to pick up a pair of the rereleased controllers Nintendo made for Smash Ultimate. They’re identical to the original but the cable is 3 meters instead of the original’s 2 meters. These were used a bit by me and my kids with our GameCube before the Carby was broken, so they’re still in like new condition.</p>

<p>But how to get an original GameCube controller to talk to macOS?</p>

<p>8BitDo actually sells a <a href="https://shop.8bitdo.com/products/mod-kit-for-original-ngc">“mod kit”</a> for the controller. It’s only available through their official store. At $26.98 shipped, it’s not a bad price really. My complaint is that it fully replaces the guts of the controller including the thumbsticks. It also doesn’t support the analog R/L trigger inputs, which isn’t absolutely essential to me, but if I’m jumping through this many hoops to use an official controller, it feels like a compromise not worth making. At that point, I feel I’d be better off just using one of the third party controllers that might already support macOS. Not to mention, the battery is charged with a special cable which like, I do not want to have to keep around for the rest of my life. The 8BitDo product page also only lists support as “Switch, Android” so it might not work on macOS at all anyways!</p>

<p>The obvious choice would be the official Nintendo USB adapter. However, that’s out of production, very expensive, and still doesn’t work on macOS! There are a lot of third party adapters which appear to be fully functional clones of the official adapter but still have the same limitation that they do not work with macOS. The most popular option is a clone produced by Mayflash known as the <a href="https://www.mayflash.com/product/W012.html">“Mayflash 4-port GameCube adapter”</a> (model number W012). This has a switch on it to select between Switch and PC mode. I read reports that the PC mode would work on my Mac, so I ordered one from Amazon for $18.99.</p>

<p>The day it arrived I locked myself in my office to quickly attempt using it. At first, it seemed like it would work! PC mode showed up on Mac and I was able to map buttons. But no matter what I did, I couldn’t get rumble to work. At this point my kids were about to break the door down, so I had to take a break. The next day, I resumed testing. The <a href="https://dolphin-emu.org/docs/guides/how-use-official-gc-controller-adapter-wii-u/#GCAdapterDriver">Dolphin docs</a> say to use an app called <a href="https://secretkeys.io/gcadapterdriver/">“GCAdapterDriver”</a>. It’s an app that runs on macOS that supposedly allows the official (and official-compatible) GameCube adapters to work. However, for the life of me, I couldn’t get it to do a single thing. Fortunately the application is open source, but it hasn’t seen an update in 5 years so I assume that something Apple changed in macOS since then broke it. Even on top of all of this, I couldn’t find any definitive source that said rumble would work.</p>

<p>I turned back to the Mayflash adapter, wondering if possibly a firmware update might make it work on macOS with rumble. Mayflash’s support pages are almost offensively vague, but they have a page for a firmware update for both <a href="https://www.mayflash.com/Support/246.html">Windows</a> and <a href="https://www.mayflash.com/Update/260.html">macOS</a>. The single mac version says it’s version “V1.04”, while the latest windows version says it’s “V10”. Both have identical notes which say “Compatible with MacOS (must update it on Windows ,however).” The macOS driver is distributed in an unsigned PKG which doesn’t feel great. Even after accepting the macOS gatekeeper warnings, it requires Rosetta 2 to run, at which point I nope’d out. Once installed, Rosetta 2 is very hard to uninstall and at this point it’s going to be discontinued next year. I tried to set up a Windows virtual machine but after letting it cook for several hours, nothing was working. Again, I didn’t even have a promise of rumble working at all even if I did eventually get the firmware update to work.</p>

<p>At this point, I was ready to give up and write it off as “not possible” until maybe the official Switch ‘Cube controller was supported by Mayflash or possibly macOS. Being a frugal cheapskate, I also <em>already own</em> all the hardware, including a $20 adapter that was now essentially e-waste to me.</p>

<p>However, like the legend of the Phoenix, I rose from the ashes. I refused to give up.</p>

<h2 id="the-solution">The Solution</h2>

<p>One thing I learned from some of the past controller adapter projects I’ve done is that the Raspberry Pi Pico’s <a href="https://en.wikipedia.org/wiki/RP2040">RP2040</a> is potentially the single best piece of silicon produced in the last decade, save possibly Apple’s own M-series chips. Unlike the other Raspberry Pi Single-Board Computers that run a full OS like Linux, the RP2040 is a microcontroller, like the ATMega328 of Arduino fame. You compile and flash firmware onto the chip to do one specific thing. The 2040 has some really incredible features that allow it to do native USB stuff very well. Potentially the best feature is its “programmable I/O” (PIO) that give it almost-FPGA like abilities to emulate hardware protocols that a standard microcontroller wouldn’t be able to do. A full Pi Pico dev board is $5, and the chip on its own is well under a dollar. My favorite RP2040 dev board is the “RP2040-Zero” which is tiny, has USB-C, and is $10 for a 3-pack on Amazon. I picked up a 3-pack a few years ago for my other controller projects, and wouldn’t you know it, I still had one more waiting for me to use.</p>

<p>Some basic googling turned up a project on GitHub called <a href="https://github.com/loopj/libjoybus">“libjoybus”</a>. From the readme, it describes itself as “An implementation of the Joybus protocol used by N64 and GameCube controllers, for 32-bit microcontrollers.” Apparently, it spun off another project <a href="https://github.com/loopj/wavephoenix">“WavePhoenix”</a>, which recreated the receiver for the GameCube WaveBird wireless controller. libjoybus can do both host and target operations. You can use it to build your own Joybus controllers from scratch (send inputs into a real console), or use it to build things you can plug original controllers and accessories into. It’s the host mode I want to use.</p>

<p>There are some excellent example projects using the RP2040. Most notable is one that <a href="https://github.com/loopj/libjoybus/tree/main/examples/pico-sdk/gcn-adapter-nintendo">fully implements the official USB GameCube controller adapter</a> (the thing my useless Mayflash thing is). And, more important for me, is one that implements a single port GameCube to USB HID adapter. From Wikipedia:</p>

<blockquote>
  <p>The USB human interface device class (USB HID class) is a part of the USB specification for computer peripherals: it specifies a device class (a type of computer hardware) for human interface devices such as keyboards, mice, touchscreen, touchpad, game controllers and alphanumeric display devices.</p>
</blockquote>

<p>Put simply, if a device supports USB HID, then no driver is needed on anything that supports the HID protocol, which in 2026, is virtually everything with a USB port including macOS, Windows, and Linux.</p>

<p>An example project that allows me to flash a RP2040 board I already own and <em>finally</em> use my GameCube controller on macOS felt too good to be true!</p>

<p>Well, it was about 90% of the way there, but unfortunately, not 100%. The issue, I learned, is that USB HID doesn’t support haptic feedback, or rumble, without an additional driver. This is probably why none of the other GameCube adapters I searched for supported rumble. The GCAdapterDriver attempted to implement a custom macOS driver using macOS APIs to support the custom Nintendo peripheral, but as I found, it didn’t work for me.</p>

<p>So the question became “What controllers does macOS support natively that have rumble?” One of the curious things about the Mayflash wireless adapters is that they have a “macOS” mode. When the adapter is in this mode, the computer sees that a Sony DualShock 4 (PS4) controller is plugged in. From experience, I knew that this controller had native macOS support and also supported rumble. So my task became clear: I needed to get the libjoybus example to pretend to be a PS4 controller instead of a USB HID device.</p>

<p>From years of experience, I know enough about how USB works to know that the USB governing body (the USB Implementers Forum) charges a license fee to companies who want to use USB and assigns them a “USB Vendor ID”. It’s very expensive, and I’m not sure if the price is even publicly known. From there, the vendor can build products at will and specify their own “Product ID”. Think of it like a domain name. I can buy the “catskull” domain from whoever manages the .net top level domain. From there, I can assign my own subdomain like “<a href="https://buttons.catskull.net">buttons.catskull.net</a>”. However, unlike domains, there’s nothing in the USB spec to verify a device that claims to be a Sony DualShock 4 controller actually is a Sony DualShock 4 controller. A device manufacturer would need to implement their own authentication if they were worried about devices masquerading as their own. In fact, back in 2014, FTDI rolled out a driver update that bricked clone chips that were using their Vendor and Product IDs without permission. It was a fiasco.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup></p>

<p>Also from years of experience, I know that I have no idea how these USB VID/PIDs are implemented. It mostly depends on the microcontroller. For example, I know that the ATMega32u4 used in the Arduino Pro Micro (Leonardo) supports custom USB VID/PIDs, and that the VID/PID that Arduino specifies when compiling the firmware are built into the IDE and, at least as far as I remember off the top of my head, cannot be manually specified, unless you are god-tier and know how to set up a custom board definition which I may or may not have done. Unlike the Teensy 2.0, which also used the ATMega32u4 but <em>did</em> allow you to specify the VID/PID. I don’t actually have enough experience with the RP2040 to understand how the VID/PID stuff works there, but it appears that we have full control over them. This is very useful to us, as we want to pretend to be a DualShock 4 controller so macOS knows what to do when we plug our adapter in. This is also how the existing libjoybus example can easily pretend to be the official Nintendo GameCube adapter since the VID/PID is known and the Switch will happily accept anything with those IDs.</p>

<p>So, with the knowledge that macOS has a built in driver for the DualShock 4 which supports rumble, and that the RP2040 can do whatever USB stuff it wants, I set up my USB sniffer, ordered a DS4 off eBay, and spent the next 4 weeks reverse engineering the USB protocol. Just kidding! I cloned the libjoybus repo, pointed Claude Code at the USB HID example, and asked it to make it pretend to be a DS4 instead of a generic USB HID gamepad. Twenty minutes later, it had some code ready for testing. “Holy crap, is this actually going to work?” I asked myself. How could this take less time than setting up a Windows VM? Thanks to a lot of hard work from the libjoybus developer, GP2040-CE, and a little bit from Claude, that’s how. Now the fun part - I needed to actually build the thing.</p>

<p>I could have simply cut the end off the GameCube controller and wired it directly into the RP2040-zero, but that is extremely lame. I do not condone destroying hardware no longer in production when things that are still being produced could suffice. I’d usually grab a controller extension cable since they’re cheap and plentiful, or even a raw female controller socket since apparently those are still being made as well, but I already had the Mayflash adapter sitting there and I didn’t feel like waiting a week for other stuff to show up. I was already high from the success I’d had. I opened up the Mayflash adapter and looked at the controller port PCB. It is just a simple 4 port breakout board with the controller pins nicely broken out into a header with some ribbon wires connecting the controller port PCB to the main PCB with all the components on it. As luck had it, the four ports were actually broken out into two 8 pin headers on opposite sides of the long narrow controller port PCB. I scored it in the middle and snapped it in half, leaving the first two ports still intact and functional, and with a nice two port breakout PCB ready to recycle for my custom adapter.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Not completely useless<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/macos-gamecube-controller/mayflash.jpg" alt="The mayflash usb adapter with two of the controller ports missing" />
	
	</div>
</figure>

<p>The next issue was that there needs to be a pullup resistor between the single data line and the 3.3v pin. The libjoybus readme recommended a 750Ω resistor, but the only resistor I had laying around was 100KΩ. Way too much resistance! I scrounged around in my electronics bin for any random PCBs I had that might have something in the ballpark that could work. Fortunately, back in the Catskull Electronics days, I made a really stupid decision. The long story short is that I made my own ATmega328p “Arduino Nano” board but with USB-C. This was when USB-C was still pretty new, and there weren’t any commercially available Arduino boards with USB-C, so it seemed like a decent idea. The mistake was that in order to get the boards made for a price that could even be somewhat competitive, I had to order like 500 which was way way more than I typically ordered when testing out a new product. I figured worst case scenario I’d design some other product that could use them. They also needed the Arduino bootloader to be flashed which was really annoying to do. In the end I only sold like 5 of them and I still have a bag in my closet full of 495 of them, just sitting there. I actually remember one guy bought one and I didn’t ship it for a few days so he cancelled the order and was like “btw these are sold on Amazon now for 1/4 of the price, you moron” to which I couldn’t help but to agree. Well, as fate would have it, those boards have a built in LED on pin 13 as any proper Arduino clone should have, and it just so happens that we used a 1KΩ resistor on it! The problem was that the resistor is very small, either 0402 or 0603, which is at the limit of what is possible for me to hand solder with an iron. Due to the dual layer design of the RP2040-zero, reflowing it on wasn’t really an option. I decided to give it a shot anyways, and if worst came to worst I’d go to the thrift and buy a VCR or some other e-waste to salvage a resistor from, or possibly try to make my own resistor with some graphite lead or something. Getting it off the Arduino was easy thanks to my cute little Miniware MHP30 USB-C hot plate. I no longer have a nice soldering area, so I ripped apart a cardboard box, laid it on top of a pile of Dr. Pepper cans, plugged the hot plate in, set the Arduino on it to warm up, and then just picked it off with some tweezers. The resistor is so small that I almost lost it immediately. Seriously, those things are so small if you sneeze they’ll just vanish into thin air! If you know, you know.</p>

<p>One of the most useful tools I’ve purchased is a spool of <a href="https://en.wikipedia.org/wiki/Wire_wrap">wire wrapping wire</a>. A single $10 spool is a lifetime supply. It’s a little thin for what I’d normally use, and single stranded wire has pretty bad mechanical strength. It will snap if you pull too hard. But stripping it is super easy, I just pinch with my fingernails and pull the end off, then trim to length with cutters. I’ve done it enough that it’s pretty easy for me now. I got all 4 wires connected on both ends pretty easily, so now it was time for the hard part. Luckily the RP2040-zero has a GPIO right next to the 3.3v pin, so I could just solder the resistor in between the two pads. Looking at the photo now, it almost looks like there might be surface mount pads for both pins as well, but I didn’t notice that at the time. The hard part doing this is keeping the wire from falling out while you heat up both sides and the resistor in place as the solder cools down. By some miracle, I was able to get it taken care of! It’s not my proudest work, that would go to hand reflowing a BGA64 chip with only hot air, but it’s up there.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Finger for scale<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/macos-gamecube-controller/soldering.jpg" alt="A closeup shot of a small surface mount resistor soldered to a PCB" />
	
	</div>
</figure>

<p>I plugged it in, flashed the code, plugged it in, and suddenly my computer recognized that a DualShock 4 was plugged in! Holy cow! However, none of the buttons worked. Was this the moment it would all fall apart? Like Icarus, had I flown too close to the sun, drunk on my own AI psychosis? The readme for the libjoybus example says to connect the data pin to “any” GPIO, which I took to mean the code would somehow scan for data on all the GPIO. What it actually meant was connect the data pin to GPIO 12, but that you could use any other GPIO as long as you updated the code. Since I was using the GPIO nearest the 3.3v pin, I updated the code to use that one, rebuilt and reflashed, and suddenly, it worked! Even vibration worked!</p>

<p>“No way, no absolute way…” It was too good to be true. Still skeptical, I booted up Dolphin, mapped the controller, and had a test. The only game I had backed up was <em>Disney Extreme Skate Adventure</em>. If you don’t know, it’s literally a reskin of <em>Tony Hawk’s Pro Skater 4</em> but with Disney characters and levels. All the tricks are unique to the characters, so I laughed my butt off doing the most insane grinds with Woody as I felt the incredible rumble motor <em>fully working</em>. I would have thought there would have been some nuance or bug with some of the analog inputs, but as far as I can tell, there are none. It works perfectly. I’m not a professional smash player, so I’m not sure if there are some nuances I’m missing, but as far as I’ve been able to tell everything works perfectly.</p>

<p>To finish it off, I just wrapped the wires around the PCB and taped the RP2040-zero to it with some sweet silicone tape. It’s really stretchy and only sticks to itself, no adhesive to gum everything up. I’m half tempted to design a board to make it easier for others to build, or possibly even sell a premade adapter, but at this point I’m having too much fun just using it to bother. That’s one regret I have from Catskull Electronics. I was always so busy with literally everything else that I never really had any time to actually use any of the gear I was making.</p>

<h2 id="the-end">The End</h2>

<p>And that, folks, is the long tale of how I got a GameCube controller working on macOS. I am so thankful to the libjoybus project for allowing this to happen. It’s exciting and a bit scary that I was able to point Claude Code at the problem and it worked it out. I would have spent weeks trying to figure out all the random data needed to get the RP2040 to do DualShock 4 stuff, but Claude went through it like a hot knife through butter.</p>

<p>If you’re interested in building your own, I <a href="https://github.com/loopj/libjoybus/pull/35">submitted a pull request to libjoybus</a> with my firmware and instructions.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>FTDI’s USB-serial chips became ubiquitous by 2014, thanks in no small part to the official Arduino boards using them. However, to save money, a lot of clone Arduino boards (which in itself wasn’t strictly illegal since Arduino was kind of open source) started using clone FTDI chips as well. In fact, to an end user, it wouldn’t even be totally clear if their device had a real or a clone FTDI chip in it. To combat this, FTDI pushed a driver update through Windows Update that would do a bit of additional handshaking to determine if the chip it was talking to was authentic or not. One of the features of the FTDI chips is that they have an internal EEPROM that can be flashed with a custom Product ID. This was useful to device manufacturers that wanted to have a custom driver to talk to their custom chip, or have it show up in Windows as something other than an FTDI USB-serial chip. The updated driver would set the Product ID to something other than the default if it detected it was counterfeit, which resulted in the chip being bricked. It actually wasn’t fully bricked, but it couldn’t be used with the official FTDI driver. On principle it doesn’t actually feel that wrong to me since cloners were illegally profiting off of FTDI’s name and software. The problem is that the only people who suffered the consequences were end users. One day their device worked fine, then Windows updated the driver, and suddenly it stopped working with no clear explanation or warning about why. This caused the cloners to simply stop pretending to be FTDI chips and use their own Vendor and Product ID with their own driver. Today, FTDI USB-serial chips are no longer common in cheap hardware, instead using the new Chinese chips such as the WCH ch34* series which are cheap and excellent but lack some of the advanced features of the FTDI chips such as the programmable EEPROM. In fact, as an intern, I had a project where I was writing software to support a new development board that had a nice 4-port FTDI USB-serial chip on it. It was essentially 4 USB-serial chips in a single package with a built in USB hub. One of the ports was used for serial communication, and the other three were used to drive GPIO of a microcontroller on the development board. Part of my project involved writing a serial number into the FTDI chip’s EEPROM for company inventory tracking. I’m sure it never really mattered but it was a lot of fun to build out and in hindsight, I’m very proud of the job I did on it. I think I was more impressive to some of my coworkers than I gave myself credit for! <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>catskull</name></author><category term="macos" /><category term="nintendo" /><category term="gaming" /><category term="diy" /><category term="hacks" /><summary type="html"><![CDATA[TL;DR: I made a GameCube to USB controller adapter that works on macOS including rumble. You can build your own.]]></summary></entry><entry><title type="html">A Kanban of My Own</title><link href="https://catskull.net/a-kanban-of-my-own.html" rel="alternate" type="text/html" title="A Kanban of My Own" /><published>2026-07-02T20:04:34+00:00</published><updated>2026-07-02T20:04:34+00:00</updated><id>https://catskull.net/a-kanban-of-my-own</id><content type="html" xml:base="https://catskull.net/a-kanban-of-my-own.html"><![CDATA[<p><strong>TL;DR - I built my own kanban and <a href="https://catskull.net/ohnoban">you can play with it</a>.</strong></p>

<p>About two years ago <a href="/index-card-productivity.html">I wrote about how I use 3x5 index cards for productivity</a>. It was a lot of words about basically saying I use a little paper to write down a daily todo list and save the cards in an archive. I also designed a neat little monthly calendar to print out on a 3x5 card and keep on my desk. I still use that daily.</p>

<p>Soon after, <a href="/index-card-journaling.html">I designed a daily bullet journal to be printed on the back of my calendar card</a>. I still use that daily as well. Just a few words about my day. Some times it’s simply “work”.</p>

<p>Since then, I <a href="/resigning.html">resigned from my job</a>, <a href="/a-few-thoughts-about-teaching-school.html">spent 6 months substitute teaching public school</a>, and got a new web development job. My daily productivity workflow needed to change to adapt to the new workplace. Before, I was mostly working independently on fewer long-term projects. Now, I’m daily collaborating and iterating with a small team. Put simply, I have my fingers in more pies now.</p>

<p>I never had any formal project management training or education. I mostly just learned on the job and adapted to whatever system was in place, not feeling a need to give it a name. However, in retrospect I now realize that by far the most productive times of my career have been when I was able to work off a pure kanban board. A pile of work “to do” is very motivating to me. I love the principles of kanban, as I understand them. Only do one thing at a time. It’s not done until it’s merged and deployed. Back then it was trello. It was simple and worked very well. Later we moved to more formal planning sessions, “groomings”, and pre-assigned issues which still worked okay but felt limiting to me. I’d rather explore the question “What <em>could</em> I get done?” than “What do I <em>have</em> to get done?”. It feels like a carrot versus a stick.</p>

<p>The issue for me is the project management software we <del>choose</del> are forced to use. I won’t name names, but it labels itself as “Software to replace all software”, which not only violates the <a href="https://en.wikipedia.org/wiki/Unix_philosophy#Do_One_Thing_and_Do_It_Well">Unix philosophy of “do one thing and do it well”</a> but is also an affront to my very morality. I can promise you that this “everything software” truly does <em>nothing</em> well. Not one thing. It is extremely slow. I actually just timed it and it takes <em>15 full seconds</em> to load our current sprint. It is extremely limiting to my workflow. I hate it.</p>

<p>The rest of my team feels less passionately than I do on this subject, and honestly I’m happy to yield my personal feelings in the spirit of cooperation. It’s more important to me to get along with people that are doing things well enough than to seek absolute perfection in every preference I have. Honestly, I use Safari and I’d be willing to bet the experience is much better in Chrome but I’m just not going to use Chrome, sorry. I’ll work around slow loading times.</p>

<p>For the last few months at The Bullpen Inc., we’ve been very interested in pure kanban. We’ve had a lot of discussions about productivity workflows. This dovetails with the current zeitgeist around “AI fatigue” which seems to me to largely be caused by trying to do too much at once. There’s a real temptation to spin up 20 agents working on different things all at once. Perhaps that works for some people, but I’m just not smart enough for that. I can do a single thing at once, or perhaps 1.5 things poorly. It doesn’t really matter what tools I use. I <a href="/re-opinions-about-vibe-coding.html">truly believe that AI tools have not introduced any fundamentally “new” problems in software development.</a> It was entirely possible 10 years ago to try to do 20 things at once, get totally overwhelmed, and flame out hard. Maybe the temptation is new, maybe the productivity hype is new, but the fundamental issue is not new.</p>

<p>Sam in particular seems to be very concerned with two questions: “What am I doing right now?” and “What should I be doing right now?”. The harder those two questions are to answer, the more hopeless your situation becomes. If your goal is to get things done, things need to be as easy to do as they possibly can be. We can still do very hard, complicated, and long things by breaking them down into very small actionable items. “Rome wasn’t built in a day.” This is where a system like kanban truly shines for me. Individual things queued up that can be done one at a time in a sane and orderly manner.</p>

<p>While I can’t force my team to become kanban zealots, I can supplant my team workflow with a kanban of my own. Ideally, something that doesn’t take 15 seconds to answer either of those questions.</p>

<p>I looked around at some basic kanban software a bit. The simpler the better. Please no sparkle emojis. Nothing that’s going to send me marketing emails. Nothing that is going to change the UI every 3 months. I’m by no means an expert on the current kanban software landscape, but I wasn’t able to find anything that scratched my itch. So I did what any bad engineer does - I built it myself.</p>

<p>I call it “ohnoban”. It’s fun to say. I named it after Taiichi Ohno, the absolute GOAT and Toyota engineer who developed the kanban process. I truly believe this innovation is what made Toyota into the global powerhouse it still is. It’s just <em>the best</em>. Kanban is practically my religion. The tenets are so simple, a bit restrictive, but empower so much great work to happen.</p>

<p>My kanban only has four columns: backlog, doing, done, and archive. Backlog is almost not used at all other than keeping a personal list of things I probably won’t ever do. When I pick up a new ticket at work, I make a card in “doing” that links to the work system, any notes I need, and eventually a link to the pull request. “Done” is usually empty. It doesn’t mean the work is totally done, it just means I’m done with it for now. For example, if something is waiting on a code review, I will put it in “done” while waiting. Once the code is merged and deployed, I move it to “archive”. When I’m working fast, I might have a few things in “done” and a single thing in “doing”, but if there’s a lot of back and forth things might juggle around quite a bit. That’s okay, for me.</p>

<p>Technically, I took a minimal approach. I could probably talk more about things it <em>does not</em> do than things it does do. There is absolutely no cloud storage at all. It’s all local to the browser. There is no collaboration. I can’t share my board with you. There’s no way for you to leave a comment for me. It’s entirely offline, private, personal. There’s no sparkle emoji. There are no analytics.</p>

<p>I believe that modern web browsers are the most complicated and high quality software that has ever been created and I want to lean on them as much as I possibly can. I hate installing node modules. I don’t want to run a build pipeline. It should just be as plain as possible. Turns out, in 2026, you can actually build a functional kanban without installing 18 CVEs. It only has a single dependency, <a href="https://github.com/markedjs/marked">Marked.js</a> which is downloaded to the repo and included as a regular JS asset. No bundle, no pack, no bloat. I like markdown and it works very well for what I need. There’s no wysiwyg, it’s just a textbox you can type markdown into. Turns out, I love the blind speed of raw text after having to fight with the latency every wysiwyg has. It just gets out of my way! It’s fully keyboard navigable. Everything is stored in local storage, which isn’t great, but it also works quite well. I did build in a JSON export/import but don’t really use it. Yes, this means that from one device to another I can’t track my work. But I only really use it for my job so syncing across devices hasn’t been something I’ve cared about. I have some fun ideas about using webauthn and passkeys to encrypt the board data to store in the cloud, but I just haven’t been chuffed to deal with it.</p>

<p>Since my initial MVP, I’ve added surprisingly little. I felt like I wanted tags, so I added them. I wanted a timestamp for when I create and update cards. I wanted to add a background image. The biggest feature I’ve added is a simple notes section. Functionally it’s exactly the same as a card - a title, a body, and tags. Just the UI is more “notes” style.</p>

<p>I keep the app pinned to my dock so it renders kind of like it’s own app, which is pretty slick. It’s been about two months since I built it and I use it every day to track my work. I feel like the proof is in the pudding: I currently have 53 cards in the “archived” column which represents quite literally every single thing I’ve done at work (and a few at home) in that time.</p>

<p>Compared with someone’s crafted Obsidian vault with every life data possibly imaginable, it’s hilariously lacking. Compared to a 3x5 notecard with that day’s to do list, it’s quite nice for me. Sure, there are many like it, but this one is mine.</p>

<p>It’s a kanban of my own.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>ohnoban<span class="img-icon">┅</span></figcaption>
	
	<img src="/assets/images/posts/a-kanban-of-my-own/ohnoban.jpg" alt="ohnoban screenshot" />
	
	</div>
</figure>

<p>Further reading:</p>
<ul>
  <li><a href="https://samwarnick.com/blog/kombating-fatigue-with-kanban/" target="_blank">Sam Warnick - Kombating Fatigue With Kanban↗</a></li>
  <li><a href="https://www.lucasfcosta.com/blog/scrum-versus-kanban" target="_blank">Lucas F. Costa - You don’t need Scrum. You just need to do Kanban right.↗</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Kanban" target="_blank">Wikipedia - Kanban↗</a></li>
  <li><a href="https://en.wikipedia.org/wiki/Taiichi_Ohno" target="_blank">Wikipedia - Taiichi Ohno↗</a></li>
</ul>]]></content><author><name>catskull</name></author><category term="productivity" /><category term="kanban" /><category term="ohnoban" /><summary type="html"><![CDATA[TL;DR - I built my own kanban and you can play with it.]]></summary></entry><entry><title type="html">Vices</title><link href="https://catskull.net/vices.html" rel="alternate" type="text/html" title="Vices" /><published>2026-06-03T22:36:37+00:00</published><updated>2026-06-03T22:36:37+00:00</updated><id>https://catskull.net/vices</id><content type="html" xml:base="https://catskull.net/vices.html"><![CDATA[<blockquote>
  <p>A vice is a practice, behaviour, habit or item generally considered morally wrong in the associated society. In more minor usage, vice can refer to a fault, a negative character trait, a defect, an infirmity, or a bad or unhealthy habit.</p>
  <ul>
    <li><a href="/wiki#vice">Wikipedia</a></li>
  </ul>
</blockquote>

<p>One of my friends was telling me that his team makes fun of him for using light mode in apps. Shortly after, I was browsing some Cloudflare docs and noticed that they store a <code class="language-plaintext highlighter-rouge">preferred-color-scheme</code> URL parameter which, besides being awful engineering, could potentially dox someone for their theme preference. I joked we might as well store all our dirty secrets in URL parameters which got me thinking about my own vices.</p>

<p>Here are some of my vices.</p>

<h3 id="dr-pepper">Dr. Pepper</h3>
<p>I drink way, way, way too much Dr. Pepper. I’m up to like 4 cans a day which is a ton of sugar and can’t be very healthy. I’ve always been a soda drinker but it was usually only one maybe two a day, but when I was substitute teaching school I got in a bad habit. Some of the schools actually had soda fountains in the teacher’s lounge with Dr. P on tap and I would just chug it all day. When you’re getting paid like $50 for a day’s work it feels like I should be taking full advantage of every perk. When I started software development again full time I guess my intake just stayed the same. I drink it room temperature which I think makes it easier to chug and less refreshing, I think if I cooled it I wouldn’t drink as much, but I used to have a mini fridge in my office and it was so loud I got rid of it.</p>

<h3 id="food">Food</h3>
<p>I don’t and haven’t ever struggled with weight, but I have pretty bad food hygiene. Most days I eat nothing all day until late afternoon or early evening. I guess I could rebrand and call it “intermittent fasting” since that’s “cool”, but it’s really just laziness. Then when I do eat, I just grab whatever like a slice of bread or a banana. I think I need more nutrients.</p>

<h3 id="screen-time">Screen Time</h3>
<p>I’ve gotten in a bad habit with YouTube Shorts. During the pandemmy I got into TikTok but eventually I deleted the app. A few months ago I started getting sucked into short form video on YouTube. It’s just lame. But after a day’s work my brain feels kind of cooked and zoning out for an hour or two (or three or four) is just something that is easy to do. Generally speaking I’m probably too addicted to my phone other times, checking Slickdeals, Discord, Feeed, Hacker News, etc. I wish I read more. I play some amount of video games but honestly it doesn’t come naturally to me so it feels more wholesome. I play the games with the intent of beating them and after I do, I write up a short review. You can read my <a href="/games-i-played-in-2024.html">list from 2024</a> or <a href="/every-game-I-beat-in-2025.html">from 2025</a>.</p>

<h3 id="lack-of-mindfulness">Lack of Mindfulness</h3>
<p>I don’t really get super deep into thought very often. I’m generally closed off to new ideas and approach them with skepticism and suspicion. I’m biased <em>against</em> new things. Maybe that’s just age.</p>

<h3 id="sedentary-lifestyle">Sedentary Lifestyle</h3>
<p>I don’t get enough exercise. Like almost none. Again, I’m not physically <em>unhealthy</em> but I also don’t think I’m super <em>healthy</em>. I used to go on walks more and I like that, and I think movement helps get my mind primed which helps with the mindfulness. A few years ago I started doing some basic weight lifting and I actually saw pretty immediate results. It is hard to find the time to exercise but some amount of basic exercise and situps and pushups feels achievable in my home office. We’ve decided the Bullpen HQ will have a gym with a rowing machine so maybe one day it will be easier. Now that my kids are out of school for the summer, I really have to hide in my office. If they see me, they ask me for things and I refuse to be rude to them, but really my priority focus needs to be work during the day. We’re also getting into the days where it’s breaking 100°F (38°C) and will stay in the 90’s from sunup to sundown which makes me want to go outside less.</p>

<h3 id="fidgeting-with-my-beard">Fidgeting with my beard</h3>
<p>I compulsively play with my beard. I don’t think it’s the ultimate social <em>faux pas</em>, but it’s kind of strange. Most of the time when I’m alone it’s not a problem but I also do it in public a lot. I don’t think it’s super sanitary. I’ve always done this in some form.</p>

<h3 id="the-end">The End</h3>

<p>That’s all I can think of right now. I think writing these down and putting in even the slightest amount of effort will go a long way. Should I set a goal to drink no more than two cans of Dr. Pepper a day? Should I start being more disciplined and exercise every day? I’m not sure if I can or even really want to pull it off. Maybe I need to find a good book I can read while I’m laying with my kids as they fall asleep. If you have any tips on very small, incremental changes I could make or that have worked for you, please let me know!</p>]]></content><author><name>catskull</name></author><category term="meta" /><category term="lore" /><summary type="html"><![CDATA[A vice is a practice, behaviour, habit or item generally considered morally wrong in the associated society. In more minor usage, vice can refer to a fault, a negative character trait, a defect, an infirmity, or a bad or unhealthy habit. Wikipedia]]></summary></entry><entry><title type="html">Quantum Fiber with OpenWrt on a GMKtec NucBoxG9</title><link href="https://catskull.net/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9.html" rel="alternate" type="text/html" title="Quantum Fiber with OpenWrt on a GMKtec NucBoxG9" /><published>2026-05-11T19:55:15+00:00</published><updated>2026-05-11T19:55:15+00:00</updated><id>https://catskull.net/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9</id><content type="html" xml:base="https://catskull.net/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9.html"><![CDATA[<p><a href="/openwrt-centurylink-fiber-mikrotik.html">Back in January 2025, I got OpenWrt running on a MikroTik Hex S and detailed the setup process for CenturyLink fiber</a>. Since then a few things have changed.</p>

<h2 id="the-backstory">The Backstory</h2>
<p>The short story for my neck of the woods (Southern Utah) is that originally, we had a local DSL provider named “Qwest”. You can still see random Qwest logos on some of the infrastructure around here. <a href="https://en.wikipedia.org/wiki/Lumen_Technologies#2010_merger_with_Qwest" target="_blank">According to Wikipedia↗</a>, in 2010 Qwest merged with CenturyLink. From that point on, CenturyLink has been one of two ISPs in my area and is the only one I’ve ever used. They’re “not great, not terrible” generally speaking. Since I’ve moved back to St. George about 5 years ago, I’ve happily paid for their gigabit fiber and it’s really been quite reliable. I really can’t recall a single outage.</p>

<p>Then, as businesses do be doing business, CenturyLink rebranded themselves as “Lumen Technologies” and also rebranded their residential fiber to “Quantum Fiber”. It seems like the only real reason you’d change the name of an established business is to distance yourself from negative brand impressions. Imagine if Nike changed their name. I’m sure there were legal shenanigans as well, such as maneuvering to get out of “price for life” guarantees they’d sold, but maybe that’s just overly cynical. Then, in 2025, Lumen sold “95%” of Quantum Fiber to AT&amp;T. I’m not entirely sure how this got past the FCC and the FTC, but it did. Long story short: I now have AT&amp;T internet. I don’t feel great about that, but it’s not like the track record was pristine before. I love the idea of municipal fiber or at least small, locally owned ISPs, but apparently we’re living in some form of private-equity ultra-conglomerated late-stage capitalism so it’s just the way things will be until the market can outcompete conglomerates. I personally have optimism that the technology will progress to allow more competition, but I don’t have any strong arguments there.</p>

<p>All things said, my service has been reliable, a decent price: $75/month that is now somehow $80/month for more-or-less symmetrical 1 gig fiber. The other option in my neighborhood offers 1 gig fiber as well for $75/month “price for life” so it’s about sixes either way.</p>

<h2 id="the-bufferbloat-problem">The Bufferbloat Problem</h2>
<p>I’d been running the MikroTik router for a while, and it was really “fine”. It seemed like it limited my bandwidth to just about 600 Mbps up/down, which I deemed “okay”. But it only has an 880MHz MediaTek MT7621A which is pretty anemic on a busy home network. I’ll have kids online gaming, multiple streams running to TVs and tablets, HomePods streaming music, and on top of that I work from home full time and really just need a “balls to the walls” home internet connection. Even infrequent quality issues on a work call can really look unprofessional and limit my ability to do my job to my full abilities. I wanted something better. With summer just around the corner where my network would be heavily used, I decided the time to improve it was now.</p>

<p>It finally came to a head one day when I decided to run some <a href="https://en.wikipedia.org/wiki/Bufferbloat" target="_blank">bufferbloat↗</a> tests using <a href="https://www.waveform.com/tools/bufferbloat" target="_blank">Waveform’s browser-based tool↗</a> and I got a solid <strong>F</strong>. I investigated a few options in OpenWrt. The best solution is to use the <a href="https://openwrt.org/docs/guide-user/network/traffic-shaping/sqm" target="_blank">Smart Queue Management↗</a> package to run the “cake” queuing discipline. Look, I don’t fully understand what all this means or how it works in the nitty gritty, but I understand it’s just the modern Linux solution to the bufferbloat problem. Have you ever been downloading a large file at your full ISP bandwidth and noticed your internet seems to drop out? That’s bufferbloat. More subtly, if you’re using ping-intensive applications like online gaming and video conferencing, and someone starts a big download like an OS update, HD video stream, or game download, those services’ ping will suddenly spike and degrade performance until, just as magically as it appeared, the issue vanishes when the download finishes. Running an SQM algorithm like cake will reserve a chunk of your <em>actual</em> ISP download/upload speeds and <em>never use them</em>. It literally decreases your maximum download speed in order to make sure the upstream queue (ISP) never fills and can completely eliminate the bufferbloat problem. If you live alone or only have a single device using the network at a time, this is all probably irrelevant. But if you are like the majority of modern households with multiple people using multiple devices to do almost everything all at once, it can be a real problem. Which is why sacrificing ~5% of your theoretical ISP limit is a fair tradeoff. However, all this queue management stuff runs directly on the CPU, so the CPU becomes the real bottleneck when trying to run some form of queue management. Most consumer networking hardware includes only the bare minimum specs to support whatever label marketing needs to slap on it such as “Gigabit”. My MikroTik just didn’t have the CPU overhead to reliably run any queue management without absolutely tanking my network speeds.</p>

<h2 id="the-router">The Router</h2>
<p>I spent the better part of an afternoon searching for a great standalone router. I don’t want or need my router to have any wireless hardware on it. I use a set of dedicated mesh APs and I want to be able to swap them out without my actual network going down. I just want the separation of concerns, I guess. It feels much more robust. Initially I was leaning towards one of the Banana Pi router boards but they were all out of stock and the ones that existed were just not worth the inflated prices. I thought about building some kind of small form factor PC but that felt out of my price range. I was looking to spend around $150-$200 and I wanted the most bang-for-buck possible. Things like 2.5GbE+ were desirable for future proofing, if possible. I almost pulled the trigger on a GL.iNet Flint 2 but I just really really did not want to pay for something with WLAN hardware, and at $169 it just felt overkill. The issue is there’s not a great middle ground between the bare-minimum marketing-approved hardware and totally overkill “prosumer” gear like Ubiquiti. Heck, even the entry level Ubiquiti EdgeRouter X has the same CPU as my MikroTik! There really wasn’t a great single option.</p>

<p>I was eventually led to the “mini PC” space. These are pretty popular right now, and seem to be about a dime a dozen with a new model coming out weekly. However, depending on the model, they can be attained for my target price range. Specifically, mini PCs running the Intel N150 platform are all the rage, and they generally seem to be great bang for buck and have only 6 watts of idle power consumption which is acceptable for an always-on appliance. Most even have dual 2.5G NICs. An important limitation for my selection was something with <em>Intel</em> NICs, not Realtek. I just don’t trust Realtek NICs. I’m sure for others they’d be fine but I wanted Intel!</p>

<p>The “NucBoxG9” from Shenzhen-based GMKtec was released some time in early 2025 and is actually billed as an “NVMe mini NAS”. Its standout feature is 4x M.2 NVMe slots that you’re supposed to fill up with SSDs. However, actually doing so is a thermal nightmare, <a href="https://www.jeffgeerling.com/blog/2025/almost-perfect-mini-nas-my-mini-rack/" target="_blank">most detailed by the venerable Jeff Geerling↗</a>. For my use case, I don’t care about using SSDs at all. The built-in 64GB eMMC storage is way more than an OpenWrt install would ever need. Most notably, it has two 2.5 GbE Intel i226-V NICs.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>I'll buy it at a high price<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9/ebay.jpg" alt="ebay listing of the gmktec g9" />
	
	</div>
</figure>

<p>It seems that, at the time of writing, it’s actually out of stock now and unproduced, so unfortunately if you’re hoping to follow along exactly you might be out of luck. However, I’m sure there are plenty of other N150 based mini PCs that would be equally suitable! I was able to score an open box unit on eBay for $170 (before tax) after a bit of back and forth with sending an offer. It arrived within a few days and I had it up and running quickly. It comes with a USB-C power adapter that outputs a constant 19 volts always. That is a huge spec violation and would very likely result in ruining whatever you plug it into that isn’t the NucBoxG9. Seriously, what were they smoking? If you’re going to do that, just use a barrel connector. I actually think it should be illegal. Fortunately, there’s another USB-C port that supports the regular USB-C power delivery stuff so I am powering mine with an Apple 96W USB-C power adapter. I actually cut the cord off the cursed power adapter right away and threw it in the trash because there’s a good chance one of my kids would try to plug it in to their iPad or Nintendo Switch. Again, <em>what were they thinking</em>.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Violate the spec, get the guillotine<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9/IMG_4942.jpeg" alt="the sketchy power adapter with it's cable cut" />
	
	</div>
</figure>

<h2 id="x86-openwrt">x86 OpenWrt</h2>
<p>OpenWrt started back in 2004 after Linksys was pressured into releasing the GPL’d source for the WRT54G router. Up until a few years ago, running OpenWrt on x86 just didn’t make a ton of sense and was, generally speaking, total overkill for any consumer setup. Heck, it still really is overkill most of the time. But Intel really knocked it out of the park with the N100/N150 and systems based on that platform have become ubiquitous in 2026. I’m happy to report that, more or less, x86 is now a first-class citizen in OpenWrt.</p>

<p>The one snag is that there’s no “installer”. Most Linux distros ship an installer ISO that can be booted to install the actual distro. OpenWrt just ships the raw filesystem ISO. You’re expected to just <code class="language-plaintext highlighter-rouge">dd</code> it onto your storage medium directly. Feels a bit clunky, but works pretty well. However, the G9’s eMMC is internal and soldered down, so I’d have to flash a bootable drive with some distro and then do it all manually. It’s really not that bad, but I decided to make my own bootable OpenWrt installer.</p>

<p>Alpine Linux is pretty cool and pretty small. It also has a great paradigm of “overlays” to make custom images based on the Alpine default. I decided to vibe code up an Alpine based OpenWrt installer. I won’t go too much into the details but it’s really just Alpine Linux, the latest OpenWrt image, and a bash script all bundled into one ISO that can be flashed and booted from any USB drive. Ideally it would be totally headless, but I didn’t make it that far before the G9 arrived and I haven’t been chuffed to work on it at all since. You can <a href="https://github.com/catskull/openwrt-x86-installer" target="_blank">check out the repo on GitHub↗</a> if you’re interested.</p>

<h2 id="the-migration">The Migration</h2>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Test bench<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9/IMG_4941.jpeg" alt="the g9 on my desk" />
	
	</div>
</figure>

<p>I wanted minimal downtime as I switched out the routers. I had Claude ssh into my existing router and create a bash script with all the current config. It doesn’t seem like there’s a great way in native OpenWrt to fully back up all configs for a system transfer like this, at least not that I’ve found yet. Claude was also able to help me prune the generated restore script for things that were redundant or unused. I plugged the G9’s WAN into my LAN, and the LAN port into my MacBook and ran everything over ssh. It “just worked”. Then, I opened my server closet, unplugged the MikroTik, plugged in the G9, and everything came back online within a few minutes. No finagling! I couldn’t really believe how smoothly it all went. Like everything, it’s still a work in progress. Better automated backups and redundancies is an ongoing project.</p>

<p>From there, I did some additional installation and configuration of the SQM package. It needs a few runs with the Waveform bufferbloat tool to dial in the bandwidth settings exactly, and your network needs to be relatively quiet when doing it, so I had to wait for a more opportune time. Eventually I got things dialed in very well, and <a href="https://www.waveform.com/tools/bufferbloat?test-id=1e0e16c0-79a7-41fa-94cd-af42a8b749f2" target="_blank">now have an A+ score↗</a>.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>A New Home<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/quantum-fiber-with-openwrt-on-a-gmktec-nucboxg9/IMG_4943.jpeg" alt="my network cabinet" />
	
	</div>
</figure>

<h2 id="shaka-when-the-walls-fell">“Shaka, when the walls fell”</h2>
<p>This all worked very well for about a month! However, yesterday (Mother’s Day in the USA) my internet randomly went down.</p>

<p>I tried the usual. Reset the ONT. Checked for OpenWrt updates. Rebooted the WAN connection. Nothing! I know that there are some kind of behind the scenes infrastructure upgrades happening with the Quantum Fiber rollout and further complicated by the AT&amp;T buyout, and I’ve feared this will break my amazing “bare metal” OpenWrt directly to the ONT setup for a while. The problem is there’s no way to get reliable information from the ISP. You can search through Reddit threads of people from all over the country talking about their specific issues, but it seems like the actual infrastructure is highly dependent on your specific area. A solution from Oklahoma isn’t necessarily the solution for Utah.</p>

<p>I checked for an outage in my area, but their website said there was none. After a quick Google, it seemed like Quantum Fiber was removing PPPoE in favor of IPoE (which is apparently just their way of saying DHCP). PPPoE is some relic of the dial-up internet days, IPoE just means regular old modern networking via DHCP. I flipped my config to see if DHCP would fix it. No dice. Neither DHCP nor PPPoE could connect to my ISP.</p>

<p>This is where I made a mistake. What I <em>should</em> have done is touched grass. But I work from home and really needed to have a working internet connection at 9AM Monday morning. What I did was call support. Huge mistake.</p>

<p>Thankfully, they answered pretty quickly and their offshore agent informed me my account had been migrated from PPPoE to IPoE and that they’d need to schedule a service visit to upgrade my modem to support IPoE. It sounded authoritative, but the earliest appointment wasn’t until Tuesday morning. It was the best option I had though, so I scheduled it and the agent was happy to get off the phone.</p>

<p>I decided to keep troubleshooting on my own, hoping to get something working before Tuesday. I called support again to see if there was anything on their end they could do. I suspected perhaps my router’s MAC address needed to be allowed to talk to the ONT? Still, it didn’t explain why it worked fine until randomly on a Sunday it did not. During this second call, I was informed there was actually an outage in my area. The support rep told me they couldn’t do anything during the outage and hung up. Checking their support page again, it showed an outage. For some reason I found their outage report to be suspicious but if the ISP is telling me it’s not going to work, I don’t think there’s anything I can do to make it work.</p>

<p>The next morning I checked the support page at 8AM. Outage still reported, so I packed up and headed to my Mom’s house to work for the morning. Ironically, she also has Quantum Fiber but hers was working fine. After a few meetings, I checked again and the outage was reportedly fixed. So I packed up and headed back home to see if it really was.</p>

<p>Still no dice. Neither PPPoE nor DHCP was working at all. I called support a third time, still answered by a “helpful” offshored agent who more or less told me the same thing as the first call - we needed to schedule a service visit to upgrade my modem. Explaining that I had no modem, just a router and the ONT didn’t yield any fruitful dialog trees. Helpfully, my existing appointment had been automatically cancelled once the outage was fixed, so we rescheduled it for Tuesday morning.</p>

<p>I decided to have Claude poke around in my router again to see if it could find anything useful. This was a pretty delicate dance since I had to hotspot on my phone for Claude to work, but still needed access to my LAN to get access to the router. I found that starting a Claude session with WiFi connected to my hotspot and then plugging in an ethernet adapter connected to my LAN allowed Claude to still work but also gave me LAN access. We tried flipping back and forth between PPPoE and DHCP. Claude determined DHCP was totally dead, no traffic or responses at all from WAN there. PPPoE was more interesting. There was a response from the ISP that my DSL account had been deactivated. Claude told me I needed to call support and tell them this was the response I was seeing.</p>

<p>Fourth time’s the charm, right? Well, it turns out it was. As soon as someone answered the phone, I knew it would be okay. It was a person who was apparently a native English speaker and wasn’t simply reading a script. I explained that after an outage, my PPPoE connection was returning an error about my DSL account being inactive. He put me on hold to talk to the “programming team”. It was a grueling 15-minute wait, but eventually he popped back on and asked which modem I was using. I explained I was using a custom router with OpenWrt but that it had been working fine with PPPoE up until yesterday. He let me know they were probably going to switch me to DHCP but would confirm with the programming team on the other line. After another short wait, he asked me to switch my router to DHCP. Immediately after doing so, my WAN came online, fully active. Apparently there was some switch they had to make on their end and just like that, it was fixed.</p>

<p>Lesson learned? <strong>If you call Quantum Fiber and someone answers that does not sound like they’re based in the US, just hang up and call again.</strong> I absolutely despise that this is the solution. It’s a training and skill issue, not a language barrier issue. I actually asked Mr. USA what the deal with that was, that this was my fourth call into support and the first one that was helpful at all. He explained that his group works regular 9-to-5-ish hours M-F. If you call outside of those hours, or if the call center is getting a lot of traffic, they might route the call to an offshore call center. So again, my takeaway is that you should wait until “normal business hours” in the US and then call. If an offshore agent answers the phone, just hang up and call back. Talking to the offshore support reps is a waste of time at best, and might have actually been what broke the existing working PPPoE setup when the outage was resolved.</p>

<p>It’s frustrating that this is the state of ISPs in the US. Gone is the local support. It’s all been lobotomized and commoditized at the cost of the end user. It would be so much better in my opinion if I’d called on a Sunday and they’d just said that their support would be back Monday morning and to try again later. Then, I would have touched grass and not wasted an hour of my time on the phone with support that was unable to actually help. Even from a business perspective, the result of the call being to schedule a technician to come to my house feels like a huge waste of resources. Why make a person schlep all the way to my house when the problem is totally solvable over the phone? It seems like the offshore call center is actually <em>losing</em> them money, not saving them any.</p>

<h2 id="the-working-config">The Working Config</h2>
<p><a href="https://minus273.org/project/openwrt-centurylink/" target="_blank">This page from minus273.org↗</a> really has what you need, both for PPPoE and IPoE, but I’ll reiterate my working setup just for posterity. Both configurations still need vLAN 201 tagging. That has not changed. Here’s the relevant config:</p>

<link rel="stylesheet" href="/assets/css/highlight.css" />

<figure class="code-frame img-frame " style="margin:0;">
	<div id="figure-container">
		
		<div class="github-code">
			

<div class="highlight highlight-source-shell"><pre>config device <span class="pl-s"><span class="pl-pds">'</span>wan_vlan<span class="pl-pds">'</span></span>
	option <span class="pl-c1">type</span> <span class="pl-s"><span class="pl-pds">'</span>8021q<span class="pl-pds">'</span></span>
	option ifname <span class="pl-s"><span class="pl-pds">'</span>eth1<span class="pl-pds">'</span></span>
	option vid <span class="pl-s"><span class="pl-pds">'</span>201<span class="pl-pds">'</span></span>
	option name <span class="pl-s"><span class="pl-pds">'</span>eth1.201<span class="pl-pds">'</span></span>
	option ipv6 <span class="pl-s"><span class="pl-pds">'</span>0<span class="pl-pds">'</span></span>
	option txqueuelen <span class="pl-s"><span class="pl-pds">'</span>5<span class="pl-pds">'</span></span>

config interface <span class="pl-s"><span class="pl-pds">'</span>wan<span class="pl-pds">'</span></span>
	option device <span class="pl-s"><span class="pl-pds">'</span>eth1.201<span class="pl-pds">'</span></span>
	option proto <span class="pl-s"><span class="pl-pds">'</span>dhcp<span class="pl-pds">'</span></span></pre></div>

		</div>
	</div>
</figure>

<h2 id="next-steps">Next Steps</h2>
<p>I’ve been dreaming of <em>redundant</em> ISPs for a while. If my neighborhood has two gigabit fiber providers, I kind of want to just have both of them to try and eliminate any possibility of outages. It just feels so overkill to pay for a whole other ISP just to not really use it ever. It’s possible to do some load balancing in OpenWrt, but to bond them to a single 2 gig connection requires an external server in a datacenter so that feels not worth it. I wonder if I could just get the other ISP set up to failover at any time, and then park the account until I needed it? A fifteen-minute outage while I log in to an account to reactivate it feels like a great disaster recovery scenario to me.</p>

<p>My G9 only has the two NICs, but it has M.2 PCIe lanes up the wazoo. You can buy an M.2 Intel i226 NIC for like $25 so I think it would be relatively doable on the software side to get another one set up. Physically, I’m not sure how it would all go together. If I went with my ready to go, inactive account plan, then I guess I would just swap a single ethernet cable between the fiber ONT and the cable modem which wouldn’t be too bad either. Ah, but then I’d need to also have the necessary config for either, which would presumably be at least removing the vLAN tagging.</p>

<p>On the other hand, I work from home and it’s my family’s sole income source. $150/month for redundant ISPs doesn’t seem out of the question, as crazy as that may be.</p>

<h2 id="the-end">The End</h2>
<p>This post has gone on for way too long already! I’m really happy with my new router. I really enjoy fiddling with OpenWrt. I’m casually eyeing some APs I could run it on as well to get rid of my proprietary Chinese setup, but so far there’s nothing with WiFi 7 that can run OpenWrt and I don’t really want to degrade my network. It might be worth it though, I’m really debating it!</p>]]></content><author><name>catskull</name></author><category term="diy" /><category term="home network" /><category term="centurylink" /><category term="quantum fiber" /><category term="openwrt" /><summary type="html"><![CDATA[Back in January 2025, I got OpenWrt running on a MikroTik Hex S and detailed the setup process for CenturyLink fiber. Since then a few things have changed.]]></summary></entry><entry><title type="html">Car Tools</title><link href="https://catskull.net/car-tools.html" rel="alternate" type="text/html" title="Car Tools" /><published>2026-05-04T21:31:15+00:00</published><updated>2026-05-04T21:31:15+00:00</updated><id>https://catskull.net/car-tools</id><content type="html" xml:base="https://catskull.net/car-tools.html"><![CDATA[<p>A few years ago, I became aware of Van Neistat’s YouTube channel. I really enjoy his extreme utilitarian Anderson-esque approach to filmmaking and storytelling. It seems like everything he owns, says, or does is extremely thought out and purposeful.</p>

<p>He has a few videos about “kits” he keeps for various things. He actually did two videos on kits for his car. Like him, I’m also in a codependent relationship with a Toyota Land Cruiser.</p>

<lite-youtube videoid="bihjKg3l4Ns" style="background-image: url('https://i.ytimg.com/vi/bihjKg3l4Ns/hqdefault.jpg');" title="Essential Car Toolkit (LandCruiser)">
  <a href="https://youtube.com/watch?v=bihjKg3l4Ns" class="lty-playbtn" title="Play Video">
    <span class="lyt-visually-hidden">Play Video: Essential Car Toolkit (LandCruiser)</span>
  </a>
</lite-youtube>
<script defer="" src="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.css" rel="stylesheet" />

<hr />

<lite-youtube videoid="pKUmSDYkiTU" style="background-image: url('https://i.ytimg.com/vi/pKUmSDYkiTU/hqdefault.jpg');" title="The Essential Tools in Every Vehicle (LandCruiser)">
  <a href="https://youtube.com/watch?v=pKUmSDYkiTU" class="lty-playbtn" title="Play Video">
    <span class="lyt-visually-hidden">Play Video: The Essential Tools in Every Vehicle (LandCruiser)</span>
  </a>
</lite-youtube>
<script defer="" src="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.css" rel="stylesheet" />

<p>Inspired by him, I decided to build out my own car tool kit. I wanted something practical and cheap that I could reasonably accomplish most home improvement and vehicle maintenance tasks with. I wanted just the essentials. Nothing more than is absolutely necessary. Here’s the kit I’ve carried for the last 4 years and a memo about each item.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Car Tools<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/car-tools/IMG_4994.jpeg" alt="My current tool kit spread out on a sheet." />
	
	</div>
</figure>

<h3 id="paracord">paracord</h3>
<p>Something to tie things (and not people) down with. I can’t remember if I’ve ever used it. It’s too big, gets tangly, and generally speaking I think was a mistake.</p>

<h3 id="electrical-tape">electrical tape</h3>
<p>Very useful. Not all electrical tape is created equal. This stuff is on the low end. But it’s still been very useful. It lasts pretty well even in dirty environments.</p>

<h3 id="multi-tool-folding-knife">multi-tool folding knife</h3>
<p>I just had this laying around and figured it might be useful. It has not been. Generally speaking, tools should follow the <a href="https://en.wikipedia.org/wiki/Unix_philosophy" target="_blank">Unix philosophy↗</a>. In my experience tools that do multiple things are compromised at everything and excel at nothing.</p>

<h3 id="ear-plugs-3-pairs">ear plugs (3 pairs)</h3>
<p>Very useful. These are silicone conical style that you can get at Harbor Freight for about $6 for 25 pairs. Protect your hearing.</p>

<h3 id="rubber-bands">rubber bands</h3>
<p>Very good to have on hand. My go-to cable management solution is rubber bands. Also cheap. They’ll get crusty over time so it’s probably best to keep them in an airtight container like a Ziploc bag.</p>

<h3 id="channel-lock-pliers">channel lock pliers</h3>
<p>Incredibly useful. I just used these to install my Mom’s washing machine a few days ago.</p>

<h3 id="side-cutters">side cutters</h3>
<p>Get good ones. Don’t cut things that will damage them. Great at clipping off zip ties.</p>

<h3 id="needle-nose-pliers">needle nose pliers</h3>
<p>One of the most useful general-purpose tools around. Great at pulling out carpet staples or nails. Also built-in cutters for things you don’t want to use the side cutters for.</p>

<h3 id="magnetic-level">magnetic level</h3>
<p>Possibly the GOAT. So useful. A level takes amateur effort and turns it into a professional result. Picture hanging. This one has a (very weak) magnet which is surprisingly useful. You do <em>not</em> have the ability to eyeball levelness, do not try.</p>

<h3 id="phillipsflathead-screwdriver">Phillips/flathead screwdriver</h3>
<p>Absolutely essential. In an ideal world we’d use <a href="https://en.wikipedia.org/wiki/Robertson_screw#Licensing" target="_blank">Robertson bits↗</a> instead of Phillips but oh well.</p>

<h3 id="blue-loctite">blue loctite</h3>
<p>I’ve used this several times. This is actually the only bottle I have that I know where it is. Whenever I screw something in that I don’t ever want to do again, I use a little bit. My friend told me that nail polish works just as well and I believe him. Nail polish seems to also be generally more useful all around.</p>

<h3 id="measuring-tape">measuring tape</h3>
<p>Don’t be a dingus and do measurements with your arms and strange things. Settle the debate! “Will this fit through the door?” Now you know. Measure twice, cut once. For critical measurements, burn an inch.</p>

<h3 id="socket-wrench-set">socket wrench set</h3>
<p>This set is pretty much bottom of the barrel quality-wise, but it’s still <em>so much better</em> than no set at all. I just used this last weekend installing a TV mount. Absolutely essential. It seems like no matter what size sockets you have, you’ll always be missing the one you need.</p>

<h3 id="monkey-wrench">monkey wrench</h3>
<p>Very helpful in a pinch. Not really very good at wrenching, but compared to your fingers it works wonders.</p>

<h3 id="metal-picks">metal picks</h3>
<p>I’m not sure if I’ve ever used these.</p>

<h3 id="zip-ties">zip ties</h3>
<p>Similar to rubber bands, these are very useful to have around. Great for cable management. Can be daisy-chained to increase length. They come in all sizes, but I go for the small but not tiny size. I just used these to secure a washing machine drainage hose. I had the drain hose come out of a washing machine once and it was a total disaster.</p>

<h3 id="bic-lighter">Bic lighter</h3>
<p>The romantic in me wants to buy a flint fire starter, but the realist knows a cheap lighter is about a thousand times better at fire than a flint will ever be. Good for melting the end of a nylon rope after you cut it.</p>

<h3 id="hot-glue-stick">hot glue stick</h3>
<p>I learned from Van Neistat that you can use hot melt glue sticks with a lighter and it’s just about as good as a glue gun. Very useful in a pinch. Most recently I used some to temporarily secure a nylon wall anchor in place until the screw caught on. Man, I hate those nylon wall anchors.</p>

<h3 id="battery-terminal-cleaner">battery terminal cleaner</h3>
<p>Don’t think I’ve ever used this. But a pretty common vehicle issue is battery-related and I’ve seen enough vehicles not start because the battery terminals were corroded that I felt like it justified the $3 price tag.</p>

<h3 id="industrial-sharpie">industrial sharpie</h3>
<p>Very useful. I like the “industrial” kind because it makes me think it’s better.</p>

<h3 id="breakaway-box-cutters">breakaway box cutters</h3>
<p>Possibly the best dollar you can spend. The blades are designed to be broken off as they dull so it’s like 20 blades in one. Very, very useful.</p>

<h3 id="allen-key-set">allen key set</h3>

<p>I’m not sure if I’ve ever used these. Usually things come with their own allen key, but if they don’t, this kit will come in handy.</p>

<p>I keep all the tools in a cheap bag. I wrote my name, number, and the date I assembled the kit in white paint pen. I like putting my name on things because it helps honest people not walk off with my stuff accidentally, and keeps dishonest people honest.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Car Bag<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/car-tools/IMG_4995.jpeg" alt="My tool bag showing my last name written." />
	
	</div>
</figure>

<p>In addition to the tool bag, I keep a few other essential tools in my car.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Ratchet Straps are so Fetch<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/car-tools/IMG_4997.jpeg" alt="An orange ratchet strap." />
	
	</div>
</figure>

<p>Most importantly, I keep a cheap set of ratchet straps. It is impossible for me to state just how much better the <em>worst</em> set of ratchet straps is compared to a rope. Ratchet straps have a bit of a learning curve though. You put the end of it through the ratchet and only ratchet it about 5-10 times to get it tight. Do not try and ratchet the entire strap or it will not work! A set of 4 like these at Harbor Freight is $10. I like that they’re cheap enough I can give them away when I need to. I feel a lot better about donating a $2.50 strap to an underprepared fella than risking the possibility of losing a load on the road and someone getting hurt.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Gloves go in the Glove Box<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/car-tools/IMG_4998.jpeg" alt="Leather gloves, a sunglasses case, and a 3x5 index vehicle maintenance log." />
	
	</div>
</figure>

<p>In my glove box, I keep a nice pair of leather gloves that fit well. You’ll probably want to spend about $20. Make sure you try them on before you buy them. I also keep an emergency pair of polarized sunglasses. These are about $20 on Amazon and in a pinch make for better-than-nothing safety glasses. I also keep my vehicle maintenance log which is currently filled so I need to print a new page.</p>

<p>I can’t recall how much this tool kit cost me, so I decided to just recreate it. Harbor Freight doesn’t seem to be quite as cheap as it used to be, and they’re pushing the more expensive items and gating deals behind paid memberships. But the “Pittsburgh” brand has a lifetime warranty on most items, the quality is “good enough”, and they mostly seem to be cheaper than comparable tools from Walmart. I mostly went for a 1-to-1 reproduction of my tool kit, but I varied a bit.</p>

<p>Here’s what I got:</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>Car Tools Neu<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/car-tools/IMG_5007.jpeg" alt="My new tools laid out on a sheet." />
	
	</div>
</figure>

<table>
  <thead>
    <tr>
      <th>Item</th>
      <th>Price</th>
      <th>Store</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://www.harborfreight.com/12-in-tool-bag-with-21-pockets-61467.html" target="_blank">12 in. Tool Bag W/ 21 Pockets↗</a></td>
      <td>$4.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/color-snap-blade-utility-knife-60828.html" target="_blank">Breakaway Box Cutters (x2)↗</a></td>
      <td>$0.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/7-inch-wire-stripper-with-cutter-98410.html" target="_blank">Wire Stripper↗</a></td>
      <td>$6.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/micro-flush-cutter-90708.html" target="_blank">Micro Flush Cutters↗</a></td>
      <td>$2.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/nitrile-dipped-work-gloves-large-66374.html" target="_blank">Nitrile Dipped Work Gloves↗</a></td>
      <td>$1.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/10-inch-steel-adjustable-wrench-65801.html" target="_blank">10 in. Adjustable Steel Wrench↗</a></td>
      <td>$7.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/6-in-diagonal-cutters-63816.html" target="_blank">6 in. Diagonal Cutters↗</a></td>
      <td>$3.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/17-piece-sae-metric-hex-key-set-94650.html" target="_blank">SAE/Metric Hex Key↗</a></td>
      <td>$6.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/12-ft-x-12-in-quikfind-tape-measure-with-abs-casing-62464.html" target="_blank">12 ft. Tape Measure↗</a></td>
      <td>$2.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/6-in-1-screwdrivernut-driver-61988.html" target="_blank">6-in-1 Screwdriver↗</a></td>
      <td>$2.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/6-inch-level-with-magnetic-strip-37588.html" target="_blank">6 in. Magnet Level↗</a></td>
      <td>$2.49</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/1-4-quarter-inch-x-10-ft-diamond-braid-rope-96186.html" target="_blank">10 ft. Rope↗</a></td>
      <td>$1.49</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/10-in-groove-joint-pliers-69379.html" target="_blank">10 in. Groove Joint Pliers↗</a></td>
      <td>$8.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/60-ft-x-34-in-industrial-grade-electrical-tape-63239.html" target="_blank">Electrical Tape↗</a></td>
      <td>$1.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/8-in-needle-nose-pliers-63824.html" target="_blank">8 in. Needle Nose Pliers↗</a></td>
      <td>$3.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td><a href="https://www.harborfreight.com/14-in-drive-sae-and-metric-high-visibility-socket-set-21-piece-62303.html" target="_blank">3/8 in. 21 pc. Socket Set↗</a></td>
      <td>$24.99</td>
      <td>HF</td>
    </tr>
    <tr>
      <td>Bic Lighters (x2)</td>
      <td>$3.24</td>
      <td>WM</td>
    </tr>
    <tr>
      <td>Hot Glue Sticks</td>
      <td>$1.27</td>
      <td>WM</td>
    </tr>
    <tr>
      <td>Nail Polish</td>
      <td>$0.98</td>
      <td>WM</td>
    </tr>
    <tr>
      <td>Sharpie Extreme (x2)</td>
      <td>$3.44</td>
      <td>WM</td>
    </tr>
    <tr>
      <td><strong>Total</strong></td>
      <td><strong>$94.69</strong></td>
      <td> </td>
    </tr>
  </tbody>
</table>

<p>I couldn’t find a large pair of flush cutters, just some “diagonal cutters” and some “micro flush cutters”. I also added a wire stripper. You can use a blade if you’re legit, but I just find a cheap pair of wire strippers to be very useful when I need to strip wire. The screwdriver is a 6-in-1, but it should be fine. You could probably just get a single phillips and a single flathead instead. The gloves I got are half a step above disposable latex gloves but can still keep your hands clean without breaking your gentlemanly facade. I really kind of splurged on the socket set. I think they had the same kind I got originally for about half the price, but I wanted something nicer and with larger sockets. I really wanted a deep socket set as well, but they were more expensive. As I write this, I realize I should have grabbed an extension or two.</p>

<p>I didn’t buy more zip ties, but a pack of 100 is about $3. I also didn’t get more rubber bands, but a lifetime supply from Walmart is just $1.18. The ear plugs were the ones I really debated. A pack of 25 pairs at Harbor Freight is $6.39, and I just didn’t want that many of them. The silicon ones can technically be washed and reused and it seems like you can get them for free if you try. See if your next flight attendant or hotel clerk will give you a pair.</p>

<p>I think $100 for these essential tools to keep in your car is a very good idea, but also too much to spend. I think you could find a lot of this stuff secondhand if you go spend a morning yard sale shopping. With any luck you can score some higher quality vintage tools as well for less than Harbor Freight will sell them new.</p>]]></content><author><name>catskull</name></author><category term="diy" /><category term="tools" /><summary type="html"><![CDATA[A few years ago, I became aware of Van Neistat’s YouTube channel. I really enjoy his extreme utilitarian Anderson-esque approach to filmmaking and storytelling. It seems like everything he owns, says, or does is extremely thought out and purposeful.]]></summary></entry><entry><title type="html">Winning The Fight Against OG Images</title><link href="https://catskull.net/winning-the-fight-against-og-images.html" rel="alternate" type="text/html" title="Winning The Fight Against OG Images" /><published>2026-04-30T07:45:20+00:00</published><updated>2026-04-30T07:45:20+00:00</updated><id>https://catskull.net/winning-the-fight-against-og-images</id><content type="html" xml:base="https://catskull.net/winning-the-fight-against-og-images.html"><![CDATA[<p><a href="https://samwarnick.com/blog/winning-the-fight-against-og-images/" target="_blank">Open Graph images are the bane of my existence.↗</a> Seeing a beautiful one in a link preview in iMessage or Discord is just a chef’s kiss. But for whatever reason, generating them has nearly destroyed me. Last June, nearly a full year ago, I spent 4 hours on stream designing and coding up some super fancy solution using Cloudflare Workers. I thought I’d be super smart and cool and use an <em>SVG</em> to do it since an SVG is technically an “image” and of course og:image supports SVG, right??? Wrong! I honestly don’t even know what formats <em>are</em> supported, but I know for sure SVG is not.</p>

<lite-youtube videoid="9hLW_ZiHfBQ" style="background-image: url('https://i.ytimg.com/vi/9hLW_ZiHfBQ/hqdefault.jpg');" title="Add dynamic og:images to my blog with Cloudflare Workers">
  <a href="https://youtube.com/watch?v=9hLW_ZiHfBQ" class="lty-playbtn" title="Play Video">
    <span class="lyt-visually-hidden">Play Video: Add dynamic og:images to my blog with Cloudflare Workers</span>
  </a>
</lite-youtube>
<script defer="" src="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.js"></script>

<link href="https://cdn.jsdelivr.net/npm/lite-youtube-embed@0.3.2/src/lite-yt-embed.min.css" rel="stylesheet" />

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>A Critical Hit!<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/images/posts/winning-the-fight-against-og-images/chat.jpg" alt="Sam pointing out that og:images cannot be SVG" />
	
	</div>
</figure>

<h2 id="the-backstory">The Backstory</h2>

<p>The “Open Graph protocol” (<a href="https://en.wikipedia.org/wiki/Open_Graph_protocol" target="_blank">wiki↗</a>) is some random crap that Facebook cooked up back in 2010 to make web content shared on Facebook look better. It’s not an official specification and how applications use it is pretty much just random. At its core, it’s just a bunch of special <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta" target="_blank">&lt;meta&gt;↗</a> tags. However, as of the Year of our Lord 2026, the only thing that you really need to worry about is <code class="language-plaintext highlighter-rouge">og:image</code>, <code class="language-plaintext highlighter-rouge">og:title</code>, <code class="language-plaintext highlighter-rouge">og:site_name</code> and <code class="language-plaintext highlighter-rouge">og:description</code>. Rather than describe what each of these is, I’ll just show you:</p>

<p><img src="assets/images/posts/winning-the-fight-against-og-images/ogog.jpg" alt="the various og meta tags in a discord preview" /></p>

<p>Even on top of the unofficial and random spec that is “Open Graph”, there are seemingly endless additional properties and fields that random applications have added. Twitter is the most notable case here, but really there is no set standard or predictable way to figure out how to specify the preview content when sharing a URL in an application. It just varies! <a href="https://catskull.net/google-search-is-hallucinated-meta-description.html">Even Google search is now showing its own AI slopified page summary in search</a>, instead of the established <code class="language-plaintext highlighter-rouge">&lt;meta name="description"&gt;</code> that’s served the web well enough for the last 20 years.</p>

<h2 id="the-problem">The Problem</h2>

<p>Leaving aside the boutique implementations of link preview images, I focused only on getting them to show up in Discord, Mastodon, and iMessage. These applications seem to at least attempt to follow the fledgling “Open Graph” protocol. Since I know <em>for sure</em> now that SVGs are not supported, it needs to be a proper “image”. Previously my site has been using a single static image in WebP format for all links. It was fine, but I wanted the image to reflect more closely what the content of the page would be. It should be a nice little enticing preview! At a bare minimum, this would include things like a date, the page title, and the site name.</p>

<figure class="img-frame " onclick="this.classList.toggle('full');">
	<div id="figure-container">
	
	
		<figcaption>My OG og:image<span class="img-icon">┅</span></figcaption>
	
	<img src="assets/opengraph.webp" alt="My OG og:image" />
	
	</div>
</figure>

<p>But how to generate a bunch of custom images on the fly? How to template the images? I wanted something fully automated and custom designed, and I didn’t want to add a heavy media processing pipeline to my application. I wanted it to be as close to how the rest of the website is built as possible, not some custom ImageMagick or ffmpeg design system. I really like HTML and CSS. Generally speaking, they are really powerful and modern web browsers are <em>really, really</em> good at rendering them. I can’t think of another content layout system that is as powerful and flexible as HTML and CSS, not to mention ease of use and interoperability. This is why I initially leaned toward SVG, since it’s actually basically fully interoperable with HTML including having HTML in an SVG and having SVG in HTML.</p>

<p>Another problem is where to store all those images? Compared to plain text HTML, CSS, and JS files, images take up a lot of room! I don’t want my git repo to be gigantic, but it felt like the og images should live beside the content. Sam feels very opposed to storing media in his blog repo, which I think has merit, but I also think that having a single source of truth for the content makes sense for me. I already cache everything with Cloudflare, and I optimize my images with their image transformation service so images are served over the network as efficiently as realistically possible.</p>

<h2 id="my-solution">My Solution</h2>

<p>This blog is produced with the Jekyll static site generator. I prototyped a basic og:image design with HTML and CSS. One really nice feature of web content is that it is pretty good at fitting dynamic content into fixed-size content areas. For example, if a blog post has a title of “My Story” and another has a title of “My Incredible Journey: In and Out of the World’s Top Businesses”, web layout engines are smart enough to fit both of those into the same content area. For og:images, this is important because the images are (almost) <em>always</em> exactly 1200x630 pixels. Like I said, there’s not any kind of hard rule here; these are just the most common conventions and there are just as many exceptions to every rule as there are reasons why the rule exists in the first place.</p>

<p>I had Claude cook up a decent HTML page that worked across a variety of titles. It even made a cool splatter/TV static style randomized background using a canvas. Here’s the design I settled on:</p>

<p><img src="assets/og/winning-the-fight-against-og-images.html.png" alt="this page's og image" /></p>

<p>But how to convert an HTML page to an image? There are a lot of solutions to this and they all vary in complexity. To me, it made the most sense to generate the images during the development build process. I wanted to create a new blog post and have Jekyll automatically generate the image for me. I also didn’t really want to pull in some complicated gem dependency to do it. I also wanted it to be “fast”.</p>

<h2 id="enter-ferrum">Enter ferrum</h2>

<p>In 2026 people do be like “ruby is an outdated ecosystem, that’s why I’m switching to the most complicated JS toolchain my AI token spend allowed me to hallucinate”. To that I say, good luck and have fun! I found the <a href="https://github.com/rubycdp/ferrum" target="_blank">ferrum gem↗</a> which works with a standard Chromium installation to control it, similar to any of the other standard browser automation tools, but it felt way more lightweight and a lot easier to use. Yeah I know, a hard dependency on Chromium for my blog is not great and is a lot heavier than some simple ImageMagick install. But I already have <a href="https://github.com/ungoogled-software/ungoogled-chromium" target="_blank">ungoogled-chromium↗</a> in my Brewfile for no apparent reason other than to use it with Amazon Luna to play Forknife with my kids (which I haven’t done in well over a year). It just feels like a decent thing to have around, and running a completely de-Googled Chrome feels very punk rock to me. I wasn’t sure if it would work with ferrum, but it “just did”. Straight up, no problems, and it was fast!</p>

<p>I decided I wanted to make a Jekyll plugin that would hook into each page’s generate function. I could add the gem only in development mode so my GitHub Action would ignore it. I wanted to have my og image templates follow Jekyll’s built-in templating and layout as closely as possible. I wanted them to just feel like normal off-the-shelf Jekyll pages, just special for images. In Jekyll’s <code class="language-plaintext highlighter-rouge">_config.yml</code>, an og image template is specified by either collection or path. I needed a path override for special pages like the site’s index page, which is technically part of the <code class="language-plaintext highlighter-rouge">pages</code> collection but should be unique. I actually have quite a few collections now for recipes, playlists, podcasts, newsletters, and more.</p>

<p>Any time Jekyll builds a new page in development mode for the first time, my gem will read the config, render the template passing in that specific page’s front matter, open the page in Chromium, and take a screenshot. The screenshots are all named accordingly and saved to my site’s /assets directory. There’s really not much else to say about it, other than it’s really fast. Generating all <strong>239</strong> unique images for every page in my blog takes just over <strong>27 seconds</strong>. Subsequent runs have no additional build time added.</p>

<p>It just so happens that I recently turned 35, so I decided to take the day off work and ended up working on og images all day. It took quite a bit of testing and debugging, but it all worked! I should say it worked surprisingly well, too. I even got everything buttoned up and pushed the plugin gem to RubyGems.org. This was only my second ever gem, so it was fun learning about how to do that.</p>

<h2 id="conclusion">Conclusion</h2>

<p>I’m super proud of what I came up with! Especially considering that I’d tried at least one other time and <em>completely</em> failed. <code class="language-plaintext highlighter-rouge">ferrum</code> is a cool gem, and I think I’ll probably find other uses for it in the future as well. If you’d like to use this for your blog, see the <a href="https://github.com/catskull/jekyll-templated-og-image" target="_blank">jekyll-templated-og-image↗</a> gem on GitHub.</p>]]></content><author><name>catskull</name></author><category term="og image" /><category term="jekyll" /><category term="ruby" /><summary type="html"><![CDATA[Open Graph images are the bane of my existence.↗ Seeing a beautiful one in a link preview in iMessage or Discord is just a chef’s kiss. But for whatever reason, generating them has nearly destroyed me. Last June, nearly a full year ago, I spent 4 hours on stream designing and coding up some super fancy solution using Cloudflare Workers. I thought I’d be super smart and cool and use an SVG to do it since an SVG is technically an “image” and of course og:image supports SVG, right??? Wrong! I honestly don’t even know what formats are supported, but I know for sure SVG is not.]]></summary></entry><entry><title type="html">April Blogging Challenge Wrap-Up</title><link href="https://catskull.net/april-blogging-challenge-wrapup.html" rel="alternate" type="text/html" title="April Blogging Challenge Wrap-Up" /><published>2026-04-29T20:39:00+00:00</published><updated>2026-04-29T20:39:00+00:00</updated><id>https://catskull.net/april-blogging-challenge-wrapup</id><content type="html" xml:base="https://catskull.net/april-blogging-challenge-wrapup.html"><![CDATA[<p>That’s a wrap on The First Annual Bullpen Inc. Blog Prompt Challenge Extravaganza 2026! It was fun and challenging to get the blog posts out each week. I’m glad I was able to get something out. I liked seeing how different everyone’s posts were even having the same prompt. I think my favorite was Jared’s prompt about the internet. I was tempted to just do “lore drop” for my prompt because I like reading casual autobiographies and not at all because this has all just been one giant social engineering experiment to hax them each.</p>

<p>Sam says for the last week we should each say something we like about each other’s blogs.</p>

<h2 id="carter">Carter</h2>

<p>He recently nuked all his CSS. I wish he’d add just a bit more. I like that he calls himself a “code plumber”. I like his blog posts, but I wish he’d write more.</p>

<h2 id="jared">Jared</h2>

<p>I like his image previews:</p>

<p><img src="https://jaredezz.tech/Fantastic%20Computers%20&amp;%20Where%20to%20Find%20Them.png" alt="og:image" /></p>

<h2 id="sam">Sam</h2>

<p>I like how often he blogs. He’s pretty sneaky and will just post quietly. Luckily, I’m subscribed to his RSS feed so I never miss a post (except for when I do). I like his <a href="https://samwarnick.com/uses/">/uses</a> page!</p>

<hr />

<p>My friends and I decided to do a weekly blog challenge for the month of April, 2026! Each week, one of us chooses a prompt and we all write posts.</p>
<p>For week 5, we chose the prompt:<br /> "<b>What is one thing you like about each other's blogs?</b>"</p>

<p>My friends' posts this week:</p>
<ul>

<li>Sam: <a href="https://samwarnick.com/blog/april-blogging-challenge-wrap-up/">April Blogging Challenge Wrap-Up</a></li>










<li>Carter: <a href="https://carter.works/blog/">tba</a></li>










<li>Jared: <a href="https://jaredezz.tech/posts/">tba</a></li>



























































</ul>

<p><strong>My other posts in this series:</strong></p>
<ul>

<li><a href="/doing-the-most-good.html">Doing the most good</a></li>

<li><a href="/how-i-compute-2026.html">How I Compute (2026)</a></li>

<li><a href="/the-internet-is-a-vast-place.html">The Internet is a vast place.</a></li>

<li><a href="/nintendo-announces-3ds-successor-3ds-plus.html">Nintendo announces 3DS successor '3DS Plus'</a></li>

</ul>]]></content><author><name>catskull</name></author><category term="blog challenge 2026" /><summary type="html"><![CDATA[That’s a wrap on The First Annual Bullpen Inc. Blog Prompt Challenge Extravaganza 2026! It was fun and challenging to get the blog posts out each week. I’m glad I was able to get something out. I liked seeing how different everyone’s posts were even having the same prompt. I think my favorite was Jared’s prompt about the internet. I was tempted to just do “lore drop” for my prompt because I like reading casual autobiographies and not at all because this has all just been one giant social engineering experiment to hax them each.]]></summary></entry><entry><title type="html">Doing the most good</title><link href="https://catskull.net/doing-the-most-good.html" rel="alternate" type="text/html" title="Doing the most good" /><published>2026-04-28T16:10:27+00:00</published><updated>2026-04-28T16:10:27+00:00</updated><id>https://catskull.net/doing-the-most-good</id><content type="html" xml:base="https://catskull.net/doing-the-most-good.html"><![CDATA[<p>Carter’s prompt this week is as follows:</p>

<blockquote>
  <p>You have been given one month away from your obligations to use your talents to enact societal good. What do you go do?</p>
</blockquote>

<p>This is the first prompt in the challenge where I genuinely feel like I have nothing really novel to say. If you know me, this is quite unusual.</p>

<p>I have two immediate questions:</p>

<p>First: What are my obligations I’d be free from? My 9-5 job, sure. But my family? Taking care of my kids? Feeding myself? What am I obligated to do? Beyond providing an income for them, I don’t particularly feel like I have many obligations. Generally speaking, if I don’t want to do something, I don’t do it. There are some things I’m involved with at my church that maybe aren’t my first choice but really it’s quite minimal.</p>

<p>Second: What are my talents? Hard for me to really even say. Most of the talents I feel like I might have seem to have little to no direct way to impact “societal good”. What does that even mean anyway? Who’s to say what’s “good” or “bad” for society?</p>

<p>The other issue is a month hardly feels like enough time to really move any kind of needle. But say I had a year? A decade? What would I do then? My answer doesn’t change much because I still have no real answer.</p>

<p>The first thought I had was I could help the elderly load groceries in their car, but approaching strangers in a parking lot is a skill I lack and have no desire to develop.</p>

<p>Then my thoughts drifted towards more selfish endeavors. Truly I think the greatest skill I have with a potential to impact some kind of arbitrary “societal good” is raising my kids to be productive members of society. So I’d spend <em>more</em> time with them, not less. I could try to work hard and get some home improvement projects finished that would make our home a more nurturing environment. I’ve wanted to remodel my sons’ bedroom for a few years, I’d like to build them a cool loft bed with their own desk space underneath to try to better utilize our seemingly ever shrinking floor space as they get older and seek more privacy. However, I tentatively pitched my wife on the idea a while ago and her response was less than enthusiastic. Increasing marital friction seems like it would not do society any good.</p>

<p>Desperate, I turned to Wikipedia to see if there is an article on “societal good”. The closest I could find was the page for <a href="https://en.wikipedia.org/wiki/Public_good">“public good”</a>. There’s a list of example “public goods”, the first of which is “public fireworks”. So I guess I’d go out and buy fireworks and light them off for my community. I’m sure my neighbors would be <em>thrilled</em>. Still no luck.</p>

<p>Maybe it’s a complete cop-out but I really do feel like the best method I have to do “societal good” is exactly what I’m already doing. I earn an honest wage. I try my best at work to be thoughtful and deliberate to do the best work I can. I make myself available to my kids and give them real attention. We spend quality time together. I don’t use harmful or addictive substances. I pay my taxes. I literally donate 10% of my paycheck to charity. I try to be generous with my time and money. My kids have a friend from the neighborhood eat dinner at our house nearly every weeknight. My wife spends a lot of time each week volunteering with our local youth organization and I know for sure her only motivation is to try to make people’s lives better. I help my mom take care of her aging father, directly. This week I’ve gone over to help her move every day after work to make things more comfortable for my grandpa. I spent all weekend doing that. My feet hurt right now because of it. I judged an elementary school science fair. I taught the children’s class at church today in addition to teaching the adult class once a month. I volunteer to play in the community concert band, which I do enjoy, but I also probably wouldn’t do if they didn’t really need a drummer. I know for a lot of the elderly members of the band it’s one of the only things they do each week.</p>

<p>I’m not perfect. In all of those things I mentioned, there’s more I could do, and I could do what I do even better. I could try harder. But if someone said I had a month off to go and do some other thing for “societal good”, I’d probably say “no thank you”. I don’t need some fantasy scenario where I travel halfway around the world to feel like I’m a contributing member of society. I already try to be. I’m not good at building schools. I’m good at being me. Ultimately, I want to <em>be</em> societal good, continually.</p>

<hr />

<p>My friends and I decided to do a weekly blog challenge for the month of April, 2026! Each week, one of us chooses a prompt and we all write posts.</p>
<p>For week 4, Carter chose the prompt:<br /> "<b>You have been given one month away from your obligations to use your talents to enact societal good. What do you go do?</b>"</p>

<p>My friends' posts this week:</p>
<ul>

<li>Sam: <a href="https://samwarnick.com/blog/talents-for-good/">Talents for Good</a></li>










<li>Carter: <a href="https://carter.works/blog/2026-04-28-get-small-muchachos/">Get small, muchachos</a></li>










<li>Jared: <a href="https://jaredezz.tech/posts/">TBA</a></li>



























































</ul>

<p><strong>My other posts in this series:</strong></p>
<ul>

<li><a href="/april-blogging-challenge-wrapup.html">April Blogging Challenge Wrap-Up</a></li>

<li><a href="/how-i-compute-2026.html">How I Compute (2026)</a></li>

<li><a href="/the-internet-is-a-vast-place.html">The Internet is a vast place.</a></li>

<li><a href="/nintendo-announces-3ds-successor-3ds-plus.html">Nintendo announces 3DS successor '3DS Plus'</a></li>

</ul>]]></content><author><name>catskull</name></author><category term="blog challenge 2026" /><summary type="html"><![CDATA[Carter’s prompt this week is as follows:]]></summary></entry></feed>