All Posts

Page 1 of 23

Give coding agents a tmux session with your real shell environment

Note: Using tmux and other more-or-less built for agent multiplexers is a slightly different approach to the one I describe here - I’m talking about using tmux from an agent instead of the agent running shell commands directly. Just saying that upfront to avoid confusion! Coding agents spawn a fresh shell by default. That shell is usually set up slightly...

Building resilient personal apps

I’ve written before about passion projects and how taking care of them feels more like tending a garden than running a product. Mowing the lawn and weeding, with the occasional prune. That metaphor still feels right to me, but I’ve started thinking more concretely about what happens when a project reaches the point where I actually use it most days,...

How I publish Standard.site records from a Rails Bluesky delivery channel

Aotearoa, Again is a Rails app that publishes a daily colourised photo edition and fans it out to email, Instagram, Facebook, YouTube Shorts, and now Bluesky. I wanted those Bluesky posts to pick up the enhanced link cards Bluesky renders for Standard.site documents, instead of a plain Open Graph preview. Standard.site is a set of AT Protocol lexicons for publications...

Re-enable SVG after Rails blocks untrusted libvips loaders

I patched Virtualtrails for GHSA-xr9x-r78c-5hrm (CVE-2026-66066), the Active Storage arbitrary file read via libvips. The fix has Active Storage call Vips.block_untrusted(true) at boot, which turns off libvips loaders marked “unfuzzed” (unsafe for untrusted content). That includes SVG. On Virtualtrails I render share cards as SVG and convert them to PNG with libvips. After the upgrade, that conversion failed with a...

When VPN DNS works in dig but not in curl on macOS

I occasionally use openfortivpn on macOS. The VPN comes up fine, but internal hostnames still fail in apps. curl can’t resolve them. Browsers can’t either. Meanwhile dig internal.example.com returns the right answer. openfortivpn has added VPN nameservers to /etc/resolv.conf, but on macOS that file isn’t what curl, browsers, or ping use. dig still reads it, which is why lookups can...

Bind XPath variables in Nokogiri instead of string interpolation

When you look up XML nodes by attribute value in Nokogiri, it’s tempting to interpolate the value straight into the XPath: doc.at_xpath("//item[@id='#{id}']") That works when id is predictable. It’s shakier when the value comes from a user upload, an API payload, or anything else you don’t control. A value like foo" or @id="bar can break out of the string literal...

A generic Stimulus controller for toggling fieldsets

I was building an achievement editor in Virtualtrails where route creators can place a milestone either as a distance along the route, or as a latitude/longitude pair that gets converted server-side. The form needs to offer both options, but only one should be active at a time — and only the active panel’s fields should be submitted. My first iteration...

Handle Cloudflare Challenge pages when making Turbo requests

How to handle Cloudflare Challenge pages when making Turbo requests

Turbo Drive intercepts link clicks and form submissions, fetching the next page with fetch() and swapping the <body> in place rather than doing a full browser reload. That’s great for smoothness, but it means the browser never performs a real navigation — and some things depend on a real navigation to work. Cloudflare challenge pages are one of those things....

How to test new Github Actions workflows

I recently build out a data processing pipeline that uses Github Actions, and so needed to add several new workflows. I couldn’t for the life of me figure out how to test them. They were going to be initiated either as part of a pipeline (using workflow_call), or manually, using workflow_dispatch. In most cases, each workflow had inputs which needed...

← Previous Page: 1 of 23