<?xml version="1.0" encoding="UTF-8" ?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Jan Tuomi</title>
    <subtitle>Minimalist, digital gardener, senior software engineer.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://jan.systems/atom.xml" />
    <link
        rel="alternate"
        type="text/html"
        href="https://jan.systems"
    />
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated
    >2026-08-10T00:00:00+00:00</updated>
    <id>https://jan.systems/atom.xml</id>
    <entry xml:lang="en">
        <title>Linux OCI containers as plain FreeBSD jails (without Podman)</title>
        <published>2026-08-10T00:00:00+00:00</published>
        <updated
        >2026-08-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/linux-oci-containers-as-jails/"
        />
        <id>https://jan.systems/posts/linux-oci-containers-as-jails/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/linux-oci-containers-as-jails/"
        >
        &lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Abstract&lt;&#x2F;strong&gt;: Linux software distributed as OCI container images can be extracted into a FreeBSD jail with Linux compatibility enabled. With a couple of mounts set up to make the extracted root a valid Linux userland, the &quot;container&quot; can run like a regular jail without involving virtual machines or a separate control interface (&lt;code&gt;podman&lt;&#x2F;code&gt;). Isolation is handled by the jail subsystem.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;!-- toc --&gt;
&lt;p&gt;FreeBSD has a respectable library of ported software in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.freshports.org&quot;&gt;ports tree&lt;&#x2F;a&gt;. Getting ported software set up is usually just a &lt;code&gt;pkg install&lt;&#x2F;code&gt; away. Sometimes you have to take a couple of extra steps and run a &lt;code&gt;make install&lt;&#x2F;code&gt;. Some programs do not have a port but can be made to run anyway, by tweaking a Makefile, setting up an ad-hoc Python environment, or something to that effect.&lt;&#x2F;p&gt;
&lt;p&gt;Some programs, however, are designed to be run in a very particular, Dockerfile-specified, Linux-library-dependent environment.&lt;&#x2F;p&gt;
&lt;p&gt;My first encounter with this kind of challenge was &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;immich.app&quot;&gt;Immich&lt;&#x2F;a&gt;: a self-hostable personal photo and video gallery.&lt;&#x2F;p&gt;
&lt;p&gt;Immich has an elaborate Dockerfile setup. I tried my darndest to reverse engineer the steps to install it into a fresh FreeBSD jail, free from any extra layers of administrative abstraction.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a NodeJS app, can&#x27;t be that hard. I&#x27;ve done this a bunch of times.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out, in contemporary NodeJS fashion, the build involved:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;various Rust build steps for some kind of SDKs and plugin systems&lt;&#x2F;li&gt;
&lt;li&gt;binary blob direct downloads (Linux-only of course)&lt;&#x2F;li&gt;
&lt;li&gt;a flurry of bind mounts from earlier build stages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;etc, etc.&lt;&#x2F;p&gt;
&lt;p&gt;The absolute state of web software complexity in &lt;code&gt;$current_year&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I gave up and started looking for an alternate approach.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-classic-solution-linux-vm&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-classic-solution-linux-vm&quot; aria-label=&quot;Anchor link for: the-classic-solution-linux-vm&quot;&gt;The classic solution (Linux VM)&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I booted up a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;bhyve.org&quot;&gt;bhyve&lt;&#x2F;a&gt; virtual machine running &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.alpinelinux.org&quot;&gt;Alpine Linux&lt;&#x2F;a&gt;. I used the venerable &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;freebsd&#x2F;vm-bhyve&quot;&gt;vm-bhyve&lt;&#x2F;a&gt; wrapper.&lt;&#x2F;p&gt;
&lt;p&gt;With the vm running, I just installed podman with &lt;code&gt;apk add podman&lt;&#x2F;code&gt; and started the immich services with &lt;code&gt;podman compose up&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Ok, not all services. I still ran PostgreSQL and Valkey in a FreeBSD jail, outside the vm. It would be wasteful to virtualize those, since they run without a hitch on FreeBSD. I just made sure to give the vm a local IP that&#x27;s in the same subnet as my jails.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The problem was immediately solved. It is trivial to run Linux containers when you have a Linux box.&lt;&#x2F;p&gt;
&lt;p&gt;However, I was not content. The virtual machine was consuming more resources than all of my jails and natively running services combined. A fresh Alpine install has more background processes running than my production FreeBSD server.&lt;&#x2F;p&gt;
&lt;p&gt;I have to run a full Linux kernel and a bunch of processes just to run this one Node.js app.&lt;&#x2F;p&gt;
&lt;p&gt;Also, the vm sticks out like a sore thumb in my otherwise clean jail setup. It needs a bunch of purpose built machinery to provision and manage. I cannot keep it up to date with &lt;code&gt;zfs clone&lt;&#x2F;code&gt;, &lt;code&gt;freebsd-update&lt;&#x2F;code&gt; and &lt;code&gt;pkg upgrade&lt;&#x2F;code&gt;. Monitoring needs to be set up separately. Logging needs to be set up separately.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-cutting-edge-solution-podman-linux-containers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-cutting-edge-solution-podman-linux-containers&quot; aria-label=&quot;Anchor link for: the-cutting-edge-solution-podman-linux-containers&quot;&gt;The cutting edge solution (Podman + Linux containers)&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The container runtime &lt;em&gt;podman&lt;&#x2F;em&gt; &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;podman.io&#x2F;docs&#x2F;installation#installing-on-freebsd&quot;&gt;supports FreeBSD since &lt;code&gt;14.3-RELEASE&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. In addition to being able to run OCI images built on a FreeBSD base image, podman is able to run Linux containers as well using the FreeBSD Linux compatibility layer.&lt;&#x2F;p&gt;
&lt;p&gt;This is fantastic and probably the future of containers of FreeBSD. However, I do not want to set up podman on my server, at least not yet.&lt;&#x2F;p&gt;
&lt;p&gt;Podman has an opinionated approach to networking and storage. Networking requires specific &lt;code&gt;pf&lt;&#x2F;code&gt; firewall rules and storage is modelled in a way that is not compatible with how I manage my jail ZFS datasets. And maybe most importantly, container services would require using the &lt;code&gt;podman&lt;&#x2F;code&gt; interface instead of the traditional &lt;code&gt;service&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;rc&lt;&#x2F;code&gt; interface, which I use to manage everything else on the box.&lt;&#x2F;p&gt;
&lt;p&gt;Using podman for a single service would complicate things. Unnecessarily, in my opinion.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t set up podman to run Immich. Instead, I started designing a custom approach, that would still let me use the tools I use everywhere else and avoid needless performance overhead.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-custom-solution-linux-jail-extracted-container-file-system&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-custom-solution-linux-jail-extracted-container-file-system&quot; aria-label=&quot;Anchor link for: my-custom-solution-linux-jail-extracted-container-file-system&quot;&gt;My custom solution (Linux jail + extracted container file system)&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The core idea of my solution is to note a central fact: a container image is basically an archived Linux userland. What if I follow the FreeBSD Handbook&#x27;s instructions on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.freebsd.org&#x2F;en&#x2F;books&#x2F;handbook&#x2F;jails&#x2F;#creating-linux-jail&quot;&gt;how to run a Linux jail&lt;&#x2F;a&gt; and adjust the steps to run in the container root filesystem instead?&lt;&#x2F;p&gt;
&lt;p&gt;Through some trial and error, I got a setup working. I&#x27;ll walk you through the main steps in a tutorial fashion, using the &lt;code&gt;immich-server&lt;&#x2F;code&gt; container as an example.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;preparing-the-host-and-starting-an-interim-jail&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#preparing-the-host-and-starting-an-interim-jail&quot; aria-label=&quot;Anchor link for: preparing-the-host-and-starting-an-interim-jail&quot;&gt;Preparing the host and starting an interim jail&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;First things first: Enable Linux compatibility on the host. You don&#x27;t want to mount any special filesystems on the host, so disable those separately:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;[host]$ sysrc linux_enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;YES&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;[host]$ sysrc linux_mounts_enable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;NO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ service linux start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, set up a jail filesystem. I used &lt;code&gt;zfs clone&lt;&#x2F;code&gt; to clone my &lt;code&gt;15.1-RELEASE&lt;&#x2F;code&gt; template (which is based on the same release as my host) into a new jail root. Call it &lt;code&gt;immich-server&lt;&#x2F;code&gt; because it matches the name of the container. My jail filesystems live under &lt;code&gt;zroot&#x2F;jails&#x2F;containers&#x2F;&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ zfs clone zroot&#x2F;jails&#x2F;templates&#x2F;15.1-RELEASE@base zroot&#x2F;jails&#x2F;containers&#x2F;immich-server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Use a direct &lt;code&gt;jail&lt;&#x2F;code&gt; command to set up basic networking and select the filesystem path. This isn&#x27;t the final configuration: you just need it running to set up the immich container.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# see that the IP and interface match your setup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ jail -cm &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;\&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;    name&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;immich-server&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    host.hostname=&amp;quot;immich-server&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    path=&amp;quot;&#x2F;usr&#x2F;local&#x2F;jails&#x2F;containers&#x2F;immich-server&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    exec.clean&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;    allow.raw_sockets&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    interface=&amp;quot;lan0&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    ip4.addr=&amp;quot;192.168.4.1&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    exec.start=&amp;quot;&#x2F;bin&#x2F;sh &#x2F;etc&#x2F;rc&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;    exec.stop=&amp;quot;&#x2F;bin&#x2F;sh &#x2F;etc&#x2F;rc.shutdown&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ jexec immich-server &#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You now have a shell in the jail.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extract-the-oci-image&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#extract-the-oci-image&quot; aria-label=&quot;Anchor link for: extract-the-oci-image&quot;&gt;Extract the OCI image&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Next, you need to 1) download the image, and 2) extract the image filesystem into a jail. Luckily, the OCI ecosystem has suitable programs for these tasks: the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;opencontainers&#x2F;umoci&quot;&gt;umoci&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;podman-container-tools&#x2F;skopeo&quot;&gt;skopeo&lt;&#x2F;a&gt; command line tools.&lt;&#x2F;p&gt;
&lt;p&gt;These tools are only needed during &lt;em&gt;build time&lt;&#x2F;em&gt;, i.e. while constructing the container jail. They are not needed during runtime. You might choose to install them in the jail, and perhaps uninstall them later. Or maybe install them on the host and operate on the jail filesystem from the host side, keeping the jail &lt;em&gt;clean&lt;&#x2F;em&gt;. Maybe one could even set up a temporary &quot;build jail&quot;? I went with the first option, and just installed the tools in the target jail, &lt;code&gt;immich-server&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With &lt;code&gt;skopeo&lt;&#x2F;code&gt;, you can download a Linux OCI image into a file in the filesystem:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ pkg install skopeo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;[immich-server]$ TAG&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;immich-server:v3.0.3&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;[immich-server]$ skopeo copy --override-os linux docker:&#x2F;&#x2F;ghcr.io&#x2F;immich-app&#x2F;$TAG oci:$TAG&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a directory called &lt;code&gt;immich-server&lt;&#x2F;code&gt; in the current directory. The directory contains the manifest and the layers.&lt;&#x2F;p&gt;
&lt;p&gt;Then, use &lt;code&gt;umoci&lt;&#x2F;code&gt; to extract the layers into a valid Linux userland. Just one problem: &lt;code&gt;umoci&lt;&#x2F;code&gt; does not have a FreeBSD port or a FreeBSD binary build. Fear not! The Linux binary is statically linked and the jail has Linux compatibility, since you enabled it on the host.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Assuming amd64 arch here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ fetch https:&#x2F;&#x2F;github.com&#x2F;opencontainers&#x2F;umoci&#x2F;releases&#x2F;latest&#x2F;download&#x2F;umoci.linux.amd64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ install -m 755 umoci.linux.amd64 &#x2F;usr&#x2F;local&#x2F;bin&#x2F;umoci&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;[immich-server]$ umoci unpack --image $TAG unpacked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;unpacked&lt;&#x2F;code&gt; directory contains the &lt;code&gt;rootfs&lt;&#x2F;code&gt; as well as some metadata. &lt;code&gt;unpacked&#x2F;config.json&lt;&#x2F;code&gt; is worth checking out: it contains values that are defined in the Dockerfile, such as: &lt;code&gt;env&lt;&#x2F;code&gt;, &lt;code&gt;command&lt;&#x2F;code&gt;, user &lt;code&gt;uid&lt;&#x2F;code&gt; and &lt;code&gt;gid&lt;&#x2F;code&gt;, working directory path. Those can be used later, when we set up the service to start up the app.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s move &lt;code&gt;unpacked&#x2F;rootfs&lt;&#x2F;code&gt; to an easy absolute path: &lt;code&gt;&#x2F;linux&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ mv unpacked&#x2F;rootfs &#x2F;linux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Chroot inside and see that it acts like a Linux system should:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ chroot &#x2F;linux &#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; uname&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Linux&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;mounting-special-device-nodes-and-defining-the-final-jail&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mounting-special-device-nodes-and-defining-the-final-jail&quot; aria-label=&quot;Anchor link for: mounting-special-device-nodes-and-defining-the-final-jail&quot;&gt;Mounting special device nodes and defining the final jail&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Some containerized apps might work already, but most need specific device nodes to function. Let&#x27;s exit the chroot and shell, and stop the jail.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ jail -r immich-server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;immich-server:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; removed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, define the jail properly with a &lt;code&gt;jail.conf&lt;&#x2F;code&gt;, taking inspiration from the Handbook. You can also use &lt;code&gt;bastille&lt;&#x2F;code&gt;, &lt;code&gt;iocage&lt;&#x2F;code&gt; or any other jail manager&#x2F;wrapper to do this. The goal here is to set up a normal Linux userland with the expected dev nodes as well as special filesystems like &lt;code&gt;&#x2F;proc&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;sys&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Mount also any directories that are used during runtime, i.e. those that are handled by volume or bind mounts when using OCI containers.&lt;&#x2F;p&gt;
&lt;p&gt;Note that the jail does not need any of the &lt;code&gt;allow.mount.*&lt;&#x2F;code&gt; directives, since the mount syscalls happen on the host side.&lt;&#x2F;p&gt;
&lt;p&gt;Edit as needed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;immich-server {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # STARTUP&#x2F;LOGGING&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  exec.start = &amp;quot;&#x2F;bin&#x2F;sh &#x2F;etc&#x2F;rc&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  exec.stop = &amp;quot;&#x2F;bin&#x2F;sh &#x2F;etc&#x2F;rc.shutdown&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  exec.consolelog = &amp;quot;&#x2F;var&#x2F;log&#x2F;jail_console_${name}.log&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # PERMISSIONS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  allow.raw_sockets;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  exec.clean;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount.devfs;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  devfs_ruleset = 4;  # Ensure that you use a devfs ruleset that exposes all basic devices&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  enforce_statfs = 1; # Allow the jail to see what&amp;#39;s mounted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # HOSTNAME&#x2F;PATH&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  host.hostname = &amp;quot;${name}&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  path = &amp;quot;&#x2F;usr&#x2F;local&#x2F;jails&#x2F;containers&#x2F;${name}&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # NETWORK&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ip4.addr = 192.168.4.1;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  interface = lan0;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # LINUX SPECIAL MOUNTS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;devfs       $path&#x2F;linux&#x2F;dev     devfs     rw  0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;tmpfs       $path&#x2F;linux&#x2F;dev&#x2F;shm tmpfs     rw,size=1g,mode=1777  0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;fdescfs     $path&#x2F;linux&#x2F;dev&#x2F;fd  fdescfs   rw,linrdlnk 0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;linprocfs   $path&#x2F;linux&#x2F;proc    linprocfs rw  0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;linsysfs    $path&#x2F;linux&#x2F;sys     linsysfs  rw  0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  # CONTAINER VOLUME MOUNTS&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  mount += &amp;quot;&#x2F;usr&#x2F;local&#x2F;jails&#x2F;volumes&#x2F;immich_server_data   $path&#x2F;linux&#x2F;data  nullfs  rw  0 0&amp;quot;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;I actually use vnet networking and not simple networking. It&#x27;s not relevant for this demo though.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you put the &lt;code&gt;jail.conf&lt;&#x2F;code&gt; in its proper place, you can now control the jail with &lt;code&gt;service&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ service jail start immich-server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Starting&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; jails: immich-server.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;creating-the-rc-service&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#creating-the-rc-service&quot; aria-label=&quot;Anchor link for: creating-the-rc-service&quot;&gt;Creating the &lt;code&gt;rc&lt;&#x2F;code&gt; service&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Now, we need to configure the containerized app to start when the jail starts. We can use a simple &lt;code&gt;rc&lt;&#x2F;code&gt; service for this! Let&#x27;s &lt;code&gt;jexec&lt;&#x2F;code&gt; into the jail and create a service file.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[host]$ jexec immich-server &#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ vi &#x2F;usr&#x2F;local&#x2F;etc&#x2F;rc.d&#x2F;immich_server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;My service looks like this. The idea is to run the application in a &lt;code&gt;chroot&lt;&#x2F;code&gt;. The startup command is improvised based on the container image &lt;code&gt;config.json&lt;&#x2F;code&gt; in the extracted &lt;code&gt;unpacked&lt;&#x2F;code&gt; directory.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;bin&#x2F;sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# PROVIDE: immich_server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# REQUIRE: NETWORKING&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# KEYWORD: shutdown&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;. &#x2F;etc&#x2F;rc.subr&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;name=&amp;quot;immich_server&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rcvar=&amp;quot;${name}_enable&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;pidfile=&amp;quot;&#x2F;var&#x2F;run&#x2F;${name}.pid&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;logfile=&amp;quot;&#x2F;var&#x2F;log&#x2F;${name}.log&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;load_rc_config $name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: ${immich_server_enable:=&amp;quot;NO&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: ${immich_server_envfile:=&amp;quot;&#x2F;usr&#x2F;local&#x2F;etc&#x2F;immich_server.env&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: ${immich_server_root:=&amp;quot;&#x2F;linux&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: ${immich_server_user:=&amp;quot;root&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;: ${immich_server_group:=&amp;quot;wheel&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;start_cmd=&amp;quot;${name}_start&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;stop_cmd=&amp;quot;${name}_stop&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;status_cmd=&amp;quot;${name}_status&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;immich_server_start() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    echo &amp;quot;Starting ${name}.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    set -a    # export all variables&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    . ${immich_server_envfile}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    set +a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &#x2F;usr&#x2F;sbin&#x2F;daemon -P ${pidfile} -o ${logfile} \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;usr&#x2F;sbin&#x2F;chroot \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        -u ${immich_server_user} -g ${immich_server_group} \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ${immich_server_root} \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;usr&#x2F;local&#x2F;bin&#x2F;node &#x2F;usr&#x2F;src&#x2F;app&#x2F;server&#x2F;dist&#x2F;main.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;immich_server_stop() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if [ -f ${pidfile} ]; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        echo &amp;quot;Stopping ${name}.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        kill $(cat ${pidfile}) 2&amp;gt;&#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        rm -f ${pidfile}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        echo &amp;quot;${name} is not running.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;immich_server_status() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    if [ -f ${pidfile} ] &amp;amp;&amp;amp; kill -0 $(cat ${pidfile}) 2&amp;gt;&#x2F;dev&#x2F;null; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        echo &amp;quot;${name} is running as pid $(cat ${pidfile}).&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        echo &amp;quot;${name} is not running.&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        return 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;run_rc_command &amp;quot;$1&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, some things don&#x27;t map to this filesystem-focused way of running container apps. Containerfiles can define:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;environment variables&lt;&#x2F;li&gt;
&lt;li&gt;exposed ports&lt;&#x2F;li&gt;
&lt;li&gt;user and group ids&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You might want to implement these in some way. My own approach can be seen in the &lt;code&gt;rc&lt;&#x2F;code&gt; service example: you can supply an envfile with &lt;code&gt;FOO=bar&lt;&#x2F;code&gt; style variables and specify user and group with &lt;code&gt;rc.conf&lt;&#x2F;code&gt; variables.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Set up environment variables in this file&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ touch &#x2F;usr&#x2F;local&#x2F;etc&#x2F;immich_server.env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ chmod 600 &#x2F;usr&#x2F;local&#x2F;etc&#x2F;immich_server.env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Enable the service to start automatically on jail startup, and start it once right away:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ service immich_server enable&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[immich-server]$ service immich_server start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;upgrading-the-container&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#upgrading-the-container&quot; aria-label=&quot;Anchor link for: upgrading-the-container&quot;&gt;Upgrading the container&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I have yet to upgrade to a more recent version of the image, but I&#x27;d imagine it is as simple as:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;stopping the service,&lt;&#x2F;li&gt;
&lt;li&gt;unmounting all mountpoints,&lt;&#x2F;li&gt;
&lt;li&gt;removing the &lt;code&gt;&#x2F;linux&lt;&#x2F;code&gt; directory, and&lt;&#x2F;li&gt;
&lt;li&gt;redoing the steps that download and extract an image into &lt;code&gt;&#x2F;linux&lt;&#x2F;code&gt;, using the new image TAG&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Then, a jail restart should bring everything up again.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#thoughts&quot; aria-label=&quot;Anchor link for: thoughts&quot;&gt;Thoughts&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I have had a good experience with this setup. It is somewhat &lt;em&gt;manual&lt;&#x2F;em&gt;, sure, and I guess some utility scripts could be used for the download and extract part at least, but that doesn&#x27;t bother me really. Obviously adhoc setups like this could be improved and made more robust&#x2F;rigorous.&lt;&#x2F;p&gt;
&lt;p&gt;You can also run multiple container images in a single jail with this method. Immich has a separate machine learning container for running face recognition tasks etc. I run that in the same jail with a separate &lt;code&gt;rc&lt;&#x2F;code&gt; service.&lt;&#x2F;p&gt;
&lt;p&gt;One fun approach could be to wrap this into a FreeBSD port, and build the image fs with poudriere. A simple &lt;code&gt;pkg install&lt;&#x2F;code&gt; could then set up the service and everything. Something to consider!&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>🌱 Now, spring 2026</title>
        <published>2026-04-12T00:00:00+00:00</published>
        <updated
        >2026-04-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2026-spring/"
        />
        <id>https://jan.systems/posts/now-2026-spring/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2026-spring/"
        >
        &lt;p&gt;Planning the wedding, reworking my wardrobe. Home reno.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2026-spring&amp;#x2F;17670809516920.jpg&quot; alt=&quot;Dogs playing in the dog park.&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Dogs playing in the dog park.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Jojo (the shiba inu) breached containment one night. We were going to take him out on a walk but the dog was nowhere to be seen. The backyard gate was slightly open. We tried finding him by driving around the neighborhood. Some people on social media had seen him walking around so we tried to track him, but we were always too late to the scene.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily, he walked back home and was waiting for us in the backyard.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;re a smart boy, Jojo, but don&#x27;t do that again. One of the most stressful evenings of my life.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;tv-anime&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tv-anime&quot; aria-label=&quot;Anchor link for: tv-anime&quot;&gt;TV &amp;amp; anime&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was sick for a week so I had ample time to watch a bunch of stuff from my backlog.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Chiikawa. The episodes are so short that we watch a bunch at a time.&lt;&#x2F;li&gt;
&lt;li&gt;Jojo&#x27;s Bizarre Adventure: Steel Ball Run aka part 7 (where are the episodes, Netflix?)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Finished&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Dandadan S2&lt;&#x2F;li&gt;
&lt;li&gt;The Witcher, seasons S2–S4&lt;&#x2F;li&gt;
&lt;li&gt;Golden Boy&lt;&#x2F;li&gt;
&lt;li&gt;Yuru Camp S1&lt;&#x2F;li&gt;
&lt;li&gt;Beyond the Boundary (series and Future movie)&lt;&#x2F;li&gt;
&lt;li&gt;Paprika (movie)&lt;&#x2F;li&gt;
&lt;li&gt;The Boy and the Heron (movie)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;manga&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#manga&quot; aria-label=&quot;Anchor link for: manga&quot;&gt;Manga&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Jojo&#x27;s Bizarre Adventure: Jojolion, aka part 8&lt;&#x2F;li&gt;
&lt;li&gt;Witch Hat Atelier (in Japanese)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Finished&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Berserk 1–43. RIP Kentaro Miura.&lt;&#x2F;li&gt;
&lt;li&gt;DU-RAN-KI&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#games&quot; aria-label=&quot;Anchor link for: games&quot;&gt;Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Stray&lt;&#x2F;li&gt;
&lt;li&gt;Pizza Tower (dropped midway, didn&#x27;t spark interest)&lt;&#x2F;li&gt;
&lt;li&gt;Castlevania: Symphony of the Night&lt;&#x2F;li&gt;
&lt;li&gt;Warhammer 40K: Space Marine II&lt;&#x2F;li&gt;
&lt;li&gt;Absolum&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I also won a game of Twilight Imperium (4th ed).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;music&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#music&quot; aria-label=&quot;Anchor link for: music&quot;&gt;Music&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Stopped using Spotify. I already pay for Youtube Premium + Music so I already have a streaming service that I can use. I also try to be more mindful of my music consumption and focus on supporting independent artists and creators. Mostly by buying digital albums or tapes from Bandcamp or distributors.&lt;&#x2F;li&gt;
&lt;li&gt;Saw &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;merzbow.net&#x2F;show&#x2F;&quot;&gt;Merzbow&lt;&#x2F;a&gt; live.&lt;&#x2F;li&gt;
&lt;li&gt;Went to a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.bongosbingo.fi&#x2F;&quot;&gt;Bongo&#x27;s Bingo&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I listen to a bunch of black metal and adjacent genres. There&#x27;s a lot of white supremacists and fascists in those communities, especially in the Finnish scene (sadly). I figured I should use a couple of hours to go through some of the artists that I have listened to and see if they&#x27;re sketchy or not.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m happy to find that most of the stuff I listen to is a-ok, and a great deal of artists are vocally against hate, or at least apolitical (whatever that means). However, some artists had slipped through the cracks.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Mgła&lt;&#x2F;strong&gt; collaborates with Nazis and the frontman has an earlier project with clearly anti-semitic titles and lyrics.
&lt;strong&gt;Drudkh&lt;&#x2F;strong&gt; has used Nazi imagery during their live shows.&lt;&#x2F;p&gt;
&lt;p&gt;I really enjoyed their music in 2025, but I won&#x27;t be supporting them anymore.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;top-tunes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#top-tunes&quot; aria-label=&quot;Anchor link for: top-tunes&quot;&gt;Top tunes&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;iframe style=&quot;border: 0; width: 400px; height: 472px;&quot; src=&quot;https:&#x2F;&#x2F;bandcamp.com&#x2F;EmbeddedPlayer&#x2F;album=2777761805&#x2F;size=large&#x2F;bgcol=ffffff&#x2F;linkcol=0687f5&#x2F;artwork=small&#x2F;transparent=true&#x2F;&quot; seamless&gt;&lt;a href=&quot;https:&#x2F;&#x2F;agriculturemusic.bandcamp.com&#x2F;album&#x2F;the-spiritual-sound&quot;&gt;The Spiritual Sound by Agriculture&lt;&#x2F;a&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;this-website&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#this-website&quot; aria-label=&quot;Anchor link for: this-website&quot;&gt;This website&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The new Zola-based implementation has been working well. There was a bunch of figuring-out to do at first, like how to structure the templates to get the &lt;a href=&quot;&#x2F;now&quot;&gt;&#x2F;now&lt;&#x2F;a&gt; page working, or how to get the previous&#x2F;next post links appended to each post, but it&#x27;s been worth it. Zola compilation is way faster than my earlier custom Scheme-based SSG.&lt;&#x2F;p&gt;
&lt;p&gt;My earlier system had the major upside that it directly read Markdown from my Obsidian vault, which allowed me to edit posts on the go. Currently everything&#x27;s laptop-driven, which is bit of a pain. I might set up something with file shares to allow editing posts from my phone. Deployments will still happen from the laptop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;home-improvements&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home-improvements&quot; aria-label=&quot;Anchor link for: home-improvements&quot;&gt;Home improvements&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Quick-fire round:&lt;&#x2F;p&gt;
&lt;p&gt;Woodworking:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;new windowsill and shelf in the living room
&lt;ul&gt;
&lt;li&gt;great for positioning the rear bluetooth speakers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;shoe rack inside a cabinet in Marika&#x27;s room&lt;&#x2F;li&gt;
&lt;li&gt;shoe rack inside a cabinet in the front door hallway&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Bought furniture:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;new metal and glass cabinet in the kitchen&lt;&#x2F;li&gt;
&lt;li&gt;a bench for not-clean-but-not-dirty clothes, upstairs&lt;&#x2F;li&gt;
&lt;li&gt;a new towel rack for the bathroom&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The studio room is also progressing after a hiatus. I got the server&#x2F;synth rack set up and the old studio desk disassembled. Excited about getting things moving in that room too.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2025-winter&quot;&gt;❄️ Now, winter 2025&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>A home server journey, part 5: Ansible</title>
        <published>2025-12-26T00:00:00+00:00</published>
        <updated
        >2025-12-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/home-server-part-5/"
        />
        <id>https://jan.systems/posts/home-server-part-5/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/home-server-part-5/"
        >
        &lt;p&gt;This is the fifth episode in a series where I set up a FreeBSD home server, explaining all the steps, problems and solutions along the way. This time we&#x27;re provisioning the server with Ansible.&lt;&#x2F;p&gt;
&lt;p&gt;Check out episodes &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-1&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-2&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-3&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-4&quot;&gt;4&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;automation&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#automation&quot; aria-label=&quot;Anchor link for: automation&quot;&gt;Automation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.ansible.com&#x2F;projects&#x2F;ansible&#x2F;latest&#x2F;index.html&quot;&gt;Ansible&lt;&#x2F;a&gt; is a tool for running a bunch of Python scripts, or &quot;tasks&quot;, in a kind of declarative way. It&#x27;s commonly used for provisioning servers.&lt;&#x2F;p&gt;
&lt;p&gt;My ansible configuration is publicly available on my GitHub. I update it regularly.&lt;br &#x2F;&gt;
🌐 &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantuomi&#x2F;ansible-freebsd-home-server&quot;&gt;jantuomi&#x2F;ansible-freebsd-home-server&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I have only one server, and I&#x27;m planning to have it for a long time. Why would I need automated provisioning? It seems like an extra step when I could just provision everything manually. There is no need to synchronise multiple hosts or anything like that.&lt;&#x2F;p&gt;
&lt;p&gt;I view this kind of automation as executable documentation, or readable instructions for both the computer and the human. If I define the server in terms of these Ansible tasks, I can (in theory) reconstruct what&#x27;s been done with the server just by reading the Ansible playbook.&lt;&#x2F;p&gt;
&lt;p&gt;To be reliable, this necessitates that everything is defined in Ansible. Which happens to be very difficult to actually do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;project-structure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#project-structure&quot; aria-label=&quot;Anchor link for: project-structure&quot;&gt;Project structure&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There are many ways to structure an Ansible project. I picked one that I consider to be simple:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a single main playbook, with tags for selective execution of sub-playbooks&lt;&#x2F;li&gt;
&lt;li&gt;a non-tracked &lt;code&gt;secrets.yml&lt;&#x2F;code&gt; that contains all secret values
&lt;ul&gt;
&lt;li&gt;reasoning: this way I don&#x27;t need to parse a separate configuration format and I get access to all features of YAML, including its data structures (lists, objects), which I would be lacking if I used a flat file format such as &lt;code&gt;.env&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;a flat file hierarchy with names mirroring the target path, e.g. &lt;code&gt;templates&#x2F;root_ssh_config.j2&lt;&#x2F;code&gt; goes to &lt;code&gt;&#x2F;root&#x2F;ssh&#x2F;config&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;no handlers and notify, because I want complete control over the execution order&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is my first non-trivial Ansible project, so I wouldn&#x27;t be surprised if I have some anti-patterns in there.&lt;&#x2F;p&gt;
&lt;p&gt;The playbooks are designed to be fully &lt;em&gt;idempotent&lt;&#x2F;em&gt;: I can run the playbook again after the first run and expect the system to be in the same state. This is very important, since I want to be able to fearlessly run any playbook at any time and expect a success, or at least a helpful failure that doesn&#x27;t compromise basic operation of the host.&lt;&#x2F;p&gt;
&lt;p&gt;The only host in the Ansible inventory is my server. I run the playbook like this from my development laptop:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ansible-playbook&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; inventory playbook.yml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If I want to only update the &lt;code&gt;ingress&lt;&#x2F;code&gt; jail, I run:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ansible-playbook&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; inventory playbook.yml&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; jail_ingress&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;separation-of-responsibilities&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#separation-of-responsibilities&quot; aria-label=&quot;Anchor link for: separation-of-responsibilities&quot;&gt;Separation of responsibilities&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s borrow and tweak the standard cloud infrastructure stack diagram that shows what parts of the stack are handled by the cloud provider and what parts are handled by the customer.&lt;&#x2F;p&gt;
&lt;p&gt;My current approach is to use Ansible for the &quot;core&quot; parts of server administration, installing packages, defining service jails, etc:&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-5&amp;#x2F;pursotin_stack.svg&quot; alt=&quot;A stack model depicting the software layers in my server&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A stack model depicting the software layers in my server&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Some notes about this setup:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The top &quot;application layer&quot; is currently a chaotic no-man&#x27;s-land with varying levels of automation. Optimally I could rebuild all service jails completely with Ansible.&lt;&#x2F;li&gt;
&lt;li&gt;The host OS release is managed manually, i.e. release upgrades are run manually with &lt;code&gt;freebsd-update&lt;&#x2F;code&gt;. This is fine IMHO. I will be supervising all the few and in between OS upgrades anyway, so automation does not help much there.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;why-not-chef-salt-etc&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-not-chef-salt-etc&quot; aria-label=&quot;Anchor link for: why-not-chef-salt-etc&quot;&gt;Why not Chef, Salt, etc?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Ansible is very simple. It&#x27;s pretty much a framework to write idempotent shell scripts without having to write shell scripts. Or Python scripts, whichever way you want to look at it.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s agentless. I don&#x27;t have to run a service on the FreeBSD host to use Ansible. It uses SSH as transport and runs the tasks with Python, both of which are software that I expect to be present on any *nix server.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-5&amp;#x2F;ansible_task_postgres.png&quot; alt=&quot;A snippet of Ansible playbook installing PostgreSQL in the &amp;quot;postgres&amp;quot; jail&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A snippet of Ansible playbook installing PostgreSQL in the &amp;quot;postgres&amp;quot; jail&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;how-about-containers&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-about-containers&quot; aria-label=&quot;Anchor link for: how-about-containers&quot;&gt;How about containers?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;d image that most new server setups in 2025 are designed with container workloads in mind. This one is too, kinda.&lt;&#x2F;p&gt;
&lt;p&gt;I isolate most services with FreeBSD jails, which are similar to OCI containers. Processes are isolated from other jails and the host automatically. Virtual networking (&lt;code&gt;vnet&lt;&#x2F;code&gt;) isolates the network. ZFS snapshots and clones provide thin jail filesystems that have a small storage footprint. Jails share the host kernel.&lt;&#x2F;p&gt;
&lt;p&gt;Many OCI container concepts translate well to jails, but jails are somewhat more &lt;em&gt;persistent&lt;&#x2F;em&gt;. Conventionally, jail filesystems are not ephemeral and survive jail restarts.&lt;&#x2F;p&gt;
&lt;p&gt;Only &lt;code&gt;ssh&lt;&#x2F;code&gt;, &lt;code&gt;bash&lt;&#x2F;code&gt;, &lt;code&gt;vim&lt;&#x2F;code&gt; and some other packages are installed on the host directly. Everything else is 🔐 &lt;em&gt;incarcerated&lt;&#x2F;em&gt; 🔐.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-5&amp;#x2F;pursotin_jls.png&quot; alt=&quot;A list of running jails on the server&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A list of running jails on the server&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;freebsd-oci-container-support&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#freebsd-oci-container-support&quot; aria-label=&quot;Anchor link for: freebsd-oci-container-support&quot;&gt;FreeBSD OCI container support&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;FreeBSD was &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;forums.freebsd.org&#x2F;threads&#x2F;an-introduction-to-oci-containers-on-freebsd.99907&#x2F;&quot;&gt;recently welcomed&lt;&#x2F;a&gt; into the prestigious club of platforms that can run OCI containers. This was achieved by using jails as the container runtime and &lt;code&gt;podman&lt;&#x2F;code&gt; as container manager.&lt;&#x2F;p&gt;
&lt;p&gt;This pretty much means that we can use Dockerfiles&#x2F;Containerfiles to define our jails. Existing Containerfiles cannot be used directly, since they are built on Linux base images, but they can be used as a starting point for building new, FreeBSD-based images.&lt;&#x2F;p&gt;
&lt;p&gt;While this sounds very cool and promising, I want to let the implementation mature a bit before building everything on it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I tried installing the &lt;code&gt;podman-suite&lt;&#x2F;code&gt; package and following the instructions, and promptly broke all of my networking, so yeah 😑&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;m sure OCI containers will be a valid and respectable approach to containerization on FreeBSD, after the kinks have been ironed out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-be-continued&quot; aria-label=&quot;Anchor link for: to-be-continued&quot;&gt;To be continued&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the next part(s) we will be looking at the specifics of the jail setup, e.g. how to create jails using &lt;code&gt;jail.conf.d&lt;&#x2F;code&gt; scripts.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>A home server journey, part 4: Networking</title>
        <published>2025-12-24T00:00:00+00:00</published>
        <updated
        >2025-12-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/home-server-part-4/"
        />
        <id>https://jan.systems/posts/home-server-part-4/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/home-server-part-4/"
        >
        &lt;p&gt;This is the fourth episode in a series where I set up a FreeBSD home server, explaining all the steps, problems and solutions along the way. This time we&#x27;re looking at networking.&lt;&#x2F;p&gt;
&lt;p&gt;Check out episodes &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-1&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-2&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-3&quot;&gt;3&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;dual-interfaces&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dual-interfaces&quot; aria-label=&quot;Anchor link for: dual-interfaces&quot;&gt;Dual interfaces&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I already have a private network (&lt;code&gt;192.168.0.0&#x2F;16&lt;&#x2F;code&gt;) that uses my pfSense router as its gateway. The FreeBSD server will connect to this network through ethernet interface number one (&lt;code&gt;lan0&lt;&#x2F;code&gt;). Packets to and from local devices go through this interface.&lt;&#x2F;p&gt;
&lt;p&gt;The host will also be connected directly to the ISP&#x27;s network using its second interface (&lt;code&gt;wan0&lt;&#x2F;code&gt;). This interface will get its IP using DHCP. Packets to the internet, as well as packets to public services running on the FreeBSD host go through this interface.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-4&amp;#x2F;pursotin_network.svg&quot; alt=&quot;A simplified diagram showing the two interfaces&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A simplified diagram showing the two interfaces&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;A dual interface set up like this is simple in theory, but there are important nuances, e.g. regarding return traffic routing and DNS.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;routing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#routing&quot; aria-label=&quot;Anchor link for: routing&quot;&gt;Routing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If we send a packet out through &lt;code&gt;eth0&lt;&#x2F;code&gt;, we should expect return traffic to arrive on &lt;code&gt;eth0&lt;&#x2F;code&gt; (and vice versa). However, it is rather easy to misconfigure the system.&lt;&#x2F;p&gt;
&lt;p&gt;Assume we have a public service, such as an &lt;code&gt;nginx&lt;&#x2F;code&gt; web server, which listens on the public interface. If we set up &lt;code&gt;rc.conf&lt;&#x2F;code&gt; naively like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;defaultrouter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;192.168.0.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;...the request might arrive through the public interface, but the response from the web server leaves through the private interface since we have configured the private gateway as the default. This is called &lt;em&gt;asymmetric routing&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This might work! Both routes lead to the internet. From the perspective of the ISP, it doesn&#x27;t really matter whether the packet went through the home router or not, in this case.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes however, this setup leads to the most vexing of networking issues. Figuring out what&#x27;s wrong taught me a bunch about DHCP and layer 3 routing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;routing-issue-1-isp-dhcp-server-and-mac-addresses&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#routing-issue-1-isp-dhcp-server-and-mac-addresses&quot; aria-label=&quot;Anchor link for: routing-issue-1-isp-dhcp-server-and-mac-addresses&quot;&gt;Routing issue #1: ISP DHCP server and MAC addresses&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A DHCP server can keep track of clients using their MAC address. Separate physical interfaces have separate MAC addresses, so there is an obvious problem here. If an initial DHCP request broadcast leaves through one interface, and following DHCP requests (non-broadcast) leave through another interface, the DHCP server might see this as a forged packet.&lt;&#x2F;p&gt;
&lt;p&gt;This happened to me because of the &lt;code&gt;defaultrouter&lt;&#x2F;code&gt; setting:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The initial broadcast leaves through the &lt;code&gt;wan0&lt;&#x2F;code&gt; interface correctly. An IP is received.&lt;&#x2F;li&gt;
&lt;li&gt;When the lease is about to expire, a targeted request is sent to the now known DHCP server address. This is routed through &lt;code&gt;lan0&lt;&#x2F;code&gt; and &lt;code&gt;192.168.0.1&lt;&#x2F;code&gt;, since it&#x27;s the &lt;code&gt;defaultrouter&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The DHCP server sees the FreeBSD WAN MAC on first contact, and the home router MAC address on second contact. The lease is not renewed.&lt;&#x2F;li&gt;
&lt;li&gt;The lease expires, repeat from step 1.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;My solution? Just don&#x27;t use &lt;code&gt;defaultrouter&lt;&#x2F;code&gt;. As part of the routine DHCP song and dance, &lt;code&gt;dhclient&lt;&#x2F;code&gt; adds a valid default route to the system routing table. Just use that for all internet-bound traffic. You can see the routes with &lt;code&gt;netstat -rn&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# netstat -rn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Routing&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; tables&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Internet:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Destination&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;        Gateway            Flags         Netif Expire&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;default&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;            87.92.64.1&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;         UGS           wan0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;87.92.64.0&#x2F;18&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;      link#36            U             wan0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;192.168.0.0&#x2F;16&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;     link#25            U             lan0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;127.0.0.1&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;          link#57            UH            lo0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;default&lt;&#x2F;code&gt; route was added by DHCP.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;routing-issue-2-dns-level-ad-blocker-in-my-router&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#routing-issue-2-dns-level-ad-blocker-in-my-router&quot; aria-label=&quot;Anchor link for: routing-issue-2-dns-level-ad-blocker-in-my-router&quot;&gt;Routing issue #2: DNS-level ad blocker in my router&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I run &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.netgate.com&#x2F;pfsense&#x2F;en&#x2F;latest&#x2F;packages&#x2F;pfblocker.html&quot;&gt;pfBlockerNG&lt;&#x2F;a&gt; on my router to block ads on a DNS level (think Pi-hole). If I route traffic through the router, the return traffic is processed by this extra firewall. Some packets got dropped by pfBlockerNG and never arrived at the destination.&lt;&#x2F;p&gt;
&lt;p&gt;This was very annoying to debug! At least I had logging on, so I could confirm what&#x27;s happening pretty quickly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dns&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dns&quot; aria-label=&quot;Anchor link for: dns&quot;&gt;DNS&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Nameservers are defined on a whole system basis in &lt;code&gt;&#x2F;etc&#x2F;resolv.conf&lt;&#x2F;code&gt;, and not per interface or per IP. I have a basic DNS resolver running on my home server, so I simply configured my &lt;code&gt;resolv.conf&lt;&#x2F;code&gt; as:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;nameserver&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 192.168.0.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;split-horizon&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#split-horizon&quot; aria-label=&quot;Anchor link for: split-horizon&quot;&gt;Split horizon&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Which IP should be returned from the DNS server if I query &lt;code&gt;some-public-service.jan.systems&lt;&#x2F;code&gt;? The private address from &lt;code&gt;192.168.0.0&#x2F;16&lt;&#x2F;code&gt;, or a public IP reachable from the internet?&lt;&#x2F;p&gt;
&lt;p&gt;This is called &lt;strong&gt;split horizon&lt;&#x2F;strong&gt; DNS. One approach is to configure the DNS server to respond differently based on the address of the querying host. A private network host gets the private IP, etc.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to not worry about this, and just use a separate domain, &lt;code&gt;local.jan.systems&lt;&#x2F;code&gt; for local addresses. This is a bit inconvenient, but I don&#x27;t mind. The fewer DNS problems the better.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dhcp-and-resolv-conf&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dhcp-and-resolv-conf&quot; aria-label=&quot;Anchor link for: dhcp-and-resolv-conf&quot;&gt;DHCP and &lt;code&gt;resolv.conf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;By default, &lt;code&gt;dhclient&lt;&#x2F;code&gt; has a hook that overwrites &lt;code&gt;resolv.conf&lt;&#x2F;code&gt; with the DHCP-provided nameserver information. I want to always use my router as the DNS server, so I had to disable this functionality.&lt;&#x2F;p&gt;
&lt;p&gt;On FreeBSD, there are two simple solutions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;resolvconf.conf&lt;&#x2F;code&gt;: a meta-config that configures the &lt;code&gt;resolvconf&lt;&#x2F;code&gt; tool, which is used internally by &lt;code&gt;dhclient&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;To disable &lt;code&gt;resolvconf&lt;&#x2F;code&gt; altogether, add this to &lt;code&gt;&#x2F;etc&#x2F;resolvconf.conf&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;resolvconf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;NO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, &lt;code&gt;resolvconf&lt;&#x2F;code&gt;, and transitively &lt;code&gt;dhclient&lt;&#x2F;code&gt;, won&#x27;t overwrite your changes anymore.&lt;&#x2F;p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;making &lt;code&gt;&#x2F;etc&#x2F;resolv.conf&lt;&#x2F;code&gt; immutable with a flag&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;As root, you can set the file as immutable with &lt;code&gt;chflags&lt;&#x2F;code&gt;. Then, nothing can edit the file before the flag is removed.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;chflags&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; schg &#x2F;etc&#x2F;resolv.conf&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that if you have a non-default &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;man.freebsd.org&#x2F;cgi&#x2F;man.cgi?securelevel&quot;&gt;&lt;code&gt;securelevel&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, you might not be able to remove this flag.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;dual-stack&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dual-stack&quot; aria-label=&quot;Anchor link for: dual-stack&quot;&gt;Dual stack?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;For now, I&#x27;m building everything on IPv4. My ISP has no support for SLAAC which makes IPv6 somewhat non-trivial. I will most likely add IPv6 at some point, but it&#x27;s not a priority.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-be-continued&quot; aria-label=&quot;Anchor link for: to-be-continued&quot;&gt;To be continued&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the next part(s) we will be looking at provisioning with Ansible.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>A home server journey, part 3: Installation</title>
        <published>2025-12-23T00:00:00+00:00</published>
        <updated
        >2025-12-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/home-server-part-3/"
        />
        <id>https://jan.systems/posts/home-server-part-3/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/home-server-part-3/"
        >
        &lt;p&gt;This is the third episode in a series where I set up a FreeBSD home server, explaining all the steps, problems and solutions along the way. This time we&#x27;re installing the OS for the first time.&lt;&#x2F;p&gt;
&lt;p&gt;Check out episodes &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-1&quot;&gt;1&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-2&quot;&gt;2&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;installing-freebsd&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#installing-freebsd&quot; aria-label=&quot;Anchor link for: installing-freebsd&quot;&gt;Installing FreeBSD&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Installation should be pretty straightforward. I&#x27;m going to go with the latest stable release installer (&lt;code&gt;14.3-RELEASE&lt;&#x2F;code&gt;) in the &lt;code&gt;amd64&lt;&#x2F;code&gt; + &lt;code&gt;memstick.iso&lt;&#x2F;code&gt; variety. Let&#x27;s burn the installer onto a memory stick, plug it in, and was enter the install wizard.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note from the future! &lt;code&gt;15.0-RELEASE&lt;&#x2F;code&gt; is already out, and I have recently updated to it. This post is already outdated!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The initial idea is this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ZFS, for all the automatic durability gains and easy-to-setup RAID1+0 (stripe of mirrors)&lt;&#x2F;li&gt;
&lt;li&gt;encrypted ZFS datasets (kinda similar to partitions) for &lt;strong&gt;jails&lt;&#x2F;strong&gt;, which would contain all the actual services&lt;&#x2F;li&gt;
&lt;li&gt;unencrypted root, so that I can reboot the machine remotely, physically unattended&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.freebsd.org&#x2F;en&#x2F;books&#x2F;handbook&#x2F;jails&#x2F;&quot;&gt;Jails&lt;&#x2F;a&gt; are a lightweight way to containerize applications. They can get a separate process space, network stack, file system, root user, among others. They use the same kernel as the host, so they are more similar to LXC or OCI containers (think Docker, but more mature and flexible) than to virtual machines, which in FreeBSD are managed with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.freebsd.org&#x2F;en&#x2F;books&#x2F;handbook&#x2F;virtualization&#x2F;#virtualization-host-bhyve&quot;&gt;bhyve&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Teaser: we will be installing &lt;code&gt;bhyve&lt;&#x2F;code&gt; later too.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Using the install wizard, I set up the system following my rough plan. And here we go, all up and running:&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-3&amp;#x2F;pursotin_fastfetch.png&quot; alt=&quot;Fastfetch output showing the machine specs. Disregard the uptime (I took the screenshot way later)&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Fastfetch output showing the machine specs. Disregard the uptime (I took the screenshot way later)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;raidz&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#raidz&quot; aria-label=&quot;Anchor link for: raidz&quot;&gt;RAIDZ&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You might notice that I bought 4 x 1 TB of NVMe disks, but &lt;code&gt;fastfetch&lt;&#x2F;code&gt; is only showing ~2TB. That&#x27;s because I set up the disks into a &quot;RAID 1+0&quot; configuration:&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-3&amp;#x2F;pursotin_raidz.svg&quot; alt=&quot;A &amp;quot;RAID 1+0&amp;quot; configuration&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A &amp;quot;RAID 1+0&amp;quot; configuration&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;RAID 1+0 is a combination of two RAID levels. RAID 1, or &lt;em&gt;mirror&lt;&#x2F;em&gt;, consumes two disks to produce one virtual disk that has the capacity of only one physical disk but can withstand the loss of either one. ZFS can automatically heal missing data in a member of a mirror by copying it over from the healthy member. RAID 0, or &lt;em&gt;stripe&lt;&#x2F;em&gt;, consumes two disks to produce a disk that has the capacity of the sum of its member capacities as well as double I&#x2F;O speed, but fails if either member disk fails. The combination of these is a useful way to get increased speed and increased durability at the price of half your raw capacity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;threat-profile-and-encryption&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#threat-profile-and-encryption&quot; aria-label=&quot;Anchor link for: threat-profile-and-encryption&quot;&gt;Threat profile and encryption&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;To figure out what level and what kind of at-rest encryption I need, I need to stop and think about the threat model.&lt;&#x2F;p&gt;
&lt;p&gt;Scenario A, &lt;em&gt;online intruder&lt;&#x2F;em&gt;. An attacker that gains shell access can exfiltrate any data that the user has access to, since the decryption key has been activated at boot. At-rest encryption won&#x27;t help here.&lt;&#x2F;p&gt;
&lt;p&gt;Scenario B, &lt;em&gt;burglar&lt;&#x2F;em&gt;. An intruder grabs the machine and brings it to a place where they can inspect the disks, possibly with sophisticated recovery tools.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;In a non-encrypted-root install, everything outside the encrypted jails is instantly accessible. Stored API keys and similar secrets leak. The administrator must take great care not to keep &lt;em&gt;anything&lt;&#x2F;em&gt; of value on the unencrypted partition(s), and store everything in encrypted datasets.&lt;&#x2F;li&gt;
&lt;li&gt;An encrypted root would be safe from this attack, assuming the cryptography used in the encryption is bulletproof.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Scenario C, &lt;em&gt;evil maid attack&lt;&#x2F;em&gt;. A friend, spouse or similar tampers with the unattended physical system. Illegitimate access is blocked by requiring login and keeping the credentials in my personal vault. If the whole disk is encrypted, the worst thing the attacker can do is cause temporary harm and possibly data loss by powering off the system. If not, the attacker can do all kinds of nasty things, like booting a live environment and copying the disk contents, replacing the kernel&#x2F;bootloader, installing malware etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;Conclusion&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It makes sense to encrypt the whole thing.&lt;&#x2F;p&gt;
&lt;p&gt;⚠️ But wait a minute! This is against my initial idea:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;unencrypted root, so that I can reboot the machine remotely, physically unattended&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Encrypting the whole disk loses the ability to do unattended reboots. A bit inconvenient, but it is what it is. I&#x27;ll just make sure to only reboot when I&#x27;m physically near the machine, which should be often, considering I work from home and the server is, uh, right there.&lt;&#x2F;p&gt;
&lt;p&gt;Because of this architectural change, I ended up installing FreeBSD for a second time, now with full disk encryption.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;system-disk&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#system-disk&quot; aria-label=&quot;Anchor link for: system-disk&quot;&gt;System disk?&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes a separate small system disk is used in addition to a redundant array of &quot;data disks&quot;. I decided to not pursue this setup, since then the system would not benefit from the RAIDZ redundancy and self-healing capabilities. In fact, I&#x27;d imagine that the ability to repair the OS and packages is even more important to me than repairing bulk data.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to keep the system next to the data on the disks (a &quot;traditional&quot; install).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-be-continued&quot; aria-label=&quot;Anchor link for: to-be-continued&quot;&gt;To be continued&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the next part(s) we will be looking at networking. I had some – let&#x27;s say curious – issues with DHCP leases from the ISP. We&#x27;ll also take a look at my jail setup.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>❄️ Now, winter 2025</title>
        <published>2025-12-16T00:00:00+00:00</published>
        <updated
        >2025-12-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2025-winter/"
        />
        <id>https://jan.systems/posts/now-2025-winter/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2025-winter/"
        >
        &lt;p&gt;Fall has gone by, fast. Mostly rain and darkness.&lt;&#x2F;p&gt;
&lt;p&gt;Managing the estate of my late father has taken up a lot of time I would&#x27;ve otherwise spent on hobbies.&lt;&#x2F;p&gt;
&lt;p&gt;Some decor and reno at home, some game programming, a lot of games. Game design has been on my mind a lot lately.&lt;&#x2F;p&gt;
&lt;p&gt;I also attended my first ever Finnish defence forces reservist refresher training exercise. I had a good time!&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;metroidvania-game-dev&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#metroidvania-game-dev&quot; aria-label=&quot;Anchor link for: metroidvania-game-dev&quot;&gt;Metroidvania game dev&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;🚨 Original game idea alert 🚨&lt;&#x2F;p&gt;
&lt;p&gt;I decided to make a 2D platformer with ability-gated progression. It&#x27;s gonna have pixel art too.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s been a very long time since I made a complete game project. I don&#x27;t care about how many players I&#x27;m gonna get. I just want to create a game, for me.&lt;&#x2F;p&gt;
&lt;p&gt;I figured I should do some proper design upfront instead of doing the easy thing, i.e. jumping straight into programming mechanics. That&#x27;s why I started writing a Game Design Document ASAP. With that, maybe I can come back to this project easier, after the inevitable plateau and loss of interest.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe some screenshots later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;future-of-atk16&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#future-of-atk16&quot; aria-label=&quot;Anchor link for: future-of-atk16&quot;&gt;Future of ATK16&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;An idea came to me in my sleep. I want to change the course of my computing project ATK16 and turn it into a stack-based, Forth-like system.&lt;&#x2F;p&gt;
&lt;p&gt;Clearly I&#x27;ve been subconsciously processing ideas from Forth and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.xxiivv.com&#x2F;site&#x2F;uxntal.html&quot;&gt;Uxntal&lt;&#x2F;a&gt;, since my current vision is a hodgepodge of features from those two:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;an interactive REPL-compiler, able to compile native code on the fly&lt;&#x2F;li&gt;
&lt;li&gt;a system-wide dictionary (map of symbols to code or data), giving structure to the linear memory&lt;&#x2F;li&gt;
&lt;li&gt;reverse Polish notation, which falls naturally out of the stack machine design&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A basic session could look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;ATK16&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; v0.1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Dictionary&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; usage:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; KB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; average3&lt;&#x2F;span&gt;&lt;span&gt; (a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; b c&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; avg&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    +&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span&gt; ;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This would define a new symbol, &lt;code&gt;average3&lt;&#x2F;code&gt;, that computes the average of the top 3 elements on the stack and pushes the result on the stack. The stack could be implemented in hardware with a top-of-stack optimization: the top three slots live in fast registers, while the rest is in dedicated SRAM. This would allow fast access to the most important, i.e. topmost, elements of the stack.&lt;&#x2F;p&gt;
&lt;p&gt;In addition, I&#x27;m thinking of doing some things a bit unorthodox:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The system dictionary could live in FRAM, which is non-volatile. All symbol definitions would then be automatically persistent, like files on a disk. This could be huge.&lt;&#x2F;li&gt;
&lt;li&gt;A programmer board could then allow me to directly update this dictionary from my dev laptop, possibly by emulating the same REPL software that runs on the hardware.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In fact, it might be possible to design this in a way that completely does away with assemblers and cross-compilers. I would just need an emulator and a bit of bootstrap assembly that implements a minimal REPL that can self-compile new symbols into the dictionary. Like this, I wouldn&#x27;t need to worry about ensuring that the assembler and self-compiler work exactly the same: there is only one compiler implementation! I just emulate it on my laptop. Everything is implementation defined!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;home&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home&quot; aria-label=&quot;Anchor link for: home&quot;&gt;Home&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;dopamine-office-decor&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#dopamine-office-decor&quot; aria-label=&quot;Anchor link for: dopamine-office-decor&quot;&gt;”Dopamine office” decor&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Office room number one is now pretty much complete. We tried to combine some of pastel colors and fun patterns, while keeping it smart and tidy.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;dopamine_office.jpeg&quot; alt=&quot;The dopamine office&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;The dopamine office&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;backdoor-clothes-rack-and-wall-feature&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#backdoor-clothes-rack-and-wall-feature&quot; aria-label=&quot;Anchor link for: backdoor-clothes-rack-and-wall-feature&quot;&gt;Backdoor clothes rack and wall feature&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We use our apartment&#x27;s back door a lot, more than the front door really. It&#x27;s easier to take the dog out that way.&lt;&#x2F;p&gt;
&lt;p&gt;Until now, there has been nowhere to really hang our outdoor clothes nicely. The dining table and its chairs have doubled as a clothing rack (not ideal).&lt;&#x2F;p&gt;
&lt;p&gt;So we got the idea to repurpose the empty space in the corner, adding some visual interest with acoustic paneling as well as utility with some colorful &quot;knobs&quot; that you can hang coats and scarves on.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;backdoor_feature_wip.jpeg&quot; alt=&quot;Install in progress&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Install in progress&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;And the final product (in completely different lighting):&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;backdoor_feature_done.jpeg&quot; alt=&quot;An installed wood paneling with some coats hanging off of colored knobs&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;An installed wood paneling with some coats hanging off of colored knobs&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The white box is an IKEA shoebox. We keep some dog gear in there.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;cool-dog-print&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#cool-dog-print&quot; aria-label=&quot;Anchor link for: cool-dog-print&quot;&gt;Cool dog print&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;taulu_koira.jpeg&quot; alt=&quot;Cool dog&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Cool dog&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#games&quot; aria-label=&quot;Anchor link for: games&quot;&gt;Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Played&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;God of War: Ragnarök&lt;&#x2F;li&gt;
&lt;li&gt;Cocoon&lt;&#x2F;li&gt;
&lt;li&gt;Another Crab’s Treasure&lt;&#x2F;li&gt;
&lt;li&gt;Super Metroid&lt;&#x2F;li&gt;
&lt;li&gt;Metroid Dread&lt;&#x2F;li&gt;
&lt;li&gt;Prince of Persia: The Lost Crown&lt;&#x2F;li&gt;
&lt;li&gt;Ori and the Blind Forest&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;anime&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#anime&quot; aria-label=&quot;Anchor link for: anime&quot;&gt;Anime&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Finished&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Bocchi the Rock S1&lt;&#x2F;li&gt;
&lt;li&gt;Spirited Away (movie)&lt;&#x2F;li&gt;
&lt;li&gt;Ghost in the Shell (movie)&lt;&#x2F;li&gt;
&lt;li&gt;Kill la Kill&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Dandadan S2&lt;&#x2F;li&gt;
&lt;li&gt;Spy x Family S1&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;tunes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tunes&quot; aria-label=&quot;Anchor link for: tunes&quot;&gt;Tunes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Mostly &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;Bf1pR4rkPK8&quot;&gt;Moomin music&lt;&#x2F;a&gt;. I went to see the &lt;em&gt;Muumimusiikkia&lt;&#x2F;em&gt; show in Musiikkitalo (Helsinki) in November, and after that I haven&#x27;t been able to get Shiratori&#x27;s melodies out of my head. This is a positive problem.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;muumimusiikkia.jpeg&quot; alt=&quot;A photo from our seats at Musiikkitalo, showing the brochure&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A photo from our seats at Musiikkitalo, showing the brochure&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;spotify-wrapped&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#spotify-wrapped&quot; aria-label=&quot;Anchor link for: spotify-wrapped&quot;&gt;Spotify Wrapped&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I feel like I&#x27;m using Spotify less and less. Might drop the subscription in the near future. I really don&#x27;t like the policies they are driving.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-winter&amp;#x2F;2025_wrapped_genres.jpeg&quot; alt=&quot;My top genres, all over the place&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;My top genres, all over the place&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2025-fall&quot;&gt;🍁 Now, fall 2025&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>EuroBSDCon 2025: conference notes</title>
        <published>2025-10-17T00:00:00+00:00</published>
        <updated
        >2025-10-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/eurobsdcon-2025-conf-notes/"
        />
        <id>https://jan.systems/posts/eurobsdcon-2025-conf-notes/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/eurobsdcon-2025-conf-notes/"
        >
        &lt;p&gt;EuroBSDCon is one of the events organized under the BSD community umbrella, focusing on all things related to and descending from 4.4 Berkeley Software Distribution. This year&#x27;s event was held between September 25th and 28th in Zagreb, Croatia, but the city and country change every year.&lt;&#x2F;p&gt;
&lt;p&gt;My event experience consisted of two days of &quot;trainings&quot;, sometimes also referred to as labs, and two days of conference proper. In addition to these, there were also dev summits and smaller &quot;conference-in-a-conference&quot; kind of deals related to some BSD subsystems like &lt;code&gt;bhyve&lt;&#x2F;code&gt;, but those were not relevant to me so I skipped them outright.&lt;&#x2F;p&gt;
&lt;p&gt;The event was held at the Faculty of Electrical Engineering and Computing (University of Zagreb). The venue was nostalgic in a way since it reminded me a lot of the building where I started my studies in Aalto University. Functional.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;eurobsdcon-2025-conf-notes&amp;#x2F;eurobsdcon_2025_hallway.jpeg&quot; alt=&quot;A photo of a stairway in the conference venue&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A photo of a stairway in the conference venue&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Recordings from the presentation rooms can be found on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLskKNopggjc6bnhXOX5MQbaPKEvohzjYH&quot;&gt;Youtube&lt;&#x2F;a&gt;. You might have to check the talk schedule to find the timestamp of a given talk.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;kernel-deep-dive&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#kernel-deep-dive&quot; aria-label=&quot;Anchor link for: kernel-deep-dive&quot;&gt;Kernel deep-dive&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I attended a two day lab by Prof. Kirk McKusick, who is a well known figure in the BSD world, having worked on core operating system components in the formative years of the BSD lineage. I really enjoyed his relaxed but still intensive teaching style. The lab, or maybe &lt;em&gt;interactive lecture&lt;&#x2F;em&gt; would be more fitting, was an introduction to the FreeBSD kernel subsystems, explaining how things like I&#x2F;O, scheduling, filesystems and networking work in the kernel.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that the FreeBSD kernel is not the same as the Linux kernel, or the other *BSD kernels either for that matter. It is FreeBSD exclusive, but it shares history with other systems that descend from 4.4BSD, which is the common ancestor for many Unix-like OSs.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You see, I&#x27;m not a huge kernel guy (at least not yet) so I was a bit worried that the lab content would go over my head. What happened though surprised me: I understood something like 90% of the material. Apparently I had the necessary prerequisite knowledge after all, or maybe Kirk was just so good at teaching. Maybe both.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;eurobsdcon-2025-conf-notes&amp;#x2F;eurobsdcon_2025_binders.jpeg&quot; alt=&quot;Two spiral notebooks of lecture notes, one for each day&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Two spiral notebooks of lecture notes, one for each day&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;favorite-talks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#favorite-talks&quot; aria-label=&quot;Anchor link for: favorite-talks&quot;&gt;Favorite talks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There was a healthy range of themes in the talk schedule. Some were straightforward demos of recent development efforts, some were about history (mostly 1970–80s, the childhood years of the BSD project). Some war stories as well, explaining how the speaker et al. dealt with some hindrance. There were also some, I would say &lt;em&gt;not so BSD-related&lt;&#x2F;em&gt;, talks about things like data and identity ownership, federated social media, freedom. There is a bunch of overlap between BSD enthusiasts and digital freedom enthusiasts so it was only fitting.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my top 3, based on overall vibes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;lessons-learned-open-sourcing-the-uk-s-covid-tracing-app-by-terence-eden&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lessons-learned-open-sourcing-the-uk-s-covid-tracing-app-by-terence-eden&quot; aria-label=&quot;Anchor link for: lessons-learned-open-sourcing-the-uk-s-covid-tracing-app-by-terence-eden&quot;&gt;&lt;em&gt;Lessons learned Open Sourcing the UK&#x27;s Covid Tracing App&lt;&#x2F;em&gt;, by Terence Eden&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Terence was the third in line in the Saturday&#x27;s opening keynote schedule. Terence was one of the key engineers behind the open source Covid tracing app that the UK developed and used. The keynote handled themes of public relations, talking about software licenses to lawyers, protecting privacy of developers from the wrath of the public, successful platform launches and shutdowns... a lot of stuff delivered with great pacing and enthusiasm.&lt;&#x2F;p&gt;
&lt;p&gt;Timestamped link to this talk: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;live&#x2F;qdEMmM5g27M?t=1221&quot;&gt;Youtube&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;enhancing-unix-education-through-chaos-engineering-and-gamification-using-freebsd-by-benedict-reuschling-andreas-kirschner&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enhancing-unix-education-through-chaos-engineering-and-gamification-using-freebsd-by-benedict-reuschling-andreas-kirschner&quot; aria-label=&quot;Anchor link for: enhancing-unix-education-through-chaos-engineering-and-gamification-using-freebsd-by-benedict-reuschling-andreas-kirschner&quot;&gt;&lt;em&gt;Enhancing Unix Education through Chaos Engineering and Gamification using FreeBSD&lt;&#x2F;em&gt;, by Benedict Reuschling, Andreas Kirschner&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Benedict and Andreas had noticed that their students at Hochschule Darmstadt were not very proficient when it came to administering a UNIX system and debugging issues on it. They devised a platform where their students use exercise environments (with root access) and solve problems like DNS problems against the clock. The fastest student teams get winning points on a leaderboard.&lt;&#x2F;p&gt;
&lt;p&gt;The talk format was a mix of a war story and tech demo: they explained their pedagogical conundrum and walked the viewers through their solution, which was based on FreeBSD and liberal use of jails.&lt;&#x2F;p&gt;
&lt;p&gt;Timestamped link to this talk: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;live&#x2F;FgtTVzYFEF0?t=23860&quot;&gt;Youtube&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ai-slop-attacks-on-the-curl-project-by-daniel-stenberg&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ai-slop-attacks-on-the-curl-project-by-daniel-stenberg&quot; aria-label=&quot;Anchor link for: ai-slop-attacks-on-the-curl-project-by-daniel-stenberg&quot;&gt;&lt;em&gt;AI slop attacks on the curl project&lt;&#x2F;em&gt;, by Daniel Stenberg&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Daniel is the main developer in the &lt;code&gt;curl&lt;&#x2F;code&gt; project. In his Sunday keynote he explained how the project is being pestered by AI generated, low effort security issue reports that take up a lot of contributor time to review. Why not just block those people outright? Daniel argues that they take all reports indicating a critical vulnerability seriously; they can not allow to mistakenly disregard a valid report.&lt;&#x2F;p&gt;
&lt;p&gt;Daniel is an entertaining speaker with big responsibility in the open source world.&lt;&#x2F;p&gt;
&lt;p&gt;Sadly, the talk seems to not be recorded on Youtube.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;community&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#community&quot; aria-label=&quot;Anchor link for: community&quot;&gt;Community&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was curious to see what the people are like. I knew that BSD is a pretty niche thing so I was afraid the community might be hard to get into, but I was happy to notice that everyone that I talked to seemed very welcoming and excited to share.&lt;&#x2F;p&gt;
&lt;p&gt;I noticed that there was very little &quot;hierarchy&quot; at the event. Speakers, sponsors and attendees were all mingling together at lunches and at social events. I didn&#x27;t really see signs of cliques or circles that didn&#x27;t accommodate newcomers.&lt;&#x2F;p&gt;
&lt;p&gt;Since the event, I have familiarised myself with BSD community channels such as the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.freebsd.org&#x2F;IRC&#x2F;Channels&quot;&gt;FreeBSD IRC channels&lt;&#x2F;a&gt; on Libera.Chat and the various forums of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;wiki.bsd.cafe&#x2F;&quot;&gt;BSD Cafe&lt;&#x2F;a&gt;, hosted by Stefano Marinelli, who was also speaking at the conference.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;eurobsdcon-2025-conf-notes&amp;#x2F;eurobsdcon_2025_group.jpeg&quot; alt=&quot;A group photo was taken on the final day&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A group photo was taken on the final day&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;there-and-back-again-and-the-touristy-bits&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#there-and-back-again-and-the-touristy-bits&quot; aria-label=&quot;Anchor link for: there-and-back-again-and-the-touristy-bits&quot;&gt;There and back again, and the touristy bits&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There were no direct flights from Helsinki to Zagreb, so I had to layover in Germany both on the way there and on the way back. Leaving Finland, the departure time was gnarly: around five in the morning. Good thing I can sleep in economy. I had plenty of time to change planes, and got to the destination without a hitch.&lt;&#x2F;p&gt;
&lt;p&gt;I stayed in B&amp;amp;B Boutique Casablanca, which was a nice bed &amp;amp; breakfast place with friendly staff.&lt;&#x2F;p&gt;
&lt;p&gt;Traditional &amp;amp; local foods were my primary target when looking for restaurants. My top picks:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ćevapi&lt;&#x2F;strong&gt; at &lt;em&gt;Plac&lt;&#x2F;em&gt; (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.tripadvisor.com&#x2F;Restaurant_Review-g294454-d7075337-Reviews-Plac-Zagreb_Central_Croatia.html&quot;&gt;Tripadvisor&lt;&#x2F;a&gt;), pictured below&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;štrukli&lt;&#x2F;strong&gt; at &lt;em&gt;La Štruk&lt;&#x2F;em&gt; (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.lastruk.com&#x2F;&quot;&gt;Website&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;eurobsdcon-2025-conf-notes&amp;#x2F;eurobsdcon_2025_cevapi.jpeg&quot; alt=&quot;A serving of ćevapi with cream cheese and paprika spread&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A serving of ćevapi with cream cheese and paprika spread&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The return trip was more eventful. The plane didn&#x27;t start boarding until it was more than an hour late. We were never told the reason. That delay caused me to miss my connecting flight 😑&lt;&#x2F;p&gt;
&lt;p&gt;I was rerouted via Frankfurt which turned my nice &quot;get home for dinner&quot; schedule into a gruesome &quot;you&#x27;re home sometime between midnight and 2 AM&quot; trek. Luckily I got a seat on both of the rerouted flights (that was not a given). I was home after 1 AM.&lt;&#x2F;p&gt;
&lt;p&gt;I have then learned that there would have been a very nice direct route from Finland to Ljubljana, which lies just across the Croatian border, accompanied by a bus route that would have taken me from city to city in a couple of hours. Now I know.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-next-event&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-next-event&quot; aria-label=&quot;Anchor link for: the-next-event&quot;&gt;The next event&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;...will be held in Brussels, Belgium. It&#x27;s closer to Finland (👍) but I&#x27;ve heard that it&#x27;s kind of a dull city to visit. Whether that&#x27;s true or not, I don&#x27;t know really, never been there before. But I&#x27;d prefer going a bit farther away from my home to escape the weather 😅&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;2026.eurobsdcon.org&#x2F;&quot;&gt;EuroBSDCon 2026 website&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>🍁 Now, fall 2025</title>
        <published>2025-10-05T00:00:00+00:00</published>
        <updated
        >2025-10-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2025-fall/"
        />
        <id>https://jan.systems/posts/now-2025-fall/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2025-fall/"
        >
        &lt;p&gt;Late summer and early autumn have been a time of ups and downs.&lt;&#x2F;p&gt;
&lt;p&gt;I lost my father to illness. The passing was expected, in a way, so I have had time to process things in advance, but when it actually happened it turned everything on its head for a while.&lt;&#x2F;p&gt;
&lt;p&gt;Sorting out things after the death has taken a lot of time and effort, but things are now more or less in order. I have got a lot of support and help from my peers, and I feel I have let myself process the grief well. I try to focus on positive things.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve also been planning the wedding that&#x27;s taking place in 2026. In many ways I feel out of my element, but things are moving forward.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;home&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home&quot; aria-label=&quot;Anchor link for: home&quot;&gt;Home&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A lot of renovations this summer. One of the biggest ones was the translucent roofing installation:&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-fall&amp;#x2F;valokate_1.jpg&quot; alt=&quot;Installing the sunroof&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Installing the sunroof&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;After around six hours (including breaks), it&#x27;s all done! Just in time for rain.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-fall&amp;#x2F;valokate_2.jpg&quot; alt=&quot;Complete sunroof&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Complete sunroof&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;You can see the gnarly reddish wall in the picture. That and a bunch of other grime and residue was cleaned off as part of a neighborhood exterior painting project. The place is looking a lot nicer now from the outside (no pictures of this I&#x27;m afraid).&lt;&#x2F;p&gt;
&lt;p&gt;We also got some minor things done, like installing curtains to my fiancé&#x27;s office room. I got some new toolboxes that I use to keep everything nice and organized, and hidden away when not needed.&lt;&#x2F;p&gt;
&lt;p&gt;With all the materials and tools lying around our home has felt like a job site, but now that everything&#x27;s done and cleaned up, I&#x27;m happy that everything got done as well as it did.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;freebsd-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#freebsd-server&quot; aria-label=&quot;Anchor link for: freebsd-server&quot;&gt;FreeBSD server&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I have been setting up a home server and writing a blog series about it. Check out parts &lt;a href=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;now-2025-fall&#x2F;home-server-part-1&quot;&gt;one&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;now-2025-fall&#x2F;home-server-part-2&quot;&gt;two&lt;&#x2F;a&gt;! My plan is to move things back from the cloud &amp;amp; other people&#x27;s computers back to my home and self-host as many things as possible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;books&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#books&quot; aria-label=&quot;Anchor link for: books&quot;&gt;Books&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Finished&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Robert Nystrom, &lt;em&gt;Crafting Interpreters&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Michael W. Lucas, &lt;em&gt;Absolute FreeBSD&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Michael W. Lucas, &lt;em&gt;FreeBSD Mastery: ZFS&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Michael W. Lucas, &lt;em&gt;FreeBSD Mastery: Jails&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Leo Brodie, &lt;em&gt;Thinking Forth&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Benjamin C. Pierce, &lt;em&gt;Types and Programming Languages&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Adam Aleksic, &lt;em&gt;Algospeak&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#games&quot; aria-label=&quot;Anchor link for: games&quot;&gt;Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Played&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Bomb Rush Cyberfunk&lt;&#x2F;li&gt;
&lt;li&gt;Hollow Knight: Silksong&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;God of War: Ragnarök&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Enjoyed as a VOD&#x2F;live stream&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Watched CDawgVA&#x27;s playthroughs of MGS 1–3 and Revengeance&lt;&#x2F;li&gt;
&lt;li&gt;A lot of Silksong VODs and speedruns&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;travel&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#travel&quot; aria-label=&quot;Anchor link for: travel&quot;&gt;Travel&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I late August I went on a company trip to Krakow, Poland for one weekend. A nice city with beautiful old town buildings, but sadly the weather was a bit dull. My favorite food spot was the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;czarnakaczka.pl&#x2F;&quot;&gt;Black Duck&lt;&#x2F;a&gt; 😋.&lt;&#x2F;p&gt;
&lt;p&gt;In September we had booked a trip to Italy with a group of friends. We stayed in Rome for a couple of days, enjoying the 30C heat and sights such as the Villa Borghese. We had already visited the Colosseum and the Vatican on earlier trips so we decided to avoid the queues and visit less popular places. It was the Jubileum year so many sights were jam-packed. After that we got a rental and drove ~500km south to Calabria, where we spent the next week at a really nice villa overlooking the sea.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-fall&amp;#x2F;villa.jpeg&quot; alt=&quot;The view from the villa&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;The view from the villa&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Later in September I attended EuroBSDCon 2025 in Zagreb, Croatia. A lot of great talks and lab sessions! I might write a conference notes post about the trip.&lt;&#x2F;p&gt;
&lt;p&gt;So, a lot of traveling in a short timespan, so I&#x27;ve been feeling quite exhausted. The rest of the autumn is looking less hectic, so I might be able to work on projects more 🤔&lt;&#x2F;p&gt;
&lt;h2 id=&quot;anime&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#anime&quot; aria-label=&quot;Anchor link for: anime&quot;&gt;Anime&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Puella Magi Madoka Magica&lt;&#x2F;li&gt;
&lt;li&gt;Started Kill la Kill&lt;&#x2F;li&gt;
&lt;li&gt;Chainsaw Man (still a couple of eps to go)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2025-summer&quot;&gt;☀️ Now, summer 2025&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>A home server journey, part 2: Hardware</title>
        <published>2025-10-04T00:00:00+00:00</published>
        <updated
        >2025-10-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/home-server-part-2/"
        />
        <id>https://jan.systems/posts/home-server-part-2/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/home-server-part-2/"
        >
        &lt;blockquote&gt;“My first impulse, when presented with any spanking-new piece of computer hardware, is to imagine how it will look in ten years’ time, gathering dust under a card table in a thrift shop.”
&lt;cite style=&quot;float: right&quot;&gt;― William Gibson, Distrust That Particular Flavor&lt;&#x2F;cite&gt;
&lt;&#x2F;blockquote&gt;
&lt;br&gt;
&lt;p&gt;This is the second post in a blog series chronicling my progress with designing and setting up a new, physical FreeBSD home server. Last time we went over my motivations, and this time we&#x27;re looking at the physical hardware.&lt;&#x2F;p&gt;
&lt;p&gt;Check out episode &lt;a href=&quot;&#x2F;posts&#x2F;home-server-part-1&quot;&gt;1&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;budget-specs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#budget-specs&quot; aria-label=&quot;Anchor link for: budget-specs&quot;&gt;Budget specs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;My yearly cost in the public cloud is around 420€. It feels reasonable for a project of this caliber that a year’s expenses could buy a capable minicomputer.&lt;&#x2F;p&gt;
&lt;p&gt;What is a capable minicomputer then, you may ask. Well, my needs are pretty straightforward:&lt;&#x2F;p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;td colspan=2&gt;&lt;strong&gt;Qualitative requirements&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Usefulness&lt;&#x2F;td&gt;&lt;td&gt;Can run a range of small to medium load web services without a hitch&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Trustworthiness&lt;&#x2F;td&gt;&lt;td&gt;Data is durable, disk failures are not catastrophic, I can sleep at night&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Expandability&lt;&#x2F;td&gt;&lt;td&gt;Can add more terabytes in the future&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td colspan=2&gt;&lt;strong&gt;Quantitative requirements&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;CPU&lt;&#x2F;td&gt;&lt;td&gt;Low power, economical, can run FreeBSD.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Memory&lt;&#x2F;td&gt;&lt;td&gt;Won’t OOM swap in day-to-day operation, ZFS is happy&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Disk I&#x2F;O&lt;&#x2F;td&gt;&lt;td&gt;I don’t really care, no high-throughput required. As long as it’s not especially bad (i.e. HDD-level bad).&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Disk space&lt;&#x2F;td&gt;&lt;td&gt;Slots for multiple SSDs (RAID). Enough space to hold important files and some media with redundancy. No need for data hoarding.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Network&lt;&#x2F;td&gt;&lt;td&gt;2 interfaces (private and public networks), Gigabit ethernet. No need for wireless.&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Annoyance&lt;&#x2F;td&gt;&lt;td&gt;Low noise, low temps, sleek form factor&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;table&gt;
&lt;p&gt;With this list of requirements, I handwaved some models and numbers:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CPU: Intel N series ⭐️&lt;&#x2F;li&gt;
&lt;li&gt;Memory: 16GB&lt;&#x2F;li&gt;
&lt;li&gt;Disk space: 2TB (usable)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;⭐️ Intel N series processors, formally known as &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Alder_Lake#Alder_Lake-N&quot;&gt;Alder Lake-N&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Alder_Lake#Twin_lake-N&quot;&gt;Twin Lake-N&lt;&#x2F;a&gt;, are affordable processors designed to be primarily used in mobile devices, lower end laptops (think Chromebooks) and small workstations. Some models, such as the N100, N150, N200 and N250, have thermal power ratings as low as 6W, making them popular in the passively-cooled, quasi-embedded design space: routers, NAS&#x27;s, and the like.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I got the idea for an Intel N series chip from a friend who has succesfully set up computing cluster from a bunch of small machines with those chips. They seemed to be just what I was looking for.&lt;&#x2F;p&gt;
&lt;p&gt;With these, I sail off towards the east (AliExpress).&lt;&#x2F;p&gt;
&lt;p&gt;By the way, I also looked at ARM and RISC-V chips for this. I figured that if Apple Silicon can work as well as it does while being based on ARM, what&#x27;s stopping me from reaping the same benefits, at least in part? ARM has T1 support from FreeBSD.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out that there simply aren&#x27;t any reasonably priced machines based on chips comparable to e.g. the Intel N150. Everything&#x27;s either designed for mobile devices or just prohibitively expensive. Maybe there is no market yet for small non-x64 computers?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sourcing-the-silicon&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#sourcing-the-silicon&quot; aria-label=&quot;Anchor link for: sourcing-the-silicon&quot;&gt;Sourcing the silicon&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;AliExpress, an online retailer based in China, is nice since they have a huge variety of devices at prices that would never be profitable if sold in a western shop. I would prefer to buy European in this current political atmosphere, but I&#x27;m working off a budget, and limiting my options to reputable and ethical vendors would force me to relax my requirements a lot or stop the project in its tracks altogether.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-2&amp;#x2F;aliexpress_minipc_results.png&quot; alt=&quot;Some of the results of a &amp;quot;mini pc&amp;quot; search query&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Some of the results of a &amp;quot;mini pc&amp;quot; search query&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I&#x27;m happy to notice that there is a varied selection of devices at the ~200€ price point. Some are designed to be network devices, with a bunch of physical interfaces and limited storage options, while some are clearly NAS oriented, with SATA 3.5&quot; drive bays capable of housing two or four disks, most commonly.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m looking for something in the middle ground: I need two physical interfaces (a property of router-likes) but I also need storage, preferably of the solid state variety (a property of NAS-likes).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;&#x2F;strong&gt;: I&#x27;m going with this one (possibly stale link warning): &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.aliexpress.com&#x2F;item&#x2F;1005007905020917.html&quot;&gt;Topton Pocket Mini PC&lt;&#x2F;a&gt;. With some mystery promotions applied, I had to only pay 162.27€, with free shipping. I also purchased 16GB of RAM (Crucial DDR5 SODIMM 4800MHz) separately, also from AliExpress.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;AliExpress tends to have multiple layers of ‼️ LIMITED SALE ‼️ and 🤑 TOP DEAL COUPON 🤑 going on at the same time. In the mobile app they also have a bunch of connect-four-type games that can score you big AliExpress Coins©. Please continue consuming.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-2&amp;#x2F;aliexpress_minipc.png&quot; alt=&quot;A promotional image of the mini PC I picked, hosted here in case the product page goes poof. Please enjoy the broken English.&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A promotional image of the mini PC I picked, hosted here in case the product page goes poof. Please enjoy the broken English.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Regarding disks, I think I want to buy them locally, just in case there are issues with them. It&#x27;s way less stressful to handle returns and such with a local vendor than with an overseas megacorp. I feel that stuff like RAM is pretty often OK even if bought from less reputable sources, but with disks I don&#x27;t want to test my luck.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;&#x2F;strong&gt;: I&#x27;m going to get 4 x 1 TB (double the required usable space because of mirroring) of basic NVMe drives from a computer parts shop (Gigantti) nearby.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-hardware&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#setting-up-hardware&quot; aria-label=&quot;Anchor link for: setting-up-hardware&quot;&gt;Setting up hardware&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The box arrives after a bit of a wait (par for the course for the cheapest delivery option, China Airmail). Everything seems to be in order content-wise, but there is no installation manual whatsoever included. I would like one, since I need to figure out how to install the RAM stick. It seems to somehow go below the NVMe sticks, but there is no obvious mechanism for detaching the NVMe bay.&lt;&#x2F;p&gt;
&lt;p&gt;However, with a bit of trial and error I find the correct screw to remove. The top layer comes off, revealing the RAM stick slot underneath. Very carefully I slot in the memory and push down the clamp that fixes it in place.&lt;&#x2F;p&gt;
&lt;p&gt;With similar care I put the drive bay back in, and connect the four NVMe drives and stick on their respective thermal material stickers that conduct heat for the drives to the heatsink on top of the machine. I screw everything in place with the tiny screws that came with the machine. A magnetic screwdriver would be a godsend now!&lt;&#x2F;p&gt;
&lt;p&gt;After a bit, everything&#x27;s put together. After connecting the power cable, the machine powers on without me even touching the power switch, which is a bit surprising, but ok. I hear the POST beep.&lt;&#x2F;p&gt;
&lt;p&gt;Success!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-be-continued&quot; aria-label=&quot;Anchor link for: to-be-continued&quot;&gt;To be continued&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the next part(s) we will be looking at installing FreeBSD for the first (and second) time, and networking. I had some – let&#x27;s say curious – issues with DHCP leases from the ISP. We might also take a look at my jail setup.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>A home server journey, part 1: Motivation</title>
        <published>2025-06-30T00:00:00+00:00</published>
        <updated
        >2025-06-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/home-server-part-1/"
        />
        <id>https://jan.systems/posts/home-server-part-1/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/home-server-part-1/"
        >
        &lt;p&gt;This is the first post in a blog series chronicling my progress with designing and setting up a new, physical FreeBSD home server. I&#x27;m guessing this will become at least a three-parter, but that depends on how things actually go, and how in-depth I happen to write. This first part is about my motivations and requirements, whereas the latter ones will be more technical-deepdive-like.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-1&amp;#x2F;aliexpress_minipc.png&quot; alt=&quot;A promotional image portraying the mini-PC I ended up getting&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A promotional image portraying the mini-PC I ended up getting&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;i-need-a-place-to-run-code-and-store-files&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-need-a-place-to-run-code-and-store-files&quot; aria-label=&quot;Anchor link for: i-need-a-place-to-run-code-and-store-files&quot;&gt;I need a place to run code and store files&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I have pretty simple requirements for a server. I need to run a bunch of services, most of which are Node.js or Python apps, and serve some static websites and files. These are primarily personal applications: tracking finances, or keeping track of house chores, but also hobby projects that I host and expose to the world.&lt;&#x2F;p&gt;
&lt;p&gt;I also need to store files somewhere, like everyone that partakes in the modern digital society. There isn&#x27;t that much data to store, really. I don&#x27;t take that many photos or videos, which tend to be the main driver for purchasing more cloud storage, but I have some Ableton project files (music production) that take up some space. Currently all my project files fit in a couple dozen gigabytes. I store them alongside my documents in Google Drive, using a real-time Drive sync app. This does not include the audio samples and recordings in my projects: I would have to buy a restrictively expensive plan to store those in Drive.&lt;&#x2F;p&gt;
&lt;p&gt;I have a trusty Ubuntu server, chugging away in some rack in a DigitalOcean datacenter in Amsterdam. Even though DigitalOcean pricing is pretty reasonable for individuals in general, my monthly price tag is around ~35€, after taxes. That sum is based on the cost of running a medium sized x86-64 instance and storing some backup snapshots from that machine. Times twelve, that comes to around 420€ per year. To me, that is a sizeable amount of money.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-1&amp;#x2F;do-june-billing.png&quot; alt=&quot;DigitalOcean is charging $36.14 this month&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;DigitalOcean is charging $36.14 this month&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;blockquote&gt;
&lt;p&gt;🤔 I would like to run a smaller instance, but I needed a certain level of RAM to reliably run Obsidian Sync. For whatever reason, less memory would have the app crash intermittently.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Having upgraded to instance to the current size, it is hard to downscale back to a smaller machine. It is possible, of course. But instead of doing that, I started to consider an alternative: maybe I could save some coins and get some hardware of my own, like in the prehistoric times, before cloud services became the de-facto way to run anything. With one year&#x27;s cloud costs, I could surely get a capable small machine.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-1&amp;#x2F;old-man-yells-at-cloud.png&quot; alt=&quot;Old man yells at Cloud (FFVII)&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Old man yells at Cloud (FFVII)&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;ownership-and-control&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ownership-and-control&quot; aria-label=&quot;Anchor link for: ownership-and-control&quot;&gt;Ownership and control&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Money is not the only motivator. I dislike the idea of having the continuity of my digital presence rely on American &#x2F; multinational corporations that have no real responsibility when it comes to ensuring my files stay intact, containers running and website up. Google could ban my account without batting an eyelid, which would directly make me lose access to my collection of personal photos, important documents, and various other files I prefer not lose. DigitalOcean could flag my server for suspicious activity.&lt;&#x2F;p&gt;
&lt;p&gt;All this isn&#x27;t very likely, but it still happens. And even if it didn&#x27;t, I don&#x27;t like having a soulless, corporate entity being the arbitrator on whether I can access my own digital life or not. I would prefer to have the probability of an account-frozen catastrophe be exactly zero instead of 0.1%.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;🤔 In terms of cloud provider trustworthiness, I think DigitalOcean is pretty high up in the rankings. I haven&#x27;t seen much ire against them.🤔&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I could provide these capabilities to myself, by managing my own hardware and networking. I could set up a NAS (network attached storage) box for the files, and run the services locally on either the same machine or some other hardware. It would have to be publicly accessible (Internet), just like my DigitalOcean VPS. It would have to provide &lt;em&gt;at least&lt;&#x2F;em&gt; the same guarantees as the status quo: periodic restorable backups, redundant storage and at-rest encryption for stored files.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;As an alternative, I could move my stuff to a European, maybe even Finnish, company. There are some &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Shared_web_hosting_service&quot;&gt;shared hosting&lt;&#x2F;a&gt; providers that exist. I don&#x27;t really consider this to be that good of an option. I feel that if I&#x27;m moving out of the public cloud, I might as well go all the way.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To replace something as generally trusted and widely used as Google Drive requires some proper thinking. I haven&#x27;t previously set up any redundant disk storage. My systems have never followed the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Backup#3-2-1_Backup_Rule&quot;&gt;3-2-1 backup rule&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;home-server-part-1&amp;#x2F;3-2-1-Backup-Rule.png&quot; alt=&quot;Maintain at least 3 copies of your data, keep 2 copies stored at separate locations, store at least 1 copy at an off-site location. Source: msp360.com&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Maintain at least 3 copies of your data, keep 2 copies stored at separate locations, store at least 1 copy at an off-site location. Source: msp360.com&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Reaching for full ownership and control is not solely an objective aim. It is in part psychological, an attempt to get back control in a world of weakening privacy and walled content gardens. It is driven by emotion. And this is completely fine: humans are allowed to be motivated by emotion, especially when it comes to personal, hobby-adjacent things. To claim otherwise would be dishonest.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;physical-electronics-are-fun-tinkering-is-fun&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#physical-electronics-are-fun-tinkering-is-fun&quot; aria-label=&quot;Anchor link for: physical-electronics-are-fun-tinkering-is-fun&quot;&gt;Physical electronics are fun, tinkering is fun&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There is an inherent value in physical things. People collect music on CDs, cassette tapes, and vinyls. Playing games on real hardware is seen differently from emulation. A computer running in my home is &quot;more real&quot; than a resource-sharing virtual machine in some datacenter.&lt;&#x2F;p&gt;
&lt;p&gt;It is nice to own, instead of rent. It is nice to not be bound by a contract, or a service agreement. Having fewer dependencies, legal or technical, is often favorable.&lt;&#x2F;p&gt;
&lt;p&gt;I can pick out the components and design the infrastructure for my system. I know the moving parts, I know that if something fails, one of the cogs in the machine has stopped turning. I then replace the cog. There is no mystery, at least in an &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;There_are_unknown_unknowns&quot;&gt;unknown-unknowns&lt;&#x2F;a&gt; kind of way.&lt;&#x2F;p&gt;
&lt;p&gt;As long as things are not that serious, maintaining and administrating physical systems can be pretty fun.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simplicity-begets-reliability&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#simplicity-begets-reliability&quot; aria-label=&quot;Anchor link for: simplicity-begets-reliability&quot;&gt;Simplicity begets reliability&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Physical components are one thing, software is another. I have been running Ubuntu on the VPS for a long time, but I can think of multiple reasons to reconsider that choice.&lt;&#x2F;p&gt;
&lt;p&gt;When I picked Ubuntu years ago, I didn&#x27;t know what kind of programs I would be running on the machine. I opted for a Linux distribution that is well supported and offers relatively recent versions of software. I was also uncertain about whether or not I would need to run graphical software. Ubuntu ships with &lt;code&gt;snap&lt;&#x2F;code&gt; and &lt;code&gt;flatpak&lt;&#x2F;code&gt; , both of which are used to run desktop apps. It is now clear to me that I don&#x27;t need any of this; stable server software is all I need.&lt;&#x2F;p&gt;
&lt;p&gt;I am inspired by simplicity. I feel that systems are often improved by removing, not adding.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t understand what is going on in a Ubuntu installation. There are so many components that interact, it&#x27;s hard to make sense of what affects what. There are sediment layers of Linux history in there, with some Canonical-added goodness in between. To be fair, I don&#x27;t think I understand the inner workings of any Linux distribution, for that matter.&lt;&#x2F;p&gt;
&lt;p&gt;A friend had recently set up a bunch of machines using FreeBSD. The elegance of simplicity lures me in. FreeBSD is well respected when it comes to running stable server software. The system boots up with just a couple of processes running in &lt;code&gt;top&lt;&#x2F;code&gt;. The fantastic &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.freebsd.org&#x2F;en&#x2F;books&#x2F;handbook&#x2F;&quot;&gt;FreeBSD handbook&lt;&#x2F;a&gt; and supplementary reading by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mwl.io&#x2F;nonfiction&#x2F;os&quot;&gt;Michael W. Lucas&lt;&#x2F;a&gt; directly teach what each file under &lt;code&gt;&#x2F;etc&#x2F;&lt;&#x2F;code&gt; does.&lt;&#x2F;p&gt;
&lt;p&gt;I did some more reading as well as some experimentation with a FreeBSD virtual machine. It quickly became clear that this should be the way forward. I am excited to try out new, but at the same time respected and old, technology.&lt;&#x2F;p&gt;
&lt;p&gt;FreeBSD is different. A familiar, but still strange system is fascinating. I have only worked with Linux systems before, so many things are as I expect them to be, while similarly many surprise me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;to-be-continued&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#to-be-continued&quot; aria-label=&quot;Anchor link for: to-be-continued&quot;&gt;To be continued&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In the next part we&#x27;ll dive into technical good stuff: setting up the machine and installing FreeBSD.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>☀️ Now, summer 2025</title>
        <published>2025-06-15T00:00:00+00:00</published>
        <updated
        >2025-06-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2025-summer/"
        />
        <id>https://jan.systems/posts/now-2025-summer/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2025-summer/"
        >
        


&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-summer&amp;#x2F;kenrokuen.jpg&quot; alt=&quot;A torii gate in the Kenroku-en garden&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A torii gate in the Kenroku-en garden&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Here&#x27;s three seasons of updates in one package.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;auteredb-project&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#auteredb-project&quot; aria-label=&quot;Anchor link for: auteredb-project&quot;&gt;AutereDB project&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I was reading &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;designing-data-intensive-applications&#x2F;9781491903063&#x2F;&quot;&gt;Designing Data-intensive Applications&lt;&#x2F;a&gt; and I came across a chapter where the author goes over log-structured database architecture. I knew about B-tree based systems, but this seemingly simpler approach was new to me. I was intrigued, but I also felt like I didn&#x27;t fully grok how every cog in the theoretical database machine worked together.&lt;&#x2F;p&gt;
&lt;p&gt;Therefore, I decided to write a quick MVP thing in Rust during a conference flight. The result is &lt;strong&gt;AutereDB&lt;&#x2F;strong&gt; (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantuomi&#x2F;autere_db&quot;&gt;Github&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;AutereDB is more like a database engine. It doesn&#x27;t have a query language, authorization mechanisms, multi-table support or other higher-level features. It simply makes sure that records are written to disk as expected, and queries return the correct data.&lt;&#x2F;p&gt;
&lt;p&gt;You can read more about the project in the &lt;code&gt;README.md&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I have no plans to really further this project. I feel it was a one-off to better grok how these things work. I consider this goal met; I am now more confident about reasoning about data systems. Many things that I learned the hard way during this project have already proved to be beneficial in my day job, so mission success.&lt;&#x2F;p&gt;
&lt;p&gt;Help yourself to the &lt;code&gt;ARCHITECTURE.md&lt;&#x2F;code&gt; file in the repository, and have a laugh at how I made multiple major miscalculations early on in the project, and then scrambled to fix these as I realized my folly. I tried to document most of these in the architecture decision log, for posterity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;femtoqueue-project&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#femtoqueue-project&quot; aria-label=&quot;Anchor link for: femtoqueue-project&quot;&gt;FemtoQueue project&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Another data-centric project I conjured up: a Python task queue library that uses files in the filesystem for task persistence, and &lt;code&gt;rename()&lt;&#x2F;code&gt; to atomically move tasks from one state to another.&lt;&#x2F;p&gt;
&lt;p&gt;FemtoQueue is on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantuomi&#x2F;femtoqueue&quot;&gt;Github&lt;&#x2F;a&gt;, as well as on &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;pypi.org&#x2F;project&#x2F;femtoqueue&#x2F;&quot;&gt;PyPI&lt;&#x2F;a&gt; as a pip-installable package.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m pretty proud of this one. The core idea fits in under 100 lines of no-dependency Python. The current version with QoL stuff and docstrings clocks in at around 350 LoC. I could see it being very useful for small projects where you need some kind of a durable queue for sending emails etc., but don&#x27;t want to add a whole separate networked Redis-like thing, or Celery or a home-cooked SQLite-based solution.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;japan-trip&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#japan-trip&quot; aria-label=&quot;Anchor link for: japan-trip&quot;&gt;Japan trip&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Me and my fiancée made a long-awaited trip to Japan in the spring. We landed in the KIX airport near Ōsaka right in the middle of the cherry blossom season, hanami.&lt;&#x2F;p&gt;
&lt;p&gt;We traveled the central island, Honshū, for three weeks. Mostly by train, but also by bus. We visited Ōsaka, Nara, Kyōto, Gifu-Takayama, Kanazawa and finally Tōkyō. This route skips the popular south-coast bullet train connection, instead going north through the mountains.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-summer&amp;#x2F;samurai_garden.jpeg&quot; alt=&quot;The garden of a preserved samurai family home, with a koi fish pond and stone lantern&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;The garden of a preserved samurai family home, with a koi fish pond and stone lantern&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;I got to finally test my Japanese. Speaking was very laborious at first, but it got way easier the more I got to practise. My vocabulary didn&#x27;t improve that much, but what improved were things like &lt;em&gt;cadence&lt;&#x2F;em&gt;, &lt;em&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Aizuchi&quot;&gt;aizuchi&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;, and ability to respond with more natural word choices. Big up to active listening and copying native speakers.&lt;&#x2F;p&gt;
&lt;p&gt;We ate and drank well. We mostly gravitated towards traditional-style, or &lt;em&gt;washoku&lt;&#x2F;em&gt;, establishments. Our favorite meals were prepared by a sweet grandma in a hot springs guesthouse in the mountains. Simple and clean ingredients.&lt;&#x2F;p&gt;
&lt;p&gt;Something to note: pickled veggies go great with meat-centered, hearty meals. We were eager to implement this in our home cooking as soon as we got home.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2025-summer&amp;#x2F;wagyu_grill.jpeg&quot; alt=&quot;Wagyu beef, mushroom and peppers on a tabletop charcoal grill&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Wagyu beef, mushroom and peppers on a tabletop charcoal grill&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;We visited the obligatory tourist spots, like the &lt;em&gt;Thousand torī gates&lt;&#x2F;em&gt; in Kyōto, and the &lt;em&gt;Dōtonbori shopping street&lt;&#x2F;em&gt; in Ōsaka. We like seeing sights, but we know that our most memorable experiences have always come naturally from exploration and keeping an open mind. That&#x27;s why we spent only a fraction of our time visiting Tripadvisor locations and most of the time wandering around, following local recommendations and keeping our eyes, ears and nostrils peeled.&lt;&#x2F;p&gt;
&lt;p&gt;We are planning to travel there again, maybe on honeymoon.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;freebsd-home-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#freebsd-home-server&quot; aria-label=&quot;Anchor link for: freebsd-home-server&quot;&gt;FreeBSD home server&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I have recently started a project of setting up a small publicly accessible home server that will replace my DigitalOcean VPS in some time. The system will be very FreeBSD jail focused. I have just received the hardware and verified that the everything is up to par. Very hyped about this one.&lt;&#x2F;p&gt;
&lt;p&gt;I am planning to write some posts about the progress.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;home&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home&quot; aria-label=&quot;Anchor link for: home&quot;&gt;Home&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A lot of small things need renovated. I guess this is what owning a house is. We have done a lot, and even more is still on the backlog.&lt;&#x2F;p&gt;
&lt;p&gt;しょうがないね。&lt;&#x2F;p&gt;
&lt;p&gt;Going to install some patio roofing during the next couple of weeks, first time doing that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;books&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#books&quot; aria-label=&quot;Anchor link for: books&quot;&gt;Books&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Mostly FreeBSD stuff by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;mwl.io&#x2F;nonfiction&#x2F;os&quot;&gt;Michael W. Lucas&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Finished&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;designing-data-intensive-applications&#x2F;9781491903063&#x2F;&quot;&gt;Designing Data-intensive Applications&lt;&#x2F;a&gt; (took a long time to work through this one)&lt;&#x2F;li&gt;
&lt;li&gt;FreeBSD Mastery: Storage Essentials&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Started&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Absolute FreeBSD&lt;&#x2F;li&gt;
&lt;li&gt;FreeBSD Mastery: ZFS&lt;&#x2F;li&gt;
&lt;li&gt;FreeBSD Mastery: Jails&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#games&quot; aria-label=&quot;Anchor link for: games&quot;&gt;Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;Played&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Gato Roboto&lt;&#x2F;li&gt;
&lt;li&gt;A Short Hike&lt;&#x2F;li&gt;
&lt;li&gt;Cozy Grove (watched my fiancée play this one)&lt;&#x2F;li&gt;
&lt;li&gt;Warhammer 40k: Boltgun&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Enjoyed as a VOD&#x2F;live stream&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Blue Prince&lt;&#x2F;li&gt;
&lt;li&gt;Rainworld&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;anime&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#anime&quot; aria-label=&quot;Anchor link for: anime&quot;&gt;Anime&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Jujutsu Kaisen 0, S1, S2&lt;&#x2F;li&gt;
&lt;li&gt;FLCL Progressive&lt;&#x2F;li&gt;
&lt;li&gt;Demon Slayer: Mugen Train&lt;&#x2F;li&gt;
&lt;li&gt;Evangelion rebuild movies&lt;&#x2F;li&gt;
&lt;li&gt;Blame! (movie)&lt;&#x2F;li&gt;
&lt;li&gt;Puella Magi Madoka Magica&lt;&#x2F;li&gt;
&lt;li&gt;Dandadan&lt;&#x2F;li&gt;
&lt;li&gt;Chainsaw Man (still a couple of eps to go)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2024-fall&quot;&gt;🍁 Now, fall 2024&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>My pile of incomplete projects</title>
        <published>2025-04-28T00:00:00+00:00</published>
        <updated
        >2025-04-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/pile-of-incomplete-projects/"
        />
        <id>https://jan.systems/posts/pile-of-incomplete-projects/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/pile-of-incomplete-projects/"
        >
        &lt;p&gt;The number of started projects and&#x2F;or empty checkboxes in my to do list is getting out of hand. I have:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;gt; 7 hobby tech projects in a state where I need to put ”just a bit” more effort to them to get them to the finish line,&lt;&#x2F;li&gt;
&lt;li&gt;2 or 3 hobby tech projects that have an incalculable number of man-hours left, and&lt;&#x2F;li&gt;
&lt;li&gt;a nebulous cloud of non-tech stuff that should be done (and preferably prioritized).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Starting is easy (and fun), finishing is hard (and dull). Despite having internalized this, it is very tempting to step into the trap willingly and start another one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;and-now&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#and-now&quot; aria-label=&quot;Anchor link for: and-now&quot;&gt;And now?&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I’ll make the conscious effort to finish incomplete work before starting new projects. I’m going to be a lot stricter in the scoping of my DIY software: things that are supposed to be small should be very small indeed. No extra features.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>jan.systems tech stack retro&#x2F;prospective</title>
        <published>2025-04-28T00:00:00+00:00</published>
        <updated
        >2025-04-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/tech-stack-retro-prospective/"
        />
        <id>https://jan.systems/posts/tech-stack-retro-prospective/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/tech-stack-retro-prospective/"
        >
        &lt;p&gt;I have a website authoring system that involves Obsidian sync and a hand-crafted static site generator (SSG) &lt;a href=&quot;&#x2F;posts&#x2F;deploying-the-garden&quot;&gt;written in Scheme&lt;&#x2F;a&gt; that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;syncs Markdown files to my VPS&lt;&#x2F;li&gt;
&lt;li&gt;compiles Markdown source files into HTML pages using pandoc&lt;&#x2F;li&gt;
&lt;li&gt;parses and processes Markdown frontmatter as metadata&lt;&#x2F;li&gt;
&lt;li&gt;builds an RSS feed from the pages&lt;&#x2F;li&gt;
&lt;li&gt;builds the blog archive page that shows a time-sorted list of blog posts&lt;&#x2F;li&gt;
&lt;li&gt;builds the &lt;code&gt;&#x2F;now&lt;&#x2F;code&gt; page based on the most recent post with frontmatter binding &lt;code&gt;type: now&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;copies the compilation artifacts and static files to the web server directory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;i-have-a-couple-of-qualms-with-this-setup&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-have-a-couple-of-qualms-with-this-setup&quot; aria-label=&quot;Anchor link for: i-have-a-couple-of-qualms-with-this-setup&quot;&gt;I have a couple of qualms with this setup&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Running Obsidian Sync requires a graphical X environment, as Obsidian is a GUI app
&lt;ul&gt;
&lt;li&gt;I have successfully used &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xpra.org&#x2F;index.html&quot;&gt;xpra&lt;&#x2F;a&gt; for this, since my VPS seems to have X installed (why, I&#x27;ll never know). A properly headless server could use &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.x.org&#x2F;releases&#x2F;X11R7.6&#x2F;doc&#x2F;man&#x2F;man1&#x2F;Xvfb.1.xhtml&quot;&gt;xvfb&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Refactoring scheme source code to weave new data through the compilation pipeline is kind of rough, and makes me long for static typing&lt;&#x2F;li&gt;
&lt;li&gt;A full build is slow (several seconds), mainly because of pandoc, but also because of interpreted scheme&lt;&#x2F;li&gt;
&lt;li&gt;A change in one file triggers a full build, since the build system does not understand incremental builds.&lt;&#x2F;li&gt;
&lt;li&gt;Pandoc handles inline HTML in a very weird way when using specific tags. I have not looked into why. This makes writing HTML-infused Markdown very fragile since I have to constantly manually inspect the output HTML for quirks.&lt;&#x2F;li&gt;
&lt;li&gt;Pandoc is very heavy in terms of disk space (I have a small VPS SSD)&lt;&#x2F;li&gt;
&lt;li&gt;When making edits, CPU usage on the VPS easily jumps to over 50%&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-good-parts&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-good-parts&quot; aria-label=&quot;Anchor link for: the-good-parts&quot;&gt;The good parts&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;It works pretty well. There as some small issues here and there, but it works.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;new-feature-linklog&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#new-feature-linklog&quot; aria-label=&quot;Anchor link for: new-feature-linklog&quot;&gt;New feature: linklog&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I would like to add a new feature: rendering recent links from &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;ln.ht&#x2F;~jant&quot;&gt;my linkhut profile&lt;&#x2F;a&gt; on the &lt;code&gt;&#x2F;liked&lt;&#x2F;code&gt; page (possibly to be renamed to &lt;code&gt;&#x2F;linklog&lt;&#x2F;code&gt; or something). Adding this to the current setup is possible. Linkhut has a JSON API for this use case, so I would only have to 1) parse the JSON, and 2) render a HTML fragment.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;approach&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#approach&quot; aria-label=&quot;Anchor link for: approach&quot;&gt;Approach&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;In the light of my &lt;a href=&quot;&#x2F;posts&#x2F;pile-of-incomplete-projects&quot;&gt;most recent post&lt;&#x2F;a&gt; I should fight the urge to rewrite the thing in Rust. However, I really want to get rid of pandoc.&lt;&#x2F;p&gt;
&lt;p&gt;As such, I will probably RiiR at some point, using a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;wooorm&#x2F;markdown-rs&quot;&gt;markdown library&lt;&#x2F;a&gt; instead of pandoc.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>MacOS: Opening Git commit prompt in active GUI editor</title>
        <published>2025-01-29T00:00:00+00:00</published>
        <updated
        >2025-01-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/macos-opening-git-commit-prompt-in-active-gui-editor/"
        />
        <id>https://jan.systems/posts/macos-opening-git-commit-prompt-in-active-gui-editor/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/macos-opening-git-commit-prompt-in-active-gui-editor/"
        >
        &lt;p&gt;This is a quick, neat thing I happened to find on accident. As a result of the application bundle system that MacOS uses ubiquitously, you can figure out the currently open application with the &lt;code&gt;__CFBundleIdentifier&lt;&#x2F;code&gt; environment variable.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $__CFBundleIdentifier&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;dev.zed.Zed&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This bundle identifier uniquely identifies an installed application.&lt;&#x2F;p&gt;
&lt;p&gt;Many editors or IDEs (Zed in my example) allow you to open an integrated terminal. It can be convenient to use that same graphical editor as the &lt;code&gt;$EDITOR&lt;&#x2F;code&gt; for commands like &lt;code&gt;git commit&lt;&#x2F;code&gt; or &lt;code&gt;git rebase -i&lt;&#x2F;code&gt; instead of using &lt;code&gt;vim&lt;&#x2F;code&gt; or &lt;code&gt;nano&lt;&#x2F;code&gt; or some other TUI editor inside the integrated terminal.&lt;&#x2F;p&gt;
&lt;p&gt;Use it like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# $HOME&#x2F;.zshrc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$__CFBundleIdentifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;dev.zed.Zed&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; EDITOR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;zed -w&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;elif&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$__CFBundleIdentifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;com.jetbrains.intellij&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; EDITOR&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;idea --wait&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alternatively, if your editor supports setting custom environment variables for the integrated terminal, you can do that and avoid having the if-else in your shell rc. I prefer to have all my environment shenanigans in one place so I chose not to follow that route.&lt;&#x2F;p&gt;
&lt;p&gt;Note that your editor must support a flag like &lt;code&gt;-w&lt;&#x2F;code&gt; or &lt;code&gt;--wait&lt;&#x2F;code&gt; that blocks the editor command from exiting before you close the file. If you don&#x27;t do that, you will see stuff like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; git commit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;Aborting&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; commit due to empty commit message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Path-conditional config and SSH key signing in `git`</title>
        <published>2024-12-21T00:00:00+00:00</published>
        <updated
        >2024-12-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/git-path-conditional-config-and-ssh-key-signing/"
        />
        <id>https://jan.systems/posts/git-path-conditional-config-and-ssh-key-signing/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/git-path-conditional-config-and-ssh-key-signing/"
        >
        &lt;p&gt;Today I learned about two recent-ish Git features.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conditional-configs&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#conditional-configs&quot; aria-label=&quot;Anchor link for: conditional-configs&quot;&gt;Conditional configs&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Since Git version 2.13, it is possible to use different Git configurations based on values such as the current branch, or what&#x27;s useful in my use case, path prefix:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# File: .gitconfig&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[includeIf &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;gitdir:~&#x2F;Projects&#x2F;Work&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    path&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = .gitconfig-work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;git-config#_includes&quot;&gt;🔗 &lt;code&gt;includeIf&lt;&#x2F;code&gt; in Git documentation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I personally use separate Git identities and SSH keys for personal and work projects. With &lt;code&gt;includeIf&lt;&#x2F;code&gt;, using the right ones is a breeze. Just make sure to &lt;code&gt;includeIf&lt;&#x2F;code&gt; after your default configuration, in order for precedence to function as expected.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# File: .gitconfig-work&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[core]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    sshCommand&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = ssh&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;.ssh&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;work key nam&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; IdentitiesOnly=yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[user]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    email&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;work emai&lt;&#x2F;span&gt;&lt;span&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    signingkey&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;.ssh&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;work key nam&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[gpg]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    format&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = ssh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[commit]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    gpgsign&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m happy that this is supported natively, so I don&#x27;t have to use any wrapper scripts, such as those described in &lt;a href=&quot;&#x2F;posts&#x2F;directory-aware-git&quot;&gt;a previous note&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ssh-key-signing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#ssh-key-signing&quot; aria-label=&quot;Anchor link for: ssh-key-signing&quot;&gt;SSH key signing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The above config snippet also includes another recent addition:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[user]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    signingkey&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;.ssh&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;key nam&lt;&#x2F;span&gt;&lt;span&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[gpg]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    format&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; = ssh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;docs&#x2F;gitformat-signature#Documentation&#x2F;gitformat-signature.txt-codesshcodeSSH&quot;&gt;🔗 &lt;code&gt;gpg.format = ssh&lt;&#x2F;code&gt; in Git documentation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can use the same SSH keys that you use to authenticate your pushes to sign your commits. This is a nice for multiple reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Most developers already have an SSH key&lt;&#x2F;li&gt;
&lt;li&gt;PGP keys are not as popular as SSH keys, and suffer from usability issues&lt;&#x2F;li&gt;
&lt;li&gt;Managing one key is less work than managing multiple keys&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After configuring your key as a signing key, you need to enable commit signing either with &lt;code&gt;git commit -S&lt;&#x2F;code&gt; or the inaccurately named configuration value:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[commit]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    gpgsign&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Your Git server must support SSH key signing for your commits to show up as &lt;em&gt;Verified&lt;&#x2F;em&gt;. At least GitHub and modern versions of GitLab support this. Just make sure your corresponding public key is listed as a &lt;em&gt;Signing key&lt;&#x2F;em&gt; (GitHub) or an &lt;em&gt;Authentication &amp;amp; signing key&lt;&#x2F;em&gt; (GitLab).&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>🍁 Now, fall 2024</title>
        <published>2024-12-10T00:00:00+00:00</published>
        <updated
        >2024-12-10T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2024-fall/"
        />
        <id>https://jan.systems/posts/now-2024-fall/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2024-fall/"
        >
        &lt;!-- toc --&gt;
&lt;h2 id=&quot;home&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home&quot; aria-label=&quot;Anchor link for: home&quot;&gt;Home&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Dog dug up the yard and the new grass. Multiple times even. Planted some clovers to hide the bald spot. Fixed up a vine that fell down from the wall.&lt;&#x2F;li&gt;
&lt;li&gt;Painted some rooms. The previous owner left them in a sorry state.&lt;&#x2F;li&gt;
&lt;li&gt;Built a little bathroom shelf, a little nightstand and a sound diffuser. Getting the hang of woodworking.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;style&gt;
.gallery {
  display: flex;
  flex-flow: row wrap;
  margin: -10px;
}
.gallery a {
  flex: 1;
  min-width: 250px;
  margin: 10px;
}
.gallery img {
  margin-bottom: 0;
}
.gallery label {
  text-align: center;
  margin: auto;
  font-style: italic;
}
&lt;&#x2F;style&gt;
&lt;p class=&quot;gallery&quot;&gt;
&lt;a href=&quot;diffuser_1.jpg&quot;&gt;&lt;img src=&quot;diffuser_1.jpg&quot; alt=&quot;Sawed and varnished wooden pegs waiting for assembly.&quot;&gt;&lt;&#x2F;a&gt;
&lt;a href=&quot;diffuser_2.jpg&quot;&gt;&lt;img src=&quot;diffuser_2.jpg&quot; alt=&quot;A plywood board with a single row and column of pegs assembled on top.&quot;&gt;&lt;&#x2F;a&gt;
&lt;a href=&quot;diffuser_3.jpg&quot;&gt;&lt;img src=&quot;diffuser_3.jpg&quot; alt=&quot;A complete sound diffuser, comprising a sizeable grid of differently angled pegs.&quot;&gt;&lt;&#x2F;a&gt;
&lt;label&gt;The sound diffuser being built.&lt;&#x2F;label&gt;
&lt;&#x2F;p&gt;
&lt;h2 id=&quot;relationship&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#relationship&quot; aria-label=&quot;Anchor link for: relationship&quot;&gt;Relationship&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Got engaged!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;quests&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#quests&quot; aria-label=&quot;Anchor link for: quests&quot;&gt;Quests&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I remember seeing some blog post somewhere that advocated, possibly a bit ironically, the concept of using &lt;strong&gt;main quests&lt;&#x2F;strong&gt;, &lt;strong&gt;side quests&lt;&#x2F;strong&gt; and &lt;strong&gt;daily quests&lt;&#x2F;strong&gt; in tracking your to-dos. I decided to give them a try. I now use main and side quests to keep track of my higher level objectives. These objectives function at the week or month level. I do not use daily quests.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve always found it hard to stick to to-do tracking schemes. They are often too complex and unable to track priorities or time spans in a way that would feel natural. Maybe this approach is simple and video-gamey enough to stick.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;live-music-metal&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#live-music-metal&quot; aria-label=&quot;Anchor link for: live-music-metal&quot;&gt;Live music 🤘🏻&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Valkeat, Ethereal Sin @ Lepakkomies&lt;&#x2F;li&gt;
&lt;li&gt;Queendyster, Liturgy @ Kuudes Linja&lt;&#x2F;li&gt;
&lt;li&gt;Havukruunu, Satanic North @ Tavastia&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;video-games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#video-games&quot; aria-label=&quot;Anchor link for: video-games&quot;&gt;Video games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Played:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Outer Wilds (this was a good game) and the DLC (not entirely my vibe)&lt;&#x2F;li&gt;
&lt;li&gt;Plucky Squire&lt;&#x2F;li&gt;
&lt;li&gt;Neon White&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Enjoyed watching:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Metal Gear Solids 1–4 in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLXlhzeWIuTHLfGvMnnWLJK3DRLQwcTpwd&quot;&gt;Giant Bomb longplay format&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Ultrakill&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;career&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#career&quot; aria-label=&quot;Anchor link for: career&quot;&gt;Career&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Finished some certifications: &lt;em&gt;GCP-ACE&lt;&#x2F;em&gt;, &lt;em&gt;AZ-900&lt;&#x2F;em&gt;, &lt;em&gt;AI-900&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reading&quot; aria-label=&quot;Anchor link for: reading&quot;&gt;Reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Still reading &lt;em&gt;Designing Data-intensive Applications&lt;&#x2F;em&gt;. Got sidetracked by a database project that was inspired by the book.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;top-tunes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#top-tunes&quot; aria-label=&quot;Anchor link for: top-tunes&quot;&gt;Top tunes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Djennaration – Liturgy&lt;&#x2F;li&gt;
&lt;li&gt;The Ultrakill OST&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2024-summer&quot;&gt;🌞 Now, summer 2024&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>🌞 Now, summer 2024</title>
        <published>2024-07-18T00:00:00+00:00</published>
        <updated
        >2024-07-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2024-summer/"
        />
        <id>https://jan.systems/posts/now-2024-summer/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2024-summer/"
        >
        &lt;!-- toc --&gt;
&lt;h2 id=&quot;home&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#home&quot; aria-label=&quot;Anchor link for: home&quot;&gt;Home&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Yard work: some small landscaping, changed the topsoil to something less dry, and planted grass and flowers.&lt;&#x2F;li&gt;
&lt;li&gt;Purchased a &lt;strong&gt;wine fridge&lt;&#x2F;strong&gt; for some reason. This led directly to...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;a-new-hobby&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-new-hobby&quot; aria-label=&quot;Anchor link for: a-new-hobby&quot;&gt;A new hobby&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Started learning about wines. Organized a wine tasting. My top pick: Domaine Laroche Chablis 1er Cru Les Vaudevey 2022.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2024-summer&amp;#x2F;2024-wines.jpg&quot; alt=&quot;Wines from the tasting&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Wines from the tasting&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h3 id=&quot;career&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#career&quot; aria-label=&quot;Anchor link for: career&quot;&gt;Career&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;I passed the &lt;strong&gt;AWS Certified Developer Associate&lt;&#x2F;strong&gt; certificate exam! A celebratory espresso martini was in order. Planning to do &lt;strong&gt;Solutions Architect Associate&lt;&#x2F;strong&gt; next.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;now-2024-summer&amp;#x2F;aws-exam-drink.jpeg&quot; alt=&quot;My hand holding an espresso martini outside in sunny weather&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;My hand holding an espresso martini outside in sunny weather&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;played-games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#played-games&quot; aria-label=&quot;Anchor link for: played-games&quot;&gt;Played games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Animal well&lt;&#x2F;li&gt;
&lt;li&gt;Ratchet &amp;amp; Clank: A Crack in Time&lt;&#x2F;li&gt;
&lt;li&gt;Snufkin: Melody of Moominvalley&lt;&#x2F;li&gt;
&lt;li&gt;Cult of the Lamb&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;miru-an-analog-adventure-game&quot;&gt;Miru&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Celeste&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;finished-reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#finished-reading&quot; aria-label=&quot;Anchor link for: finished-reading&quot;&gt;Finished reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Finished &lt;em&gt;Category Theory for Programmers&lt;&#x2F;em&gt; by Bartosz Milewski. Had to work through it twice, taking notes on the second pass. I wish I started taking notes from the get go.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Wine Simple&lt;&#x2F;em&gt; by Aldo Sohm, wine fundamentals for beginners.&lt;&#x2F;li&gt;
&lt;li&gt;Some CS papers:
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Do Be Do Be Do&lt;&#x2F;em&gt; by Sam Lindley et al. The paper defines a language called Frank that uses a novel algebraic effects based typed effect system instead of monads. Also has types for suspended calculations (think Haskell thunks). Similar to Koka I believe.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Cons should not eval its arguments&lt;&#x2F;em&gt;, a classic that paves the way for lazily evaluated languages.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Japanese short stories (Yomu Yomu app) such as &lt;em&gt;人間椅子 (Human chair)&lt;&#x2F;em&gt;, a story about a carpenter that decides to live inside a chair that he’s made. Adapted to a vocabulary of 1500 words.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;started-reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#started-reading&quot; aria-label=&quot;Anchor link for: started-reading&quot;&gt;Started reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Designing Data-intensive Applications&lt;&#x2F;em&gt; by Martin Kleppmann.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;top-tunes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#top-tunes&quot; aria-label=&quot;Anchor link for: top-tunes&quot;&gt;Top tunes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;open.spotify.com&#x2F;track&#x2F;5cyKheZd5E3jBtjcflE7Gz?si=y-mOWdUVT36XOo0zl1kK_w&amp;amp;context=spotify%3Aalbum%3A2BwbUYJeuUsv6LUA6GZHB4&quot;&gt;maudlin of the Well - Gleam in Ranks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;wyOG7ZiVLLo&quot;&gt;predawka - erynias&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2024-spring&quot;&gt;🌱 Now, spring 2024&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Subjective review: Miru</title>
        <published>2024-07-14T00:00:00+00:00</published>
        <updated
        >2024-07-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/subjective-review-miru/"
        />
        <id>https://jan.systems/posts/subjective-review-miru/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/subjective-review-miru/"
        >
        


&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;subjective-review-miru&amp;#x2F;miru_time_to_kill_triple.jpg&quot; alt=&quot;A graphic from the first pages of the Miru zine with the text ”Time to kill a god” overlaid on an imposing four-eyed figure.&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A graphic from the first pages of the Miru zine with the text ”Time to kill a god” overlaid on an imposing four-eyed figure.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;tl-dr&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tl-dr&quot; aria-label=&quot;Anchor link for: tl-dr&quot;&gt;TL;DR&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;It’s my favorite solo game, but it has some pacing problems. 8&#x2F;10.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;miru-the-solo-analog-adventure&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#miru-the-solo-analog-adventure&quot; aria-label=&quot;Anchor link for: miru-the-solo-analog-adventure&quot;&gt;Miru, the solo analog adventure&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;blockquote&gt;
&lt;p&gt;🎲 &lt;strong&gt;Intended audience&lt;&#x2F;strong&gt;: people who are familiar with basic tabletop role-playing game concepts.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;miru-an-analog-adventure-game&quot;&gt;&lt;strong&gt;Miru&lt;&#x2F;strong&gt;&lt;&#x2F;a&gt; is a journaling solo RPG adventure by &lt;em&gt;Hinokodo&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Solo RPGs are pen-and-paper games that require only one player to play. Whereas most tabletop games require one player to take up the role of the game master or referee, solo RPGs replace this role with various mechanisms, such as an &lt;em&gt;oracle&lt;&#x2F;em&gt; or a system of &lt;em&gt;random event tables&lt;&#x2F;em&gt;, paired with an increased player responsibility over the progression of the game. What would be ruled by the referee in a non-solo TTRPG, is resolved instead with a roll of the dice, or some other, potentially more mechanically curious procedure.&lt;&#x2F;p&gt;
&lt;p&gt;Many solo games, Miru included, tell a single curated story. The focus can be very narrow: the game might take place in a specific location at a specific time, for instance. Miru does not allow you to create or customize your character either, which is par for the course. Solo systems are usually not generic enough to support arbitrary settings or adventure prompts. That is one of their strengths; the system can be tightly intertwined with the story in a way that would be inconvenient or even impossible in non-solo systems.&lt;&#x2F;p&gt;
&lt;p&gt;Solo games are mechanically diverse and often involve innovative approaches to both visuals and game design. They also tend to be rather lightweight, including only the necessary rules or content in the often somewhat slim zine. Players often have to massage their brains a bit and deduce how some properties of the game interact. This is part of the appeal, for me at least.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I enjoy &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;soloist.substack.com&#x2F;&quot;&gt;The Soloist&lt;&#x2F;a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;danews.substack.com&#x2F;&quot;&gt;Dan&#x27;s News&lt;&#x2F;a&gt; for my solo RPG news.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;Journaling&lt;&#x2F;em&gt; solo games are solo games that involve writing a journal of some kind as you progress. Miru asks you to write down a short rundown of what happened on each day of your adventure. You also draw the adventure map, hex by hex, on a blank map template.&lt;&#x2F;p&gt;
&lt;p&gt;With definitions out of the way, let me start by saying that &lt;em&gt;Miru is the best solo adventure I have played&lt;&#x2F;em&gt;. The creative setting, the mechanics and distinctive visual identity come together to create a cohesive, well designed experience that I can wholeheartedly recommend. However, it is not perfect. There is a bit of a pacing issue during mid-game as well as some confusing or contradictory rules that are either explained by looking up the author&#x27;s comments on the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;miru-an-analog-adventure-game&quot;&gt;itch.io&lt;&#x2F;a&gt; page or not explained at all. These are minor issues, though, and can be circumvented with some &lt;em&gt;GM&#x27;s fiat&lt;&#x2F;em&gt; (which is of course also &lt;em&gt;player&#x27;s fiat&lt;&#x2F;em&gt; when you&#x27;re flying solo).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;first-look&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#first-look&quot; aria-label=&quot;Anchor link for: first-look&quot;&gt;First look&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;subjective-review-miru&amp;#x2F;miru_gameplay.jpeg&quot; alt=&quot;The Miru zine, polyhedral dice, various stationery and other items strewn across a dining table.&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;The Miru zine, polyhedral dice, various stationery and other items strewn across a dining table.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;Miru paints a picture of a dystopian, post-metropolitan society future where humans begrudgingly coexist with humanoid and animal-like robots. The story is kicked off in a scene where a farm worker robot kills a relative of the main character in an act of self defense when it evaluates their drunken hooliganism to be a threat. Stricken, the main character vows revenge and sets off to find out the truth about the machines’ origins.&lt;&#x2F;p&gt;
&lt;p&gt;The game is a procedurally generated hex crawl with apocalyptic gear-scavenging mechanisms and straightforward combat with a dash of god-slaying thrown in for good measure.&lt;&#x2F;p&gt;
&lt;p&gt;Hexes are generated by combining 3 oracle tables:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Hex type&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; (mountain, grassland, swamp...) → &lt;strong&gt;&lt;em&gt;Event type&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt; → &lt;strong&gt;&lt;em&gt;Particular event&lt;&#x2F;em&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Visiting a single hex takes one in-game day. The main gameplay loop is about generating a new hex, surviving the generated event, and planning your next move such that you don&#x27;t lose too many resources, such as food or sleep. Near the endgame, this survival aspect turns into loadout optimization when you prepare for the final boss.&lt;&#x2F;p&gt;
&lt;p&gt;There is also currency management. Money, or &lt;strong&gt;bitliths&lt;&#x2F;strong&gt; (a snarky reference to cryptocurrency), are used to purchase survival resources as well as equipment from villages. The available stock improves as you discover more villages: the first village has level 1 stock, the second one level 2, and all the following ones level 3.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-experience&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-experience&quot; aria-label=&quot;Anchor link for: my-experience&quot;&gt;My experience&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The beginning was excellent. I quickly &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.merriam-webster.com&#x2F;dictionary&#x2F;grok&quot;&gt;grokked&lt;&#x2F;a&gt; the rules and the objective of the game. The first hexes were significant; you could never know if you&#x27;re going to get important gear, or nearly die to a robot-wolf. The plot was nicely propelled forwards by special events that happened on predetermined dates, and character progression felt good. These events, as well as random events dropped little bits of lore here and there. As a big fan of From Software games and figuring out the world by reading item descriptions, this kind of soft worldbuilding hit the nail on the head.&lt;&#x2F;p&gt;
&lt;p&gt;This well thought-out and balanced part of the game lasted maybe 10 to 20 in-game days.&lt;&#x2F;p&gt;
&lt;p&gt;My entire playthrough lasted 41 days. In wall clock time, it lasted me over three of months. There wasn’t actually enough much gameplay to warrant multiple months of play; I just had an extended break near the near-endgame part. I felt that the pacing there was a bit off, every hex felt very same-y. I had obtained enough resources and gear for exploring to be very safe and so the random events began to mean less and less. Little by little, the grinding-adjacent preparation for the final boss fight started to feel repetitive, and so I inadvertently took a break from the game.&lt;&#x2F;p&gt;
&lt;p&gt;I returned to the game just recently and decided that I&#x27;m just going to have a try at the boss. Screw getting the best gear, I will finish the game! This was a good call, since the battle was intense. I threw everything at my disposal at my opponent and was &lt;em&gt;just&lt;&#x2F;em&gt; strong enough to defeat it with one (1) HP remaining! Talk about a close call.&lt;&#x2F;p&gt;
&lt;p&gt;I will not spoil the final boss or the ending here, but I want to say a bit about it nevertheless. Depending on whether or not you picked up on the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Solarpunk&quot;&gt;solarpunky&lt;&#x2F;a&gt; undertones of the game during your playthrough, the ending may come either as a bit of a surprise. It surprised me at least.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s also something that seems to be a hook for the next game in the series, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;miru-ii-an-analog-horror-game&quot;&gt;Miru 2&lt;&#x2F;a&gt;, subtitled &quot;Analog Horror Game&quot;. The third installation, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.kickstarter.com&#x2F;projects&#x2F;mimicpublishing&#x2F;miru-3-an-analog-defense-game&quot;&gt;Miru 3&lt;&#x2F;a&gt;, is an &quot;Analog Defense Game&quot;. I am left wondering what happens in the plot between parts two and three.&lt;&#x2F;p&gt;
&lt;p&gt;I give Miru a solid 8 out of 10. The visuals, the theme, the mechanics, and the overall vibe are all excellent. The grindy bit in the middle was really the only thing that reduced my enjoyment somewhat. Note however that your mileage may vary. Everything is determined by a roll of the dice.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mixed-media-addendum&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#mixed-media-addendum&quot; aria-label=&quot;Anchor link for: mixed-media-addendum&quot;&gt;Mixed media addendum&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I love mixed media art. Combining disciplines and breaking traditional boundaries makes great art.&lt;&#x2F;p&gt;
&lt;p&gt;With that in mind, the designer has released an official soundtrack called &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;static-abyss&quot;&gt;Static Abyss&lt;&#x2F;a&gt; for the Miru game series. The physical minidisk release of the soundtrack comes with some kind of game rules too, based on photos that I&#x27;ve seen. Sadly, it has sold out, possibly soon after its release in April 2023. The digital version is available on the itch.io page.&lt;&#x2F;p&gt;
&lt;p&gt;The concept of a soundtrack for a physical game is badass. I own one physical release of such a thing, the Mörk Borg adventure &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;jnohr.itch.io&#x2F;putrescence-regnant&quot;&gt;Putrescence Regnant&lt;&#x2F;a&gt;, which takes the form of a highly stylized LP disc and sleeve. Very cool.&lt;&#x2F;p&gt;
&lt;p&gt;


&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;subjective-review-miru&amp;#x2F;hinokodo-static-abyss.png&quot; alt=&quot;A photo of the Static Abyss minidisk in its enclosure&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A photo of the Static Abyss minidisk in its enclosure&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;

Photo attribution: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;static-abyss&quot;&gt;Hinokodo&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Managing dotfiles in `git`</title>
        <published>2024-07-08T00:00:00+00:00</published>
        <updated
        >2024-07-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/managing-dotfiles-in-git/"
        />
        <id>https://jan.systems/posts/managing-dotfiles-in-git/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/managing-dotfiles-in-git/"
        >
        &lt;p&gt;Dealing with dotfiles can be a nuisance. I use a method that I read about in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;git&#x2F;tutorials&#x2F;dotfiles&quot;&gt;this Git tutorial&lt;&#x2F;a&gt; (which is in turn based on a Hacker News post).&lt;&#x2F;p&gt;
&lt;p&gt;It is the most elegant approach I&#x27;ve come across. It does not need to clutter your home directory, it does not cause your shell prompt to show Git status info whenever you&#x27;re in a subdirectory of &lt;code&gt;$HOME&lt;&#x2F;code&gt;. It stays out of the way, but is still instantly accessible whenever you need it and allows you to use the full power of Git.&lt;&#x2F;p&gt;
&lt;p&gt;Check out the linked tutorial for the full rundown. Here&#x27;s the core idea, with my personal improvements added on top.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;git init --bare&lt;&#x2F;code&gt; to initialize a bare &lt;code&gt;dotfiles&lt;&#x2F;code&gt; Git repository somewhere on your file system. I used &lt;code&gt;$HOME&#x2F;.dotfiles&#x2F;&lt;&#x2F;code&gt;. This repository will track all of your dotfiles while hiding away in a convenient location, which is not your home directory root.&lt;&#x2F;li&gt;
&lt;li&gt;Add a shell alias that uses this newly created bare dotfiles repository:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; dot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&#x2F;usr&#x2F;bin&#x2F;git --git-dir=$HOME&#x2F;.dotfiles&#x2F; --work-tree=$HOME&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Configure the &lt;code&gt;dotfiles&lt;&#x2F;code&gt; repo to not show untracked files in status output:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;dot&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; config --local status.showUntrackedFiles no&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Add some prints to your shell &lt;code&gt;rc&lt;&#x2F;code&gt; file in order to be reminded when you have forgotten to sync your stuff:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;dotfiles_status&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;dot&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt; status)&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if !&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$dotfiles_status&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -q&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;nothing to commit&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;#39;There are dotfiles changes to commit. Run &amp;quot;dotfiles status&amp;quot; to see them.&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;$dotfiles_status&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; -q&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Your branch is ahead&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;#39;There are dotfiles commits to push. Run &amp;quot;dotfiles log&amp;quot; to see recent commits.&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Opening multiple DB tunnels with AWS SSM</title>
        <published>2024-06-26T00:00:00+00:00</published>
        <updated
        >2024-06-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/opening-multiple-db-tunnels-with-aws-ssm/"
        />
        <id>https://jan.systems/posts/opening-multiple-db-tunnels-with-aws-ssm/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/opening-multiple-db-tunnels-with-aws-ssm/"
        >
        &lt;p&gt;To open a tunneled connection to an AWS managed database, such as RDS or DocumentDB, the commonly recommended way is to use a bastion host and &lt;code&gt;aws ssm start-session&lt;&#x2F;code&gt;. The bastion host is an EC2 instance that is in the same VPC as your database. The AWS CLI command allows you to connect to SSM-enabled EC2 instances from your development machine using IAM authorization, without having to manage SSH keys.&lt;&#x2F;p&gt;
&lt;p&gt;The script below connects to the specified PostgreSQL RDS and DocumentDB instances using a bastion host. Both databases will be accessible on a &lt;code&gt;localhost&lt;&#x2F;code&gt; port. Modify as necessary.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Configure to match your environment&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;&amp;lt;YOUR_BASTION_INSTANCE_NAME&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_CLUSTER_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;&amp;lt;YOUR_DOCDB_CLUSTER_NAME&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_CLUSTER_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;&amp;lt;YOUR_RDS_CLUSTER_NAME&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;27017&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;quot;5432&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Fail on errors, do not allow use of unset variables&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;set -eu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Colors for nicer output&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BB&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;\\&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;033[34m&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RST&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;\\&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;033[0m&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Fetch the endpoints URIs and bastion instance ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;aws&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; docdb describe-db-clusters&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --db-cluster-identifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_CLUSTER_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;DBClusters[0].Endpoint&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --output&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;aws&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; rds describe-db-clusters&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --db-cluster-identifier&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_CLUSTER_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;DBClusters[0].Endpoint&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --output&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_INSTANCE&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;aws&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ec2 describe-instances&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --filter&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Name=tag:Name,Values=${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Reservations[].Instances[?State.Name == &amp;#39;running&amp;#39;].InstanceId[]&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; --output&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; text&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;DocumentDB endpoint: ${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;PostgreSQL endpoint: ${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Bastion host instance ID: ${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_INSTANCE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support z-constant&quot;&gt;echo -e&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BB&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;}Starting SSM sessions with DocumentDB and PostgreSQL port forwarding in localhost in this terminal...${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;RST&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# DocumentDB port forwarding&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;aws&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ssm start-session&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_INSTANCE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --document-name&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; AWS-StartPortForwardingSessionToRemoteHost&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --parameters&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;#39;{&amp;quot;portNumber&amp;quot;:[&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;${DOCDB_PORT}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;quot;],&amp;quot;localPortNumber&amp;quot;:[&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;${DOCDB_PORT}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;quot;],&amp;quot;host&amp;quot;:[&amp;quot;&amp;#39;&amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;DOCDB_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&amp;#39;&amp;quot;]}&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PID1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;$!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# PostgreSQL port forwarding&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;aws&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; ssm start-session&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --target&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;BASTION_INSTANCE&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --document-name&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; AWS-StartPortForwardingSessionToRemoteHost&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;  --parameters&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;#39;{&amp;quot;portNumber&amp;quot;:[&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;${PG_PORT}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;quot;],&amp;quot;localPortNumber&amp;quot;:[&amp;quot;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;${PG_PORT}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;quot;],&amp;quot;host&amp;quot;:[&amp;quot;&amp;#39;&amp;quot;${&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PG_ENDPOINT&lt;&#x2F;span&gt;&lt;span class=&quot;z-string z-punctuation z-definition z-string&quot;&gt;}&amp;quot;&amp;#39;&amp;quot;]}&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;PID2&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;$!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cleanup&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;Caught CTRL-C, stopping both tunnels...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  kill&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $PID1 $PID2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Catch the interrupt signal (CTRL-C)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;trap&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; cleanup INT&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# Wait for both processes to exit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;wait&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $PID1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;wait&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $PID2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>It doesn&#x27;t mean that</title>
        <published>2024-05-16T00:00:00+00:00</published>
        <updated
        >2024-05-16T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/it-doesnt-mean-that/"
        />
        <id>https://jan.systems/posts/it-doesnt-mean-that/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/it-doesnt-mean-that/"
        >
        &lt;img src=&quot;wikimedia_monad.svg&quot; alt=&quot;A plain circle with a small dot in the center&quot; style=&quot;max-height: 200px; margin-bottom: 0px&quot; &#x2F;&gt;
&lt;h2 id=&quot;when-i-learned-my-first-programming-language&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#when-i-learned-my-first-programming-language&quot; aria-label=&quot;Anchor link for: when-i-learned-my-first-programming-language&quot;&gt;When I learned my first programming language&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I learned about functions.&lt;&#x2F;p&gt;
&lt;p&gt;A &lt;em&gt;function&lt;&#x2F;em&gt; is a thing you can call. The function might then &lt;em&gt;return&lt;&#x2F;em&gt; a value.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;then-i-learned-about-functional-programming&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#then-i-learned-about-functional-programming&quot; aria-label=&quot;Anchor link for: then-i-learned-about-functional-programming&quot;&gt;Then I learned about functional programming&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;And learned that those words did not mean that at all. Or at least that was not their full, universal meaning.&lt;&#x2F;p&gt;
&lt;p&gt;Those &lt;em&gt;functions&lt;&#x2F;em&gt; were actually &lt;em&gt;procedures&lt;&#x2F;em&gt;. Or &lt;em&gt;subroutines&lt;&#x2F;em&gt;. A function should be mathematical and pure. A mapping from a type to another.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Returning&lt;&#x2F;em&gt; was not optional. All functions return, explicitly or implicitly, since function application must be assigned a value.&lt;&#x2F;p&gt;
&lt;p&gt;I learned new words too.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Functors&lt;&#x2F;em&gt; are containers. &lt;em&gt;Monads&lt;&#x2F;em&gt; are powered-up functors with useful computational context.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;now-that-i-m-learning-about-category-theory&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#now-that-i-m-learning-about-category-theory&quot; aria-label=&quot;Anchor link for: now-that-i-m-learning-about-category-theory&quot;&gt;Now that I’m learning about category theory…&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;…I’m learning that those words do not mean what I thought they meant, either.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Functions&lt;&#x2F;em&gt; are apparently morphisms, or arrows, in the category of types. &lt;em&gt;Functors&lt;&#x2F;em&gt; are not containers, they are morphisms between categories. They are also their own category.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Monads&lt;&#x2F;em&gt; are — I haven’t gotten to monads yet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-read-that-they-have-a-monad-in-philosophy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-read-that-they-have-a-monad-in-philosophy&quot; aria-label=&quot;Anchor link for: i-read-that-they-have-a-monad-in-philosophy&quot;&gt;I read that they have a Monad in philosophy&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;And it means &lt;em&gt;god&lt;&#x2F;em&gt;. &lt;a href=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;it-doesnt-mean-that&#x2F;it-doesnt-mean-that#fn1&quot;&gt;¹&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Monad_(philosophy)&quot; id=&quot;fn1&quot;&gt;More or less.&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Directory aware `git`</title>
        <published>2024-05-07T00:00:00+00:00</published>
        <updated
        >2024-05-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/directory-aware-git/"
        />
        <id>https://jan.systems/posts/directory-aware-git/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/directory-aware-git/"
        >
        &lt;blockquote&gt;
&lt;p&gt;Update 2024-12-22: The functionality described here is available natively in modern versions of Git, see more recent note &lt;a href=&quot;&#x2F;posts&#x2F;git-path-conditional-config-and-ssh-key-signing&quot;&gt;Path-conditional config and SSH key signing in &lt;code&gt;git&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Update 2024-05-10: Fixed a bug in the provided script.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Update 2024-05-14: Added user name and email config features.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have separate SSH keys and email addresses for personal projects and work projects. My work-related Git projects reside in &lt;code&gt;~&#x2F;Projects&#x2F;Work&lt;&#x2F;code&gt;. Without a &quot;directory aware&quot; git config, I would have to define &lt;code&gt;GIT_SSH_COMMAND&lt;&#x2F;code&gt; when cloning to use the correct key, as well as set up each work repository to use the work key for interacting with the remote, as well as the &lt;code&gt;user.name&lt;&#x2F;code&gt; and &lt;code&gt;user.email&lt;&#x2F;code&gt; configuration values manually using &lt;code&gt;git config&lt;&#x2F;code&gt; commands. This is naturally very error prone and is an easy thing to forget.&lt;&#x2F;p&gt;
&lt;p&gt;To avoid these issues, here&#x27;s a script that wraps &lt;code&gt;git&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt;# If GIT_SSH_COMMAND is set, skip automatic key selection&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; $GIT_SSH_COMMAND ];&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-comment&quot;&gt; # skip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;elif&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; [[ $PWD&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; == *&lt;&#x2F;span&gt;&lt;span&gt;Projects&#x2F;Work&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; ]];&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;  echo&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt; &amp;quot;dir-aware-git: using work profile&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; GIT_SSH_COMMAND&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;ssh -i ~&#x2F;.ssh&#x2F;work_rsa&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; GIT_AUTHOR_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;lt;my work name&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage&quot;&gt;  export&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; GIT_AUTHOR_EMAIL&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string z-string&quot;&gt;&amp;#39;&amp;lt;my work email&amp;gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;git&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$@&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Update the &lt;code&gt;GIT_&lt;&#x2F;code&gt; environment values as well as the directory matcher in the &lt;code&gt;elif&lt;&#x2F;code&gt; conditional to suit your needs.&lt;&#x2F;p&gt;
&lt;p&gt;Save the script as &lt;code&gt;directory-aware-git&lt;&#x2F;code&gt;, make it executable with &lt;code&gt;chmod +x directory-aware-git&lt;&#x2F;code&gt; and put it in some directory that is in &lt;code&gt;$PATH&lt;&#x2F;code&gt;. For me, that would be &lt;code&gt;~&#x2F;.local&#x2F;bin&#x2F;&lt;&#x2F;code&gt;. Then, add this alias to your shell config, updated with your selected path:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other&quot;&gt; git&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=~&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;.local&#x2F;bin&#x2F;directory-aware-git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Enjoy 👍&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>ATK16 – My homegrown computer</title>
        <published>2024-05-01T00:00:00+00:00</published>
        <updated
        >2024-05-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/atk16-my-homegrown-computer/"
        />
        <id>https://jan.systems/posts/atk16-my-homegrown-computer/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/atk16-my-homegrown-computer/"
        >
        &lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;atk16-my-homegrown-computer&#x2F;atk16-text-mode.png&quot; alt=&quot;A screenshot of a text mode program displaying a work-in-progress hardware monitor&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;ATK16&lt;&#x2F;strong&gt; is a learning project I&#x27;ve been working on for some time. It started off as a 16-bit, reduced instruction set computer (RISC) CPU design but evolved into an entire computer with peripherals and software.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of repeating myself, I&#x27;m going to direct you to the &lt;a href=&quot;&#x2F;projects&#x2F;atk16&quot;&gt;ATK16 project page&lt;&#x2F;a&gt;. It&#x27;s not complete by any means, but I&#x27;ve written a bunch about the project and my motivations already! &lt;a href=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;atk16-my-homegrown-computer&#x2F;#footnote&quot;&gt;¹&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div id=&quot;footnote&quot;&gt;&lt;&#x2F;div&gt;
1. There&#x27;s actually more text in there than in my bachelor&#x27;s thesis.

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>🌱 Now, spring 2024</title>
        <published>2024-04-22T00:00:00+00:00</published>
        <updated
        >2024-04-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2024-spring/"
        />
        <id>https://jan.systems/posts/now-2024-spring/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2024-spring/"
        >
        &lt;p&gt;Inches of snow are falling on the yard, covering any new green growths. The dog likes it, I don&#x27;t.&lt;&#x2F;p&gt;
&lt;video width=&quot;100%&quot; controls&gt;  
  &lt;source src=&quot;jojo-in-snow.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;  
Your browser does not support the video tag.  
&lt;&#x2F;video&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;travel&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#travel&quot; aria-label=&quot;Anchor link for: travel&quot;&gt;Travel&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Attended the &lt;a href=&quot;&#x2F;posts&#x2F;qcon-london-2024-takeaways&quot;&gt;QCon London 2024&lt;&#x2F;a&gt; in London&lt;&#x2F;li&gt;
&lt;li&gt;Went to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.tahko.com&#x2F;en&#x2F;&quot;&gt;Tahko&lt;&#x2F;a&gt; for a company leisure trip to downhill ski and snowmobile&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;music&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#music&quot; aria-label=&quot;Anchor link for: music&quot;&gt;Music&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;projects#blek&quot;&gt;BLEK&lt;&#x2F;a&gt; was live for 3 hours on student radio Radiodiodi with a dungeon synth genre deepdive program. According to metrics, 20 people (!) were listening to our show, which was also our grand EP premiere. Big leagues.&lt;&#x2F;p&gt;
&lt;p&gt;I bought a &lt;a href=&quot;&#x2F;posts&#x2F;cassettes&quot;&gt;cassette player&lt;&#x2F;a&gt; and a bunch of metal and dungeon synth tapes, as well some empty ones from the Bezos shop. I&#x27;m planning to record BLEK&#x27;s music on some of them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;games&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#games&quot; aria-label=&quot;Anchor link for: games&quot;&gt;Games&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve been on a proper Ratchet and Clank binge, together with Marika.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Ratchet and Clank: Rift Apart&lt;&#x2F;li&gt;
&lt;li&gt;Ratchet and Clank: Tools of Destruction&lt;&#x2F;li&gt;
&lt;li&gt;Ratchet and Clank: Gladiator (Deadlocked)&lt;&#x2F;li&gt;
&lt;li&gt;Also we played the original trilogy in 2023&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Non-Ratchet:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Rollerdrome&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Tabletop games:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Started playing &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;hinokodo.itch.io&#x2F;miru-an-analog-adventure-game&quot;&gt;Miru&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reading&quot; aria-label=&quot;Anchor link for: reading&quot;&gt;Reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Category Theory for Programmers&lt;&#x2F;em&gt; by Bartosz Milewski
&lt;ul&gt;
&lt;li&gt;Turned out to be quite a bit more difficult than I expected. The first half was completely doable with my functional programming and limited math background, but the second half is a piece of work. I&#x27;ll push through though.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;movies&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#movies&quot; aria-label=&quot;Anchor link for: movies&quot;&gt;Movies&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I went to see Dune 2. It was as ok as the first part.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;projects&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#projects&quot; aria-label=&quot;Anchor link for: projects&quot;&gt;Projects&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;atk16&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#atk16&quot; aria-label=&quot;Anchor link for: atk16&quot;&gt;ATK16&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Picked up &lt;a href=&quot;&#x2F;projects&#x2F;atk16&quot;&gt;ATK16&lt;&#x2F;a&gt; again, adding some major stuff:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A calling convention for functions: arguments in registers &lt;code&gt;RA&lt;&#x2F;code&gt;–&lt;code&gt;RG&lt;&#x2F;code&gt;, return value in register &lt;code&gt;RG&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;An emulator &amp;amp; step debugger in Python + Pygame for easier debugging&lt;&#x2F;li&gt;
&lt;li&gt;A bump allocator for using the heap&lt;&#x2F;li&gt;
&lt;li&gt;Ergonomics features in the assembler:
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;@data&lt;&#x2F;code&gt; directive for storing data in a data segment&lt;&#x2F;li&gt;
&lt;li&gt;A string datatype that is encoded as a length and data bytes in memory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Some &quot;standard library&quot; stuff, like &lt;code&gt;memset&lt;&#x2F;code&gt; and &lt;code&gt;memcopy&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A hardware monitor &#x2F; shell-like program for interacting with the system in text mode&lt;&#x2F;li&gt;
&lt;li&gt;An MMIO register for setting the interrupt flag in order to define critical sections for primitive interrupt concurrency&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I feel like a proper introduction post &#x2F; documentation is long overdue 😅 I should fix that.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;garden-jan-systems&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#garden-jan-systems&quot; aria-label=&quot;Anchor link for: garden-jan-systems&quot;&gt;Garden (jan.systems)&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Rewrote the site generator flow. Previously, each file in the content directory was accessed and processed from start to finish separately. This meant that there was no knowledge of other notes available when rendering a note. This made stuff like a &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;derekkedziora.com&#x2F;blog&#x2F;dynamic-now-page&quot;&gt;&lt;em&gt;dynamic now page&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; impossible.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the flow starts by traversing the entire content directory and indexing it into an in-memory database. The database contains all the metadata and filesystem info of all the notes, as well as static files. This makes dynamic pages possible since the database can be queried at render time. This approach also improved performance, since I could reduce the number of filesystem reads and writes.&lt;&#x2F;p&gt;
&lt;p&gt;Things I still want to add:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A bookmarking system, maybe somehow connected to the &lt;del&gt;&lt;a href=&quot;&#x2F;liked&quot;&gt;liked&lt;&#x2F;a&gt; page&lt;&#x2F;del&gt;?
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Update from the future&lt;&#x2F;em&gt;: the liked page was migrated to the &lt;a href=&quot;&#x2F;linklog&quot;&gt;linklog&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;A project page structure (just a content thing, no code required)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;previous-update&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#previous-update&quot; aria-label=&quot;Anchor link for: previous-update&quot;&gt;Previous update&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;posts&#x2F;now-2023-winter&quot;&gt;❄️ Now, winter 2023&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Automatic slideshows with reveal-unfold.js</title>
        <published>2024-04-21T00:00:00+00:00</published>
        <updated
        >2024-04-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/unfold/"
        />
        <id>https://jan.systems/posts/unfold/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/unfold/"
        >
        &lt;h2 id=&quot;lightning-talk&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lightning-talk&quot; aria-label=&quot;Anchor link for: lightning-talk&quot;&gt;Lightning talk&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p data-unfold-hidden&gt;
I recently presented a lightning talk about the IndieWeb and personal websites to my colleagues at the office. Even though I don&#x27;t normally enjoy presenting, speaking about things I&#x27;m passionate about always turns out to be rather easy. The talk and the presentation that I used to support the talk were well received.
&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;unfold&amp;#x2F;indieweb_presentation.png&quot; alt=&quot;A slide from the PowerPoint presentation&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A slide from the PowerPoint presentation&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;tinkering&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tinkering&quot; aria-label=&quot;Anchor link for: tinkering&quot;&gt;Tinkering&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p data-unfold-hidden&gt;
Later, when I shared my PowerPoint slides in the IndieWeb chat, I was asked if I had considered using HTML slides instead of pptx. The thought had crossed my mind, but I didn’t have time to put the thought into practice before my talk. It was a curious idea though.
&lt;&#x2F;p&gt;
&lt;p data-unfold-hidden&gt;
So curious in fact, that I decided to study up on the ”HTML slideshow landscape”: what kind of tools there are, how do they work on a static site, things like that. Turns out that a tool called `reveal.js` is very popular and often recommended.  An idea entered my mind: what if I could turn an HTML note (like this one that you’re reading) into an HTML slideshow, in a somewhat automated fashion?
&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;unfold&amp;#x2F;reveal-js-logo.png&quot; alt=&quot;Reveal.js logo&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Reveal.js logo&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;literate-slideshow&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#literate-slideshow&quot; aria-label=&quot;Anchor link for: literate-slideshow&quot;&gt;Literate slideshow&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;A portmanteau of &lt;em&gt;literate program&lt;&#x2F;em&gt; and &lt;em&gt;slideshow&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Convert a semantically marked up post to a slideshow with a click&lt;&#x2F;li&gt;
&lt;li&gt;This post is a literate slideshow&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;direction&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#direction&quot; aria-label=&quot;Anchor link for: direction&quot;&gt;Direction&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t know if I&#x27;ll even need this project. Maybe I&#x27;ll use it in some posts in the future?&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>QCon London 2024 takeaways</title>
        <published>2024-04-20T00:00:00+00:00</published>
        <updated
        >2024-04-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/qcon-london-2024-takeaways/"
        />
        <id>https://jan.systems/posts/qcon-london-2024-takeaways/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/qcon-london-2024-takeaways/"
        >
        &lt;p&gt;QCon London 2024 was a multidisciplinary software engineering conference in the London QEII Centre in April 2024. I attended with the intention to learn about what&#x27;s going on in the software-sphere. Especially in terms of software architecture.&lt;&#x2F;p&gt;
&lt;p&gt;This was also my first time in the UK so I had to do some touristing. The weather was a bit chilly, half-cloudy with some intermittent drizzle.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;qcon-london-2024-takeaways&amp;#x2F;london-from-above.jpg&quot; alt=&quot;London from above in the late evening&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;London from above in the late evening&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;common-themes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#common-themes&quot; aria-label=&quot;Anchor link for: common-themes&quot;&gt;Common themes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;data-products&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#data-products&quot; aria-label=&quot;Anchor link for: data-products&quot;&gt;Data products&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A term from data mesh architecture. Think of the data you produce as a product. A product has an audience and marketing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;platform-thinking-platform-teams&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#platform-thinking-platform-teams&quot; aria-label=&quot;Anchor link for: platform-thinking-platform-teams&quot;&gt;Platform thinking, platform teams&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Enable your software teams by building a platform of supported programming languages&#x2F;runtimes, CI&#x2F;CD pipeline configurations, deployment clusters, etc. so that each team does not have to spend resources reinventing the wheel.&lt;&#x2F;p&gt;
&lt;p&gt;Provide a &lt;em&gt;golden path&lt;&#x2F;em&gt; (tried and tested technologies supported by the platform team) for new projects but do not smother your engineers&#x27; freedom of choice.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;genai-llms-are-not-necessarily-that-useful&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#genai-llms-are-not-necessarily-that-useful&quot; aria-label=&quot;Anchor link for: genai-llms-are-not-necessarily-that-useful&quot;&gt;“GenAI&#x2F;LLMs are not necessarily that useful”&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Probably comes as a surprise but the latest wave of AI hype isn&#x27;t very substantiated. Issues of security, trust, quality, ownership and licensing. Keep a human in the loop.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;safe-performant-and-ecological&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#safe-performant-and-ecological&quot; aria-label=&quot;Anchor link for: safe-performant-and-ecological&quot;&gt;Safe, performant and ecological&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Running software causes carbon emissions. Choose runtimes, programming languages and hosting options that minimize idle and active compute use.&lt;&#x2F;p&gt;
&lt;p&gt;Avoid entire categories of vulnerabilities by picking safe languages such as Rust.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;most-inspiring-talks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#most-inspiring-talks&quot; aria-label=&quot;Anchor link for: most-inspiring-talks&quot;&gt;Most inspiring talks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;the-home-computer-that-roared-how-the-bbc-micro-shaped-our-world-by-jeremy-ruston&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-home-computer-that-roared-how-the-bbc-micro-shaped-our-world-by-jeremy-ruston&quot; aria-label=&quot;Anchor link for: the-home-computer-that-roared-how-the-bbc-micro-shaped-our-world-by-jeremy-ruston&quot;&gt;&lt;strong&gt;The Home Computer That Roared: How the BBC Micro Shaped Our World&lt;&#x2F;strong&gt; by &lt;em&gt;Jeremy Ruston&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A deep history dive to the advent of home computing in the UK. Jeremy was involved in building the educational BBC Micro computer as well as TV children&#x27;s show animations and Doctor Who games. Super inspiring stuff, especially since I can relate to many of the constraints Jeremy et al. faced since I&#x27;m working on my very constrained &quot;ATK16&quot; hobby CPU project myself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;thinking-like-an-architect-by-gregor-hohpe-aws&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#thinking-like-an-architect-by-gregor-hohpe-aws&quot; aria-label=&quot;Anchor link for: thinking-like-an-architect-by-gregor-hohpe-aws&quot;&gt;&lt;strong&gt;Thinking like an architect&lt;&#x2F;strong&gt; by &lt;em&gt;Gregor Hohpe (AWS)&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Architects find connections. Between perspectives, between technologies, between people.&lt;&#x2F;p&gt;
&lt;p&gt;The director&#x27;s budgets, risks and customer success are connected to the engineer&#x27;s refactorings, scalability and backups.&lt;&#x2F;p&gt;
&lt;p&gt;Architects are an IQ multiplier, not the smartest person in the room. Architects make other people smarter.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;architecting-for-data-products-by-danilo-sato-thoughtworks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#architecting-for-data-products-by-danilo-sato-thoughtworks&quot; aria-label=&quot;Anchor link for: architecting-for-data-products-by-danilo-sato-thoughtworks&quot;&gt;&lt;strong&gt;Architecting for Data Products&lt;&#x2F;strong&gt; by &lt;em&gt;Danilo Sato (Thoughtworks)&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Move from &lt;em&gt;left-to-right&lt;&#x2F;em&gt; architectures (operational ↣ analytics) to a data mesh.&lt;&#x2F;p&gt;
&lt;p&gt;Make your data discoverable and usable via different protocols and formats. Make it self-serve (platform thinking).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;🙋🏼 Note: I didn&#x27;t know much about data mesh literature before this talk. The most useful things I got from it were the words and terms to look up, such as the &lt;em&gt;DATSIS principles&lt;&#x2F;em&gt;, and the books to read (see &lt;a href=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;qcon-london-2024-takeaways&#x2F;#reading-list&quot;&gt;Reading list&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;building-your-first-platform-team-in-a-fast-growing-startup-by-jessica-andersson-kognic&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-your-first-platform-team-in-a-fast-growing-startup-by-jessica-andersson-kognic&quot; aria-label=&quot;Anchor link for: building-your-first-platform-team-in-a-fast-growing-startup-by-jessica-andersson-kognic&quot;&gt;&lt;strong&gt;Building Your First Platform Team&lt;&#x2F;strong&gt; in a Fast Growing Startup by &lt;em&gt;Jessica Andersson (Kognic)&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The spirit of DevOps has in part transmogrified into the idea of &lt;em&gt;software platforms&lt;&#x2F;em&gt;. DevOps as a term has degraded to just mean Ops in many places.
Empower product teams with an explicit platform. Each company has a platform, implicit or explicit.
A &lt;em&gt;base plaform&lt;&#x2F;em&gt; should provide:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CI&#x2F;CD&lt;&#x2F;strong&gt;: pipeline templates, runner images, best practices&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Runtime&lt;&#x2F;strong&gt;: programming languages and runtimes, but also container runtimes, orchestration&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Observability&lt;&#x2F;strong&gt;: distributed tracing, logging, monitoring, alerting&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;reading-list&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reading-list&quot; aria-label=&quot;Anchor link for: reading-list&quot;&gt;Reading list&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;⭐️ &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;teamtopologies.com&#x2F;book&quot;&gt;Team topologies&lt;&#x2F;a&gt; by &lt;em&gt;Matthew Skelton and Manuel Pais&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Domain-Driven-Design-Tackling-Complexity-Software&#x2F;dp&#x2F;0321125215&quot;&gt;Domain-Driven Design&lt;&#x2F;a&gt; by &lt;em&gt;Eric Evans&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;data-mesh&#x2F;9781492092384&#x2F;&quot;&gt;Data Mesh&lt;&#x2F;a&gt; by &lt;em&gt;Zhamak Dehghani&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;architectelevator.com&#x2F;&quot;&gt;Architect Elevator&lt;&#x2F;a&gt; by &lt;em&gt;Gregor Hohpe&lt;&#x2F;em&gt;
&lt;ul&gt;
&lt;li&gt;all the other books by Hohpe also&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;word-dump&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#word-dump&quot; aria-label=&quot;Anchor link for: word-dump&quot;&gt;Word dump&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I did not recognize these words or concepts.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;attestation testing, SLSA&lt;&#x2F;li&gt;
&lt;li&gt;server-driven UI&lt;&#x2F;li&gt;
&lt;li&gt;DATSIS principles&lt;&#x2F;li&gt;
&lt;li&gt;data vault architecture&lt;&#x2F;li&gt;
&lt;li&gt;source-aligned, consumer-aligned data products&lt;&#x2F;li&gt;
&lt;li&gt;galaxy apps&lt;&#x2F;li&gt;
&lt;li&gt;2-step commit&lt;&#x2F;li&gt;
&lt;li&gt;wardley map&lt;&#x2F;li&gt;
&lt;li&gt;BPMN (business process model and notation)&lt;&#x2F;li&gt;
&lt;li&gt;eBPF&lt;&#x2F;li&gt;
&lt;li&gt;expand-contract pattern&lt;&#x2F;li&gt;
&lt;li&gt;SCI (software carbon intensity)&lt;&#x2F;li&gt;
&lt;li&gt;WASI (WebAssembly System Interface)&lt;&#x2F;li&gt;
&lt;li&gt;Gartner hype cycle&lt;&#x2F;li&gt;
&lt;li&gt;trust boundary (in context of LLMs)&lt;&#x2F;li&gt;
&lt;li&gt;LLM agent&lt;&#x2F;li&gt;
&lt;li&gt;shift left (in context of software platforms)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;food-and-drink&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#food-and-drink&quot; aria-label=&quot;Anchor link for: food-and-drink&quot;&gt;Food and drink&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;fantastic-indian-cuisine-dishoom-covent-garden&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fantastic-indian-cuisine-dishoom-covent-garden&quot; aria-label=&quot;Anchor link for: fantastic-indian-cuisine-dishoom-covent-garden&quot;&gt;Fantastic Indian cuisine: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.dishoom.com&#x2F;covent-garden&#x2F;&quot;&gt;Dishoom Covent Garden&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;You don&#x27;t get Indian food like this in Finland. Super flavourful, suitably spicy. Was handed a gratis chai to drink while queuing up in the drizzling rain too. Excellent customer service.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;real-ale-pub-the-harp&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#real-ale-pub-the-harp&quot; aria-label=&quot;Anchor link for: real-ale-pub-the-harp&quot;&gt;Real Ale Pub: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.harpcoventgarden.com&#x2F;&quot;&gt;The Harp&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Nothing mindblowing, just good ale and a comfy vibe. A good place to sit out the rain.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bloody-lovely-duke-of-argyll&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#bloody-lovely-duke-of-argyll&quot; aria-label=&quot;Anchor link for: bloody-lovely-duke-of-argyll&quot;&gt;Bloody lovely: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;maps.app.goo.gl&#x2F;21YHi1ZBrXohhczj9&quot;&gt;Duke of Argyll&lt;&#x2F;a&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I had to eat fish &amp;amp; chips at least once just to check it off my bucket list. I don&#x27;t know if theirs is the best or even proper, but I enjoyed it a lot.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;qcon-london-2024-takeaways&amp;#x2F;fish-and-chips-duke-of-argyll.jpg&quot; alt=&quot;Fish and chips at the Duke of Argyll&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Fish and chips at the Duke of Argyll&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Cookbook</title>
        <published>2024-03-28T00:00:00+00:00</published>
        <updated
        >2024-03-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/cookbook/"
        />
        <id>https://jan.systems/posts/cookbook/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/cookbook/"
        >
        &lt;blockquote&gt;
&lt;p&gt;This note relates to the project &lt;a href=&quot;&#x2F;projects&#x2F;cookbook&quot;&gt;Cookbook&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have started a terse cookbook project for myself. Our household struggles with coming up with simple meals to prepare when doing the week&#x27;s shopping. This should act as inspiration.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Cassettes</title>
        <published>2024-03-27T00:00:00+00:00</published>
        <updated
        >2024-03-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/cassettes/"
        />
        <id>https://jan.systems/posts/cassettes/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/cassettes/"
        >
        &lt;p&gt;I like the idea of cassettes. They are cheap and analog, and there’s a culture of sharing and trading them. I like the sound of tape saturation and hysteresis.&lt;&#x2F;p&gt;
&lt;p&gt;They feel much more approachable than vinyls. I dislike snobbery.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-i-bought-a-cassette-player&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#so-i-bought-a-cassette-player&quot; aria-label=&quot;Anchor link for: so-i-bought-a-cassette-player&quot;&gt;So I bought a cassette player&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;cassettes&amp;#x2F;cassette_player.jpeg&quot; alt=&quot;A Sony cassette recorder next to a pile of cassettes&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A Sony cassette recorder next to a pile of cassettes&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;It’s a Sony TCM-459V and it’s only a bit noisy and wobbly. It can record too.&lt;&#x2F;p&gt;
&lt;p&gt;After listening to all of my tapes (not very many, see picture), A side and B side, I can safely say that I like cassettes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;maintenance&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#maintenance&quot; aria-label=&quot;Anchor link for: maintenance&quot;&gt;Maintenance&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The manual says that the innards need cleaning (rubbing alcohol + q-tip) after every 10 hours of operation. I don’t think this unit has ever been cleaned.&lt;&#x2F;p&gt;
&lt;p&gt;The belts might also need changing since they apparently go bad after a while.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tapes-catch-on-fire&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#tapes-catch-on-fire&quot; aria-label=&quot;Anchor link for: tapes-catch-on-fire&quot;&gt;Tapes catch on fire&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I read on the internet that peoples’ tapes have spontaneously combusted. This is a thing to avoid.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Deploying the garden</title>
        <published>2024-03-26T00:00:00+00:00</published>
        <updated
        >2024-03-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/deploying-the-garden/"
        />
        <id>https://jan.systems/posts/deploying-the-garden/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/deploying-the-garden/"
        >
        &lt;p&gt;Every respectable tech blog must contain two posts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;”This site now has RSS”&lt;&#x2F;li&gt;
&lt;li&gt;”This is how this site is deployed”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Other content is optional.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;writing-on-the-go&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#writing-on-the-go&quot; aria-label=&quot;Anchor link for: writing-on-the-go&quot;&gt;Writing on the go&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Content updates go like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I write Markdown in a special directory in my Obsidian Vault&lt;&#x2F;li&gt;
&lt;li&gt;The change is synced to my Digital Ocean server via Obsidian Sync (I run an Obsidian process on my server)&lt;&#x2F;li&gt;
&lt;li&gt;A file system monitor script picks it up and runs a build script&lt;&#x2F;li&gt;
&lt;li&gt;Built HTML files are served with Nginx&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This process gives me full control over the content wherever I have access to Obsidian. Making writing as easy as possible is the principle behind this design.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.todepond.com&#x2F;wikiblogarden&#x2F;art&#x2F;never-stop-writing&#x2F;on-your-phone&quot;&gt;This is considered a good idea in the frog community.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Making changes to the build process itself, or the HTML template, requires a computer, however.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;parentheses&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#parentheses&quot; aria-label=&quot;Anchor link for: parentheses&quot;&gt;Parentheses&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Most of the heavy lifting is done by &lt;code&gt;pandoc&lt;&#x2F;code&gt;. The glue parts use an obscure and ancient language with many curved sigils. Give it a try.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;deploying-the-garden&amp;#x2F;repo_langs.png&quot; alt=&quot;A GitHub repository language breakdown showing that most of the code is written in Scheme.&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;A GitHub repository language breakdown showing that most of the code is written in Scheme.&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;h2 id=&quot;hosting&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hosting&quot; aria-label=&quot;Anchor link for: hosting&quot;&gt;Hosting&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;My small Digital Ocean server also moonlights as a container image registry. I build the scripts into an OCI image, push it on the server and run it with &lt;code&gt;docker-compose&lt;&#x2F;code&gt;. Don’t over-engineer when you want things &lt;em&gt;done&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>Diddle, a minimalist event scheduler</title>
        <published>2024-03-12T00:00:00+00:00</published>
        <updated
        >2024-03-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/diddle-event-scheduler/"
        />
        <id>https://jan.systems/posts/diddle-event-scheduler/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/diddle-event-scheduler/"
        >
        &lt;p&gt;I don&#x27;t like Doodle. It pushes ads and its premium subscription relentlessly while simultaneously feeling like a clumsy, lumbering beast. There is also a lot to be desired in terms of accessibility and mobile-friendliness.&lt;&#x2F;p&gt;
&lt;p&gt;There are alternatives, like &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;dud-poll.inf.tu-dresden.de&#x2F;&quot;&gt;dudle&lt;&#x2F;a&gt; or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;framadate.org&#x2F;abc&#x2F;en&#x2F;&quot;&gt;framadate&lt;&#x2F;a&gt;. I could probably make do with those. However, to me this felt like an opportunity to spin up a quick MVP. This MVP turned into &lt;em&gt;diddle&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Diddle (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;diddle.jan.systems&#x2F;&quot;&gt;live instance&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jantuomi&#x2F;diddle&quot;&gt;Github&lt;&#x2F;a&gt;) is a minimalist tool that focuses on fast load times, accessibility and pragmatism. The feature set is based on my own personal requirements. It works great for quickly filling out a poll on mobile.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;: The instance hosted on my domain might go down any second. Use it at your own discretion.&lt;&#x2F;p&gt;



&lt;figure&gt;
    &lt;img src=&quot;&amp;#x2F;posts&amp;#x2F;diddle-event-scheduler&amp;#x2F;diddle_screenshot.png&quot; alt=&quot;Diddle screenshot showing an answerable poll&quot; &#x2F;&gt;
    &lt;figcaption aria-hidden=&quot;true&quot;&gt;Diddle screenshot showing an answerable poll&lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The first 80% of the project was done in two evenings. The last 80% was done during the next week. The tech stack is the &lt;em&gt;get-shit-done&lt;&#x2F;em&gt; stack: &lt;strong&gt;Python&lt;&#x2F;strong&gt; + &lt;strong&gt;Flask&lt;&#x2F;strong&gt; + &lt;strong&gt;PostgreSQL&lt;&#x2F;strong&gt;. There is no required client side javascript: all client side logic is strictly for &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Glossary&#x2F;Progressive_Enhancement&quot;&gt;progressive enhancement&lt;&#x2F;a&gt;. You can use &quot;dumb&quot; browsers like w3m or lynx to enjoy Diddle just fine.&lt;&#x2F;p&gt;
&lt;p&gt;Check out the README on Github for more info.&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>There&#x27;s an RSS feed now</title>
        <published>2024-02-15T00:00:00+00:00</published>
        <updated
        >2024-02-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/theres-an-rss-feed-now/"
        />
        <id>https://jan.systems/posts/theres-an-rss-feed-now/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/theres-an-rss-feed-now/"
        >
        &lt;p&gt;As part of the site build process, I use &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chambln&#x2F;pandoc-rss&quot;&gt;&lt;code&gt;pandoc-rss&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to automatically build an RSS feed of my posts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;jan.systems&#x2F;posts&#x2F;theres-an-rss-feed-now&#x2F;internet_surf.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;

        </content>
        
    </entry>
    <entry xml:lang="en">
        <title>❄️ Now, winter 2023</title>
        <published>2023-01-30T00:00:00+00:00</published>
        <updated
        >2023-01-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Jan Tuomi
            
          </name>
        </author>
        
        <link
            rel="alternate"
            type="text/html"
            href="https://jan.systems/posts/now-2023-winter/"
        />
        <id>https://jan.systems/posts/now-2023-winter/</id>
        
        <content
            type="html"
            xml:base="https://jan.systems/posts/now-2023-winter/"
        >
        &lt;!-- toc --&gt;
&lt;h2 id=&quot;currently&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#currently&quot; aria-label=&quot;Anchor link for: currently&quot;&gt;Currently&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Working on this site&lt;&#x2F;li&gt;
&lt;li&gt;Doing small renovations and fixes to the house&lt;&#x2F;li&gt;
&lt;li&gt;Learning Chicken Scheme and using it to build &lt;a href=&quot;&#x2F;projects#desmoctl&quot;&gt;desmoctl&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;posts&#x2F;deploying-the-garden&quot;&gt;this website&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Reading &lt;em&gt;Category Theory for Programmers&lt;&#x2F;em&gt; by Bartosz Milewski&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;recently&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#recently&quot; aria-label=&quot;Anchor link for: recently&quot;&gt;Recently&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Purchased a 100 m2 terraced house&lt;&#x2F;li&gt;
&lt;li&gt;Upstreamed some fixes to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;spliit.app&#x2F;&quot;&gt;Spliit&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Started a project of reading more books. I never read much growing up so this is something I have to learn to do. Lately I&#x27;ve been into learning about programming languages with &quot;big ideas&quot;, i.e. Lisp (programs as data), Forth (stack focus), Smalltalk (OOP).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;reading&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#reading&quot; aria-label=&quot;Anchor link for: reading&quot;&gt;Reading&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2024&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#2024&quot; aria-label=&quot;Anchor link for: 2024&quot;&gt;2024&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;A Retargetable C Compiler: Design And Implementation&lt;&#x2F;em&gt; by David Hanson, Christopher Fraser. The book defines the &lt;code&gt;lcc&lt;&#x2F;code&gt; ANSI C compiler, which I have considered writing a custom backend for in the &lt;a href=&quot;&#x2F;projects&#x2F;atk16&quot;&gt;ATK16&lt;&#x2F;a&gt; project.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;beej.us&#x2F;guide&#x2F;bgnet0&#x2F;&quot;&gt;Beej&#x27;s Guide to Networking Concepts&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;How to Win Friends and Influence People in the Digital Age&lt;&#x2F;em&gt; by Dale Carnegie, a 2012 adaptation of the classic&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Gödel, Escher, Bach: An Eternal Golden Braid&lt;&#x2F;em&gt; by Douglas Hofstaedter&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;2023&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#2023&quot; aria-label=&quot;Anchor link for: 2023&quot;&gt;2023&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Essentials of Programming Languages&lt;&#x2F;em&gt; by Daniel Friedman&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Structure and Interpretation of Computer Programs&lt;&#x2F;em&gt; by Abelson and Sussman&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;

        </content>
        
    </entry>
</feed>
