<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feeds.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://chameth.com/">
    <title>Chameth.com - posts like moving-back-to-a-dynamic-website but not debugging-beyond-the-debugger, docker-automatic-nginx-proxy, just-a-nod, why-you-should-be-using-https</title>
    <subtitle>Personal homepage of Chris Smith</subtitle>
    <link href="https://chameth.com/feeds/posts/like/moving-back-to-a-dynamic-website/unlike/debugging-beyond-the-debugger,docker-automatic-nginx-proxy,just-a-nod,why-you-should-be-using-https/" rel="self"/>
    <link href="https://chameth.com/"/>
    <icon>https://chameth.com/favicon.png</icon>
    <updated>2026-08-27T00:00:00Z</updated>
    <id>https://chameth.com/</id>
    <author>
        <name>Chris Smith</name>
    </author>
    <entry>
        <title>Building a family of single-user services</title>
        <link href="https://chameth.com/building-a-family-of-single-user-services/"/>
        <updated>2026-08-27T00:00:00Z</updated>
        <id>https://chameth.com/building-a-family-of-single-user-services/</id>
        <content xml:lang="en" type="html">&lt;p&gt;Last month I &lt;a href=&#34;https://chameth.com/tracking-pbs-without-the-dark-patterns/&#34;&gt;made and wrote about&lt;/a&gt; a personal service
