<?xml version="1.0" encoding="UTF-8"?>
<rss  xmlns:atom="http://www.w3.org/2005/Atom" 
      xmlns:media="http://search.yahoo.com/mrss/" 
      xmlns:content="http://purl.org/rss/1.0/modules/content/" 
      xmlns:dc="http://purl.org/dc/elements/1.1/" 
      version="2.0">
<channel>
<title>Max&#39;s Nonsense</title>
<link>https://nonsense.mpwb.xyz/</link>
<atom:link href="https://nonsense.mpwb.xyz/index.xml" rel="self" type="application/rss+xml"/>
<description>Assorted writing from Max Bucknell</description>
<generator>quarto-1.9.36</generator>
<lastBuildDate>Mon, 06 Apr 2026 02:22:56 GMT</lastBuildDate>
<item>
  <title>Stowing My Dotfiles</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/stowing-my-dotfiles/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2026-04-06</p>
</header>
<p>I’ve had a <a href="https://code.mpwb.xyz/mpwb/dotfiles">Git repository storing my dotfiles</a> for well over a decade at this point, and I’ve had the same installation process for just about all of that time: A <code>script/bootstrap</code> that I originally copied from <a href="https://github.com/holman/dotfiles">Zach Holman’s dotfiles</a>, that works like this:</p>
<ul>
<li>It crawls the repository for files with a name <code>*.symlink</code>.</li>
<li>It processes each found filename thusly:
<ol type="1">
<li>Remove <code>.symlink</code></li>
<li>Remove the first directoy name (this allows config files to be organised per topic)</li>
<li>It adds a <code>.</code> to the start</li>
</ol></li>
<li>It creates a symlink from <code>~/$processedName</code> to the original found file or directory.</li>
</ul>
<p>This was perfect for traditional “dotfiles”, which are so named because they are configuration files in the root of a user’s home directory that begin with a dot.</p>
<h2 id="xdg-took-over-the-world" class="anchored">XDG Took Over The World</h2>
<p>The freedesktop organisation produce a series of specifications under the XDG (Cross-Desktop Group) moniker. One such specification is the <a href="https://specifications.freedesktop.org/basedir/latest">XDG Base Directory Specification</a>, which was <a href="https://specifications.freedesktop.org/basedir/0.6/">first published in 2003</a>. It contains within it advice for where applications should store configuration and runtime files such that they do not pollute a user’s home directory with an inconsistent spattering of hidden files. I haven’t been able to find much on the history of this specification, but my memory is that this was it was an obscurity until the <a href="https://specifications.freedesktop.org/basedir/0.7/">spec was updated in 2010</a>. This update coincides with the development of Systemd, which went pretty hard in supporting this, and I think is a large part in generating broad support for this spec.</p>
<p>Slowly, the tools I was using began to adopt the XDG base directory specification, or at least began to support it. For example, <a href="https://github.com/git/git/commit/0e8593dc5b812df00400347e88f5707225fe831e">Git added support alongside their existing configuration files in 2012</a>. My first exposure to this was <a href="https://fishshell.com">Fish</a>, which expected its configuration to live in <code>~/.config/fish/config.fish</code>, and the next was <a href="https://neovim.io">Neovim</a> that expected <em>its</em> configuration to live in <code>~/.config/nvim/init.vim</code>. To start off, I just manually made symlinks from these directories to the relevant part of my dotfiles repository. It was surprising to me that I tolerated this for well over half a decade, but I think that speaks more to how often I set up a brand new user account than anything else.</p>
<p>But two years ago, I finally decided to adopt this specification myself, and give it first-class status. I updated <code>script/bootstrap.sh</code> such that files with <code>.xdg.symlink</code> would actually get mapped to <code>~/.config/$baseName</code>. This has been in place since then and I don’t think I ever revisited it. Over time, however, I noticed something surprising: everything I was maintaining was an <code>.xdg.symlink</code> type installation, rather than a traditional “dotfile”. The workaround had become the standard path.</p>
<h2 id="the-other-xdg-variable" class="anchored">The other XDG Variable</h2>
<p>I regret to tell you that I have hit the limits of my little workaround script. You see I only link things from <code>~/.config/*</code>, which is the default value for <code>XDG_CONFIG_HOME</code>. However, if you followed any of the links to the XDG Base Directory Specification above (or if you’re just clever), you will know that there are other locations specified for different user-specific state. The pertinent one in this story is <code>XDG_DATA_HOME</code>. Here is what the spec says about it:</p>
<blockquote>
<p><code>$XDG_DATA_HOME</code> defines the base directory relative to which user-specific data files should be stored. If <code>$XDG_DATA_HOME</code> is either not set or empty, a default equal to <code>$HOME/.local/share</code> should be used.</p>
</blockquote>
<p>Gee, thanks for clearing that one up. This is a little underspecified for my tastes, but it seems to be used mostly for things like history files. <a href="https://lazy.folke.io">Lazy.nvim</a> also stores downloaded plugins there. In short, it’s full of stuff that doesn’t need to be committed and is not important. Except for one thing.</p>
<p>On my desktop, I use the Sway window manager, and an <a href="https://codeberg.org/dnkl/foot">assortment</a> <a href="https://dunst-project.org">of</a> <a href="https://codeberg.org/dnkl/fuzzel">ancillary</a> <a href="https://github.com/Alexays/Waybar">tools</a> that together form something like a desktop environment. All of these support theming, but none of them support consistent or automatic theme switching that I might get in something like KDE. I like to use a light theme during the day, and a dark theme during the evening or in other low-light situations. The solution to this exact problem is another little ancillary tool called <a href="https://darkman.whynothugo.nl">Darkman</a>. To get all of these different things working together nicely requires some custom scripting. Darkman provides hooks for these:</p>
<blockquote>
<p><code>darkman</code> can run custom executables (which can be simple shell scripts).</p>
<p>Scripts are searched in a directory named darkman inside paths defined in the <code>XDG_DATA_DIRS</code> as well as <code>XDG_DATA_HOME</code>. Each script receives the current mode (“dark” or “light”) as its first argument (<code>$1</code>).</p>
</blockquote>
<p>These scripts absolutely belong in my personal dotfiles, although I can see why these are treated as state rather than configuration by Darkman. The solution, therefore, is to be able to symlink from <code>~/.local/share/darkman</code> to somewhere in my repo.</p>
<p>Perhaps <code>darkman.xdg-data.symlink</code>?</p>
<h2 id="enter-gnu-stow" class="anchored">Enter GNU Stow</h2>
<p>Absolutely not. Enough is enough. I was considering options to evolve this script into something a little more sophisticated, was turning over ideas in my head, playing with different constraints and compromises. Before progressing any further with such a project, I wanted to look at some existing art, to see if maybe there was an existing tool that did what I wanted, or perhaps gave me an alternative way of looking at it. After all, I’ve been using the same thing for a good portion of my life at this point.</p>
<p>I know that some people have used <a href="https://www.gnu.org/software/stow/">Stow</a> for this. Stow was initially designed to symlink components of individually packaged software into system level directories so that it can more easily be used. I’m pretty sure it’s mostly a set of Perl scripts, and it was initially released before I was born, so it’s all green flags so far. Installing packages like this is a very similar job to installing dotfiles into a user’s home directory, and Stow makes this even more similar by the assumptions it makes at the outset: crucially, Stow will symlink things from the source directory into the same relative locations in its immediate parent. This means that if my dotfiles are cloned to <code>~/dotfiles</code>, then by default, Stow will put things in <code>~</code>. Hell yeah.</p>
<p>Stow has added more affordances for dotfiles in particular through its life. The most obvious of these is a <code>--dotfiles</code> flag which — while linking — rewrites directory names from <code>dot-*</code> to <code>.*</code>, meaning that ones dotfiles repo need not be a mess of hidden files.</p>
<p>After a careful read of the manpages, I was able to replace the venerable and now erstwhile <code>script/bootstrap.sh</code> with a new, sleek, modern, fresh, tasty, <code>install.sh</code> that does little other than call <code>stow</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode bash code-with-copy"><code class="sourceCode bash"><span id="cb1-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">#! /usr/bin/env bash</span></span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">function</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;"> main</span> <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">{</span></span>
<span id="cb1-4">    <span class="ex" style="color: null;
background-color: null;
font-style: inherit;">stow</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-5">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--verbose</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-6">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--stow</span> . <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-7">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--target</span> ~ <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-8">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--restow</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-9">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--dotfiles</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-10">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--ignore</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"\.DS_Store"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-11">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--ignore</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"install.sh"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-12">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--ignore</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"bin"</span> <span class="dt" style="color: #AD0000;
background-color: null;
font-style: inherit;">\</span></span>
<span id="cb1-13">        <span class="at" style="color: #657422;
background-color: null;
font-style: inherit;">--ignore</span> <span class="st" style="color: #20794D;
background-color: null;
font-style: inherit;">"accessories"</span></span>
<span id="cb1-14"><span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">}</span></span>
<span id="cb1-15"></span>
<span id="cb1-16"><span class="ex" style="color: null;
background-color: null;
font-style: inherit;">main</span></span></code></pre></div></div>
<p>I chose to abandon the top-level topic directories, and keep my dotfiles repo as a partial mirror of my home directory. The reasoning here is that since I pretty much exclusively use XDG-compliant software, that topic distinction already exists within the <code>dot-config</code> and <code>dot-local/share</code> directories. While I no longer use Zsh, I wasn’t quite ready to eradicate its config, so it lives in <code>dot-zsh</code> and <code>dot-zshrc</code>, but that is [a] rare enough that I don’t really feel the need to solve this at a systems level, and [b] obviously named enough that nobody should be confused by this.</p>
<p>I’m delighted that in 2026, I’m able to pick up an ancient piece of tooling, and over the course of a morning, totally rearrange my dotfiles to accommodate a new need. In a career that is increasingly uncertain in a world that is increasingly bleak, I have found that silly and nerdy things like this bring me a load-bearing amount of joy.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/stowing-my-dotfiles/</guid>
  <pubDate>Mon, 06 Apr 2026 02:22:56 GMT</pubDate>
</item>
<item>
  <title>Project Sunshine, Part 4: The Services</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/project-sunshine-part-4-the-services/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-10-08</p>
</header>
<p>In this post, I’m going to explain my architecture as it pertains to networking and service discovery. In broad strokes I’ll outline some of my philosophy, and lay the groundwork for actually having real services running that I might want to use. So far it’s all been Linux and hardware and super low-level stuff, but I promise that we are getting somewhere. This will be the last entry in “Project Sunshine” for a little while. I am by no means finished, but after this I plan to write some articles that are more sharply focused on specific technical aspects as opposed to these rambling overtures. There are a few things I’ve done here that I think are particularly novel (or that I’ve fought particularly hard for), and I think they are worth sharing in a way that makes them easier to find. I will be returning to this series when I have something significant to write about with my server configuration: some new hardware, or my backup strategy loom as the likely next entries.</p>
<h2 id="the-fleet" class="anchored">The Fleet</h2>
<p>In addition to my home server, I am also running a small VPS to act as an edge node. This will be responsible for hosting everything that is exposed to the wider internet, such as this very blog, and my source code. This frees up my home server to be safely tucked away behind Tailscale on my residential connection. The VPS itself is hosted in Toronto with Vultr. I’m a new customer there, but so far I have only bombastic praise: I was able to supply my own cloud-init user data, they support configurable rDNS, their customer support is prompt. They have great region availability, and have been the lowest-friction host I think I’ve ever worked with.</p>
<p>My work here would not be complete without naming them. Both Tailscale and all laws of good taste require it. Please meet Orca and Plankton. Orca is the home server. Big and full of wisdom. Plankton is the smallest VPS I can get, and provably replaceable. (Plankton is in fact already on its third incarnation.) These are in keeping with a new marine theme I seem to have struck up: my home PC is called Terrapin.</p>
<h2 id="network-topology" class="anchored">Network Topology</h2>
<p>As alluded to above, Tailscale is a really fundamental element of my infrastructure. All of my local networking is routed through Tailscale IP addresses. They are static for all time, they route directly, and it was super easy to set up. I’ve got all of my devices attached to it and I’m not yet paying them. Every feature I’ve needed has been no more than three clicks away. It works great, and I can’t imagine considering anything else for what I’m doing here. Having said that, this is still me we’re talking about here, so I was never going to stick with the defaults.</p>
<p>While it’s undeniably convenient, I found the Tailscale default TLD for nodes to be unsightly and restrictive. I plan to run multiple services and I want each of them to have separate domain names. While it would be nice if Tailscale supported subdomains, I’m kind of glad they don’t. It has allowed me to purchase perhaps my favourite domain name ever.</p>
<p>All of my internal services, all of my infrastructure nodes, all of everything that is not exposed to the internet is attached to the domain name <code>max.plumbing</code>. I bought this with my real money and it might be the best $80 or so that I’ve ever spent.</p>
<p>As well as pretty URLs, it was also very important to me to have TLS. A lot of apps and services warn if they are not running over TLS, even if (unbeknownst to them) they are running in an encrypted wireguard channel, and are locked inside a private network. I was also unwilling to deal with self-signed certificates. I don’t want to add a certificate to multiple isolated trust stores myself, let alone inflict it on somebody else I claim to care about. I use an iPhone, for crying out loud.</p>
<p>How are we to get all of these things done? Split DNS! Domain names are translated into routable IP addresses via a protocol called [the] Domain Name System. This protocol traces its origins back to a painstakingly maintained <code>HOSTS.TXT</code> file hosted at Stanford University, but is now a complex set of canonical servers and relays all over the world. When your computer makes a domain name lookup with DNS, it consults a specific DNS server — that is selected by either you or more likely your ISP — to find its answer. If that server doesn’t know, it passes it back up the chain, ultimately ending up at a server that says it is the authority on that domain name. The server that gets to be the authority is actually centrally maintained in a public database of domain names, indexed by the TLD, or top level domain. That’s your <code>.com</code>, <code>.net</code>, or even <code>.plumbing</code>.</p>
<p>Since we can select our DNS server, it is possible to override the DNS lookup process within a specific private network. This includes a Tailnet. Tailscale by default does exactly this to resolve its machine names, but they also provide an option to add other overlay servers to the chain. This technology allows me to provide public DNS records for <code>max.plumbing</code>, and an altogether different set of private records for anyone within my Tailnet. We can use the <code>dig</code> utility to demonstrate this:</p>
<pre><code>mpwb@orca:~$ dig A orca.max.plumbing +short
100.x.y.z
mpwb@orca:~$ dig A orca.max.plumbing +short @9.9.9.9
155.138.139.214</code></pre>
<p>With this one weird trick, I can have a completely different service graph exposed to my private network, while still having a public resolution for anything I wish. Right now I use that for TLS certificate verification. My private server is able to solve ACME challenges from Let’s Encrypt by having the HTTP challenge requests be proxied down to it by Plankton.</p>
<p>The actual DNS server I am running is called CoreDNS. It was really simple to set up, and configuring it in Tailscale was an absolute doddle. I am using Ansible to configure it all, the <a href="https://code.mpwb.xyz/infra/servers/src/branch/main/roles/coredns/tasks/main.yml">meat of which is visible here</a>.</p>
<p>To handle TLS connections — and to proxy all of my HTTP/S requests to their final destinations — I am using Caddy. It builds TLS support in, and is alarmingly simple to configure. I remember when Nginx was the new kid on the block, and people praised its human-friendly configuration format compared to Apache. Caddy feels like still another leap forward. It automatically rewrites all insecure requests to HTTPS, and it handles the generation and renewal of TLS certificates.</p>
<h3 id="interlude-acme" class="anchored">Interlude: ACME</h3>
<p>Automatic Certificate Management Environment, or ACME is a protocol that came up about a decade ago that has since revolutionised the process for facilitating encrypted web traffic. It used to be that to serve encrypted traffic over TLS, one had to procure a certificate from a certificate authority for some significant fee. These would have validities generally measured in a single digit number of years. That certificate would then have to be configured in your server and kept safe. Then, in 2016, The Internet Security Research Group launched Let’s Encrypt: a service that would give you certificates for free! The catch was that they were only valid for three months or so. This would be inconvenient to the point of being onerous under the traditional model, but the strategy here was to force people to automate their certificate management.</p>
<p>Having fewer people and shorter life spans for secrets are obvious boons to information security. Since then, numerous ACME clients to automate this process of creation and renewal have cropped up. While all the major web servers now integrate an ACME client, Caddy was the first to do it.</p>
<p>To obtain a certificate from an ACME authority, you need to perform some kind of verification to prove that you do indeed own the domain you are trying to certify. Under ACME, there are two primary methods: HTTP-01 and DNS-01. The former requires you to host some specific file at a provided URL, whereas the latter prescribes specific DNS records that must be publicly readable.</p>
<p>Given the inaccessible nature of Orca, I originally set up DNS-01, using a custom build of Caddy that integrated support for my DNS provider of choice, deSEC. Caddy would receive the challenge and use deSEC’s API to set records. However, this all got screwed up with my split horizon DNS, and I then resorted to HTTP-01 verification. At that point, I had to set up Caddy on Plankton to proxy these requests through. That worked perfectly!</p>
<p>I’d like to shout out a particular feature of Caddy here. I went through a lot of iterations while developing the elegant and straightforward process I described above. One early version involved using HAProxy as the front end and having a wildcard TLS certificate provisioned by Lego (a standalone ACME client). In setting this up, I ran afoul of Let’s Encrypt’s rate limits for setting up certificates, and found myself blocked for a week or so. This was bad and I don’t recommend it. I didn’t end up here through mistakes in configuration, it was because I was testing this setup in VMs that I would then blow away. So, a different kind of mistake. Let’s Encrypt has pretty harsh rate limits to ensure that misbehaving ACME clients are found out early, before failure to obtain a certificate can do any real damage. To protect fools like me from themselves, they also provide a staging certificate authority that can be used for preflight. If only I’d known about this beforehand. The feature of Caddy I’d like to shout out is that if it is unable to provision a certificate, for any reason, it automatically retries against Let’s Encrypt’s staging CA until it succeeds. I didn’t again hit their rate limits, even when I had tied myself in a hopeless knot with my DNS.</p>
<p>We now have our servers, and we have a general idea of how they are connecting. The missing piece is how we are going to actually deploy services.</p>
<h2 id="containerise-some-of-the-things" class="anchored">Containerise <em>Some</em> of The Things</h2>
<p>I adore containers. Docker came out at the start of my career, and it was one of those things that felt like magic for me once it clicked. It caused me to revisit Linux (for the first time) just to get near-native speeds with VM-like isolation. Some of the most enlightening moments of my early career were reading things like Jessie Frazelle’s blog, or learning about the jailbreaks that people were constantly finding and fixing.</p>
<p>Still, Docker is not without its issues: it requires a daemon to be running as root. It’s been a while since I heard about a serious jailbreak in Docker, but that’s still a fair bit of surface area risk! I’m also still a little bit wary of running things like databases in containers. I don’t know if that’s still the best advice or whether it’s vestigial FUD, but it gives me the willies.</p>
<p>Fortunately for me, in the decade or so since Docker launched the Open Container Initiative, there are other options. The option among those that is particularly interesting to me is Podman. It provides a mostly Docker-compatible API, but runs without a daemon, and can run without root access.</p>
<h3 id="interlude-whats-a-container" class="anchored">Interlude: What’s a Container?</h3>
<p>To understand containers, I feel like one has to first understand VMs.</p>
<h4 id="inner-interlude-whats-a-vm" class="anchored">Inner Interlude: What’s a VM?</h4>
<p>Computers are typically made up of software that runs on hardware. The device on which you’re reading this (unless you printed out, in which case baller move) probably matches this description. As I wrote in my last post, hardware is backed by software called drivers that know how to physically interact with it. The operating system boots up and uses common, shared, and pre-agreed-upon interfaces to interact with these drivers: block devices, character devices, and suchlike.</p>
<p>But we can implement those interfaces in software! We don’t have to have real hardware backing those things. A hypervisor is a piece of software that implements in software (or proxies access to) every piece of hardware an operating system needs to boot. This allows you to run a “computer” on virtualised hardware, ideally completely isolated from the actual hardware itself. My edge server Plankton is a VM, running on some giant server in some data centre.</p>
<p>Virtual machines always carry some kind of overhead, and depending on what you need to do, it can be significant. It is possible to mitigate many of these by giving direct access to some things, like storage devices or sections of RAM. However, this carries tradeoffs in resource consumption.</p>
<p>What if we could run something like a VM, but without pre-empting resource allocation or paying a hefty performance penalty?</p>
<p>Well. I’m so glad you asked. And I promise we will get to that, but we have to talk about the history of multitasking first. I swear it’s relevant.</p>
<h4 id="inner-interlude-2-a-brief-history-on-multitasking-kernels" class="anchored">Inner Interlude 2: A Brief History on Multitasking Kernels</h4>
<p>As I write this, I have a Lofi Girl stream playing in the background; I have Wikipedia open in one window; I have the textbook Operating Systems: Three Easy Pieces in another. A quick scan of <code>/System/Library/LaunchDaemons</code> lists 419 services that are provided by Apple and considered essential to even run my computer. Our computers are doing hundreds of things literally all the time. That was not always the case.</p>
<p>In times of yore (like the 1950s), we had computers. Albeit slow and expensive, but definitely things we would recognise as computers by modern standards. One large deviation from modernity was in their approach to multitasking. If you needed to use a computer, you had to rent time on it! And then your thing would run, and then you would collect your results and let someone else have a go. There was a clear need to streamline this, to empower computers to do more than one thing at a time.</p>
<p>An early approach to multitasking was known as co-operative multi-tasking. This required that programmes cede control back to the operating system when they were able to, so that something else could be scheduled, and control later given back to the original process. This saw widespread adoption, including in early versions of both Windows and Mac OS. Despite that adoption, it is pretty clear in hindsight that it was only ever barely sufficient for the needs of the era, let alone the demands we make of our computers today. We didn’t have to wait very long, though: something better was just around the corner.</p>
<p>The late-1960s saw the development and release of a new operating system: Multics. Multics is the first time we saw an implementation of pre-emptive multitasking, which takes control of when to start and stop programmes from the programmes themselves and reserves that right for itself. This requires the ability to “interrupt” a running process, and sure enough Multics is the first time that we see the modern notion of software-driven interrupts (as opposed to the extant hardware-level interrupts of the time which allowed a programmer to halt execution of a programme, or to restart it once they had supplied input or read output). We still use this term to describe the same thing today. This powerful idea was a stepping stone to even more sophisticated notions that arose in successors to Multics, as we will see.</p>
<p>Through the 60s and early 70s, the folks at Bell Labs were developing the early versions of Unix. Unix is notable for a great many things, but today I want to focus on the concept of the process. A process is an abstraction of a running programme. A process presents a consistent view of low-level machine state: registers and memory to a programme that is running. This wrapper combined with interrupts gives us the building blocks for still more powerful features to delegate process management to processes, and giving us the idea of the process tree. In Unix, these primitives (still today) are called <code>fork</code>, <code>exec</code>, and <code>wait</code>.</p>
<p>These features themselves are a form of virtualisation. And the trend thereafter has been to virtualise basically everything. Memory access is virtualised and addresses are translated. Storage and other syscalls access runs through mandatory access control layers. This is done not only for security, but also to preserve the lie that we have access to an entire machine. Modern computers are incredibly complex. In fact, they contain more complexity than one could reasonably expect any programmer to hold when they want to build whatever new front-end to ChatGPT they’re expecting to become the next unicorn. The modern operating system’s built-in virtualisation is essential to how we understand and imagine computers to work today.</p>
<h3 id="interlude-okay-whats-a-container" class="anchored">Interlude: Okay, what’s a Container?</h3>
<p>With all of that backstory out of the way, we find ourselves without too much work to do. The modern kernel is a virtualisation device by design. The only thing we need to do now is convince it to lie about what else is going on on the computer. That’s what kernel namespaces do, and that’s how containers work. A container is basically just a collection of namespaces around a kernel’s existing virtualisation features.</p>
<p>For example, while running <code>bash</code> in my shell, I can use the <code>$$</code> variable and the <code>$PPID</code> variable to see my process ID, and my parent’s id:</p>
<pre><code>mpwb@orca:~$ echo $$
429982
mpwb@orca:~$ echo $PPID
429978
mpwb@orca:~$</code></pre>
<p>However, using a Linux kernel feature known as namespacing, I can run a process that maps a process ID on the host to a different one in the container. To see this in action, we are going to start a container using Podman:</p>
<pre><code>podman run -it --rm quay.io/fedora/fedora:43 bash</code></pre>
<p>We now have a contained Fedora 43 installation, which is distinct from the Fedora 42 installation on the host. One difference is that btop is not installed. Let’s install it so that we have a long-running process that can be inspected:</p>
<pre><code>bash-5.3# dnf install -yq btop
bash-5.3# btop --force-utf</code></pre>
<p>![][ztop.png]</p>
<p>Right off the bat, there are some things that stick out. <code>bash</code> is running with PID 1, and <code>btop</code> is running with PID 22, and nothing else is here. That’s clearly not the case on my host, where it is very clear that the root process is Systemd:</p>
<pre><code>mpwb@orca:~$ ps -p 1
    PID TTY          TIME CMD
      1 ?        00:01:39 systemd</code></pre>
<p>We can inspect this a little bit further. Let’s look at all the processes I am currently running:</p>
<pre><code>mpwb@orca:~$ ps -u "$(whoami)"
    PID TTY          TIME CMD
 429961 ?        00:00:00 systemd
 429963 ?        00:00:00 (sd-pam)
 430047 ?        00:00:00 catatonit
 430058 ?        00:00:00 dbus-broker-lau
 430059 ?        00:00:00 dbus-broker
 431356 ?        00:00:00 sshd-session
 431357 pts/1    00:00:00 bash
 431611 ?        00:00:00 sshd-session
 431612 pts/2    00:00:00 bash
 431820 ?        00:00:00 sshd-session
 431821 pts/3    00:00:00 bash
 431875 pts/3    00:00:02 podman
 431897 ?        00:00:00 pasta.avx2
 431900 ?        00:00:00 conmon
 431902 ?        00:00:00 bash
 432494 ?        00:00:00 btop
 432604 pts/2    00:00:00 ps</code></pre>
<p>As you can see, it’s [a] not that many processes, and [b] you can see <code>btop</code> right there! Going further, we can inspect the tree of processes using <code>pstree</code></p>
<pre><code>mpwb@orca:~$ pstree -p -u "$(whoami)"
catatonit(430047)

conmon(431900)───bash(431902)───btop(432494)───{btop}(432495)

pasta.avx2(431897)

sshd-session(431356)───bash(431357)───pstree(432704)

sshd-session(431820)───bash(431821)───podman(431875)─┬─{podman}(431876)
                                                     ├─{podman}(431877)
                                                     ├─{podman}(431879)
                                                     ├─{podman}(431880)
                                                     ├─{podman}(431882)
                                                     ├─{podman}(431883)
                                                     ├─{podman}(431884)
                                                     ├─{podman}(431885)
                                                     ├─{podman}(431886)
                                                     ├─{podman}(431887)
                                                     ├─{podman}(431888)
                                                     └─{podman}(431899)

systemd(429961)─┬─(sd-pam)(429963)
                └─dbus-broker-lau(430058)───dbus-broker(430059)</code></pre>
<p>This shows us a few interesting things:</p>
<ol type="1">
<li>We can see the current tree, where I logged in via SSH and ran <code>pstree</code>.</li>
<li>We also see the other session where I ran the <code>podman</code> command.</li>
<li>But outside of that tree, there is a process running something called <code>conmon</code>, and then <code>bash</code>, and then we see our <code>btop</code> command that’s running inside the container.</li>
<li>The container’s <code>bash</code> process is listed as PID 431902 here, whereas inside our container it says it has PID 1.</li>
<li>It also lists that process as being run by <code>root</code>, while we can clearly see in the <code>pstree</code> command above that it is owned by me: <code>mpwb</code>.</li>
</ol>
<p>What is happening here is that Podman is using features of the Linux kernel to isolate the process we want to run without resorting to a hypervisor to run a complete virtual machine. This allows us to interact with the kernel, and with other devices in a lighter-weight fashion.</p>
<p>It’s not just PIDs and users that get namespaced in this way. Using another old Unix feature, <code>chroot</code>, it is possible to namespace filesystems and directories. This means that the processes in the container are running at <code>/</code>. In a sense, they are! But it’s a different root directory to my root directory. Different container implementations do different things, but Podman actually generates virtual file systems that aren’t accessible on the host without doing a little extra legwork. That legwork is out of scope for this, but I suggest reading <code>podman-mount(1)</code> if you’re curious.</p>
<p>This method of isolation is why I’m so jazzed about Podman’s ability to run containers as unprivileged users. Even if one exploits some issue in these mechanisms to break out of a container, they would still be stuck in an unprivileged account, subject to the standard Linux ACLs, Systemd’s isolation (it uses many of the same features to restrict access to resources in a granular fashion), and SELinux. Above the security benefits, rootless Podman containers allow me to simply organise my services without requiring all of my containers to be in one great big shared namespace.</p>
<hr>
<p>Considering all this, here’s where I landed with respect to what goes in containers vs.&nbsp;what runs on my host:</p>
<ul>
<li>All of my “services” will run in containers.</li>
<li>Anything that runs software used by multiple services can go on my host.</li>
</ul>
<p>What are the things that multiple services might use? I’m glad you asked! Right now, it’s a pretty short list. On the default installation of Fedora 42, I have added:</p>
<ul>
<li>Podman (duh)</li>
<li>Caddy</li>
<li>PostgreSQL 17</li>
<li>Postfix and OpenDKIM</li>
</ul>
<p>I’ve also clarified that SELinux, Firewalld, and Socat are all available, and I am heavily dependent on Systemd (as I’ll explain later). There are a few primary motivators for pulling things onto my host:</p>
<ol type="1">
<li>Is it convenient and self-contained? I don’t want to have to install a dozen other daemons for something to work. If it’s a single binary with some straightforward configuration, the overhead of installing it outside a container is minimal.</li>
<li>Is it a database?</li>
<li>Is it something likely to be needed by multiple services? Caddy and Postfix are perfect examples here. In Caddy’s case, only one thing can bind to <code>:443</code> on any given interface. For Postfix, networking from containers to other places is really complex. By centralising this on the host, I make one really hard problem into two much more manageable ones.</li>
<li>Can it even be containerised? I am running Samba for Time Machine backups of the household Macs, and placing that in a container is simply not an option.</li>
</ol>
<p>If you can’t answer yes to at least one of these, then you’re going to go into a container. Currently, between both Orca and Plankton, the following services are running in containers:</p>
<ul>
<li>Forgejo</li>
<li>CoreDNS</li>
<li>Nextcloud</li>
</ul>
<p>Plenty are going to be added to that list, but this is enough for me to write up my strategy as something that has been borne out and is working, rather than something purely theoretical.</p>
<p>The last piece of this puzzle for now is Systemd, which I am using to orchestrate my containers. Through Quadlet, Podman integrates with Systemd almost seamlessly. I will be discussing the particulars of this in its own post, but the gist of it is that my contained services all run as user level Systemd units in their own unprivileged accounts. Ansible makes it very easy to configure these, and I’ve gotten pretty handy at writing my own unit files. For services that need cron, I’ve set up Systemd timers within the same account that prod the container with the right command. I mount the ports from the containers at special high numbered ports on the host and listen only on the local interface. These are then forwarded to by Caddy, or <code>socat</code> when I need something non-HTTP.</p>
<h2 id="bibliography" class="anchored">Bibliography</h2>
<p>This really closes the lid on what has been a long and involved project. I may only have an MVP, but it’s terribly exciting to have a working thing! I plan to document a lot of the implementation details. However, to reflect this milestone, and I had to do an awful lot for it, I’ve collected some of the resources I’ve leaned on while doing this whole thing.</p>
<ul>
<li>Jeff Geerling’s book <a href="https://www.ansiblefordevops.com">Ansible for DevOps</a> taught me just enough Ansible to be dangerous. I read all the book chapters and then skimmed the examples. But I kept coming back to this when I had a question like “What’s the best way to do this?”. Excellent orientation.</li>
<li>The <a href="https://docs.ansible.com/ansible/latest/collections/index.html">official Ansible documentation</a> is comprehensive! In particular, I spent a huge amount of time poring over the specifics of the modules I was using to see what I had to set, what I could set, and so on. I have shied away from using any third-party modules so far, so this has basically been all I’ve needed.</li>
<li>The <a href="https://docs.podman.io/en/latest/">Podman docs</a> are extremely well-written and fun to read. Very instructive on how volumes and networks mount, as well as introducing me to Quadlet.</li>
<li>The Podman docs were missing a few of the particulars on rootless containers. Luckily, this <a href="https://blog.christophersmart.com/2021/01/31/volumes-and-rootless-podman/">blog post by Christopher Smart</a> filled in a lot of those gaps.</li>
<li>This <a href="https://forgejo.org/docs/latest/admin/installation/docker/#using-rootless-image">one page in Forgejo’s docs</a> talks about deploying Forgejo as a rootless image. It wasn’t directly related to Podman, but I’m mentioning it here because it was the key to finally understanding why my volumes weren’t working.</li>
<li>I wouldn’t have gotten anywhere on this without really getting to know Caddy, especially the proxy and TLS configuration. Their docs, especially the <a href="https://caddyserver.com/docs/caddyfile/directives">index of directives</a> are a must.</li>
<li>Here’s a <a href="https://letsencrypt.org/docs/challenge-types/">nice page from Let’s Encrypt about TLS challenge types</a>, which gives an exhaustive and authoritative list.</li>
<li>I don’t think I’ve ever read anything by Dennis Ritchie that I didn’t adore, and this essay on <a href="https://www.read.seas.harvard.edu/~kohler/class/aosref/ritchie84evolution.pdf">The Evolution of the Unix Time-sharing System</a> is no exception. An extremely readable early history of Unix that I referred to in order to shore up my historical notes.</li>
<li>The iconic 2018 article <a href="https://queue.acm.org/detail.cfm?id=3212479">C Is Not a Low-Level Language</a> was my introduction to the idea that computers were lying to us all the time, and if you haven’t read it, I’d still recommend it today.</li>
</ul>
<hr>
<p>All in all, this has been so much fun. I’ve learned a crap-ton of new stuff, in addition to formalising and strengthening a couple decades’ worth of intuition and experience, and modernising a load of old server knowledge. I’ve already moved all of my stuff out of iCloud Drive, and I can’t wait to continue picking the low hanging fruit as I move towards freeing myself from the rent-seeking ghouls in Silicon Valley.</p>
<p>In a more general sense, I’d like to praise the idea of having meaningful projects, too! I’ve read three books to get this finished. Three books that I wouldn’t have otherwise read, which isn’t even to mention the reams of documentation I’ve pored over. It’s nice to be invigorated about technology again, especially with everything the way it is these days.</p>
<p>If you’ve joined me for all or part of this journey, either contemporaneously or after the fact, thank you! I hope it’s been as fun for you as it has been for me.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/project-sunshine-part-4-the-services/</guid>
  <pubDate>Wed, 08 Oct 2025 04:15:22 GMT</pubDate>
</item>
<item>
  <title>Project Sunshine, Part 3: The Software</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/project-sunshine-part-3-the-software/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-09-28</p>
</header>
<p>Well I built the bloody thing. After my new hard drives arrived, I installed them and realised that they cut off the air intake on my upside-down PSU. So I ordered a different power extension cable that bends the <em>other</em> way, and put it in the correct way. To do this I had to take apart basically the entire thing. I swapped the orientation of my CPU cooler fan, and rearranged some of the cables to be a bit neater, too. I’m super happy with it.</p>
<p>Now I can really think about the software I want to run on this thing. Before going into the specifics, I wanted to start out with my use case. What am I really here for? There are lots of things that I ultimately would like to host myself, but to start out with, I have identified some low-hanging fruit that will allow me to lay a foundation:</p>
<ul>
<li>Nextcloud: A cloud storage application that is free for self-hosting. There are apps for every platform I care about, and this should allow me to replace Google Drive and iCloud Drive with my own infrastructure.</li>
<li>Jellyfin: The whole reason I bought an Intel CPU. I want to be able to store and serve my movie collection to my Apple TV without having to deal with streaming services. I also have a local music collection that is rather large, and Jellyfin will happily serve that, too.</li>
<li>macOS backups: It seems the correct way to support remote Time Machine backups these days is SMB, so very well: an SMB share for the three macOS devices in the household.</li>
<li>CI: I have been running my own Forgejo instance on my VPS for some time, and I’d like to have a few things build on it. That server is too wimpy to be useful for that, but this one? I think we could be onto a winner.</li>
</ul>
<h2 id="summary" class="anchored">Summary</h2>
<p>This post is long and dense, so I’m back up at the top now to outline the decisions I made, and where I am currently with this setup. Just in case you want to skip to the good parts:</p>
<ul>
<li>I am running Fedora Server 42</li>
<li>I am booting from my NVMe SSD, which is formatted (after UEFI et al) with an LVM VG that manages a few logical volumes for my root, home, var)</li>
<li>I am running two 12TB HDDs as a mirrored VDev in a ZFS pool. No other VDevs for optimisation.</li>
<li>I am planning to use the ZFS pool as data storage, and I am running my services and database servers from the NVMe drive.</li>
<li>I’m very glad I’ve got 64GB of RAM for ARC.</li>
<li>This post does not discuss backup plans. I need to be at pains to point out that redundancy is not a backup, though it narrow the spectrum of scenarios in which you need to restore from one. I am actively developing an off-site backup plan, but I’m not quite ready with it yet. Hopefully nothing breaks in the next two months or so.</li>
</ul>
<h2 id="bibliography" class="anchored">Bibliography</h2>
<p>This post is not a primary source. I read and watched a lot of things to plan this, and it would be extremely remiss of me not to point you to the countless genuine experts who helped me make my decisions. I truly cannot thank them enough for taking the time to share wisdom on forums, Reddit, YouTube, blogs, and more. Below I’ve attached some of the articles and other materials that I found particularly useful while researching all of this. All of it affected this post, some in more diffuse ways than others. In every case, learning about this stuff has been an absolute joy, in huge part thanks to the efforts of the community.</p>
<p>Outside of any of the below sections, I have consulted the book <a href="https://pages.cs.wisc.edu/~remzi/OSTEP/">Operating Systems: Three Easy Pieces</a>. I originally bought this to teach me some stuff about locking, but it has shown its stripes here too, particularly chapter 42.</p>
<h3 id="official-docs" class="anchored">(Official) Docs</h3>
<ul>
<li>The entire <a href="https://openzfs.org/wiki/System_Administration">System Administration</a> page on OpenZFS’ official wiki is worth reading. It covers common workloads, a design overview, the works. Great reference, and not too long.</li>
<li>ArchWiki is a fantastic resource in general, and this is no exception. The articles on <a href="https://wiki.archlinux.org/title/Partitioning">Partitions</a>, <a href="https://wiki.archlinux.org/title/RAID">File Systems</a>, and <a href="https://wiki.archlinux.org/title/File_systems">RAID</a> were particularly useful for me.</li>
<li>I spent a lot of time reading a lot of man pages, especially at first. In particular, <a href="https://man.archlinux.org/man/filesystems.5">filesystems(5)</a> and <a href="https://man.archlinux.org/man/lsblk.8">lsblk(8)</a> pointed me in the right direction.</li>
<li>The RHEL documentation is often directly relevant for Fedora, especially in cases like this. The entire <a href="https://docs.redhat.com/documentation/red_hat_enterprise_linux/10/html/configuring_and_managing_logical_volumes/index">chapter on LVM</a> was super helpful.</li>
</ul>
<h3 id="youtube-videos" class="anchored">YouTube Videos</h3>
<ul>
<li>Level1Tech has a lot of really great stuff, especially for novices. <a href="https://www.youtube.com/watch?v=lsFDp-W1Ks0">This intro to ZFS</a> gave me some historical context (which I always find helpful), and really oriented me. Later on, I watched <a href="https://www.youtube.com/watch?v=mD6i2toN7lE">a video he made about Optane</a> that I found super helpful.</li>
<li>From there, <a href="https://www.youtube.com/watch?v=MsY-BafQgj4&amp;t=976s">this (90 minute) conference talk by George Wilson and Matt Ahrens from Delphix</a> gave me some firm background. It’s from 2018, but ZFS hasn’t changed that much in the intervening seven years.</li>
<li>45Drives have a huge number of excellent videos about ZFS. Their Tuesday Tech Tips series is full of surprisingly in-depth videos. Here are a few I particularly enjoyed: <a href="https://www.youtube.com/watch?v=H5aLY253daE">ZFS Read &amp; Write Caching</a>, <a href="https://www.youtube.com/watch?v=0aM1iZJkOaA">Accelerating ZFS Workloads with NVMe Storage</a>. 45 Drives also spent <a href="https://www.youtube.com/watch?v=T0frbOzBlHI">a good 40 minutes designing a ZFS pool for a mixed workload</a>. The scale is not relevant to me, but the aims definitely were.</li>
<li>There’s a lot of old knowledge that has been cargo-culted around ZFS for a long time. <a href="https://www.youtube.com/watch?v=JY_-I6lQTtI">This video from SpaceRex</a> really helped clear up the value of L2ARC for me.</li>
<li>Perhaps the best thing that SpaceRex video gave me was a link to a truly excellent talk at SDC 2019: <a href="https://www.youtube.com/watch?v=yHgSU6iqrlE">Best Practices for OpenZFS L2ARC in the Era of NVMe</a>.</li>
<li>Lawrence Systems have also produced a number of good videos on ZFS. The introductory <a href="https://www.youtube.com/watch?v=nlBXXdz0JKA">ZFS is a COW</a> is very good, as is the much more recent conversation with Alan Jude: <a href="https://www.youtube.com/watch?v=BjOkWTeZJDk">Is ZFS Ready for NVMe Speed?</a></li>
</ul>
<h3 id="reddit-and-forum-threads" class="anchored">Reddit and Forum Threads</h3>
<p>The one thread I want to call out in particular is <a href="https://www.reddit.com/r/zfs/comments/ynjwit/what_are_the_bad_things_about_zfs/">What are the bad things about zfs?</a>. I think anyone considering ZFS should give this a thorough read.</p>
<p>Beyond that, the <a href="https://forum.level1techs.com">Level1Techs forums</a>, the <a href="https://forums.lawrencesystems.com">Lawrence Systems forums</a> have been very valuable resources for specific questions. I haven’t yet had one that hasn’t already been asked.</p>
<h2 id="operating-system" class="anchored">Operating System</h2>
<p>I decided pretty early on Linux. While I’m certainly BSD-curious, I just migrated to Linux on my desktop, and I like the idea of consistency. Furthermore, a familiar stack while I’m navigating a huge number of utterly new things is a welcome respite. Of course deciding on Linux is like deciding on “Asian Food” for dinner. There is no such thing, and the categorisation is so broad as to be almost useless. So let’s narrow it down a little. I come from the Red Hat tradition of Linux, in that I have typically run Fedora on clients and CentOS on servers. I know the tooling on these very well. I even quite enjoy SELinux. Having said that, I was not necessarily married to that world, either. I had a few</p>
<ul>
<li>Fedora: familiar, and matches my desktop. Frequent updates (modern tools and annoyance of upgrading, well-maintained and well-supported.</li>
<li>Rocky Linux: Equally familiar. Longer term support, so less annoying and less modern, which is annoying in a different way.</li>
<li>Debian: Major new version just came out, so everything is pretty up to date. Community-driven, which is a huge tick.</li>
<li>Arch: A genuine consideration, since I plan to move to Arch on my desktop, and I enjoy consistency. I am also embracing control, which Arch grants to some extent.</li>
</ul>
<p>I ruled out Arch fairly early on. I don’t want to be fiddling with the OS that much. I’m prepared to tolerate the at-least-annual updates of Fedora, because I know that they are relatively straightforward. But the level of tinkering required for Arch, and the ongoing management is difficult for me to justify for a server. I’d also like to unify the platform between my VPS and my home server. While Digital Ocean does support custom VM images, that seems like whole other kind of palaver that I don’t really want.</p>
<p>The next to go were Debian and Rocky. While Arch is a bit much, I already found frustration setting up a modern Rocky Linux server a few weeks ago. I really value having modern versions of things in my package manager. I’m not running a massive fleet here. It’s fine for me to be a little less conservative about what I install.</p>
<p>That left Fedora. It’s not exactly a ringing endorsement to simply be the last one standing, but I do genuinely enjoy this distribution. I like that everything in it is promptly updated, and I like the combo of firewalld and SELinux. I will also be leaning heavily on both Podman and Ansible, both of which are heavily supported by Red Hat, and are hence excellently supported by Fedora.</p>
<p>While the currently live version of Fedora is 42, the betas for 43 released only a few days before as I write this. I gave pretty serious consideration to starting there and saving myself that first update. I eventually decided not to. I felt that it was better to start out here and have the experience of an upgrade early in this system’s life, mostly to decide if I want to keep doing it.</p>
<h2 id="storage-configuration" class="anchored">Storage Configuration</h2>
<p>I’m going to talk in a lot of detail about how I’ve chosen to arrange my storage. This is because there is a lot that goes into these decisions, and I find it really fascinating. I’ve learned a stunning amount about all this just in the last week or so, and I’m very excited to share it. I’m going to start out discussing the theory of how drives are arranged and organised, before running through how these are managed on a Linux system through the command line. Then I’ll talk about the file systems available to me, and why I made the choices I did.</p>
<h3 id="block-devices-partitions-and-volumes" class="anchored">Block Devices, Partitions, and Volumes</h3>
<p>When I installed Linux for the first time ever, I had to partition my MacBook’s hard drive. That meant shrinking my HFS+ partition, and pre-allocating space for an Ext3 partition, as well as a swap partition. I was walked through this process with a very patient friend, and I did not understand what I was doing. Cut me some slack, I was but fourteen summers old. Things have changed a little since then, and for the better. This old 160GB HDD — and every stable storage device with which I’ve interacted with since — is exposed to Linux as a block device.</p>
<h4 id="interlude-device-files" class="anchored">Interlude: Device Files</h4>
<p>Unix loves the idea of presenting interfaces to things as files. Processes? Files. Streams? Files. Files? Believe it or not, also files. Devices are no exception to this rule. A device file is a special file that programmes can interact with through stdin/stdout syscalls mediated using a device driver. A device can be any number of things, like a printer, or a serial device, or a storage device. In Unix systems, device files are mounted under <code>/dev</code>.</p>
<p>Devices are varied, and the things they do are equally varied, however, they typically fall into a collection of categories. The operating system would like to be able to control as many classes of device as possible, and likewise devices would like to remain as OS-neutral as possible. To achieve this, device files were developed as a means of abstraction. There is a piece of software in the operating system that knows the details of how a device works. We call such a piece of software a driver. The other end of that driver — at least in Unix systems — is a device file. This exposes a standardised file-like interface to the operating system. This interface comes in one or both of two flavours: block, or character. The exact distinction has something to do with how they are read from and written to, but this has been enough of a diversion to me, someone who someday wants to set up their server.</p>
<p>In Linux specifically, storage devices, be they USB, SATA, NVMe, SAS, or anything else, are exposed as block devices, which is a raw way for the OS to interact with the device itself. To make this usable to a human, that device must be <em>mounted</em>. Mounting opens the device, interacts with it over a few standardised methods to work out the file system on the device, and then exposes that file system at a mount point, which is a directory. Different device types have different names under the <code>/dev</code> namespace. For example, my SATA drives will be <code>/dev/sda</code> and <code>/dev/sdb</code>, while my boot drive is <code>/dev/nvme0n1</code>.</p>
<h3 id="partitions" class="anchored">Partitions</h3>
<p>Getting back on topic, block devices can be further abstracted into partitions. A partition is a way for a single block device to expose multiple sub-block devices. On a SATA drive, these are numbered like <code>/dev/sda1</code>, or <code>/dev/sda2</code>, and so on. A partitioned drive allows you to run multiple file systems on that drive. This is essential for boot drives, because the boot process involves mounting increasingly sophisticated file systems until the operating system can be fully initialised. On secondary storage drives, partitioning is optional. As far as I can tell, you can just go ahead and format the entire storage device.</p>
<p>Partitions are pretty low level. To partition a block device, one must write a partition table at the start of it, which specifies regions that (oddly enough) partition the device. They are fixed in place. Their low level is actually a feature. They can be loaded by EFI firmware and BIOS. In fact, they are so low-level that you must choose the type of partition map based on whether your motherboard uses UEFI or BIOS. This very accessibility can cause ergonomic issues when trying to actually use them for storage management, though. If you’re anything like me, you can imagine higher level abstractions over these basic entities. If you’re anything like me, you’ve just motivated volumes!</p>
<h3 id="volumes" class="anchored">Volumes</h3>
<p>A volume manager is a tool that presents an abstraction over physical storage, and then re-presents block interfaces that can be formatted and mounted like drives or partitions. Typically, one feeds a volume manager physical volumes — which are either the storage devices themselves or a partition thereon — and exports volumes: a block interface that is mediated through the volume manager itself. Over partitions, volumes have several benefits. Chief among them is perhaps the ability to easily grow and shrink volumes. But it is also possible to have volumes share all of the available space on the physical media and use it as required, as opposed to having to segment that space ahead of time. Beyond that, volumes may also support using their physical devices in different ways to facilitate either higher throughput or redundancy.</p>
<p>The volume manager that I have on my server is called LVM2, which stands for Logical Volume Manager (2). It came by default with Fedora Server, and is a very common default in servers and on desktop. Out of the box, I was given a single volume group across (almost) my whole NVMe drive, and one logical volume was given to the OS for its root mount.</p>
<h3 id="raid" class="anchored">RAID</h3>
<p>“Redundant Array of Independent Disks” (or RAID) is the name given to basically any technology that combines a collection of storage volumes into a single block device. This is done to improve performance, or to decrease the likelihood that one needs to restore from a backup. I am not going to go into detail on the different RAID levels and what they mean here, but as far as I can tell, all RAID (and RAID-like) implementations do a combination of three things:</p>
<ol type="1">
<li>Striping: When you write to a RAID device, it may split that write into multiple smaller ones and write them simultaneously to multiple physical devices, thus increasing your throughput. Likewise, when reading data, it must be read from multiple stripes as well.</li>
<li>Mirroring: When a device is mirrored, it means a write to that device must be replayed to multiple devices. As opposed to striping, this offers redundancy, but does not increase write throughputs, although it does allow data to be read faster.</li>
<li>Parity: When writing to a RAID device with parity, it will also write parity information somewhere. This allows restoration of data in the event of a physical failure without requiring the 1-1 storage loss of mirroring, and is typically mixed into larger arrays. The checksums combine data within the array in various ways, such that the data itself can be rebuilt from the checksums along with only sum of the data. In general, the more storage space you give to parity, the more sophisticated the checksums, and the more individual device failures you can survive.</li>
</ol>
<p>In ye olde times, RAID was implemented on custom cards that slotted into servers. This is because parity computations were expensive enough — and the CPUs of the day slow enough — that it made sense to offload that calculation to a dedicated device. Today, those penalties are a lot less severe, and there are many, many software RAID implementations.</p>
<p>The classic implementation on Linux is <code>mdadm</code>, which constructs RAID arrays and exposes devices at <code>/dev/md*</code>. LVM2 also offers a RAID implementation, although this uses the standard tools provided by <code>mdadm</code>. In addition to the operating system and volume managers, more modern file systems also offer RAID. The file systems that do this also typically offer volume management, and that’s where this whole thing starts to get a lot muddier, from a conceptual point of view.</p>
<h3 id="file-systems" class="anchored">File Systems</h3>
<p>A file system sits on top of a block storage device and exposes standardised APIs to the operating system to allow interacting with it in the way the OS expects. In Linux, the <code>mount</code> command is used to make a block storage device accessible at a certain location (or mount point). The file system will write a little bit of data in a standard place on disk to allow the device to be identified as belonging to that file system. After that, all bets are off because the file system has direct access to read and write whatever it likes from the device.</p>
<p>There are <em>many</em> file systems, and Linux supports more of them than most! For this story, I will be really focusing on three:</p>
<ul>
<li>XFS: Been around forever, often a default choice on server distributions, including RHEL and Fedora. Old school and relatively basic, but that means it’s outlived basically all of its issues. RHEL say it’s stable for multi-petabyte workloads, which is more than I can say for any software I’ve ever built.</li>
<li>Btrfs: A newer, more modern file system that acts as a volume manager and RAID implementation. It’s the default on Fedora Workstation, but not on Server. Widely regarded as stable enough <em>now</em>, that has not always been true, and I’d avoid it for real production workloads. I’d have no issues with my use case. Fun fact, Btrfs stands for the B-TRee File System. This would stand it in good stead because I love b-trees, but it turns out basically all file systems use b-trees.</li>
<li>ZFS: The last word in file systems. If there’s a modern file system out there, it’s likely taken inspiration from this one. This was developed by Sun Microsystems, who open-sourced it before they were scavenged by Oracle. Today, there is OpenZFS, and a private Oracle product. There is also Linux drama around ZFS, which I understand is part of why Btrfs exists.</li>
</ul>
<p>After a lot of reading, these were my three options. This is a lot of options considering that I came into this basically certain I was going to use Btrfs for everything. The true lesson to take away from my entire experience here is that I had my mind turned around on this issue several times over the week or so that it’s taken me to pull this post together. I cannot recommend highly enough the act of learning: reading articles, papers, docs; watching videos, tutorials, lectures, conference talks. It is impossible to know going into it what will and will not be relevant. Study widely, it will pay dividends.</p>
<p>A full treatment of the internals of every file system I mentioned above should probably be out of scope of this post, so I’ll just say that I am using XFS on my NVMe drive for the OS, and have configured my HDDs as a ZFS pool.</p>
<h2 id="why-xfs" class="anchored">Why XFS?</h2>
<p>The primary reason I selected XFS is because ZFS is not a true part of the Linux project. Its implementation lags a little in kernel support, and mounting a root drive in ZFS is just a bit more hassle than I’m ready for. Defaults are powerful, and I trust XFS to do what I need it to do. Having said that, I would prefer a more sophisticated file system with some more advanced features. Btrfs and ZFS both offer that, and I’ll come back to talk about why I’m using XFS for anything later on, in the “Why <em>not</em> ZFS?” section.</p>
<h3 id="interlude-journaling-vs-copy-on-write" class="anchored">Interlude: Journaling vs Copy-on-Write</h3>
<p>Writing to disk has a serious problem: it touches the real world, and the real world sucks. It’s all nice to sit in our theoretical world where computers work, but in the real world they only barely work. And it is important for a file system to not commit nonsense to disk in the event of a sudden loss of power. How should a file system do this?</p>
<p>It turns out that file systems — like databases — often use a write-ahead log to track the integrity of data in the case of a crash. When a write to disk is executed, the data and its metadata must be written to the journal beforehand, and only once safely recorded there can they be written to disk proper. If power is lost sometime, then the log can be replayed and data is not lost. The journal has defined start and end blocks, and is written strictly in order so that partial writes to the journal can be detected and ignored. In that case, the write simply failed.</p>
<p>This raises a fairly significant issue: do we not have to write data twice? Yes, actually. And unlike databases, this is not a log structured system where the log is in fact the record. The log is only a contingency, so that write must be flushed immediately. This problem is so severe that most journaling file systems I know of don’t write the data twice. Instead, they just write the metadata to the journal. This is what XFS does by default, and all file systems I know that <em>do</em> write data to the journal have options to turn it off.</p>
<p>Copy-on-Write is an alternative way to protect data from sudden and catastrophic failures. Under such a scheme, data is never overwritten and so cannot be corrupted. That is to say that every write that takes place is a write of new data. Once that write has been committed, a pointer can be atomically updated and the “update” is complete, and the old address can be cleaned up if nothing else is pointing to it.</p>
<p>One issue that faces our naïve copy-on-write implementation here is performance, especially over time. By forcing all writes to take place on new data, we are inviting terrible fragmentation issues, especially on HDDs where the cost of random reads and writes is so much steeper. There are a few solutions to this:</p>
<ol type="1">
<li>By far the easiest option is to not support HDDs! This is a choice Apple made with APFS, which is a CoW file system. They later added support for HDDs by including a defragmentation tool.</li>
<li>Another option is to batch writes in a faster medium, like main memory. This allows our file system to buffer writes, then batch them to make them more sequential.</li>
</ol>
<p>As an aside-on-the-aside here, the block-based nature of copy-on-write file systems also makes it easier to support transparent compression, and convenient creation of snapshots. In that case, the old state of the tree needs to be preserved, and any objects it points to must not be evicted.</p>
<h2 id="introducing-zfs" class="anchored">Introducing ZFS</h2>
<p>ZFS is a copy-on-write file system, designed to safely store large amounts of data in ways that do not really require one to trust the hardware on which it is running. ZFS is awesome. Here are a few highlights:</p>
<ul>
<li>ZFS uses integrated checksums to validate the integrity of each block.</li>
<li>Where possible, ZFS will use those checksums to restore data when it detects errors.</li>
<li>ZFS builds in a volume manager and RAID implementations.</li>
<li>ZFS transparently compresses data.</li>
<li>ZFS can encrypt data.</li>
<li>ZFS has built-in support for replication.</li>
</ul>
<h3 id="zfs-architectural-concepts" class="anchored">ZFS Architectural Concepts</h3>
<p>ZFS exposes its data as a storage pool called a ZPool. This is what you mount, read from, write to, et cetera. Inside a ZPool are one or more virtual devices — notional storage devices — called VDevs. Each VDev is made up of a number of actual physical devices. Redundancy is configured at the VDev level. So we might have 6 HDDs in a RAID pattern exposed as a VDev. ZFS will stripe reads and writes across all of the ZPools <em>data</em> VDevs, with no redundancy in place. So if you want redundancy, it is very important to configure it at the VDev level.</p>
<p>I snuck a new term in to that paragraph without defining it. I specified “<em>data</em> VDevs”. When you add a new VDev to your ZPool, you can configure it to be used for a number of special purposes. Beyond using it to store data, all use cases support increasing performance. This is often necessary because ZFS is slow by design, owing to its thorough nature. To compensate for this, ZFS has a number of optimisations, some of which are optional and exposed through VDevs.</p>
<p>Beyond your storage architecture and multiplexing data access through stripes, the primary optimisation layer is an in-memory cache called the ARC, which stands for Adaptive Replacement Cache. This can use a lot of memory, but should only use memory that would otherwise go unused on the system, and even then only up to half of it. It is a fairly straightforward block cache of data that is read from disk. Operating Systems have this built-in, it’s called a page cache. ZFS has one because the file system knows its own implementation better, and so can provide a more specialised solution. In particular, ARC maintains a cache of most-recently-used blocks (MRU). This is very standard for caches of all kinds, but this implementation can be vulnerable to scans of blocks larger than the entire cache. In a database context, think about updating a value on all rows in a table. To combat this, ARC also maintains a most-frequently-used list, and to be evicted from the ARC, you have to fall off both lists. The memory within ARC is also used to batch writes to disk to increase throughput and response times.</p>
<p>However, ARC is not always enough, and that’s where non-data VDevs come in. In particular, a VDev may be configured as:</p>
<ul>
<li>L2ARC: This is Level 2 ARC, and it’s basically just more ARC. In normal running, if there is an L2ARC VDev present, ARC is constantly feeding the entries at the back of its queue into the L2ARC. That way, if it gets evicted, it might be able to catch it in the L2. It seems that L2ARC is a little simpler than the ARC, in that it is a straightforward ring buffer, meaning that new entries bump the oldest from the cache.</li>
<li>SLOG: The write architecture I described above is what happens, but the write is only confirmed to the client when that write is tagged as asynchronous. For so-called synchronous writes, the data must be persisted to a stable storage medium before it can be acknowledged as saved. Writes are still batched, and making the client application wait for five seconds for the write buffer to be flashed is not really an option. So ZFS will write this data to a persistent journal called the ZIL (ZFS Intent Log). By default, this lives in the data array. It can be slow, and it gunks up other activity on disk, especially with HDDs because they can only do one thing at a time. A SLOG is a separate log device for the ZIL, allowing that stream to be written to more quickly, and hence dramatically speeding up synchronous writes.</li>
<li>Special VDev: When a Special VDev is present, metadata (and optionally very small files) are written there instead of to the main data array. This can have a huge impact on high metadata workloads, like list-heavy workflows.</li>
</ul>
<p>These are all very cool, and each of them scratches a very particular nerd itch for me. However, they all also carry their own issues or complexities. These trade-offs have led me to integrate none of them into my home server right now. I am going to keep an eye on performance, and see what I can implement in the future to help alleviate issues that I do end up hitting.</p>
<p>There are a couple of issues that affect all three of these optimisations. First of all, each of these depend on having a storage device (or devices) that are considerably faster than HDDs. In 2025, I believed that this was either SSDs: either SATA or NVMe. However, NAND flash has a literal fatal flaw: it burns out when you write to it a lot. A lot of these workloads are write-heavy, and I am wary of burning out any drives I might use for these purposes. In the enterprise, there are actually specialised devices that don’t burn out, but I don’t feasibly have access to Optane or NVDimms at home. Of all of them, the SLOG is the one that gives me the most trouble here.</p>
<p>There are also some specific things to consider. For instance, L2ARC is useless if you don’t have data that is routinely falling out of ARC. I am anticipating very low workloads, and I have a lot of RAM considering my needs. ZFS includes excellent telemetry about ARC hits and misses that should allow me to diagnose issues that L2ARC might solve.</p>
<p>Special VDevs can be a huge accelerant for a number of workloads, especially for my SMB share and Nextcloud deployment. However, the metadata is the key to all the rest of the data in the storage pool! If that is lost or goes bad, the data on my HDDs is meaningless. Therefore, it becomes a potential point of failure for the entire ZPool. As a reminder, my server has two HDDs and one NVMe SSD. I’m not comfortable offloading a crucial component of my storage to only a single device.</p>
<p>While I am waiting, my current idea is to buy a couple of smaller SATA SSDs for my SLOG (these are very inexpensive), and a couple of larger ones for my Special VDev, arranged in some redundant way. This comes with a few snags, though. My motherboard only has four SATA ports, which requires that I buy a host bus adapter to add more. Adding four SSDs would also take up two of my drive enclosures, limiting me to only four HDDs. I think this would be okay, but it is worth noting. If I do take this route, it’s worth noting that these SSDs also suffer from the “fatal flaw” I mentioned above. I’m less worried about damaging separate devices that are not used for booting, especially when they are as cheap as they are.</p>
<h2 id="why-not-zfs" class="anchored">Why <em>not</em> ZFS?</h2>
<p>At this moment, I am deploying a mirrored VDev for my HDDs, and nothing else. Because I am lacking any other optimisation layers, there are some workloads that I am reticent to put on ZFS right now.</p>
<p>Chief among these is my database. I am planning to deploy a single PostgreSQL instance to store data for all of my services. I like PostgreSQL, and I don’t particularly care for running databases in containers, so this suits me. However, I want it to be performant and not give me headaches. Like any good database, PostgreSQL demands synchronous writes and so within ZFS today, my only option would be to have it completely dependent on the speed of my spinning drives. Given that I’ve got an NVMe drive just sitting there, this feels like a horrible waste. As such, I am keeping XFS on that drive and running my database workloads there, with frequent backups to my ZPool.</p>
<p>And that’s about it. If you read this entire thing, I hope you both enjoyed yourself and learned something. If not, I’m sorry! Although this post was very long, you have nobody to blame except yourself for reading it all. Thank you very much, and I’ll see you next time, when I’ll probably be writing about databases.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/project-sunshine-part-3-the-software/</guid>
  <pubDate>Sat, 27 Sep 2025 18:42:50 GMT</pubDate>
</item>
<item>
  <title>Project Sunshine, Part 2: The Metal</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/project-sunshine-part-2-the-metal/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-09-23</p>
</header>
<p>The wired ethernet I wrote about in <a href="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1">part 1 of Project Sunshine</a> is working wonderfully. Laura just told me she’s never before seen an OS update download so quickly. With that infrastructure in place, it’s time to move onto the next critical part of de-clouding my life: actually getting a server. At the project kick-off, I knew a few things already:</p>
<ol type="1">
<li>I want to build it myself. None of this off-the-shelf NAS nonsense, I’m a computer person and I shall be building my own for this. Partially because this is my digital sovereignty project and I shall stand on principle, but mostly because I can meet my needs better with something custom, and also a little bit because I like building computers.</li>
<li>I demand silence. This thing will be at low but steady load most of the time, and at that load, I really want it to not be a nuisance. I live in an apartment, so it’s not an option to leave something noisy in a basement.</li>
<li>I hate power bills and love the planet. I want it to sip power! There will be at least an entire post about making this server as efficient as I can, but out of the gates I want to be deliberate with my component selection to maximise efficiency.</li>
<li>I am planning to stream media, which means I need graphics. I ruled out discrete GPUs pretty early, and all the Jellyfin and Plex docs advise against AMD Ryzen processors. Apparently, Intel’s Quick Sync technology is a bit of a moat.</li>
<li>I really want something that will do better for me in this role than a Mac Mini. This is a tall order! That thing is incredible. I’ll talk about this more a bit later.</li>
</ol>
<p>With all of these things in mind, I set about planning out a build. While there are some newer cases out there for storage focused PC builds, the venerable <a href="https://www.fractal-design.com/products/cases/node/node-304/black/">Fractal Design Node 304</a> won out. I had also considered its larger cousin the Node 804. That case is awesome, but it has a much larger footprint, and comes with features that I don’t need. It’s still worth it, even factoring in the ITX tax.</p>
<p>This case fits six 3.5” hard drives in it, which is very exciting considering I haven’t owned a hard drive since 2013. This much storage gives me a lot of options, broadly in two dimensions: drive selection, and drive configuration. The latter is going to be its own post a bit later on. This post is just about hardware.</p>
<h3 id="drive-selection" class="anchored">Drive Selection</h3>
<p>Hard drives aren’t just higher capacity than SSDs, they are also considerably cheaper in $/TB, and will even outlast SSDs on a lot of workloads. That’s about what I knew coming into this, but it turns out there was a lot more knowledge coming my way. My decision-making regarding hard drives was primarily along three axes: sound, power consumption, and reliability. Alongside that, I also care about the price and the speed. The capacity is almost an orthogonal choice here: most hard drive lines come in a range of capacities, so that is something I can choose according to my needs. I very quickly learned that ordinary consumer hard drives were not going to fit my bill. For the kinds of things I’m planning, I will need NAS grade drives. I also learned that I want to make sure that they use CMR recording technology rather than the cheaper and denser SMR. SMR (Shingled Magnetic Recording) was introduced only a decade or so ago, and allows more data to be stored on a platter, but their design entails lower write speeds that all but entirely precludes doing anything with ZFS or RAID. Luckily for me, drives marketed as NAS grade almost entirely use CMR. There was a scandal about SMR drives being poorly labelled and shoved into existing NAS lines a few years ago, a scandal from which I can now benefit, because this information was easy to find.</p>
<p>It was harder to find information about another innovation in spinning rust drives over the last decade: helium filling. By hermetically sealing drives and filling them with helium, you can get some pretty serious efficiency savings, as well as better acoustic performance. As best I could tell, this was first sold in 2013, making it the second cool new thing that’s happened in this space since I last owned a hard drive. Unlike the recording technology, this is a lot harder to find on a product page. I had to go trawling through vendor data sheets to find this out, which is exactly what I did. Looking at the Seagate Ironwolf line, one can see that their 10TB drives are helium-filled, while their 8TB drives use air. This results in a 2.6W drop in idle power consumption, as well as a 10dB drop in idle sound. I cross-checked this with a few other vendors while comparison shopping, and this was pretty consistently the difference.</p>
<p>After a lot of comparison and looking around, I decided that the WD Red Plus line was probably best for me. The drives are quiet, power efficient, perfect for my needs. The only one in that line that is helium-filled is 12TB. That is honestly a little bit silly, but it does mean I can get away with two of them, rather than four or even more. It is also only one of the WD Red Plus 12TB drives that is helium-filled, since they have an air-filled model too. I needed to be very careful in buying WD120EFBX and not WD120EFGX.</p>
<p>Was I very careful? I’m sorry to report that I was not. I accidentally bought drives with the serial number WD122KFBX. These are 12TB WD Pro drives. Relative to Plus, Pro is more enterprise-focused, meaning that the drives are more reliable, support higher write volume, are faster, and are of course more expensive. I could have lived with this, except that that model is also air-filled. After taking a look at the data sheet again, I decided to RMA them and replaced them with the equivalent helium-filled drives: WD121KFBX.</p>
<p>Keen-eyed readers will note that that product code is different from the one I mentioned above. In my excitement to have found helium-filled drives, I did not note that I was still looking at WD Red Pro, rather than the WD Red Plus that I was actually aiming for. I didn’t notice this until I wrote this section and found the disagreement between SKUs. I am extremely embarrassed about this whole debacle. In short, buying hard drives is an absolute minefield and you should be very careful. Consult vendor data sheets, and paste SKUs into Newegg or something. Don’t rely on websites having good search features, because they don’t surface all the information one might care about, and it’s very easy to get confused between similarly named brands. Having said that, I do feel like I can fairly confidently recommend the WD120EFBX if you have a use case similar to mine.</p>
<p>While I am excited to find all sorts of ways to use the staggering capacities of modern 3.5” hard drives, I don’t want to rely on them all the time. Even the most demure hard drives are relatively noisy and energy intensive, so I want to use them as little as possible, and I certainly don’t want to boot off them. I am pairing these hard drives with a 1TB NVMe SSD. It is a PCIe Gen 4 drive, which is blazing fast compared to my hard drives. However, I may pin it to Gen 3 if that gives a meaningful power saving. I’m not sure yet, and making this thing as efficient as possible is going to be a future task. I went with the Crucial T500 line, which are well-reviewed but otherwise unremarkable.</p>
<h2 id="other-components" class="anchored">Other Components</h2>
<p>My first choice processor was the Intel 12400. This is an older midrange CPU with integrated graphics. It’s got great performance for the things I want it to do, with a modern enough stack that I can transcode movies all day long. Unfortunately, my ITX choices came calling, because I just couldn’t find a mini-ITX motherboard for the LGA1700 socket. Not at a reasonable price, anyway.</p>
<p>This ended up pushing me to buy the brand new successor to this chip, skipping forward four whole generations to the Core Ultra 225. Intel rebranded their CPU lineup a couple of generations back, so the names are totally different, but as far as I can tell, this is the same tier of processor. I wasn’t able to find much in the way of reviews of this chip, but it’s a 10-core (6 performance and 4 efficiency) 65W CPU that should be no match for my cooling.</p>
<p>I’m reusing 64GB of DDR5 that I had lying around. This is outrageously overprovisioned for this server, but I had it lying around. I had it lying around as a consequence of my choice of CPU cooler. I decided to use the Noctua L12Sx77 that was previously in my desktop here. I needed to buy a new CPU cooler for this server, and I chose to reconfigure my gaming PC slightly by installing a smaller CPU cooler. This Noctua unit is great, but it requires that I configure my Terra in the maximum CPU side, which dramatically restricts my GPU options. That’s probably the first component I will upgrade in a few years time, and I figured that I may as well make room for that now. I landed on the ID Cooling IS-55 for this, which is great, but my existing RAM wouldn’t fit under it. I bought new RAM, and so now I have 64GB of 6400MT/s RAM I can use here.</p>
<p>Finally, I chose the Corsair RM650E power supply. This is laughably overpowered for what I need. I’ll be surprised if I ever reach 50% of its rated output. But it was reasonably priced, and posts pretty good efficiencies at very low power. Ideally, I would have been able to find a lower power unit, but high end low power PSUs are hard to come by these days.</p>
<p>With that, I’m ready to actually assemble this thing.</p>
<h2 id="the-build" class="anchored">The Build</h2>
<p>Overall, it was a delight to build in this case. I replaced the stock fans with their Noctua equivalents (2x92mm in the front and 1x140mm in the back). This will help with my ambitions for silence, but my main motivation here was aesthetics. I’m chucking some beige into this because I think it looks sick.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-2-the-metal/build.jpg" class="img-fluid" alt="An image of a partially built computer on a desk."></p>
<p>I removed the fan control daughterboard from the back. The stock case fans did not support PWM, but the Noctua replacements do, so running them off my motherboard gives me much better control here. My motherboard only has a single fan header, so I was left with a snarl of y-splitters, but it all worked great.</p>
<p>I did have to install the power supply upside-down. The way the extension cable came in just wasn’t going to work with how close the plug was to the front of the case. This was okay, though there is one less screw connecting the PSU to the bracket. It also means the power supply is exhausting air upwards rather than down and through the vent that is literally in the case for the PSU exhaust. I’m not too worried about this, because [a] as I said I doubt the fan will ever turn on, and [b] it’s blowing that air up into the airflow of the case from front to back.</p>
<p>Apart from those hiccoughs, this build was pretty easy.</p>
<p>After assembling the system, it posted first time, and I installed Fedora 42 Server. I’ll be discussing my choice of operating system in a future update.</p>
<h2 id="benchmarking" class="anchored">Benchmarking</h2>
<p>At idle, this thing is blissful. CPU core temperatures are a balmy 28ºC. But I wanted to see how this build scaled thermally and acoustically as load increases. I first ran <code>stress-ng</code> to see sustained CPU load. On a 100% all-cores workload, my temperatures maxed out at 58ºC or so, with which I was delighted. I’ve got thermal headroom here. I don’t know what — if anything — I might want to do with it yet, but it’s nice to have.</p>
<p>After verifying that my new server was at least well-built, I decided to try some actual benchmarks. The Phoronix test suite is a community-built dizzying array of benchmarks for exactly this purpose. After reading enough of the docs to be dangerous, I merrily went ahead and ran <code>phoronix-test-suite benchmark pts/compilation</code>. This is a suite that compiles a lot of popular open source projects. There are a couple of things I learned running these benchmarks:</p>
<ul>
<li>Use Tmux for things like this. I started it off without this while at a cafe. This was a huge mistake that I didn’t notice until an hour or so later when I wanted to leave. I had to start the process again.</li>
<li>Compiling code is kind of broken. Using <code>make -j$(nproc)</code> is not technically thread safe, so my benchmark runs would occasionally fail, and then succeed later. Phoronix will give up if it fails three times, but my GCC test failed twice before it got three successful runs.</li>
</ul>
<p>With enough attempts and a tmux session, I did eventually get the compilation time test suite to run. If you’re super into it, you can <a href="https://openbenchmarking.org/result/2509207-NE-BASELINE646&amp;rmm=baseline-tmux&amp;rmmi=1&amp;ppt=D">check out the results on OpenBenchmarking</a>. There’s not that much to see, though, as the results land about where I expected: a little faster than an Intel 12400, and a little slower than a Ryzen 9600X. More importantly for me, it was fast enough, very consistent (GCC took about 800s each time), and whisper quiet the entire time.</p>
<h2 id="did-i-beat-the-mac-mini" class="anchored">Did I Beat The Mac Mini?</h2>
<p>Short answer: Of course I did!</p>
<p>Longer answer: there are a lot of workloads that a new Mac Mini would probably beat this PC at. I certainly think it would have a real good go at that Phoronix test suite, and it would likely do it with lower power consumption, too. But there are a few key ways that I’m happier where I am now than I would have been with a Mac Mini, and they’re why I chose to build this at all:</p>
<ul>
<li>I got to build it. This was a fun Max enrichment activity.</li>
<li>It has space for built-in storage. For my purposes, I’d need an outboard hard drive enclosure, since Apple charges exorbitant prices for their storage upgrades.</li>
<li>I can run whatever operating system I like.</li>
</ul>
<p>If we spec up a Mac Mini, we need to make a few concessions to make it a fair fight. Apple gates 64GB of RAM behind the M4 Pro, and then they charge CA$900 above the base 24GB you get as standard. A Mac Mini with 32GB RAM and 1TB storage sets me back CA$1999. In addition to having less RAM, it also only has 1GbE out the back, which is a further downgrade from the 2.5Gbps LAN that I have previously written (at length) about installing.</p>
<p>On top of that, an external USB 3.2 Gen2 4-bay 3.5” HDD enclosure is listed at CA$249 on Amazon right now. So before we get to any hard drives, CA$2248, plus local taxes.</p>
<p>The total bill of materials for my components was:</p>
<ul>
<li>SSD: CA$144.99</li>
<li>PSU: CA$124.99</li>
<li>Case: CA$139.99</li>
<li>CPU: CA$318.65</li>
<li>Motherboard: CA$279.99</li>
</ul>
<p>I did bring my own RAM and CPU Cooler, and upgraded the stock fans. The fans cost me CA$71.29. A set of 32GB RAM (to match the Mac) goes for about CA$140, and the CPU came with a cooler that you wouldn’t use, so let’s add CA$48.90 for the cooler I would have used instead.</p>
<p>All up we are looking at CA$1268.80. Relative to the Mac Mini, I’ve saved myself about a thousand bucks. This is not to lampoon Apple (though their prices for storage and memory are outrageous), that little computer is an excellent generalist. But I have a particular set of skills and a particular set of needs, and I love my little enthusiast-oriented server.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/project-sunshine-part-2-the-metal/</guid>
  <pubDate>Tue, 23 Sep 2025 03:50:43 GMT</pubDate>
</item>
<item>
  <title>The Setup</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/the-setup/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-09-20</p>
</header>
<p>As I have previously written, I recently moved house. Over Christmas, I moved to Canada and began (finally) to live with my partner. Her apartment in Vancouver only had room for a single desk. While I would occasionally borrow it, most of my work this year has been done from a cafe, or a kitchen table. Sometimes, from bed.</p>
<p>With our recent move to Toronto, we made space for a home office (for two) a priority. Laura is beginning a masters programme, and I no longer have an office to travel to and work from. It’s taken a month or so, but I finally have my desk set up and fully appointed. I’d like to take this chance to walk through the gear I have, the choices I’ve made, and delight in going through a few of the sentimental knick-knacks that share this space with me. Without further ado, I present to you, <em>The Setup</em>.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/the-setup.jpg" class="img-fluid" alt="An evening picture of my desk, showing dual monitors, my PC, a Noguchi lamp, and other paraphernalia."></p>
<h2 id="section-1-furniture" class="anchored">Section 1: Furniture</h2>
<p>I bought the <a href="https://desky.ca">Desky</a> Zero. I had a really hard time finding a nice desk that wasn’t a sit-stand desk. I know from experience that I prefer to sit comfortably and take frequent breaks to walk around. Unfortunately, I appear to be in the minority, as it seems that motorised sit-stand desks have well-and-truly taken over the market, and my options were either something cheap and crappy — my last home desk bowed in the middle because it was basically made of cardboard — or ludicrously expensive. Desky sell a fixed height model of their desk, and I opted for the hardwood desktop: white oak, which I chose because it’s a sustainable and durable choice.</p>
<p>I spent a bit of time looking around at office chairs, and the overwhelming recommendation coming out of the hive mind of YouTube and Reddit was that for its price, the <a href="https://www.haworth.com/na/en/products/stools/soji.html">Haworth Soji</a> is an excellent choice. I got it in green (this will be a theme).</p>
<h3 id="lighting" class="anchored">Lighting</h3>
<p>I’m interested in some bias lighting for behind my screens, but I haven’t made a choice on that yet. And while I looked at various monitor light bars, none of these seemed like good fits for me:</p>
<ul>
<li>I didn’t want a dingus that sat on my desk. I like all my lights to be controlled through the same home interface.</li>
<li>I refuse to add AA batteries to my life anymore.</li>
<li>I wanted configurable temperature.</li>
<li>I wanted to use a webcam, and all but the priciest options preclude that chance.</li>
<li>Most monitor light bars seem designed for one landscape monitor. I like a vertical monitor next to my primary.</li>
</ul>
<p>I figured that a nice diffuse light source would both look nicer, and do a better job of illuminating my space. I ended up treating myself to a genuine Noguchi lamp: The <a href="https://shop.noguchi.org/products/akari-1n">Akari 1N</a>. It wasn’t cheap, but it’s gorgeous. I fell in love with the asymmetry of it. It was handmade out of washi paper and bamboo, and its story brings me joy every time I look at it.</p>
<h3 id="desk-mat" class="anchored">Desk Mat</h3>
<p>I wasn’t going to leave a beautiful hardwood desk at the mercy of my own clumsiness and carelessness regarding sharp things and cups. I went through a stunning number of options, but I eventually chose the <a href="https://drop.com/buy/drop-dwarf-factory-terrarium-desk-mat?defaultSelectionIds=980791">Terrarium desk mat</a> by Dwarf Factory, in green.</p>
<h2 id="section-2-hardware" class="anchored">Section 2: Hardware</h2>
<p>There are three computers on my desk. Two MacBook Pros, and a Linux workstation that I built earlier this year:</p>
<h4 id="a-silver-14-m1-pro-macbook-pro-2021.-work-issued." class="anchored">A silver 14” M1 Pro MacBook Pro (2021). Work-issued.</h4>
<p>This is perhaps the finest laptop I’ve ever owned. I love it so much that I bought its successor for personal use. This thing is light, performs well, and has a battery that lasts forever. It serves my needs perfectly, and has been a transformative change from the line of laptops it replaced.</p>
<h4 id="black-14-m3-pro-macbook-pro-2023.-personal-device." class="anchored">Black 14” M3 Pro MacBook Pro (2023). Personal device.</h4>
<p>This inherits all of the benefits of my work laptop, and some. It got a bump in RAM (36GB from 32GB, which is a strange amount of memory to me), and more importantly, it is adorned with stickers I’ve collected over the years. A flash of personality that my corporate overlords neither desire nor deserve.</p>
<h4 id="fractal-terra-pc" class="anchored">Fractal Terra PC</h4>
<p>The first computer I ever built. I’d been on the fence about this for a while. After all, I already had plenty of computer between my two laptops. And while I did want to game, and I did want something with which I could return to Linux, it felt like a lot of work for very little return, especially when I had a PS5.</p>
<p>My move over the holidays gave me the opportunity, and I took it. In my post about using Linux, I told this whole story, so I shan’t tell it again. However, I never shared any specs, so I’ll do that here:</p>
<ul>
<li>CPU: AMD Ryzen 7 9700X: I love this thing. It performs only modestly better than the 7700X before it, but it does so with huge efficiency gains, which really help with this case.</li>
<li>GPU: NVIDIA 4060 Ti 8G (MSI Ventus 2X): This GPU does what I need it to do, but was probably a mistake in the overall build. In hindsight, I should have purchased the AMD 9070 XT, or at least the variant of this card with 16GB of VRAM. I was at the end of the 40-series and cards were hard to get hold of, and I didn’t have the understanding that this build gave me. Still, I can’t justify upgrading right now, so this knowledge will have to be happy serving me in the future.</li>
</ul>
<p>The other less interesting features include 2 2TB WD Black SN850X M.2 drives, the ASUS ROG Strix B650E-I motherboard, an ID Cooling IS-55 CPU cooler, and 64GB of DDR5 RAM. I had to make sure I got RAM that was small enough to fit under the cooler. I also replaced the CPU cooler fan with a Noctua 15mm fan, and placed another one of those at the bottom of the case under the power supply.</p>
<p>This whole PC runs Fedora. For software development, writing, study, and everything else like that, I use Sway. It works great with my dual monitors, fractional scaling, and everything else (despite the fact I have to start it with the <code>--unsupported-gpu</code> option). For gaming, I use XFCE4, and have the second monitor disabled.</p>
<h3 id="connectivity" class="anchored">Connectivity</h3>
<p>My Macs connect to a Caldigit TS3+ with a single cable. This connects to ethernet, my two monitors, and a USB KVM that manages peripherals. The PC connects to all of these things directly.</p>
<h3 id="monitors" class="anchored">Monitors</h3>
<p>Speaking of monitors, there are two of them. I’m unable to use multiple monitors unless they are exactly the same. These are Dell 4K 27” panels. Not the ones with USB-C. My dock goes to the HDMI inputs and runs at 60Hz, while my PC gets the DP and can drive them at 120Hz, which is amazing. I notice it for sure when browsing, but especially when gaming.</p>
<p>Both monitors are on individual arms, also from Desky. I am suspicious of dual arms, mostly because I have a number of thick cables, and the cable management systems in most monitor arms barely support one monitor, let alone two.</p>
<h3 id="peripherals" class="anchored">Peripherals</h3>
<h4 id="mouse" class="anchored">Mouse</h4>
<p>My mouse for the last forever has been the MX Master. Whenever I need a mouse, I get one, and currently I have the MX Master 3S. Fortunately, its 3 bluetooth modes map onto each of my three computers.</p>
<h4 id="keyboards" class="anchored">Keyboards</h4>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/keyboards.jpg" class="img-fluid" alt="A photograph of two mechanical keyboards, taken from above. There is a black one and an off-white one, each with accent-coloured key caps, using pastel versions of the colours of the old six-colour Apple logo"></p>
<p>At present, my desk holds two keyboards. This is admittedly probably one too many. In the past I lived alone and worked in an office. At home, I had the clicky keyboard of my dreams, while at the office I kept a much more demure silent tactile configuration.</p>
<p>I built both of them myself. The lighter one is a <a href="https://nuphy.com/collections/keyboards/products/gem80">NuPhy Gem 80</a>, fitted with <a href="https://www.kailh.net/products/kailh-box-thick-clicky-switch-set">Kailh Box Navy</a> switches. I love them, but if I had my time again, I’d likely go with the slightly softer Jades. The black one — my “stealth” keyboard — is a <a href="https://www.keychron.com/products/keychron-q1-max-qmk-via-wireless-custom-mechanical-keyboard">Keychron Q1 Max</a>, and uses <a href="https://www.kailh.net/products/kailh-deep-sea-silent-pro-box-switch-set">Kailh Deep Sea Silent Pro Box Tactile Whales</a>. Both keyboards use the MT3 2048 Extended key caps in <a href="https://drop.com/buy/drop-biip-mt3-extended-custom-keycap-set">light</a> and <a href="https://drop.com/buy/drop-biip-mt3-extended-2048-dark-keycap-set">dark</a>. I adore the texture, profile, and colours in these key caps. Keeping these the same makes switching a breeze.</p>
<h2 id="section-3-stationery" class="anchored">Section 3: Stationery</h2>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/stationery.jpg" class="img-fluid" alt="A pad of paper with two pens resting on it."></p>
<p>For years I have used <a href="https://www.studioneat.com">Studio Neat’s</a> paper products, and their <a href="https://www.studioneat.com/products/panopad">Panopads</a> fit perfectly on my desks. I’m left-handed and I write sideways (the right side of the page is the top for me), so this layout works perfectly for me.</p>
<p>My mother bought me a <a href="https://tomsstudio.com/products/lumos">Lumos Pro</a> for Christmas. I loved it, and very quickly supplemented it with a Lumos Duo for some extra colours. I’m currently rocking black in the primary pen, and then green and red in the Duo. I use the 3mm writing tips, and these are a delight to look at, to write with, and to interact with generally. They have a tendency to roll (hence the rollstop on one of them; alas the Duo doesn’t really have space for one). These pens are the real deal. My partner has one, as do two of my best friends.</p>
<h2 id="section-4-other-stuff" class="anchored">Section 4: Other Stuff</h2>
<p>Some might call my desk “cluttered”. I prefer to call it “alive”. Everything here has a story, and it makes me happy. Most of the items are gifts, or souvenirs, that I’m going to self-indulgently itemise here.</p>
<h3 id="django-and-ditto" class="anchored">Django and Ditto</h3>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/django-and-ditto.jpg" class="img-fluid" alt="A teddy bear holding an embroidery, and sitting next to a keychain pair of crocs, a crocheted cactus, and a sculpted pink model of the Pokémon Ditto"></p>
<p>To the right of my desk sits Django the teddy. When I was born, a particularly crafty aunt resurrected her grandmother’s tradition of handmaking teddy bears for grandchildren. She has one (original, and the prototype of Django), and my dad has one. He’s over 30 years old now, and he’s lost his clothes. He long since retired from bed duty, and now watches over my desk.</p>
<p>He is holding an embroidery that my dear friend <a href="https://www.monicarenn.com">Monica</a> made for me, for my 29th birthday. Between his legs sits the cork from a magnum of Ruinart that my friends and I shared for my 30th birthday, as well as a fake flower that was a gift from a bartender on a night out in Washington DC with another dear friend (and colleague!) Peter.</p>
<p>To his left are a pair of clogs that were a gift from a trip Monica took to the Netherlands. And to his right is a hand crocheted cactus. This is the creation of a younger cousin (and daughter of the aunt who made Django). Next to the cactus is a Ditto made out of polymer clay. This I owe to another close friend of mine, Sam. She has a matching blue one, an in-joke about how we first met.</p>
<h3 id="the-quarry-and-garage" class="anchored">The Quarry (and Garage)</h3>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/rocks.jpg" class="img-fluid" alt="A photograph of some rocks and sea glass, as well as some wooden toy vehicles, collected under a monitor"></p>
<p>As well as being talented artists, Sam and Monica are both avid collectors and enjoyers of rocks and sea glass, respectively. Sitting on the felt mat are an assorted collection of such items, gifted at various times.</p>
<p>Alongside the geological oddities sit a few of my toy car collection. In my last house, I treated myself to one of those rugs that have a map of a pretend town on them. When I travelled to places, I would bring back a toy car for it. Really indulging my inner child there.</p>
<p>The rubbish truck (named so for its role, not its quality) is from a child’s toy shop in Washington DC. The wooden car (it’s abstract) on the left is something I bought from a local artist’s shop while visiting Melbourne (rather aptly for the Grand Prix). And the tiny car was a gift from Monica and our mutual friend Ash.</p>
<p>Finally, the cube is a fidget toy that was a Christmas gift from my oldest sibling. They have something of a collection of them, and when I visited home last Christmas, they knew it was a slam dunk. Of all the items on my desk, this is probably the one I touch the most (excluding my keyboards and mouse).</p>
<h3 id="the-sunbathers" class="anchored">The Sunbathers</h3>
<p><img src="https://nonsense.mpwb.xyz/posts/the-setup/tempest.jpg" class="img-fluid" alt="A wooden German figurine, and a stylised panda bear, standing under a lamp. The German is wearing lederhosen, holding a pretzel in one hand, and a beer stein in the other. Leaning against him is a tag that read: Tempest: Knows results can't always be measured and rules don't always apply."></p>
<p>Underneath my lamp you’ll find two little guys. They don’t have names, but again were gifts from Monica and Sam. The German character from Monica’s European adventures, and the panda a birthday gift from Sam last year. His name is <a href="https://lonelyanimalfriendshipsociety.blogspot.com/2021/05/maran.html">Maran</a>, and he was made by The Lonely Animal Friendship Society. The Tempest tag also belongs to him.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/the-setup/</guid>
  <pubDate>Fri, 19 Sep 2025 17:07:41 GMT</pubDate>
  <media:content url="https://nonsense.mpwb.xyz/posts/the-setup/the-setup.jpg" medium="image" type="image/jpeg"/>
</item>
<item>
  <title>Project Sunshine, Part 1: Home Networking</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-09-03</p>
</header>
<p>You’d think I’d’ve learned my lesson by now that beginning series of blog posts is a mistake, because it puts the onus on me to finish something off. I’ve achieved plenty in my life, but I’ve never achieved more than I would have otherwise by putting pressure on myself to “finish something off”. Evidently I have not learned my lesson, but I promise it’s going to be different this time!</p>
<p>I am on a mission to de-cloud my life. I pay too much money on an ongoing basis to companies I despise to do despicable things with my data while degrading the quality of the original service that led me to become a customer in the first place. As I wrote in my piece about Linux, I’ve really hit a wall with this stuff recently, and my computing tastes are changing drastically, and are doing so quickly.</p>
<p>I have the good fortune and the privilege to be able to significantly distance myself from online services by building my own server to run in my own home, to be used by me and my family. Not everyone can do this, and I think few people should. In an ideal world, online services would be public goods rather than privately-owned walled gardens. This is not a particularly original take. You can read about it in many places online. For instance, I found myself nodding along with Drew Lyton’s recent blog post on the topic: <a href="https://www.drewlyton.com/story/the-future-is-not-self-hosted/">“The Future is NOT Self-Hosted”</a>. Like the author, I do not think this is a good thing for everybody to do. However, as Drew also writes:</p>
<blockquote>
<p>But if you <strong>really</strong> value control, freedom and flexibility — or you’re a software engineer that suffers from mild mania and hyperfixation — it can be a pretty fun time.</p>
</blockquote>
<p>Yeah. I’m going to write all about self-hosting. The trials and tribulations, the fun things and the less-fun things. But not in this blog post.</p>
<p>This is actually a blog post about network cables. I’m not building a server yet, I need another month or so to acquire all the things I need. But I did just move house (my partner is starting a masters programme in a new city). We moved in the other day, and I need to configure our local network. I want it to be fast, I want what can be wired to be wired, and I want it to be able to support a home server. I haven’t had wired internet since I was using dial-up, so this is all quite the novelty. We moved into a pretty new building, and this comes with a couple of benefits:</p>
<ol type="1">
<li>Fibre to the unit, and</li>
<li>Ethernet in the walls.</li>
</ol>
<h2 id="part-1.1-internet" class="anchored">Part 1.1: Internet</h2>
<p>I signed up for a fibre plan that is supported by our building. I plugged it in and it all just worked. Great.</p>
<h3 id="interlude-ethernet-primer" class="anchored">Interlude: Ethernet Primer</h3>
<p>Before I go through what I actually did here (which isn’t a lot so don’t get too excited), I’d like to run through the highlights of what Max-from-a-week-ago would have considered more than anyone outside of professional networking needed to know about ethernet cables.</p>
<p>To start with the very basics, ethernet cable is cable over which data is transferred. Typically this is for networking, but ethernet cable is used for all sorts of data transfer, including access control, telephone cables, video, or any other signal. Ethernet cable always seems to be a set of twisted pairs, and comes in shielded or unshielded varieties.</p>
<p>Shielding? Twisted pairs? Transmitting anything over a physical medium involves introducing some amount of interference, or noise. This is caused by resistance in the medium, as well as external electromagnetic waves that hit the cable. Shielded cables include a foil around them that attenuates incoming electromagnetic waves to increase signal fidelity. This protects from interference from things like electrical current, fluorescent lights, air conditioners, and almost anything else that does stuff with wires. Shielding has its downsides: it’s stiffer and thicker, making it more difficult to work with, and it costs more. The cable I have in my walls, and in my patch cables, is unshielded, which is apparently the standard. I have seen shielded cabling like this <a href="https://www.baseup.com">on site at work</a>, though.</p>
<p>Twisted pairs are another technique to reduce interference along a cable run. This is an Alexander Graham Bell invention, and the <a href="https://en.wikipedia.org/wiki/Twisted_pair">Wikipedia page</a> goes into a lot more detail than I can here and is genuinely fascinating reading (It also discusses standards for shielding in some detail, which I also recommend reading. Then you too can know the difference between F/UTP and S/FTP.) The gist of it is that twisting a pair of wires equalises the average distance of each component from a source of interference, and sending the same signal down each component allows the error to be isolated at the end. It’s something like this:</p>
<p><img src="https://latex.codecogs.com/png.latex?%0A%5Cbegin%7Baligned%7D%0AW_%7B1%7D%20&amp;=%20s%20+%20e_%7B1%7D%20%5C%5C%0AW_%7B2%7D%20&amp;=%20-s%20+%20e_%7B2%7D%20%5C%5C%0AW_%7B1%7D%20-%20W_%7B2%7D%20&amp;=%20(s%20+%20e)%20-%20(-s%20+%20e)%20%5C%5C%0A&amp;=%202s%20+%20e_%7B1%7D%20-%20e_%7B2%7D%20%5C%5C%0A&amp;%5Capprox%202s%20&amp;%5Ctext%7B%20if%20%7D%20e_%7B1%7D%20%5Capprox%20e_%7B2%7D%0A%5Cend%7Baligned%7D%0A"></p>
<p>We aren’t even close to done with the complexity inherent in ethernet cabling. Next up is the actual materials used in the wires. Ethernet can be either solid core or stranded core. That is to say that each component wire can be made of a single solid piece of material, or a lot of strands of a material. In ethernet cables, the material in question is always copper. Solid core cables experience less signal loss, but are also harder to bend, harder to stuff into RJ45 plugs, and easier to break. Stranded cables are the opposite side of those compromises. Generally speaking, solid core cables are the ones that are in wires, and get punched (technical term) into keystone jacks, which is the thing you plug an ethernet cable into. And the ethernet cables that you plug in (I think they are called patch cables) are usually stranded.</p>
<p>Ethernet cables are categorised into numbers, each representing different standards. These standards specify things like the materials, the number of pairs, the frequency of the twists, and then the throughput capacities that are supported, and the distances over which those capacities are supported. For example, Category (hereafter Cat) 5e (e for enhanced) was standardised in 2001, and guarantees at least gigabit speeds, and over short enough runs, is good enough for 2.5Gbps. Cat 6 is backwards compatible with Cat 5e, but it has stricter standards, and cannot run for as long. In return, you get support for 10Gbps. Again, each category supports both solid and stranded core, and offers different options for shielding.</p>
<h4 id="crossover-cables" class="anchored">Crossover Cables</h4>
<p>I had heard at some point in my life that the pins of ethernet cables were configured for transmission or reception, and that when connecting nodes, these pins need to be swapped over to ensure that the transmission pin at the source is connected to a receiving pin at the destination. This sounded nightmarish and dizzying. Fortunately for me, a feature called auto MDI-X (Medium-Dependent Interface-Crossover) makes this basically a non-issue in home networking.</p>
<p>After that I stopped caring. Hopefully that doesn’t come back to bite me. Anyway, back to the main thread.</p>
<h2 id="part-1.2-network-cabling" class="anchored">Part 1.2: Network Cabling</h2>
<p>When I was inspecting what is now my new apartment, I found two ethernet ports: one in the main bedroom, and one in the living room. I would have preferred one in the second bedroom, since that’s what we are going to use as a shared office, but such is life. An added bonus here is that our Apple TV and Nintendo Switch dock can plug into the internet as well. I probably wouldn’t have bothered otherwise.</p>
<p>Measuring it out, I can have a 10ft ethernet cable from the living room drop to where a TV should go. Then I’ll need a switch, and then it will continue on to the second bedroom, which is just behind the living room. Then I’ll need a second switch to fan out to four other devices.</p>
<p>But what kind of switch should I get?</p>
<h3 id="interlude-network-switches" class="anchored">Interlude: Network Switches</h3>
<p>Oh boy. I thought cables were complex. So you can’t just splice ethernet cable. The transmission requirements and the razor thin tolerances in the signal strength prohibit that. A switch is a powered piece of hardware that receives something on one of its ports, and retransmits it to one or more of its other ports.</p>
<p>Switches come in two variants: managed, and unmanaged.</p>
<p>A managed switch is the easier one to understand. A managed switch is a computer that has rules about what is allowed to be retransmitted, and the patterns in which that retransmission can happen. They can prioritise traffic, or indeed do pretty much anything else you can imagine doing with signal processing and a computer.</p>
<p>Unmanaged switches do something simpler, yet also less obvious, at least to me. My understanding is that it maintains an index of MAC addresses mapped to the port on which they reside, or at least the port that points towards them. If the switch doesn’t know how to route a particular request, it broadcasts it, and records the port on which it receives a response.</p>
<p>MAC addresses are the address structures at the link layer, as opposed to IP addresses, which are the addresses at the internet layer. They are lower level, less structured, and less abstract. In practice, the routing that the switch needs to do to find out where a particular device is located happens early during the networking relationship. Suppose a device wants to connect to another device on the same network: typically, it will send a request to an IP address. But to get to its destination, that IP address needs to be resolved into a MAC address. That happens with an ARP (Address Resolution Protocol) request. This is what the switch intercepts.</p>
<p>I am only running a home network. Nothing is exposed to the public internet, and all devices on it are implicitly trusted. Unmanaged switches are fine by me.</p>
<p>Switches also come in a variety of speeds. I didn’t want to bottleneck anything, but I’m also on some kind of budget. I chose 2.5Gbps, because 1Gbps was insufficient, and 10Gbps was (a) overkill, and (b) costly. Our two PCs only have 2.5Gbps ethernet, my Caldigit TS3+ dock has 1Gbps ethernet, and there’s only Cat 5e in the walls. This way I can keep my prices reasonable, and not bottleneck my internet.</p>
<p>I went with the TP-Link TL-SG105S-M2. While it doesn’t have a snappy name, it makes up for that by being a reasonably-priced, reasonably-sized five-port 2.5Gbps capable unmanaged switch. To be on the safe side, all my patch cables are Cat 6, and I think I’m going to plug my future home server into my fibre modem.</p>
<hr>
<p>Taking a look in the network cabinet, there was a mixture of good news and bad news. The good news is that I could see two Cat 5e cables coming down. The bad news was that they weren’t really connected to anything I’d ever seen before.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/punchdown.jpg" class="img-fluid" alt="A 66 punch-down panel in my networking cabinet"></p>
<p>With some reverse image searching, I learned that this is apparently a 66 punch-down block. These are designed for telephone cables (as pictured, how quaint), but builders of the era sometimes parked ethernet cables in them when there was no immediate use for them, but wanted the wires in the walls. Works out well for me.</p>
<p>I also learned a lot of other things. In no particular order:</p>
<ul>
<li>I can’t reasonably use that panel.</li>
<li>Ethernet cable that goes in walls is solid core and not very flexible, whereas the patch cables we use for devices are stranded. Stranded patch cables tend to be crimped into RJ45 connectors, which is what I have always called an ethernet port. Solid core internal cables do not. Solid core ethernet cables are instead more commonly terminated into the “port” version of that connector, which is called a keystone jack. (I refuse to call them “female”)</li>
<li>These aren’t just normal push-through or screw-in terminal blocks. You need a special tool called a punch-down tool.</li>
</ul>
<p>I was a little nervous about playing in this space because:</p>
<ol type="1">
<li>I rent, and this is not my infrastructure. However, I’m going to do an excellent job, and I actually unwrapped this 66 block, indicating I’m the first occupant of this apartment ever to use it. Furthermore, I’m providing a legitimately useful and obvious improvement here.</li>
<li>I’ve never done any of this before, and I’m worried I’ll do it wrong. I’ve solved this by reading a lot and watching a lot of YouTube videos.</li>
</ol>
<p>So I went onto the internet and I purchased a well-reviewed punch-down tool, some 1ft Cat 6 patch cables, and a dual-port keystone jack (I actually purchased five of these, because they came in packs of five. This stuff is really not designed for hobbyists).</p>
<p>Look out world, here I come.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/wiring-job.jpg" class="img-fluid" alt="Cat 5e cable punched down into a dual-port keystone jack"></p>
<p>The bottom one pictured is actually the second one I did. I was trying to match the length of the second one for its placement in the panel, and I didn’t quite get it right. But once the cover is on, you can’t really tell. I considered re-terminating it, but I wanted to test it first.</p>
<h2 id="part-1.3-testing" class="anchored">Part 1.3: Testing</h2>
<p>Testing proved to be somewhat challenging! Our gaming PCs both have 2.5Gbps networking, but they are temporarily out of action owing to the move. I had a Caldigit TS3+ which supports 1Gbps ethernet, but lugging that around my house attached to my laptop, and attached to its power brick is quite the palaver. I ended up buying this Belkin dongle and plugging in one of the 1ft patch cables I got into it, and bam! Mobile testing setup.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/testing-rig.jpg" class="img-fluid" alt="A Belkin USB-C to ethernet dongle attached to a 1ft black ethernet patch cable"></p>
<p>I had planned to attach this to a laptop and walk around the house, but as I looked at it, I had the devilish idea to try my phone instead. And it works great! As soon as you plug it in, a new “Ethernet” option appears in the Settings app.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/settings.png" class="img-fluid" alt="Two iPhone screenshots of the Settings. The first highlights the Ethernet option, and the second shows networking information"></p>
<p>Starting simple, I plugged this directly into my router. Nothing happened. I turned off Wi-Fi, and suddenly I was given an IP address. I ran a speed test, and things were pretty good! Not much better than Wi-Fi in this exact location, but less than 30cm away from your access point is pretty close to ideal conditions. Great start.</p>
<p>Moving on, I plugged it into the two wall-based ethernet ports. iOS is a bit weird when you move this thing around, I had stale connections until I removed the dongle from the phone side as well, but I worked into a pretty quick routine. I fiddled with the router a little bit — removing the patch cables from the back to identify the cable drops — so that I could label them (you’re welcome, future nerd resident). Once I’d had this all tidied up, I moved onto testing at the switches. And these worked perfectly! From the office, the speed at the switch was a considerable upgrade over Wi-Fi.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/speed-test.png" class="img-fluid" alt="Two internet speed tests side by side. One shows 690.91Mb down and 889.34Mb up, and the other shows 302.11Mb down and 176.54Mb up. The former is a wired internet connection, the latter is using Wi-Fi"></p>
<p>Now it’s all ready for everything to be really actually set up, with desks and lights and all of those things. I am giddy and excited in ways becoming of maybe a six year old. I can’t wait.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/project-sunshine-part-1/</guid>
  <pubDate>Wed, 03 Sep 2025 02:22:08 GMT</pubDate>
</item>
<item>
  <title>Linux</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/linux/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-08-02</p>
</header>
<p>This is the story of how I have ended up on Linux for a large amount of my computer use, and all of my gaming. It’s a personal story, but I do genuinely believe that the carrot of really great gaming compatibility on Linux in 2025, along with the ever larger sticks that large tech companies are wielding against their users is fomenting an environment that makes this kind of change attractive. This is not a “How To”, this is a story.</p>
<p>I’ve only ever owned MacBooks as personal computers. My first was the aluminium unibody MacBook in 2008, followed by a 13” retina MacBook Pro in 2013, and an M3 Pro 14” MacBook Pro that I bought in 2023. Interspersed between that, I’ve had a collection of work computers, all of which have been Macs. I know the operating system really well at this point. I like it. I like my collection of indie apps. However, a need for digital sovereignty has been growing within me in the last couple of years, and there is one real area that macOS doesn’t excel in as a general purpose computing platform: games.</p>
<p>I’ve been a lifelong PlayStation kid. I’ve owned (or had a parent who owned) every single PlayStation iteration. I was never on the bleeding edge, I loved the slim versions. The ones that came out a couple of years after the mainline release, and packed the same performance into a much smaller frame. I took my PS2 slim around with me everywhere. I moved my PS4 slim across the world. Then I bought the PS5 slim.</p>
<p>Have you seen the size of that thing? Moreover, a pretty serious knock to my wallet for a single purpose toy.</p>
<p>Enough was enough. As I began my third international move, I resolved to sell my PlayStation 5 Slim (which I could not reasonably fit in a suitcase) and not repurchase it. A huge factor in this decision was that Sony makes it impossible to move an account to a new region. I have games on my British PSN account, and I have games on my Australian one. To continue using this platform, I would need to create a third account, a Canadian one, to be able to continue purchasing things. I have been unable for years to play online, because I cannot attach a working card to my British account. So I’m losing those games. At some point, you just have to accept that you’re losing access to content that you are supposed to own, because the gatekeepers are so actively hostile to their users. You’ve got to make a principled stand some time.</p>
<p>So what now?</p>
<p>A custom gaming PC. I built a PC! Just for games! It’s amazing, I can play the original Witcher games, and I can play the Final Fantasy VII Remake, and I can play just a slew of other titles that were previously inaccessible to me. It’s been a cornucopia of PC-exclusive games for me to play. The fun doesn’t stop there, either. It seems like Sony recently started publishing their PlayStation exclusives onto Steam. I’ve been able to set up PCSX2 and have several very fun strolls down memory lane as I play Sly Cooper or Gran Turismo 3 (still the best one).</p>
<p>It works great. It’s perfect and I love it and it’s small and cool and fast and cute and I learned a lot about computers while I made it. It worked great. Except for one thing: I installed Windows on it. I had to! I wanted to play those PC exclusives, and that’s the way you play them. I accounted for that. I did not account for how hateful Windows is.</p>
<p>Windows 11 is truly horrendous. I couldn’t uninstall any of the crapware it came bundled with. Or rather I could, but they came back with every update, no matter how minor. I think I disabled all of the flags to show me ads on my desktop, on my lock screen, in my start menu, but I really had to hunt. I initially installed Windows without signing into a Microsoft account. That worked perfectly until I signed into Minecraft and it took my entire computer with it. Perhaps I’m the problem. Perhaps I’m holding it wrong. But at every turn, it felt like the operating system was fighting me, like it wasn’t for me.</p>
<p>At the same time, I was consistently hearing the same thing from a number of different sources:</p>
<blockquote>
<p>Try Linux!</p>
</blockquote>
<p>So I tried Linux. And everything just worked. Thanks to the Steam Deck, you can play most games just fine on Linux. So I installed Fedora 42 KDE on my second drive and I copied everything over and would you believe that it all worked perfectly? Every single game runs just as well as it did on Windows. I’m truly stunned.</p>
<p>To jump back into the real world for just a second, there are two caveats that I feel the need to point out:</p>
<ul>
<li>I don’t play any competitive multiplayer games. I suck. Not for any other reason, but it means that I don’t have to worry about kernel level anticheat and things like that. If you suck less, or care less about sucking, this will potentially be an issue for you.</li>
<li>There is one issue that I’ve found in Timberborn: I use a Logitech MX Master 3S, and had configured the back/forward buttons to rotate the camera, and my thumb scroller to raise and lower the camera. In Linux, under Proton, Timberborn interprets these mouse buttons as the same. My operating system distinguishes them perfectly, and so did Windows. I’ve tried fiddling with Steam input settings, and I truly have no idea what’s going on or what levers I can pull. My hope is that this bug is obsoleted by Timberborn someday supporting the standard “move around in 3d by holding the middle button down”.</li>
</ul>
<p>This probably isn’t too much of a surprise to anyone who has read even one other article on my blog, but gaming is not all I do on a computer. One of the appeals of building a PC was that I would have a general purpose workstation: something I could use to develop on, something I could use to develop things that I couldn’t develop on a Mac! It turns out that Linux does everything I want to do in this arena better than Windows as well, all without making me curse technology and everything it has wrought.</p>
<p>So I use Linux now. Not full time, because I have my MacBook, but damn close to it for everything I sit at a desk for. For this to work out as splendidly as it did, a few things had to line up for me:</p>
<ul>
<li>I have been growing increasingly frustrated and angry at the toll that large tech platforms are taking on my life, and the lives of the people around me.</li>
<li>I am similarly increasingly keen to avoid the privately-owned algorithms that govern so many of our digital experiences.</li>
<li>Linux is in a place where it is viable to use it to do all the things I want to do on a computer.</li>
<li>I had computing hardware that supports Linux well! I’ve run Linux before, and it’s especially tricky now in the wake of Apple Silicon, but using Linux on Macs has always been Linux on hard mode.</li>
</ul>
<p>I will have a lot more to say in the future about all three of those points, because I have projects underway to reclaim control over my digital presence, to be more deliberate about how I engage with computers, and all of the exciting things I can do now that I have a computer that just runs Linux things with minimal hassle.</p>
<p>On that last part, I’m truly off to the races now. I’m back into tiling window managers, I’ve disabled graphical logins, and I’m charting a course that moves me away from Fedora and onto something where I have even more control over my platform. Right now, I’m evaluating Gentoo. You can expect to see more edits to my <a href="https://code.mpwb.xyz/mpwb/dotfiles">dotfiles</a> as I continue on this journey.</p>
<p>It is such a joy to be excited about computers again. It is such a surprise to find myself wanting to play, explore, and experiment. There is utter delight here for me, and I am so grateful that these initiatives exist.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/linux/</guid>
  <pubDate>Fri, 01 Aug 2025 20:22:41 GMT</pubDate>
</item>
<item>
  <title>Andrea Gibson</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/andrea-gibson/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-07-26</p>
</header>
<blockquote>
<p>My body is a mason jar<br>
transparent as a jellyfish<br>
I wish for a heart you can see straight through<br>
for a voice that glows in the dark<br>
and a few really good friends to skip moon rocks to.</p>
</blockquote>
<p>From <a href="https://ohandreagibson.tumblr.com/Jellyfish"><em>Jellyfish</em></a>, by <em>Andrea Gibson</em>.</p>
<p>A few weeks ago, Andrea Gibson passed away. Their work touched the lives of many, many people. Today, I want to dedicate a few words to how their work touched my life in particular. It feels denigrating somehow to talk about someone’s art as only their work, when true art demands we place our humanity on display. And so I suppose this is less a discussion of how Andrea Gibson’s work has affected me, but rather how the aspects of their life they chose to share with the world have affected me. And boy howdy, have they affected me profoundly. In strange, surprising, beautiful and delightful ways.</p>
<p>The first Andrea Gibson poem I ever heard was <em>Jellyfish</em>, on <a href="https://www.last.fm/user/unaffectedscorn/library/music/Andrea+Gibson/_/Jellyfish?from=2012-09-20&amp;rangetype=1day">20th September 2012</a>. The second was <a href="https://ohandreagibson.tumblr.com/ido"><em>I Do</em></a>, later <a href="https://www.last.fm/user/unaffectedscorn/library/music/Andrea+Gibson/_/I+Do?from=2012-09-20&amp;rangetype=1day">that night</a>. I was 18. I was about to go to university. I was wide-eyed, full of joy, and utterly, perhaps blissfully unaware of what was coming my way. Both of these poems showed me a transformative way to love. To love recklessly and generously, loudly and proudly. But not to love naïvely. To love with one’s eyes as open as one’s heart. It is in that spirit that I write this tribute.</p>
<p>It’s tempting in many ways to look back at the summer of 2012 as a peak, a local maximum between the general ordeal of being a teenager, and the abject isolation of 2013–17. Tempting, I think, but harmful. Andrea Gibson’s poetry helped me see some of the deepest challenges in my life as parts of a story. They did perhaps more than anyone outside of my immediate friends and family to help me integrate the more upsetting experiences of my life into one cohesive Max. These lessons happened over years, in countless poems. Below, I am going to share a few that left particular impacts.</p>
<p>In <a href="https://ohandreagibson.tumblr.com/post/29305921209/rush-hour"><em>Rush Hour</em></a>, Gibson taught me how better to hold an open heart in a closed and callous world; how to have hope when anything we feel we can do is rendered meaningless in the face of such overwhelming cruelty:</p>
<blockquote>
<p>In the city where I live<br>
They’ve officially labeled panhandling on medians a crime<br>
because no one wants to drive by and see that shit</p>
<p>…</p>
<p>I remember being five years old<br>
Watching a feather fall from the sky<br>
and praying all day that the bird that lost it could still fly</p>
<p>…</p>
<p>So last night I freed a kite to the wind in hopes of it finding Palestine.<br>
It won’t.<br>
But I’m trying to remember who I used to be.</p>
<p>…</p>
<p>Tomorrow 600,000 feathers are going to fall from the sky.<br>
There’s no way we’re going to catch them all but somebody tell me<br>
We are at least going to try.</p>
</blockquote>
<p>In <a href="https://ohandreagibson.tumblr.com/nutritionist"><em>The Nutritionist</em></a>, Gibson taught me that to suffer is not to be alone; that there are safe spaces in which to share our pain, even in a world that demands of us our best presentation; that it is incumbent upon all of us to build those safe spaces for each other; that we do not necessarily need to be fixed:</p>
<blockquote>
<p>So let me tell you I know there are days<br>
it looks like the whole world is dancing in the streets<br>
when you break down like the doors of the looted buildings<br>
You are not alone and wondering who will be convicted<br>
of the crime of insisting you keep loading your grief<br>
into the chamber of your shame<br>
You are not weak just because your heart feels so heavy</p>
<p>if the only thing we have to gain in staying is each other,</p>
<p>my god that’s plenty</p>
<p>my god that’s enough<br>
my god that is so so much for the light to give<br>
each of us at each other’s backs whispering over and over and over</p>
<p>“Live”<br>
“Live”<br>
“Live”</p>
</blockquote>
<p>To be taught that we do not necessarily need to be fixed has been something I’ve had to have reinforced time and time again, while having the good fortune to be able to bask in the love of my friends, and my family. I wish it was not such a powerful thing to preach. I wish Andra Gibson had not been so radical. I wish their love was uncontroversial, and regular. But that isn’t our world, yet. If my words have introduced Andrea Gibson to anyone, I hope that their words in turn can teach you something about a radical, hopeful, uncompromising kind of love.</p>
<p>On the day that I met my partner for the first time, she cited an Andrea Gibson poem to me. One of those weird things that makes you feel connected to someone, I guess. The magic of both of us finding that connection in that moment is something that still brings a tear to my eye. The poem Laura had been thinking about was Tincture:</p>
<blockquote>
<p>Imagine, when a human dies,<br>
the soul misses the body,<br>
actually grieves the loss of its hands and all they could hold.<br>
Misses the throat closing shy reading out loud on the first day of school.<br>
Imagine the soul misses the stubbed toe, the loose tooth, the funny bone.</p>
<p>…</p>
<p>Nothing in space can imagine it.<br>
No comet, no nebula, no ray of light can fathom the landscape of awe,<br>
the heat of shame.<br>
The fingertips pulling the first gray hair and throwing it away.</p>
<p>I can’t imagine it, the stars say.<br>
Tell us again about goosebumps.<br>
Tell us again about pain.</p>
</blockquote>
<p>Andrea Gibson’s poetry has helped me learn how to live, and how to love, and how to grieve. Most of all, I’ve learned how to experience with patience and reverence. They transformed their life, with all their love and pain and sickness and grief, and more love into a gift. Something for all of us.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/andrea-gibson/</guid>
  <pubDate>Fri, 25 Jul 2025 22:09:51 GMT</pubDate>
</item>
<item>
  <title>Queueing in Erlang</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/queueing-in-erlang/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-02-23</p>
</header>
<p>Last year, I needed a queue to <a href="https://git.mpwb.xyz/mpwb/aoc24/src/commit/fc4b75fae4f49a53eb7724bd59903559ef9ebec7/lib/aoc/day_16.ex">implement</a> a <a href="https://en.wikipedia.org/wiki/Breadth-first_search">breadth-first search</a> for an <a href="https://adventofcode.com/2024/day/16">advent of code problem</a>. I was using Elixir, and I wasn’t sure how to implement a queue myself. Luckily, <a href="https://www.erlang.org/doc/apps/stdlib/queue.html">Erlang provides <code>queue</code></a>, and I was off to the races. I’ve been wondering off and on since then exactly how it works, since the data structure for it was both transparent and also bizarre (to me). So I’m going to read the <a href="https://github.com/erlang/otp/blob/OTP-27.2.4/lib/stdlib/src/queue.erl">source</a> and notate it here. Erlang is not a language I know, so hopefully it makes sense to me. We are all learning here.</p>
<h3 id="basic-queueing-operations" class="anchored">Basic Queueing Operations</h3>
<p>The first thing that we see from the constructor is that a queue is a tuple of two lists. This is further backed up by <code>is_queue/1</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb1" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb1-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">new()</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[],[]}.</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%{RearList,FrontList}</span></span>
<span id="cb1-2"></span>
<span id="cb1-3"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% ...</span></span>
<span id="cb1-4"></span>
<span id="cb1-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_queue({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb1-6">    <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb1-7"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_queue(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">_</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb1-8">    <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">false</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">.</span></span></code></pre></div></div>
<p>Similar to Elixir, Erlang supports and encourages branching behaviour based on different argument patterns. Essentially, the first clause of the function returns true, but only when the supplied argument is a tuple of two lists. If it is, it is a valid representation of a queue in Erlang! The second clause, with the underscore argument, matches everything else. In general, when set of parameters match multiple function definitions, the first one is taken. I find this an enjoyable and very expressive form of programming. I love me some pattern matching, truly.</p>
<p>We also get a clue in here that the first list is for the rear, and the second one for the front. Just below this, <code>is_empty/1</code> also confirms that <code>{[], []}</code> is the empty queue.</p>
<p><code>len/1</code> shows us that there is no duplication in either queue, meaning it’s memory efficient. If there were duplication, the count of items would not be simply the sum of each list:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb2" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb2-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">len({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb2-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">+</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">);</span></span>
<span id="cb2-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">len(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb2-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">erlang:error(</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">badarg</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]).</span></span></code></pre></div></div>
<p>An example about using <code>to_list/1</code> shows us the usage of its counterpart <code>from_list/1</code>, and also that the correct way to represent <code>[1, 2, 3, 4, 5]</code> is <code>{[5, 4, 3], [1, 2]}</code>. The big question is “Why?”.</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb3" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb3-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">queue:from_list([</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">3</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">4</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">5</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]).</span></span>
<span id="cb3-2"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% {[5,4,3],[1,2]}</span></span></code></pre></div></div>
<p><code>to_list/1</code> goes on to show that for any queue, its list representation is the second part followed by the first part, backwards:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb4" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb4-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">to_list({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb4-2">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lists:reverse(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[]);</span></span></code></pre></div></div>
<p>Things get really interesting when we look at the source its counterpart <code>from_list/1</code>:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb5" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb5-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">from_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">L</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">L</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb5-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f2r(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">L</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">);</span></span></code></pre></div></div>
<p>What the hell is <code>f2r/1</code>? Right down at the bottom of the file, there are two internal functions, <code>f2r/1</code> and <code>r2f/1</code>. They are documented in the code, but come under a header “Internal workers”, and are nowhere to be found in the documentation. They appear relatively simple. Considering a queue <code>{R, F}</code>, where R and F denote Rear and Front lists respectively, <code>f2r/1</code> moves half of the elements from F to R. Conversely, <code>r2f/1</code> moves half of the elements of R into F.</p>
<p>Here they are, in their entirety:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb6" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb6-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% Move half of elements from R to F, if there are at least three</span></span>
<span id="cb6-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">r2f([])</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[],[]};</span></span>
<span id="cb6-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">r2f([</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">_</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-5">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[],</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb6-6"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">r2f([</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">])</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]};</span></span>
<span id="cb6-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">r2f(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">RR</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">FF</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lists:split(length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">div</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-10">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">RR</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,lists:reverse(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">FF</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[])}.</span></span>
<span id="cb6-11"></span>
<span id="cb6-12"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% Move half of elements from F to R, if there are enough</span></span>
<span id="cb6-13"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f2r([])</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-14">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[],[]};</span></span>
<span id="cb6-15"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f2r([</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">_</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-16">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,[]};</span></span>
<span id="cb6-17"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f2r([</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">])</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-18">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]};</span></span>
<span id="cb6-19"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">f2r(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb6-20">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">FF</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">RR</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lists:split(length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">div</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">List</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb6-21">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{lists:reverse(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">RR</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[]),</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">FF</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}.</span></span></code></pre></div></div>
<p>So these functions will balance the two ends of the queue for us when they are called. One interesting feature of these two functions is that they take a list, rather than a queue, but return a queue. Meaning that they should only be called when their destination list is empty.</p>
<p>Reading these, it makes sense to me now why R is reversed: prepending to a list is O(1), meaning that enqueueing is similarly cheap. However, if we push n items into our queue, dequeueing them will be O(n). Hence, at some point we wish to rebalance our queue so that ripping items off the front of F is cheap. By reading these functions, it seems that this operation is performed when we run out of items to pop from F, and in that case we take half of the items from R and put them into F. This means, that if a queue contains n items and we drain it, we can expect <code>r2f/1</code> to be called roughly <code>log(n)</code> times. I now understand the documentation’s meaning when it states that these operations “have an amortized O(1) running time”. Every so often, you get lumped with a worst case of O(n). As long as this is rare, it is not a problem.</p>
<p>There are a few other interesting things about this code:</p>
<ul>
<li>I’m pretty sure that <code>f2r/1</code> is merely a relabelling of <code>r2f/1</code> with the order of the tuple swapped. I probably would have written it like that.</li>
<li>If the list has one item only, it is swapped to the other side. That is to say that <code>f2r/1</code> puts it in R, and <code>r2f/1</code> puts it in F.</li>
</ul>
<p>It’s relatively clear to me that we force ourselves to rebalance the queue if we run out of items at either end (<code>out/1</code> will call <code>r2f/1</code>, and <code>out_r</code> will call <code>f2r/1</code> to pull from the back. But when else? My instinct is that adding items will never rebalance the queue, because we cannot anticipate how a queue will be drained until the client starts draining it. However, there clearly is some general goal of keeping things ready for anything, since <code>from_list/1</code> calls <code>f2r/1</code> to rebalance on creation. That’s how we even got here. Let’s take a look at <code>in/2</code> to see:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb7" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb7-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">in(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">_</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,[]})</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb7-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb7-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">in(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb7-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb7-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">in(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb7-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">erlang:error(</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">badarg</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]).</span></span></code></pre></div></div>
<p>Well it’s true that it doesn’t call <code>f2r/1</code>, and I would guess that <code>in_r/2</code> behaves similarly. There are a couple of interesting clauses here, though:</p>
<ol type="1">
<li>The first deals with a queue with only a single item in its rear item, such as a queue that would be generated by creating an empty queue and adding a single item. In this case, the first item is moved to F, and the new item is added to R, leaving the queue balanced.</li>
<li>Otherwise, we simply add the new item to the front of R, which represents the back of the queue.</li>
</ol>
<p>So, if we append 1000 items to an empty queue, we would end up with one item in F, and 999 items in R.</p>
<p>If we turn our attention to <code>out/1</code>, we don’t see anything particularly surprising:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb8" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb8-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out({[],[]}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">empty</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">};</span></span>
<span id="cb8-3"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out({[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],[]})</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-4">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">value</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},{[],[]}};</span></span>
<span id="cb8-5"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out({[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],[]})</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-6">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lists:reverse(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[]),</span></span>
<span id="cb8-7">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">value</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},{[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Y</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}};</span></span>
<span id="cb8-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">value</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},r2f(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)};</span></span>
<span id="cb8-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">value</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">V</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">In</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Out</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}};</span></span>
<span id="cb8-12"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">out(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb8-13">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">erlang:error(</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">badarg</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]).</span></span></code></pre></div></div>
<p>The return value here is a 2-tuple where the first value is either an atom <code>empty</code>, or a tuple with the atom <code>value</code> and the returned value, and the second item is the new queue. Atoms are unique symbols that exist in the Erlang runtime, string constants that are used kind of like global enum values.</p>
<p>My guess above was that <code>r2f/1</code> would only be called by <code>out/1</code> iff F was empty. That is not the case. The third clause deals with an empty F, and it does so be emptying all but one element of R into F. However, <code>r2f/1</code> is called, it is just called when F has 1 element in it, rather than 0. Although, when F has a single item, the queue returned thereafter would indeed have an empty F, so I wasn’t too far off.</p>
<p>As an interesting side-quest, it is not immediately clear to me how we would ever get to the case where F is empty and R has values in it through only these two functions. I know that there are advanced functions for deletions and filtering and mapping and suchlike, so maybe this will be revealed to me later on.</p>
<p>This covers basic queueing! We have <code>in/2</code>, <code>out/1</code>, and even <code>in_r/2</code> and <code>out_r/1</code>, all powered by an ordered pair of lists, that are kinda balanced out sometimes. But there is more to this module. A lot more, in fact. There are the basic iteration functions, things like <code>all/1</code>, and <code>fold/3</code>. These seem relatively simple to implement, though I wonder if they are converted to a list, or two iterations are run. My bet is the latter. Then, there are the basic functions that seem harder to implement to me. Things like <code>split</code>, <code>join</code>, and <code>delete</code>. I feel like these require some amount of rebalancing and reversing things. Finally, the documentation contains an “Okasaki” API, which links to a 1996 paper (book? It’s 130-odd pages) called “Purely Functional Data Structures”. This is up my alley, but will not be covered in this post. I am already reading a book right now. So, let’s look at splitting and joining.</p>
<h3 id="splitting" class="anchored">Splitting</h3>
<p>(In the following examples, I am going to omit the degenerate cases that handle errors and empty inputs. They are not algorithmically relevant.)</p>
<p>The API of split doesn’t just halve the queue, it puts the first <code>N</code> items into the first queue, and the remainder into the second queue. As I suspected, the code is a little gnarly, certainly more so than anything hitherto presented:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb9" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb9-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_integer(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;=</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb9-2">    <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lf</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">erlang:length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-3">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span>  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lf</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% Lf &gt;= 2</span></span>
<span id="cb9-4">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-5">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_f1_to_r2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[],</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]);</span></span>
<span id="cb9-6">        <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lf</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb9-7">            <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lr</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">length(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-8">            <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lr</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Lf</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span></span>
<span id="cb9-9">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">if</span>  <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&lt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb9-10">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">erlang:error(</span><span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">badarg</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]);</span></span>
<span id="cb9-11">                <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">M</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">&gt;</span> <span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb9-12">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">=</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span></span>
<span id="cb9-13">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_r1_to_f2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">M</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[]);</span></span>
<span id="cb9-14">                <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% M == 0</span></span>
<span id="cb9-15">                    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">Q</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,{[],[]}}</span></span>
<span id="cb9-16">            <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">;</span></span>
<span id="cb9-17">        <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">true</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span> <span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">% N == Lf</span></span>
<span id="cb9-18">            <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{f2r(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),r2f(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)}</span></span>
<span id="cb9-19">    <span class="kw" style="color: #003B4F;
background-color: null;
font-weight: bold;
font-style: inherit;">end</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">;</span></span></code></pre></div></div>
<p>The first thing is that this is some pretty fun indenting of if statements! The second thing is that this calls two little helper functions that are defined just below:</p>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb10" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb10-1"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% Move N elements from F1 to R2</span></span>
<span id="cb10-2"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_f1_to_r2(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb10-3">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}};</span></span>
<span id="cb10-4"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_f1_to_r2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb10-5">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_f1_to_r2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">).</span></span>
<span id="cb10-6"></span>
<span id="cb10-7"><span class="co" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">%% Move N elements from R1 to F2</span></span>
<span id="cb10-8"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_r1_to_f2(</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">0</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb10-9">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">}};</span></span>
<span id="cb10-10"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_r1_to_f2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">],</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb10-11">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">split_r1_to_f2(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">N</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-</span><span class="dv" style="color: #AD0000;
background-color: null;
font-style: inherit;">1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">[</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">X</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">|</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">]).</span></span></code></pre></div></div>
<p>Now I can actually look at this. This function principally branches on whether or not we can satisfy the first queue solely by pulling elements off the front of F. Namely, that F has at least N elements. There are three possible outcomes:</p>
<h4 id="we-have-exactly-n-elements-in-f" class="anchored">We have exactly N elements in F</h4>
<p>In this case, we take F and R, and return them. We also go to the trouble of rebalancing them.</p>
<h4 id="we-have-more-than-n-elements-in-f" class="anchored">We have more than N elements in F</h4>
<p>So I thought this would be the simple operation of pulling N elements of F, and maybe calling <code>f2r</code> on it. But that’s not what we do here. For reasons surpassing my understanding, we pull one element off the front of F, and pop it into the front of the second queue. Then we grab the remaining N-1 elements, and put them into the rear of the second queue.</p>
<p>This is achieved by that handy helper function <code>split_f1_to_r2/5</code>. This has a very nice recursive definition, but one thing to note is that the base case swaps its arguments around, returning <code>{Q2, Q1}</code>. This is where the swapping is done, ensuring that the front N items go into the front queue.</p>
<h4 id="we-have-fewer-than-n-elements-in-f" class="anchored">We have fewer than N elements in F</h4>
<p>This means we are going to have to plunder R in order to get to our queue, and that is going to be at least somewhat slow. In this case, we further branch by M. M is rather confusingly written here, I would prefer it be defined as <code>Lr + Lf - N</code>, because double negatives are hard for me. But I’m a big boy and I’m used to not getting what I want at this point.</p>
<p>If M is less than 0, it means we do not have enough elements to move, and we simply bail. This feels cowardly to me, I would probably go for just putting the input queue into the first result, and leaving the second one empty, but I’m not about to come in and tell the standard library authors for a language I don’t even know how to decorate their house. I’m an idiot!</p>
<p>As it happens, though, that is exactly what we do if M is exactly 0, meaning that the input queue contains exactly N items.</p>
<p>In the final case, we actually do some splitting. In this case, we again delegate to a helper function, this time it’s <code>split_r1_to_f2/5</code>. It begins in much the same way, where we take the first element of R and put it into the rear of the second queue, then we ship the remaining M-1 elements into the front of the second queue. Since M is the number of elements left in the queue after removing the first N arguments, the first queue is the first queue according to the API contract, so no swapping occurs.</p>
<p>All of these structures are immutable, but it might be helpful to think of the previous case as moving the first N elements into a new queue, and this case as moving the last M elements into a new queue.</p>
<h3 id="joining" class="anchored">Joining</h3>
<div class="code-copy-outer-scaffold"><div class="sourceCode" id="cb11" style="background: #f1f3f5;"><pre class="sourceCode erlang code-with-copy"><code class="sourceCode erlang"><span id="cb11-1"><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">join({</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">},</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">})</span> <span class="ch" style="color: #20794D;
background-color: null;
font-style: inherit;">when</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">),</span> <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">is_list(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)</span> <span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">-&gt;</span></span>
<span id="cb11-2">    <span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">{</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F1</span><span class="op" style="color: #5E5E5E;
background-color: null;
font-style: inherit;">++</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">lists:reverse(</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">R1</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">,</span><span class="va" style="color: #111111;
background-color: null;
font-style: inherit;">F2</span><span class="fu" style="color: #4758AB;
background-color: null;
font-style: inherit;">)};</span></span></code></pre></div></div>
<p>One thing I’ve learned through this analysis is that there are a number of valid representations for the same notional queue. And nothing throws this idea into quite such sharp relief as this snippet.</p>
<p>This function might seem complicated. It did to me, but it turns out that <code>lists:reverse/2</code> only reverses the first argument, and just appends the tail.</p>
<p>And with that, I am out of questions about how Erlang’s queue works! There are other functions, and an entire API that I don’t understand (to be fair, it’s the names that make no sense to me, the behaviour seems relatively straightforward). The remaining functions usually involve visiting all elements. If the order is important, that in turn involves reversing one of the lists. In cases where we are searching for a single item <em>and</em> we need to traverse the queue in order (<code>delete/2</code> et al), then the front portion of the queue will be traversed first, only reversing the rear if needed. Finally, one beautiful feature of this design is that it is totally symmetric. For all relevant functions, there exist <code>_r</code> variants that go from the other end. But what I find particularly pleasing here is that inverting a queue is a simple matter of swapping the front and rear component lists.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/queueing-in-erlang/</guid>
  <pubDate>Sat, 22 Feb 2025 21:37:19 GMT</pubDate>
</item>
<item>
  <title>On Syntax Highlighting</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/on-syntax-highlighting/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-02-02</p>
</header>
<p><img src="https://nonsense.mpwb.xyz/posts/on-syntax-highlighting/dark.png" class="img-fluid" alt="A screenshot of the Zed text editor, showing some Rust source code in a dark colour scheme"></p>
<p>I roll my own colour scheme for any text editor I use. I don’t do this because I’m fussy and no off-the-shelf colour scheme will do (although I am fussy), I do this because I have a very particular manner of highlighting syntax, regardless of the exact colours I’m using. I’ve received a fair bit of inquiry from friends and colleagues about why I do this, and I’ve never seen anyone else do exactly what I do, so I decided I should write it down while I’m thinking about it. (I’m thinking about it because my team and I are trying out <a href="https://zed.dev">Zed</a> for pairing and remote collaboration, so I’ve had recently had to do this dance.) As it happens, I am in fact using my own custom colour scheme as well. I know just enought colour theory to be dangerous, and I had a few specific requirements that other colour schemes weren’t meeting, and I had to do all this anyway, and it’s not that hard. Some might even say it’s fun. But that is a tale for another time. What I want to write about today is not the specific selection of colours I use, but the manner in which I use them.</p>
<p>When I was a kid, I initially learned to code in Notepad. When I first got my hands on some actual editor (I think it was Dreamweaver), I was amazed mostly by syntax highlighting. For the first time, code looked <em>cool</em>. I never understood what the different colours meant, but it did make the entire thing look like a fidget toy, which was extremely up my alley. As I got older, I began to take this coding thing more seriously, and for most of my teenage years I was devouring everything I could find about software development. This included a lot of discussion about tools, and a lot of this really left its mark on me. Sometime around 2009 I started to use Vim for the first time. Some time in 2011 I switched to the Dvorak keyboard layout. I really tried everything. Some of it stuck; some of it didn’t. One thing in particular did take hold in me, though: at some point, I turned off syntax highlighting in Vim.</p>
<p>Exactly when I did this, and exactly what prompted me are sadly lost to the sands of time. I think it was a Damon Conway conference talk about his 1500 line Vimrc, but I could not find such a talk online that predates the <a href="https://git.mpwb.xyz/mpwb/dotfiles/commit/bf41472ef021bb364a4d3a468e8a957fd54e5a3e">initial commit of my Vimrc in my dotfiles</a>, which was over a decade ago. And even by then I had moved on from <code>:syntax off</code> to something more sophisticated. To, as it happens, an early version of the same syntax highlighting scheme that I use to this day. Regardless of where I heard this or who told me, someone advertised that they do not use syntax highlighting, because they find it overwhelming and useless. This resonated with me, because I still didn’t understand what it all meant. So I tried turning it off, and I liked it. But living without syntax highlighting did force me back to first principles, and I ended up pulling a few things back into my life. They haven’t really changed in the last ten years.</p>
<h3 id="get-in-the-comments" class="anchored">Get in the comments</h3>
<p>At a similar time, during a debate on the merit of code comments (if this entire blog post were not evidence enough, take it from me directly that I find it difficult to resist discussions as to the colour or colours in which to paint the bikeshed) somebody asked how we are supposed to take comments seriously when our text editors themselves dim them out relative to the background. They are, almost by definition, less unimportant.</p>
<p>This didn’t work on me, of course, my comments looked exactly like the rest of my code. But it did resonate. My general position here is that if somebody goes out of their way to write some prose inside some code, it’s probably important, and so I want to pay attention to it. Moreover, I don’t want to refactor something and miss a comment, left to go stale. In the best case, a stale comment is useless. Somewhere in the middle it is misleading, and in the worst cases can be downright dangerous.</p>
<p>So I made comments green. This was a real bummer for me, because I had to <code>:syntax on</code>, and then I had to build a whole-ass colour scheme in Vim that disabled everything, and then made comments green (<code>ctermfg=2</code>). But I did it, and by then I had eaten from the tree of knowledge of good and evil: syntax highlighting was back on the menu.</p>
<h3 id="a-little-bit-more-colour" class="anchored">A little bit more colour</h3>
<p>Since I had the power, it was pretty easy to then make Vim chrome red (status bar, split lines, and so on), and search results blue. The exact colours used here have chopped and changed over the years, but currently search is still blue (it was yellow for a while though), and Vim chrome is purple.</p>
<h3 id="literally-though" class="anchored">Literally though</h3>
<p>I stayed like this for a while, until I had my first (in this story at least) original idea: hardcoding things is bad. There’s nuance here, but in general I consider factoring things into variables good, and not doing so to be vaguely harmful. That’s not the original idea. The original idea was that I could highlight literal expressions in my code, so that a visual scan can find an overuse of them.</p>
<p>Thus, I made scalar expressions cyan. The exact timing of this is not lost to the sands of time, it was almost exactly ten years ago, on <a href="https://git.mpwb.xyz/mpwb/dotfiles/commit/3977605fd589610b7ffd2f633a5b5b665e10e7b9">11th January 2015</a>.</p>
<h3 id="just-like-that-ten-years-goes-by" class="anchored">Just like that, ten years goes by</h3>
<p>And that’s about where we are today. I went from Vim to NeoVim, there was definitely some Jetbrains shenanigans in there. I went from NeoVim to Nova, then to Zed. I liked Zed’s vim mode so much that I switched back to Vim last year, and now I’m somewhere in between Vim and Zed.</p>
<p>But my syntax highlighting, that stays with me. Here it is, in light mode.</p>
<p><img src="https://nonsense.mpwb.xyz/posts/on-syntax-highlighting/light.png" class="img-fluid" alt="A screenshot of the Zed text editor, showing some Rust source code in a light colour scheme"></p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/on-syntax-highlighting/</guid>
  <pubDate>Sun, 02 Feb 2025 08:16:21 GMT</pubDate>
  <media:content url="https://nonsense.mpwb.xyz/posts/on-syntax-highlighting/dark.png" medium="image" type="image/png" height="94" width="144"/>
</item>
<item>
  <title>Hello, World</title>
  <dc:creator>Max Bucknell</dc:creator>
  <link>https://nonsense.mpwb.xyz/posts/hello-world/</link>
  <description><![CDATA[ 
<header id="title-block-header">

<p class="date">2025-01-17</p>
</header>
<p>Last year was a pretty big one for me. I turned 30, I rediscovered the joy that I found in programming, and did a lot of other really important growing. As I write this, I am sitting at my mother’s dining table in the UK, over for Christmas in between moving from Sydney, Australia to Vancouver, Canada to live with the love of my life.</p>
<p>I struggle with finishing things off. Really putting a bow on things. At the start of a project, all of my excitement tends to get shovelled into the esoteric details of the thing: one specific mechanism that is particularly tricky, or one particular ornament that is specifically beautiful. This has its pros and cons, of course, and I’ve spent a lot of my career working at keeping the good parts of these tendencies while developing strategies to, you know, actually ever deliver anything. However, this leaves my hobbies in a state of disrepair.</p>
<p>Ever since I really got my life back together in 2018 or so, I’ve thought about — and begun to develop! — several different places to store my writing and share it online. This latest attempt is by far my lowest effort (a nearly default Quarto project) holds a lot of promise for me. So let’s see what happens.</p>
<p>Happy New Year.</p>

 ]]></description>
  <guid>https://nonsense.mpwb.xyz/posts/hello-world/</guid>
  <pubDate>Fri, 17 Jan 2025 07:51:35 GMT</pubDate>
  <media:content url="https://nonsense.mpwb.xyz/posts/hello-world/setting.jpg" medium="image" type="image/jpeg"/>
</item>
</channel>
</rss>
