Wednesday, 25. June 2025 Week 26

rachelbythebay's rollover calculator

I've long had a list of "magic numbers" which show up in a bunch of places, and even made a post about it back in November of 2020. You ever wonder about certain permutations, like 497 days, or 19.6 years, or 5184 hours, and what they actually mean?

I've been doing that stuff by hand in a calculator and finally decided to just do it in Javascript and put it online for anyone to try.

So, here's my latest waste of CPU cycles:

My rollover calculator.

(via)

Saturday, 21. June 2025 Week 25

playbackrate

Here's a tip that works on YouTube and almost any other web page that shows you a video. You can increase the playback rate beyond the usually-exposed 2x by running this in your browser DevTools console:

document.querySelector('video').playbackRate = 2.5

(via)

Sunday, 8. June 2025 Week 23

Links cleanup / Blogroll cleanup 2025

Cleaned up a bit the links in the sidebar.
I find myself clicking less and less on them.
Mostly reading blogs via NetNewsWire these days.

Removed:

Someday I should find the motivation to export an OPML file from the reader and make it available.
The full OPML list would be way too big for the sidebar, so a dedicated place would need to be found. 😅

Thursday, 5. June 2025 Week 23

Picking uncontested private IP subnets

benjojo does some clever data analysis for Picking uncontested private IP subnets with usage data.
He not only gathers the usage data of the various subnets, but also provides a nice tool to get a /24 subnet from the list that has no known users.

And to top it off the article also provides the underlying data.
A Zip file with the /24 subnets with number of known users (local copy).
And the distilled list of subnets that have no known users (local copy).

Saturday, 31. May 2025 Week 22

Creating a CAA record in bunny.net using Terraform

As part of my migration to Bunny CDN for andreas-jaggi.ch, I also moved the DNS zone over.
There are not many records in the zone, but one which turned out to be a bit more tricky was the CAA one.

I wanted to use the following Terraform snippet to create it:

resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
  zone  = bunnynet_dns_zone.andreas_jaggi_ch.id
  name  = ""
  type  = "CAA"
  value = "0 issue \"letsencrypt.org;validationmethods=http-01\""
}

But this always failed with a cryptic error message during terraform apply:

│ Error: Unable to create DNS record
│
│   with bunnynet_dns_record.andreas_jaggi_ch_CAA,
│   on dns.tf line 22, in resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA":
│   22: resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
│
│ A tag can be a maximum of 50 ASCII characters.

After some head-scratching I figured out that the Terraform provider has dedicated fields for the flags and tag parts of the CAA DNS record.
And it insists on them being used this way:

resource "bunnynet_dns_record" "andreas_jaggi_ch_CAA" {
  zone  = bunnynet_dns_zone.andreas_jaggi_ch.id
  name  = ""
  type  = "CAA"
  tag   = "issue"
  flags = 0
  value = "letsencrypt.org;validationmethods=http-01"
}

With this in place, it worked fine.
And it prepared me also for the MX record where a similar approach is required.

How to shoot yourself in the foot while setting up Bunny CDN

For andreas-jaggi.ch I wanted to try out Bunny CDN.
Everything went very smooth and I nicely used Terraform to configure Edge Rules blocking all unwanted access.
As andreas-jaggi.ch does not have much content this resulted in a list of allowed files similar to this:

resource "bunnynet_pullzone_edgerule" "andreas_jaggi_4" {
  pullzone    = bunnynet_pullzone.andreas_jaggi.id
  description = "block not(known good) http://www.andreas-jaggi.ch"
  enabled     = true
  match_type  = "MatchNone"
  actions     = [{ type = "BlockRequest", parameter1 = null, parameter2 = null, parameter3 = null }]

  triggers = [
    {
      match_type = "MatchAny",
      patterns = [
        "http://www.andreas-jaggi.ch/",
        "http://www.andreas-jaggi.ch/favicon.ico",
        "http://www.andreas-jaggi.ch/robots.txt",
        "http://www.andreas-jaggi.ch/security.txt",
      ],
      type = "Url", parameter1 = null, parameter2 = null
    },
  ]
}

After this I did setup the www subdomain as CNAME and added it as additional hostname to the CDN Pullzone.
But the process to get a Let's Encrypt certificate for the www subdomain always failed with an error.

This is where I messed up.
Turns out my Edge Rules blocking all unwanted access also blocked the Let's Encrypt validation requests. 🤦

Once I realized this (which took a shamefully long amount of time), I added an entry to the Edge Rules for the .well-known/acme-challenge/ subpath:

resource "bunnynet_pullzone_edgerule" "andreas_jaggi_4" {
  pullzone    = bunnynet_pullzone.andreas_jaggi.id
  description = "block not(known good) https://www.andreas-jaggi.ch"
  enabled     = true
  match_type  = "MatchNone"
  actions     = [{ type = "BlockRequest", parameter1 = null, parameter2 = null, parameter3 = null }]

  triggers = [
    {
      match_type = "MatchAny",
      patterns = [
        "http://www.andreas-jaggi.ch/",
        "http://www.andreas-jaggi.ch/favicon.ico",
        "http://www.andreas-jaggi.ch/robots.txt",
        "http://www.andreas-jaggi.ch/security.txt",
        "http://www.andreas-jaggi.ch/.well-known/acme-challenge/*",
      ],
      type = "Url", parameter1 = null, parameter2 = null
    },
  ]
}

With this in place, the process worked immediately and the www subdomain now also serves encrypted traffic. 🔐

Thursday, 29. May 2025 Week 22

You can style alt text like any other text

In the You can style alt text like any other text article, Andy Bell showcases how the alt test of images can be styled with CSS.
And then goes on to provide a smooth gracefully degrading experience by leveraging JavaScript to style image loading errors differently than successfully loaded images.

I like that CSS gives us plenty of opportunities to add finer details when we want them. One of those finer details is making the experience of an image not loading a little better.
The alt text is surfaced on the page when an image fails to load. It’s yet another reason to write good alt text and really hone your skills in creating a user-focused experience by being descriptive.
What’s happening here is I’m hooking a function up to the <img>’s built-in onerror event. When that event fires, I’m setting a data-img-loading-error attribute.

(via)

Tuesday, 27. May 2025 Week 22

TIL: timeout in Bash scripts

Here comes a handy utility: timeout. As the name suggests, this command adds a timeout to other commands. You specify the time limit you want to wait for a command and if that time passes, timeout sends a signal to terminate it and exits with non-zero. By default, timeout sends SIGTERM, but you can change it with the --signal flag, e.g. timeout --signal=SIGKILL 1s foo.

For example, timeout 1s sleep 5 will send the SIGTERM signal to sleep after 1 second.

(via)

Sunday, 25. May 2025 Week 21

Removed Disqus

After a bit more than 13 years I now removed Disqus from the blog.
Over these years, it only contributed 36 comments.
Most of them around a single post that made it onto the frontpage of Hacker News and Reddit.

I'll extract these comments from the export file of Disqus.
Then backfill them as static entries to the corresponding posts so they are preserved.

In the future I might add another way to comment/contribute directly on the blog.
For now, please use the communication channels listed on the About page.