to track my workouts and record personal bests. Since then, things have escalated. I now have eight different
services running in the same “family”:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Collegiate Room&lt;/td&gt;
&lt;td&gt;Simple weather overview for my current location&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;German Mall&lt;/td&gt;
&lt;td&gt;LLM broker for use by other services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gloss White&lt;/td&gt;
&lt;td&gt;Weight tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Olive Crosses&lt;/td&gt;
&lt;td&gt;Central service hub&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pompei Band&lt;/td&gt;
&lt;td&gt;Workout and personal best tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Speed Carp&lt;/td&gt;
&lt;td&gt;Spaced repetition system&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tiger Island&lt;/td&gt;
&lt;td&gt;Reading history and library tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical Horse&lt;/td&gt;
&lt;td&gt;Location history and analytics&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The names are all anagrams of something related to their function. Finding those anagrams brings me an unreasonable
amount of joy. Unscrambling them is an exercise left to the reader. On top of those eight, I currently have four other
service ideas sat in my todo list. It’s going to be a big family.&lt;/p&gt;
&lt;h3 id=&#34;a-postmortem-on-the-previous-hodge-podge&#34;&gt;A postmortem on the previous hodge-podge&lt;/h3&gt;
&lt;p&gt;Most of this functionality isn’t new to me. When I started out playing around with LLMs, I created a “personal data
aggregator” that just kept getting things bolted on. I &lt;a href=&#34;https://chameth.com/an-app-can-be-a-ready-meal/#what-ive-made&#34;&gt;said at the time&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;My biggest just-for-me project is a web app that I started to help me aggregate film recommendations. It’s since morphed into a general personal data aggregation service: it deals with data from GitHub, Todoist, Letterboxd, TMDB, Healthkit, and others. It also lets me make re-orderable lists, store recipes, and more. Parts of this could definitely be open sourced, and I might carve them out at some point, but it’s mostly a glorious hodge-podge of things specific to me. Having all these services in one place lets me make quick and dirty automations, for example: when I create a Todoist note on my phone or watch, I often forget to set the due date, so it doesn’t show up in the “Today” view. It was literally a few lines of code to plumb things together so any inbox task without a due date gets set to today automatically.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In a twist that &lt;em&gt;nobody&lt;/em&gt; could see coming, it turns out that a “glorious hodge-podge” is actually quite hard to maintain
and very annoying to work with. It didn’t help that it had a fairly extensive Svelte frontend and I really am not a fan
of JavaScript. I ended up using it less and less just because it wasn’t fun to use or work on, and I didn’t want to
waste the limited LLM tokens on doing massive refactors or reworks.&lt;/p&gt;
&lt;p&gt;I’ve got almost two decades of professional software development experience under my belt, so you’d think I would
have learnt the lessons on how to write maintainable software. Hell, my CV even mentions that as something I’m
particularly good at. So what went wrong?&lt;/p&gt;
&lt;p&gt;There were a few factors. One of the obvious things to blame is that it was written mostly with LLMs.
Most of that blame isn’t very honest, though. In the year or so since, LLMs have definitely got better at writing
good code; but I was manually reviewing all the code at the time, and telling it quite carefully what to implement
and how to do it. So the buck really stops with me.&lt;/p&gt;
&lt;p&gt;I think it’s actually more because it was my &lt;em&gt;first&lt;/em&gt; larger project using LLMs. It’s a bit like learning a new
programming language. I don’t think the first sizable project I’ve written in any language has ever been any
good. You can read and learn as much as you want, but you need to actually run into some problems headfirst to
get a grip on how the language works. Or at least I do. LLM coding agents are basically the same deal.&lt;/p&gt;
&lt;p&gt;What the LLMs actually did was mask several problems. The architecture wasn’t right for how the service developed.
It started out as a single-purpose app and gradually got things bolted on, without any major changes. Everything
ended up in horizontal slices: a massive database layer with every query used by every part of the app, a massive
HTTP layer, and so on. Working on a single feature became a tap dance all over the source tree. This sort of
friction is normally quite apparent when you’re writing code by hand, and at some point it hurts enough that
you throw in the towel and do some refactoring. But the LLM will happily plod on without exposing that pain,
so the project needed a more active kind of architecture management that it just didn’t get.&lt;/p&gt;
&lt;p&gt;The other problem they covered up was how brittle and unmaintainable the JavaScript frontend was. I’m not
even sure how I ended up with a Svelte frontend in hindsight. It’s my preferred framework, but I still only
reach for it as a last resort when there’s no other way to avoid using one. It seems like there were lots of
ways to avoid it here, but I seem to have… well… avoided them. Anyway, each feature got its own slew of
Svelte components, without much thought to reusability, shared patterns, or anything sensible. Some of them
were just thin wrappers around data supplied by Go, others had vast swathes of logic in them, with no real
rhyme or reason as to which way that would fall. It’s the same issue as before: a human would look at it,
say “WTF?” and suggest tidying it up; instead, the LLM just gets on with what you tell it to do.&lt;/p&gt;
&lt;p&gt;There’s also a big non-LLM factor: one of the primary reasons all of this unrelated functionality got
tossed into the same app was that I’d previously worked on a few projects with a microservice architecture
and really didn’t like it. My conclusion was they only really worked in large environments where you can
dedicate people or teams to working on all the boilerplate that happens from maintaining lots of
interconnected projects. Dumping everything and the kitchen sink into one badly architected monolith was,
perhaps, not the best rebound from that.&lt;/p&gt;
&lt;h3 id=&#34;pompei-band-and-beyond&#34;&gt;Pompei Band and beyond&lt;/h3&gt;
&lt;p&gt;When I wanted to implement &lt;del&gt;Strava at home&lt;/del&gt; my PB tracking app, it seemed fairly natural to build it out
as a new service, rather than bolt it on to the slowly decomposing hodge-podge that was giving off a funny
smell. Then when I wanted a weather dashboard, why not do it in the same style as Pompei Band? The same
serving architecture, the same page layout… I could even come up with a similar weird two-word name!&lt;/p&gt;
&lt;p&gt;Then wouldn’t it be nice if the weather dashboard knew where I was, so it could show the local weather?
I could just build that functionality in, but it’d also be a good opportunity to carve out the location
history feature from my old service and make a new, shinier one. Then basically every time I even thought
about going near the old service, I mentally sketched out a replacement service.&lt;/p&gt;
&lt;figure class=&#34;image right&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/building-a-family-of-single-user-services/oc.png&#34; alt=&#34;A screenshot of Olive Crosses, showing links to other services with icons based on their two-word names&#34; loading=&#34;lazy&#34; width=&#34;1230&#34; height=&#34;619&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;Olive Crosses showing links to the other services&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;At one point I had the thought that it’d be nice if the services could all link to one another, but I
didn’t want to have to touch every service whenever I added a new one. So I made a “hub” service, which
maintains the One True Catalogue, and provides a bit of JavaScript the others can all embed to add a
navigation sidebar.&lt;/p&gt;
&lt;p&gt;So after complaining about microservices and JavaScript, it sounds like I have landed on microservices
(or at least miniservices) and JavaScript? Sort of yes, but sort of no. Each service is still its own
thing and makes sense independent of the others. There are some cross-service dependencies, but they’re
very simple and don’t have the same cognitive or boilerplate overhead as a login service that talks to
an authentication service that queries a user manager and so on. And as for the JavaScript, it’s almost
exclusively used for progressive enhancement and a few limited client-side visual things like
maps and graphs. All of the logic lives in Go, which is a lot easier to maintain and test, and all of
the pages are simple Go templates.&lt;/p&gt;
&lt;p&gt;Having these services built in a way that I actually like has motivated me to play around a lot more
with new features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;My workout tracking app pulls photos from my private &lt;a href=&#34;https://immich.app&#34;&gt;Immich&lt;/a&gt; server that were
taken during workouts, and shows them alongside. Now I can see all my obligatory end-of-run selfies
in one place.&lt;/li&gt;
&lt;li&gt;The location history app can detect trips away from home, and get an LLM to automatically title them.
My most recent, very exotic, trip has the title “A fortnight in Milton Keynes and Coventry”. Each
trip gets its own little map, and in the future I want to pull in images like with workouts.&lt;/li&gt;
&lt;li&gt;My weight tracking app now exports weigh-ins to Garmin, as it doesn’t fully integrate with Apple
Health (it will export its own weights, but not import others, like the ones from my Withings scales).
Stupid walled gardens, but thankfully I can fix it myself.&lt;/li&gt;
&lt;li&gt;My book tracking app automatically cleans up the titles of books from the nonsense provided by Amazon
to something more palatable. “Killing Eve: Long Shot: A twist on obsession, betrayal and revenge from
Luke Jennings, author of the TV sensation Killing Eve” becomes “Long Shot (Killing Eve #5)”. It does
this by passing the title to an LLM with a bunch of rules.&lt;/li&gt;
&lt;li&gt;The hub generates me a snippet of Tailscale configuration that defines the minimal grants needed to
allow the intra-service dependencies to work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The LLM integrations were inspired by my friend &lt;a href=&#34;https://dataforce.org.uk/&#34;&gt;Shane&lt;/a&gt; who recently
demonstrated an inventory tracking app he’d made, which used Haiku to process images of boxes and
describe the contents. I’ve dabbled with LLM integration before, and always disliked the process
of fiddling with the prompts, and debugging when the LLM gave a nonsensical response. So I built
German Mall, which makes it a lot easier. It lets me define a “workflow” consisting of a system
prompt, initial message, and expected response format. I can browse past runs, tweak the prompt,
rerun them to see the change, all without touching the services using them. Quick iteration loops
are the best. German Mall also provides markdown documentation for each workflow, so integrating
it downstream is simply a case of pointing a coding agent at the right documentation endpoint.&lt;/p&gt;
&lt;figure class=&#34;image full&#34;&gt;
  &lt;picture&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.avif&#34; type=&#34;image/avif&#34;/&gt;
      &lt;source srcset=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.webp&#34; type=&#34;image/webp&#34;/&gt;
      &lt;img src=&#34;https://chameth.com/building-a-family-of-single-user-services/gm.png&#34; alt=&#34;A screenshot of German Mall, showing the workflow configuration for tidying book titles: a form with fields for description, system prompt, user template, output schema, model, max tokens and temperature.&#34; loading=&#34;lazy&#34; width=&#34;1482&#34; height=&#34;1080&#34;/&gt;
  &lt;/picture&gt;
  &lt;figcaption&gt;&lt;p&gt;The “tidy book titles” workflow in German Mall&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It’s not all fun and games: having a bunch of services all following the same pattern means
you need to do cross-cutting maintenance on them now and then. But that’s surprisingly easy with LLMs.
I put all the projects in a common folder, and I launch cross-cutting tasks from that folder. Usually
I’ll make the change in one project, and then the cross-cutting task is simply “Look at commit XYZ in
./foo, make the same change to the other projects”.&lt;/p&gt;
&lt;p&gt;I’ve also ended up with a shared library used by all the projects to cut down on some boilerplate.
That’s normally something I’d resist, especially with personal projects, just because the extra overhead
of working in a separate project, managing releases of it, and so on, is a bit annoying. But, again,
LLMs can do almost all of that grunt work. They effectively reduce the “cost” of maintaining separate
services to a negligible level, leaving me to focus on the more fun aspects like coming up with new names.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Monthly Meanderings: April 2026</title>
        <link href="https://chameth.com/monthly-meanderings-2026-04/"/>
        <updated>2026-05-01T00:00:00Z</updated>
        <id>https://chameth.com/monthly-meanderings-2026-04/</id>
        <content xml:lang="en" type="html">&lt;p&gt;It’s apparently been a whole month since &lt;a href=&#34;https://chameth.com/monthly-meanderings-2026-03/&#34;&gt;the last edition of Monthly Meanderings&lt;/a&gt;. Not sure when that happened. This month I’ve been to a LAN party, had an MRI, and have been trying unsuccessfully to get my doctor to change a prescription. The latter two are not doing much to help shake that middle-aged feeling I mentioned last month…&lt;/p&gt;
&lt;h3 id=&#34;website-updates&#34;&gt;Website updates&lt;/h3&gt;
&lt;p&gt;Another pair of blog posts for this month: &lt;a href=&#34;https://chameth.com/the-case-of-the-unchanging-config/&#34;&gt;The Case of the Unchanging Config&lt;/a&gt; about a problem I had with a bind-mounted config in a Docker container, and &lt;a href=&#34;https://chameth.com/migrating-from-github-to-forgejo/&#34;&gt;Migrating from GitHub to Forgejo&lt;/a&gt;, a write-up of… well… migrating from GitHub to Forgejo. That one’s been on my to-do list for a while, so it’s nice to finally get it written up.&lt;/p&gt;
&lt;p&gt;It’s not visible when you look at the site, but I’ve been doing a lot of refactoring this month. The existing design had a package for database operations, a package for the stylesheet, a package for shortcodes, and so on. As I bolt more and more things on this gets less and less useful: those packages get bigger and bigger, and trying to understand how, say, the music import functionality works means hopping all over the place. Instead, I’m moving towards having each feature contained in its own package. It defines its own stylesheet, database operations, HTTP handlers, and so on. They’re then all wired together at the top layer. There’s still some more to do on that front, but it’s progressing in a good direction, and I’m happier with the code.&lt;/p&gt;
&lt;p&gt;The only actual visible change is the addition of &lt;a href=&#34;https://chameth.com/wow/&#34;&gt;a page dedicated to World of Warcraft&lt;/a&gt; that automatically imports some data from the game’s API. We’ll come back to that later!&lt;/p&gt;
&lt;h3 id=&#34;other-projects&#34;&gt;Other projects&lt;/h3&gt;
&lt;p&gt;I made a trio of new tiny projects: &lt;a href=&#34;https://github.com/csmith/pdsps&#34;&gt;pdsps&lt;/a&gt;, a reverse proxy for a Bluesky/ATProto PDS that overrides the age verification response so you don’t need to hand over ID; &lt;a href=&#34;https://github.com/csmith/irc-adze&#34;&gt;irc-adze&lt;/a&gt;, a plugin for &lt;a href=&#34;https://github.com/greboid/irc-bot&#34;&gt;irc-bot&lt;/a&gt; that receives and announces &lt;a href=&#34;https://github.com/greboid/adze&#34;&gt;adze&lt;/a&gt; webhooks; and &lt;a href=&#34;https://github.com/csmith/wow-spec-switch&#34;&gt;wow-spec-switch&lt;/a&gt;, a super-simple addon for World of Warcraft that lets you switch specialisation with a command.&lt;/p&gt;
&lt;p&gt;I’ve also been working on a custom app launcher in the style of &lt;a href=&#34;https://ulauncher.io/&#34;&gt;ulauncher&lt;/a&gt; but without requiring WebKit to render things. I’ve not yet polished it up enough to open source it, but it’s not far off.&lt;/p&gt;
&lt;h3 id=&#34;entertainment&#34;&gt;Entertainment&lt;/h3&gt;
&lt;p&gt;Another single film month for me. It was a great one, though:&lt;/p&gt;
&lt;div class=&#34;film-review-parent&#34;&gt;
  &lt;section class=&#34;film-review raised-box&#34;&gt;
    &lt;img src=&#34;https://chameth.com/films/254/poster.jpg&#34; alt=&#34;Poster for Project Hail Mary&#34; loading=&#34;lazy&#34;/&gt;
    &lt;header&gt;
      &lt;h3 class=&#34;plain-header&#34;&gt;&lt;a href=&#34;https://chameth.com/films/project-hail-mary-2026/&#34;&gt;Project Hail Mary&lt;/a&gt;&lt;/h3&gt;
      &lt;div&gt;&lt;/div&gt;
      &lt;div title=&#34;9/10&#34;&gt;
&lt;span class=&#34;star-rating&#34;&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-2&#34;/&gt;&lt;img src=&#34;https://chameth.com/star.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-flat.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Full star&#34; class=&#34;rot-0&#34;/&gt;&lt;img src=&#34;https://chameth.com/star-half.png&#34; width=&#34;28&#34; height=&#34;28&#34; alt=&#34;Half star&#34;/&gt;&lt;/span&gt;
&lt;/div&gt;
      &lt;time&gt;2026-04-01&lt;/time&gt;
    &lt;/header&gt;
    &lt;div&gt;&lt;p&gt;A film adaptation of a book I loved that’s actually good. Huh!&lt;/p&gt;
&lt;p&gt;Ryan Gosling does great in what is often a solo act. Early on I thought the film was trying a bit too hard to be funny, but at some point it started working and I enjoyed it.&lt;/p&gt;
&lt;p&gt;There are lots of parallels to The Martian, Arrival, and so on, but it’s a sufficiently different mash-up of the concepts that it’s not a problem.&lt;/p&gt;
&lt;/div&gt;
  &lt;/section&gt;
&lt;/div&gt;
&lt;p&gt;I’m still enjoying &lt;a href=&#34;https://www.themoviedb.org/tv/288670-saturday-night-live-uk&#34;&gt;SNL UK&lt;/a&gt; a lot more than I expected to, although I keep forgetting what day it airs. They should put it in the name or something.&lt;/p&gt;
&lt;p&gt;The rest of my “entertainment” time has been spent playing World of Warcraft. I played an awful lot about 6-7 years ago, and have dipped in and out occasionally since. I got an MMORPG itch again recently and jumped back in, and I’m enjoying it a lot once again. I’ve been playing a healer properly for the first time (after enjoying healing in Final Fantasy XIV):&lt;/p&gt;
&lt;div class=&#34;wow-char-grid&#34;&gt;
&lt;div class=&#34;wow-char raised-box&#34; data-title=&#34;World of Warcraft Character Data&#34;&gt;
&lt;div class=&#34;overview&#34;&gt;
&lt;img src=&#34;https://chameth.com/wow/characters/Methrica.png&#34; alt=&#34;Methrica&#34; loading=&#34;lazy&#34;/&gt;
&lt;p class=&#34;detail&#34;&gt;Level 90&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;Female Void Elf&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;&lt;span class=&#34;wow-class-priest&#34;&gt;Holy Priest&lt;/span&gt;&lt;/p&gt;
&lt;p class=&#34;detail&#34;&gt;280 average item level&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&#34;info&#34;&gt;
&lt;h3 class=&#34;plain-header&#34;&gt;Methrica&lt;span class=&#34;realm&#34;&gt;-Terenas&lt;/span&gt;&lt;/h3&gt;
&lt;h4 class=&#34;plain-header&#34;&gt;Professions&lt;/h4&gt;
&lt;table class=&#34;plain-table professions&#34;&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Midnight Alchemy&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;100&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;100/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Engineering&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;92&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;92/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Cooking&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;1&#34; max=&#34;100&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;1/100&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Midnight Fishing&lt;/td&gt;&lt;td&gt;&lt;progress class=&#34;tier-progress&#34; value=&#34;4&#34; max=&#34;300&#34;&gt;&lt;/progress&gt;&lt;/td&gt;&lt;td class=&#34;tier-count&#34;&gt;4/300&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;div class=&#34;links&#34;&gt;
&lt;a href=&#34;https://worldofwarcraft.blizzard.com/en-gb/character/eu/terenas/methrica&#34;&gt;View on blizzard.com&lt;/a&gt;
&lt;a href=&#34;https://raider.io/characters/eu/terenas/Methrica&#34;&gt;View on raider.io&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;around-the-web&#34;&gt;Around the web&lt;/h3&gt;
&lt;h4 id=&#34;git-history-documentationhttpsgit-scmcomdocsgit-history&#34;&gt;&lt;a href=&#34;https://git-scm.com/docs/git-history&#34;&gt;git-history documentation&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The latest release of git added this handy-seeming history subcommand. It allows you to reword a commit, or split a commit up interactively, without having to go through the interactive rebase song and dance.&lt;/p&gt;
&lt;p&gt;Git has acquired a bunch of new commands in recent releases, but I tend to ignore them in favour of the old ones I know well. This one will definitely get use from me, though.&lt;/p&gt;
&lt;h4 id=&#34;git-koans-by-steve-loshhttpssteveloshcomblog201304git-koans&#34;&gt;&lt;a href=&#34;https://stevelosh.com/blog/2013/04/git-koans/&#34;&gt;Git Koans by Steve Losh&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Sticking with the same theme, this series of koans about Git and its various quirks had me laughing. If you’re reading this blog, you’re probably part of the target audience for it.&lt;/p&gt;
&lt;h4 id=&#34;sqlite-prefixes-its-temp-files-with-etilqs-httpsaviimblag2026etilqs&#34;&gt;&lt;a href=&#34;https://avi.im/blag/2026/etilqs/&#34;&gt;SQLite prefixes its temp files with etilqs_&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I think I’d come across this before, but it’s still a fun anecdote. It reminds me of &lt;a href=&#34;https://daniel.haxx.se/email/&#34;&gt;Daniel Stenberg’s e-mail collection&lt;/a&gt;; as the author of curl his name is listed on basically everything with the inevitable influx of people contacting him.&lt;/p&gt;
&lt;h4 id=&#34;a-dot-a-day-keeps-the-clutter-awayhttpsscottlawsonbccompostdot-system&#34;&gt;&lt;a href=&#34;https://scottlawsonbc.com/post/dot-system&#34;&gt;A dot a day keeps the clutter away&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I love this approach to tracking how frequently things are used, and the weird sort of archaeological record you get from rotating the colours of the stickers used.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Monthly Meanderings: December 2025</title>
        <link href="https://chameth.com/monthly-meanderings-2025-12/"/>
        <updated>2026-01-01T00:00:00Z</updated>
        <id>https://chameth.com/monthly-meanderings-2025-12/</id>
        <content xml:lang="en" type="html">&lt;p&gt;For a while I’ve been idly thinking about a way to get smaller bits of content
onto my website without it being too annoying for me, or too hard to consume. Things
like interesting links, small project updates, and so on.&lt;/p&gt;
&lt;p&gt;I didn’t immediately come up with anything I was happy with, so just sat on it.
Ignoring the problem seems to work surprisingly well for things like this.
I’d been meaning to redesign the &lt;a href=&#34;https://chameth.com/about&#34;&gt;about page&lt;/a&gt; for a while, but wasn’t
sure what to do with all the interests I wanted to list. After ignoring the
problem for a while I came across the &lt;a href=&#34;https://chrisburnell.github.io/interests-directory/&#34;&gt;interests directory&lt;/a&gt;
and realised I could just bundle all those things into their own page.&lt;/p&gt;
&lt;p&gt;This seems amazingly obvious in hindsight, but I think I needed a bit
of a push to accept that a page just about my interests was… well…
interesting. Anyway, as the end of December rolled around, I got a similar
dose of inspiration: a flurry of monthly round-up posts from various blogs
I follow. If I bundle up all the small bits and bobs, it’ll make something
vaguely blog post shaped, and writing and consuming those is already a
solved problem. Again, pretty obvious in hindsight, and again I think I
just needed a nudge to shut up the little voice in my head saying “but those
people all have interesting things to say… do you, really?”&lt;/p&gt;
&lt;p&gt;So without further ado, welcome to the inaugural post in my new monthly
meanderings series. If you have any feedback on the format or contents,
please drop me a note with the contact form at the bottom!&lt;/p&gt;
&lt;h3 id=&#34;website-updates&#34;&gt;Website updates&lt;/h3&gt;
&lt;p&gt;As already mentioned, I reworked the &lt;a href=&#34;https://chameth.com/about&#34;&gt;about page&lt;/a&gt; and split out
a new &lt;a href=&#34;https://chameth.com/interests&#34;&gt;interests page&lt;/a&gt;. The old about page had a few lists
of interests show up in thought bubbles when you hovered over things,
which was a cool effect on desktop, but barely worked at all on mobile.
The new page gives me a lot more room for waffle, and I do like waffling.&lt;/p&gt;
&lt;p&gt;Only a single new blog post this month:
&lt;a href=&#34;https://chameth.com/exposing-game-servers-over-tailscale/&#34;&gt;exposing game servers over Tailscale&lt;/a&gt;.
Just a quick how-to I wrote up, having not found it documented usefully
anywhere before. I have a bunch of ideas for posts I want to write, but
none of them have really sparked joy quite yet, so I’m once again employing
the “sit on them for a bit and maybe inspiration will strike” approach.&lt;/p&gt;
&lt;h3 id=&#34;other-projects&#34;&gt;Other projects&lt;/h3&gt;
&lt;p&gt;I created a small collection of Docker related projects this month:
&lt;a href=&#34;https://github.com/csmith/containuum&#34;&gt;containuum&lt;/a&gt; is a go library that
helps with listing containers, and keeping that list up-to-date. On top
of that I built &lt;a href=&#34;https://github.com/csmith/centauri-docker-confd&#34;&gt;centauri-docker-confd&lt;/a&gt;
which generates a config for &lt;a href=&#34;https://github.com/csmith/centauri&#34;&gt;centauri&lt;/a&gt;
(my reverse proxy) based on running Docker containers. I already had a
way of doing that, but containuum is designed to be a bit more robust,
and I made it so the config can be passed over a network connection
instead of having to write it to disk and then signal the proxy to reload.&lt;/p&gt;
&lt;p&gt;The next project has a bit of backstory: after reading
&lt;a href=&#34;https://blog.jakesaunders.dev/my-server-started-mining-monero-this-morning/&#34;&gt;I got hacked, my server started mining Monero this morning&lt;/a&gt;
[warning: the article is interesting but a bit LLM-sloppy] I realised that I, too, was
running a vulnerable version of Umami. Luckily, as far as I can tell, it wasn’t
exploited. But it got me looking into automated vulnerability scanning. I don’t
put much stock into it normally, as you get so many false alerts, but this is one
case where it would have definitely helped. There didn’t seem to be any
good solution for my “middle-of-the-road” approach to containers (lots of containers,
but not needing Kubernetes), so I wrote &lt;a href=&#34;https://github.com/csmith/purser&#34;&gt;purser&lt;/a&gt;.
It takes every image used by a running container, runs them through
&lt;a href=&#34;https://github.com/aquasecurity/trivy/&#34;&gt;Trivy&lt;/a&gt;, and generates a combined report.
I’ll add some form of active notifications to it in the future.&lt;/p&gt;
&lt;h3 id=&#34;entertainment&#34;&gt;Entertainment&lt;/h3&gt;
&lt;p&gt;This month I’ve been playing a lot of Hogwarts: Legacy, after picking it up
for free on Epic Games. I grew up with the Harry Potter books coming out,
and while I’m not a super-fan, it still has a special appeal to me.
Obviously she-who-must-not-be-named is doing her best to out-evil Voldemort,
but I think you can still enjoy the universe despite that. Especially when
you don’t have to contribute any money that might go towards her.&lt;/p&gt;
&lt;p&gt;Anyway, the game is surprisingly fun. They did a really good job of making
it feel like an open world. You &lt;em&gt;want&lt;/em&gt; to walk around from place to place
to see everything, find all the various collectables, and so on. Compared
to Far Cry or Cyberpunk the world is a lot smaller but that means it’s far
more densely packed with stuff to do. I initially didn’t vibe with the
way combat works: it’s pretty heavily reliant on dodging or shielding against
incoming attacks, which reminded me a lot of Clair Obscur: Expedition 33.
Fortunately, it’s a lot more forgiving in Hogwarts: Legacy and I got used
to it after not too long.&lt;/p&gt;
&lt;p&gt;It’s been a relatively sedate month for other forms of entertainment.
I saw Fackham Hall and Wicked: For Good at the cinema. The former was
a funny romp, vaguely reminiscent of Airplane or The Naked Gun, as you’d
expect from something penned by Jimmy Carr. Wicked was… fine? I wasn’t
overly enamoured with part one, so it’s not a surprise I didn’t vibe with
the follow-up. If you smushed them into a single normal-length feature I think they
would have made a really good film, but two films both over two hours
long is a bit much for what actually happens.&lt;/p&gt;
&lt;h3 id=&#34;around-the-web&#34;&gt;Around the web&lt;/h3&gt;
&lt;h4 id=&#34;wikipedia-list-of-citogenesis-incidentshttpsenwikipediaorgwikiwikipedialist-of-citogenesis-incidents&#34;&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Wikipedia:List_of_citogenesis_incidents&#34;&gt;Wikipedia: List of citogenesis incidents&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A list of incidents where (often false) information on Wikipedia has ended up
becoming its own source. That led to Mark Dominus’ blog post titled
&lt;a href=&#34;https://blog.plover.com/wikipedia/ysolo.html&#34;&gt;Imaginary Albanian eggplant festivals… IN SPACE&lt;/a&gt;
where he discovers that a mountain on Ceres got named after a made-up festival
(and then got it fixed!)&lt;/p&gt;
&lt;p&gt;Not relying on Wikipedia as a source was drummed into us at school, but
it feels like this sort of thing is only going to get worse and worse now
LLMs are on the scene. I assume we’re already at the point where LLM
hallucinations are prevalent enough that they’ll find their way into
the training data for future iterations.&lt;/p&gt;
&lt;h4 id=&#34;simon-collisons-websitehttpscollycom&#34;&gt;&lt;a href=&#34;https://colly.com/&#34;&gt;Simon Collison’s website&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;I really like the design here. Both the homepage and how well every
journal entry is formatted. It makes me feel bad for the many walls of
text that adorn this site. The &lt;a href=&#34;https://colly.com/v4/&#34;&gt;archived v4 design&lt;/a&gt;
is also wonderful.&lt;/p&gt;
&lt;h4 id=&#34;my-dinner-with-skinnerhttpswwwyoutubecomwatchvpk-oq8iytva&#34;&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=pk-Oq8iYtVA&#34;&gt;My Dinner with Skinner&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A 43-minute-long feature film that is “just” the Steamed Hams gag from
The Simpsons done in the style of My Dinner with Andre. There’s a (much shorter)
&lt;a href=&#34;https://www.youtube.com/watch?v=1Ecg_5r1L1M&#34;&gt;behind-the-scenes video&lt;/a&gt; that
goes over the extreme amount of effort that was put into it, too.&lt;/p&gt;
&lt;p&gt;I’m still trying to wrap my head around this. It’s… something.&lt;/p&gt;
&lt;h4 id=&#34;delete-spotify-sure-but-dont-just-replace-it-with-another-subscriptionhttpsstephveecablogentertainmentdelete-spotify-sure-but-dont-just-replace-it-with-another-subscription&#34;&gt;&lt;a href=&#34;https://stephvee.ca/blog/entertainment/delete-spotify-sure-but-dont-just-replace-it-with-another-subscription/&#34;&gt;Delete Spotify? Sure, But Don’t Just Replace it With Another Subscription&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;A similar take to my own &lt;a href=&#34;https://chameth.com/escaping-spotify-the-hard-way/&#34;&gt;escaping Spotify the hard way&lt;/a&gt;,
but it avoids getting lost in the technical weeds like I did. (In my defence,
they’re very fun weeds to get lost in.) Stephanie is
also one of the people that does monthly wrap-ups, and I was pleasantly
surprised when &lt;a href=&#34;https://stephvee.ca/blog/rewinds/rewind-dec-2025/&#34;&gt;the latest one&lt;/a&gt;
popped up in my RSS reader and I saw my own site linked.&lt;/p&gt;
</content>
    </entry>
    <entry>
        <title>Moving back to a dynamic website</title>
        <link href="https://chameth.com/moving-back-to-a-dynamic-website/"/>
        <updated>2025-10-28T00:00:00Z</updated>
        <id>https://chameth.com/moving-back-to-a-dynamic-website/</id>
        <content xml:lang="en" type="html">&lt;p&gt;For the past few weeks I’ve been working on converting chameth.com from a static site into a dynamically generated site backed by a database. This is the exact opposite process to one I went through maybe a decade ago. So why the change, and what’s different?&lt;/p&gt;
&lt;p&gt;I’d actually been toying with the idea of switching for a while. Every now and then I’d have a thought along the lines of “it would be nice if I could add X to my website… but how would that work with static site generation?”. Comments, private drafts, dynamic themes based on the date, and many more little bits and pieces. You can definitely &lt;em&gt;do&lt;/em&gt; them with a static site generator, but it felt like going against the grain. The beauty of a static site is that it’s… well… static. Shoehorning in dynamic features takes away a lot of the benefits, or leads to having lots of weird seams where you join the static and dynamic pieces together.&lt;/p&gt;
&lt;p&gt;A bunch of things have changed since I originally switched to a static site generator. In no particular order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The advent of containers, and managing them with config like in &lt;code&gt;docker-compose&lt;/code&gt;, means standing up and managing a database for the site is trivial.&lt;/li&gt;
&lt;li&gt;I’ve learnt and really like Golang, which makes writing HTTP-based servers a breeze, and makes it easy to deploy the site as a single binary (in a container).&lt;/li&gt;
&lt;li&gt;LLM agents are good enough that I can have it do all the boring CRUD work needed to actually make a dynamic site work (&lt;em&gt;so&lt;/em&gt; many admin functions…).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/csmith/chameth.com/&#34;&gt;The code&lt;/a&gt; is open source if you’re interested. I’ve made no effort to make it generally useful outside of this website, but it might serve as a useful reference if you want to do something similar. Now that’s done, I can safely procrastinate on all those dynamic features I had planned!&lt;/p&gt;
</content>
    </entry>
</feed>
