I recently converted all my machines from zram swap to zswap. In this post I go over the differences between the two and why zswap is almost certainly better for any general use-case.
I recently converted all my machines from zram swap to zswap. In this post I go over the differences between the two and why zswap is almost certainly better for any general use-case.
Decibels come up often in digital audio and I’ve always found them to be confusing. In this post, I try to explain what decibels are and more importantly, how they’re used in practice.
I’ve started running HAProxy on every machine in my fleet. This neatly solves the problem of connecting to services in my Kubernetes cluster, as well as making it possible to have nice URLs for local services running on weird ports.
Grandma wants to talk to her grandkids and also see them. The problem is that grandma cannot interact with modern technology at all. No keyboard, no mouse, no touchscreen—the solution has to be fully automated. Let’s build this.
In the first post in this series, we generated sine waves. In this post, we combine sine waves together to explore harmonics, dissonance, and the 12 tone system used in Western music.
Let’s make it impossible for someone to steal our SSH keys by storing them on a FIDO2 security token like a YubiKey. In 2026, this turns out to be easy to do and is a bit of convenience we can just buy off-the-shelf.
I find audio to be arcane and hard to think about. Let’s build an intuition together by writing increasingly complex programs that deal with sound. In this post, we go from what is sound to producing a sine wave with Rust, WebAssembly, and Web Audio.
In this post, we figure out how to include big video and WASM files in a static site without adding them to the git repo. We do this with Nix because when you have a build-system shaped hammer, everything starts to look like a build-system nail.
The Pimoroni Explorer is a nifty little RP2350 board with a 320x240 pixel screen, a speaker, 6 buttons, a breadboard, and headers galore. One problem is that it doesn’t have an SWD debug header. Let’s fix that.
Let’s write a type-safe table in Rust where the values can be of different types. This could be used as a cache sitting in front of a web server, as storage for entities in a game world, or as a config map.
All the online chatter about AI crawlers made me realize I have no concrete numbers, so I added more stats to this blog. Let’s look at the numbers together and marvel at all the bot activity.
I added a blogroll to my front page. You can see it below. This post explains how I bolted it onto my mostly static site and why I decided to add it.
I wrote another FX rates viewer, this time using the Slint toolkit. At 1k LoC, it’s a bit too long to go through line by line, but there are still lessons to be learned and traps to be weary of. Let’s see what they are.
Let’s write a simple metronome using a micro:bit and see how easy embedded programming in Rust is in 2025.
A thing I’ve been missing is the ability to easily share files with insecure devices. I’ve tried a bunch of solutions over the years, including using Syncthing and Nextcloud, but they were neither nice to use nor run. Recently, a friend found a much better trade-off in terms of setup complexity and ease-of-use. So, let’s build such a file-sharing service with rclone, Nginx, and Kubernetes.
I revamped this blog’s RSS feed. The downside is that RSS readers will probably show the last few posts again. Sorry about that. On the upside, the new feed contains full post contents and is standards compliant. This post lists the changes I made, mostly to the axum webserver setup.
Let’s build a container around a Rust webserver and some static files using Nix and Gitlab CI. The process is what you’d expect, but there are a few details that are annoying to puzzle out.
Let’s design a Currency type in Rust. We’ll iterate through several versions, starting with the ubiquitous str, and finishing with a stack-allocated type built with Rust’s new const generics.
Let’s write an app that watches some files for changes, runs them through Liquid templates (rs), and then compiles the output with LaTeX. It’ll take about 200 lines of code. This scheme is very useful when you need to produce some kind of document from data available to a Rust program, and when you expect to be iterating on the templates a lot.
The compose key on Linux lets you input characters with accents or diacritics without changing keyboard layouts. You type something like Compose’e and get é. It’s very useful if you use English almost exclusively, but occasionally need to write in a language with extra letters. Let’s see how to enable the compose key, how to check what key combos are available by default, and how to add our own.
I figured out how to access Kubernetes services on my cluster without the need for kubectl port-forward or an ingress. It can all be done with Linux routing tables, and Wireguard makes this trivial to set up.
I wrote a little OpenGL app in Rust and the hardest part was getting it to run on NixOS. This post describes the flake that works, and lists the errors that happen if the configuration isn’t right.
Let’s write a little exchange rate viewer in Rust. It will have a GUI, fetch rates from the Internet, support changing the currencies without having to recompile, and all this in about 200 lines of code.
I prefer using open-source apps on my Android phone, both as a matter of principle, but also because they tend to be less bloated and more focused. The big problem is finding good apps. In the hope that this helps others, here’s my list of good open-source Android apps.
I added a feedback form under every post on this blog. It’s a bit of client-side Javascript, a couple of serverless functions, and a proof-of-work scheme to deter spammers. Let’s see what it looks like.
While writing the nix-nar crate, I had to bend Rust’s Iterator API to do things it wasn’t designed for. The resulting code looks strange, so let’s walk through increasingly complex implementations to see why it has to be that way.
Let’s write an nbd service module for NixOS. We’ll look at the shape of NixOS modules, define the service’s options, generate the configuration for the server, write tests, and see how upstreaming into nixpkgs works.
Let’s setup Gitlab CI to trigger a rolling restart of a Kubernetes deployment. This is a lightweight alternative to a something like Flux which requires an operator running in the cluster.
This is the debugging story of a strange 12 minute hang I’d get after logging in to my SFTP server. It wasn’t the usual culprits, and it wasn’t any of the likely cloud complications. Instead it turned out to be an issue as old as POSIX itself.
Many recent distributed programs like etcd or Kubernetes require TLS certificates to communicate securely. Creating these by hand is tedious, so let’s see how to automate it with a nix flake.
NixOS 21.11 switched to the nf_tables backend for iptables. Let’s see what this means, and what new things we can and cannot do.
Let’s deploy our own peer-to-peer Dropbox-like system with Syncthing, Nginx, and Kubernetes.
I am going to describe how I store all of my Kubernetes manifests in a single directory tree. The overriding goals of this setup is to commit all of the manifests to version control, and to apply them with a single command.
Helm is an easy way of deploying to Kubernetes, but helm install is a bit annoying because it doesn’t save the changes it made to a local repo. That’s where helm template comes in.
I recently setup Umami on this blog to get some basic analytics. The deployment on Kubernetes was easy, but it required some spelunking into documentation. Here are my notes.
I just switched some of my Kubernetes nodes to run on a root ZFS system. It was mostly painless, but there were a few places that required special configuration. Here are my notes.
If we try to open an HTTPS connection with ureq, and the host part of the URL is an IP address, then we get an InvalidDNSNameError. Let’s figure out what’s going on and how to work around it.
Kubernetes networking is complicated. It’s not complex, mind you, as there’s no emergent behaviour. It’s just complicated because there are lots of moving parts that are used in different circumstances. Let’s explore how the parts fit together by walking through several scenarios.
Wireguard is a nifty little tool. The tagline describes it as an “extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography”. I have been using it for years to tunnel into my servers, as an Internet gateway, and as a jumpbox into the servers’ VLAN. Recently, I figured out how to configure it as a secure mesh between the servers.
I just finished reading Superforecasting: The Art and Science of Prediction by Philip E. Tetlock and Dan Gardner. It’s a pretty good book about the fallacies that make people, including experts, generally very bad at forecasting world events, and about the methods the authors used to generate vastly superior predictions from groups of volunteers.
I needed a way to store my personal documents remotely in a secure way, so I came up with a scheme involving GCE, nbd, wireguard, and cryptsetup.
I recently finished reading Deep Work by Cal Newport. It was an interesting, if personally depressing, read. My takeaways were a practical definition of deep work and an obvious-in-hindsight observations on what prevents deep work.
It’s occasionally useful to introduce pause points into procedures. The idea is that, when a human operator is about to do something scary, we want them to stop and think for a second and not just do things by rote. Enter challenge-prompt, a small program and Rust library which challenges the user to type something.
Most data on the Internet is transferred over TCP or UDP. The former works best when the transport needs to be reliable, and the latter is for when lower latency is more important than data loss or packet reordering. However, for a laugh, you can also transfer it over ICMP, aka ping.
When I learned C back in high-school, I was taught that all arrays declared on the stack had to have their size statically known at compile time. If we wanted variable sized arrays, we had to allocate them on the heap. It turns out that, as of C99, that’s no longer true.
Every time I’ve needed a temporary file, I’ve used mkstemp(3) or some variant of it. However, it wasn’t until recently that I wondered how it works, and more importantly, under what conditions it fails.
Most programmers are familiar with the naïve \(O(2^n)\) and the memoized \(O(n)\) algorithms for computing the nth Fibonacci number. However, with a bit of math, you can get the time down to \(O(\log_2n)\).
Premature optimisation may be the root of all evil, but it’s also dammed fun. I recently needed an OCaml library for affine transformations: essentially, I needed to multiply 3x3 matrices together.
You’ve finally snuck into the evil mastermind’s billiards room. You’re only one door away from his office and the Big Red Button that stops the moon laser from vaporizing the Great Barrier Reef.
Programming languages often have features which are not necessary but which are a boon to working with them. For example, Haskell compilers can automagically derive pretty-printers for user-defined datatypes which is a great help when debugging. Unfortunately, things don’t always go the way the language authors intended, and such features end up being more confusing than helpful. Take, for instance, the following implementation of a doubly-linked list in OCaml.
The asymptotic complexity of a loop which does at most \(N\) constant-time operations is \(O(N)\). \(M\) such loops will then have \(O(M * N)\) time complexity. But not always. If you’re careful, you can sometimes do \(M\) loops of \(O(N)\) in only \(O(M)\) time.
Here’s a challenge: cycle an array in-place. That is, given an array \([x_0, x_1, \dots, x_{n-1}]\) and a number \(m\), you must make \([x_m, x_{m+1}, \dots, x_{n-1}, x_0, x_1, \dots, x_{m-1}]\) using only a constant amount of additional memory.
I love the breaking the structure of structured programs. We have previously implemented coroutines in C using setjmp(3) and longjmp(3). We are now going to fake exceptions in C using the same functions.
The IsString type-class and the OverloadedStrings extension were meant to save Haskell programmers from having to type {ByteString,Text}.pack over and over again, but they can be used in more creative ways as well.
The only features originally allowed into C++ were those which had efficient implementations. This sounds great, but the results were sometimes dubious. Consider the following program which uses multiple inheritance.
Returning a pointer to a local variable is legal in Go. As a C programmer, the following looks like an error to me, but it’s perfectly alright in Go.
Let’s play the following game: you roll a four-sided die; if 1 or 2 come up, you get 1$; if 3 comes up, you lose 1$; if 4 comes up, the game ends, and you lose all of your gains; finally, you can stop the game at any point and keep your gains. How do you play so that you maximize your gains?
A funny thing happens when you take the imaginary unit \(i\), and raise it to the power of \(i\); it becomes real. Again, \(i^i \in \mathbb{R}\).
A quine is something that, when evaluated, yields itself. More precisely, a quine is x, such that when we evaluate, interpret, or otherwise run x, we get x as the result. Quines are interesting because their existence in a language points to the language’s self-referentiality.
When I powered on my new Raspberry Pi the other day, I realized I had a problem: even though we were both connected to the same WiFi network, I had no idea what its address was.
A while ago, SMBC had a comic about whether our reality is simulated. They suggest that a way to tell would be to check if our reality were “optimised for computation”, such as if there were a minimum temperature, or a maximum speed. That got me thinking, are there other signs like that?
Since Google has decided to shut down Google Reader, we should do what any self-respecting software developer would do: complain about it on Hacker News, and write a replacement that works for us. Today, we’ll be writing Zingr (“Zingr is not Google Reader”), a single-user web-based news aggregator in Python, SQLite3, Flask, Mootools, and Knockout.
Consider a colony of bacteria living in a fresh water lake. Suppose the lake becomes more and more salty; the salt is damaging and ultimately lethal to the bacteria. What can they do? Ignoring more esoteric adaptations like bacterial conjugation, individually, they cannot do anything. As a species though, through selection of the fittest (most salt resistant, in this case), the bacteria evolve and adapt.
We all know that \(O(N) < O(N^2)\), but what’s the relation between \(O(\sqrt{N})\) and \(O(\log_2(N))\)? Let’s determine an ordering for some common asymptotic complexities, and find the points where the smaller ones meet the larger ones.
Suppose we want to stress-test a server by hitting it with a fixed number of requests per second. Or maybe we want to write a game loop that runs at a fixed number of frames per second. In both cases, we want to run some code at a fixed frequency \(\nu\). More precisely, we want a loop that calls some function, sleeps for a bit, then restarts, and overall, the function is called \(\nu\) times per second.
As a European, I’ve had lots of problems using bank cards in the US. This wasn’t because of the bedlam that is the international banking system, but because of differences in user interfaces.
I recently ran into an issue with QuickCheck where one of my tests seemed to hang. My mistake was that I was ignoring how QuickCheck generates sized arbitrary values, and ended up creating very large structures.
Although debugging with GDB is useful, it has the big disadvantage of focusing on the program’s internal state at a single moment in time. Often, we instead need to see how a program is interacting with its environment. In this post, we look at strace, a utility which traces system calls.
I’ve been programming for over a decade now, and, although I don’t usually use debugging tools, there are a few instances where I’ve found them to be indispensable. In this post, we go over a few debugging scenarios where print statements just don’t cut it.
There are two ways of looking at the type-system of a language: as a set of rules which must be followed for the program to compile, and as a tool to make code more expressive. In this post, we talk about phantoms types, which is one way of doing the latter.
“A fixed point of a function is a point that is mapped to itself by the function”. In other words, it is \(x\), such that \(f(x) = x\). Fixed points are everywhere; in particular, the movie Looper is all about finding them.
Last week, we saw how a financial crisis unfolds. In this post, we see what can be done to bring things back to normal again.
Let’s talk about financial crises, what causes them, how they happen, and how to stop them. In this post, I tell a few made-up stories to illustrate how a crisis unfolds, and how it’s stopped; these stories are mostly modeled after the Panic of 1907; these stories are a gross simplification of what really happens and are only meant to give an intuition.
The Law of Large Numbers says that “the average of the results of a large number of trials should be close to the expected value”. This is common knowledge, but most people are not familiar with the corollary that “the average of the results of a small number of trials may be quite far from the expected value”.
Here’s a Linux permissions puzzle for you: assume you have a file, on which the owner has no permissions, the group has read/write permissions, and everybody else has no permissions; you are the owner of the file, and a member of the file’s group; do you have permission to read or write to the file?
Let’s say we’re trying to copy a \(1\) GiB file from one machine to another over a gigabit ethernet LAN. How long will it take? (Spoiler: it’s quite a bit more than the simple calculation says.)
I present here the longest and shortest math jokes I know. Furthermore, I will render the long one unfunny by explaining it.
Last week, I found out about a non-obvious pitfall of the Java language caused by the interaction of sub-classing and arrays. In short, Java arrays are covariant, so what I thought was illegal code compiles, and causes an exception at runtime. In this post, I give a quick intro to type variance, and describe the particular issue I encountered recently.
Here’s a fun bit of trivia: malloc on Linux never fails [due to the obvious cause]. In this post, we’ll show that this indeed is the case, and explore why this happens.
I tend to build very often when working; in this post, I describe how I went from a build every few minutes to automatic builds on every file change.
Here’s a puzzle for you: what does the following shell script do? (works in zsh, bash, dash, BusyBox’s sh, but not tcsh; the script must have a trailing newline)
There’s a pair of C functions that I feel are underused, namely setjmp(3), and longjmp(3). With them, you can fake exceptions in C, implement coroutines, and much more.
Suppose you have a fair coin, that, when thrown, lands heads half the time and tails the other half. Design a procedure that makes a uniformly distributed random integer in the inclusive range [1, n].
Langton’s Ant is a turmite governed by simple rules whose outcome is both unpredictable and intresting. The path taken by the ant generates some surprising shapes, never appearing when you would expect them to, but a seemingly random moments. This article describes the rules behind Langton’s Ant, shows some of the images formed and provides a Python programme to simulate the ant.
In this article I describe a way of modifying Dijkstra’s Alogrithm in order to find all the shortest path from a source to a node.
In this article, I describe a simple (adds less than 1min of work) way to speed up Dijkstra’s Algorithm for finding the single source shortest path to every node in a graph.
In this article, I describe Gauss’ algorithm for solving n linear equations with n unknowns. I also give a sample implementation in C.
In this article I describe Dijkstra’s algorithm for finding the shortest path from one source to all the other vertexes in a graph. Afterwards, I provide the source code in C of a simple implementation.
In this article, I describe the Bellman-Ford algorithm for finding the one-source shortest paths in a graph, give an informal proof and provide the source code in C for a simple implementation.
The 0-1 Knapsack Problem (AKA The Discrete Knapsack Problem) is a famous problem solvable by dynamic-programming. In this article, I describe the problem, the most common algorithm used to solve it and then provide a sample implementation in C.
In this article, I describe the greedy algorithm for solving the Fractional Knapsack Problem and give an implementation in C.
In this article I describe the Floyd-Warshall algorithm for finding the shortest path between all nodes in a graph. I give an informal proof and provide an implementation in C.
In this article I give an informal definition of a graph and of the minimum spanning tree. Afterwards I describe Prim’s algorithm and then follow its execution on an example. Finally, the code in C is provided.
In a previous article, I described the basics of binary arithmetic and gave a function to display the binary representation of a number. Here, we’ll look at several ways to count the set (1) bits in a number.
Sudoku is that Japanese puzzle that requires you to fill in a grid of numbers. Here, I describe a general algorithm to solve these puzzles. Also provided is the source code in C++.
In this article I’ll begin by defining binary numbers and describe the basic operations. Afterwords, I’ll show you several of the most common uses of binary numbers.
Checker Challenge is a very famous programming problem. It’s one of the first examples of backtracking anyone learns and variations of it frequently appare in contests.
In this article, I’ll present the classic algorithm for solving it and then a few optimisations to make it run less like a geriatric turtle.
The combinations of n elements chosen as k is the number of unique ways of selecting k elements from a set of n.
So, what are the ways of choosing 2 elements from a set of 4, {1, 2, 3, 4}?
The partitions of a set S are all the ways in which you can choose disjoint, non-empty subsets of S that unioned result in S.
So, what are the subsets of {1, 2, 3}?
There quite a few definitions of what a set is, but it all boils down to this…
Last time, we defined what permutation is and gave a few basic properties.
In a few minutes we’ll see another algorithm for generating them, but first a little theory…
A permutation of n objects is an arrangement of n distinct objects…