<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
	<channel>
		<title><![CDATA[Koen van Hove's personal writings]]></title>
		<description><![CDATA[I am a computer person who likes toying with networking, security, and data. Also creates software. Over here I post my (rather occasional) writings that are too long for Mastodon and Bluesky.]]></description>
		<link>https://blog.koenvh.nl</link>
		<image>
			<url>https://blog.koenvh.nl/assets/logo.svg</url>
			<title>Koen van Hove&apos;s personal writings</title>
			<link>https://blog.koenvh.nl</link>
		</image>
		<generator>RSS for Node</generator>
		<lastBuildDate>Tue, 16 Jun 2026 09:36:48 GMT</lastBuildDate>
		<atom:link href="https://blog.koenvh.nl/rss.xml" rel="self" type="application/rss+xml"/>
		<language><![CDATA[en]]></language>
		<ttl>60</ttl>
		<atom:link rel="first" href="https://blog.koenvh.nl/rss.xml"/>
		<item>
			<title><![CDATA[April Fools 2026]]></title>
			<description><![CDATA[<p>Even though it's already June, I still wanted to make a (short) post about the things I did for April Fools this year. Like <a target="_blank" href="https://blog.koenvh.nl/april-fools-2025-phone-books-and-adverts">previous year</a> I spent some time making some fun contraptions, where asking "why?" would be the wrong question. Sadly this year I had a bit less time to do something, and admittedly I already did some of the best ideas last year, but I am still quite happy with the result.</p>
<p>Anyway, without further ado, let me introduce to you the three things I did for April Fools in 2026:</p>
<h1 id="heading-too-long-dyet-red">Too Long; Dye't Red</h1>
<p>The first thing I did was write a paper for SIGBOVIK 2026. Explaining SIGBOVIK to someone is always quite tricky, but it's a conference for academics that simultaneously pokes fun at academia, but not in a mean-spirited way. Last year I wrote a paper titled "Ad Fund 'Em", which was about adding adverts to scientific papers to secure more funding. This year I tackled another issue many academics face: running out of pages. My solution was quite simple: stereoscopic paper. I think it is best explained using a picture:</p>
<p><img loading="lazy" src="posts/april-fools-2026/paper-both.jpg" class="image--center mx-auto" /></p>
<p>The idea is that you use 3D glasses (the classic kind with red and blue) to read this paper. For your convenience, this is what that looks like:</p>
<p><img loading="lazy" src="posts/april-fools-2026/paper-blue.jpg" class="image--center mx-auto" /></p>
<p><img loading="lazy" src="posts/april-fools-2026/paper-red.jpg" class="image--center mx-auto" /></p>
<p>Making this was surprisingly simple. I just needed to make sure to use dithering for the images (so it was purely black-and-white), and use the correct overlay settings.</p>
<p>It was well-received, and even mentioned for the paper directly after it. It can be found in <a target="_blank" href="https://sigbovik.org/2026/proceedings.pdf">the official SIGBOVIK proceedings</a> on page 962. I really recommend reading some of the others as well, there is some fascinating work done by many talented people.</p>
<h1 id="heading-ircns">ircns</h1>
<p>Even though I don't do much with DNS on a day-to-day basis, I still like to have some fun with it every now and then. This year I struggled to come up with something. In the end I built a bridge between IRC and DNS. This was my "pitch":</p>
<blockquote>
<p>DNS is about so much more than questions and answers alone. DNS also about conversations and exchanging ideas.</p>
<p>ircns is a revolutionary way to use the chat protocol of the 21st century (IRC) using the best tried and trusted technology of the 20th century (DNS). It provides a bi-directional bridge between DNS and IRC, allowing you to receive and send IRC messages over DNS using conventional tools like dig, drill, and kdig.</p>
</blockquote>
<p>Again I think this is best explained using a video:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=Afk0oiD6wNA">https://www.youtube.com/watch?v=Afk0oiD6wNA</a></div>
<p>It was too dear to me to keep it online for long after April Fools (and it was not used that much either), so I took it offline after a month. It's still a fun experiment though.</p>
<h1 id="heading-ceci-nest-pas-un-tamagotchi">Ceci n'est pas un Tamagotchi</h1>
<p>I also sent in a (video) presentation for SIGBOVIK. This is basically a small demo of the tamagotchi clone I made with DVD menus, <a target="_blank" href="https://blog.koenvh.nl/dvd-menus-and-tamagotchis">which I already wrote about previously</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=oK5M4ZSlEMI">https://www.youtube.com/watch?v=oK5M4ZSlEMI</a></div>
<p>And that is it for April Fools 2026. Now I have another year (well, 10 months) to come up with something for next year.</p>
]]></description>
			<link>https://blog.koenvh.nl/april-fools-2026</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/april-fools-2026</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Thu, 04 Jun 2026 21:00:00 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Waarom het Ministerie van Defensie een "vibe coder" zocht]]></title>
			<description><![CDATA[<p><em>Dit is een vervolg op</em> <a target="_blank" href="https://blog.koenvh.nl/vibe-coding-bij-het-ministerie-van-defensie"><em>Vibe coding bij het Ministerie van Defensie</em></a><em>. Kort samengevat: het Ministerie van Defensie had een detacheringsopdracht online gezet waarin stond dat ze op zoek waren naar een "senior vibe/AI developer". Daarop had ik een woo-verzoek (wet open overheid) gedaan om daar meer over te weten te komen.</em></p>
<p>Mijn <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7433898582459723777/">post op LinkedIn</a> over mijn woo-verzoek bij het Ministerie van Defensie over hun detacheringsopdracht voor een "senior vibe/AI developer" maakte nogal wat los bij mensen. Als ik de reacties bekijk, lijkt er een grote groep te zijn die tevens verbaasd is door deze vacature, maar ook een groep die "blij is dat Defensie met haar tijd meegaat". Die tweedeling maakt het antwoord op het woo-verzoek des te interessanter - is Defensie daadwerkelijk op zoek naar een vibe coder?</p>
<p>Het antwoord kwam sneller dan ik had verwachtte. Eerst hoorde ik een hele tijd niets, dus ben ik gaan e-mailen met de vraag "hoe staat het ermee?". Ik hoorde nog steeds niets, dus ben ik gaan bellen. Helaas verbindt <a target="_blank" href="https://organisaties.overheid.nl/woo/4958/Defensie">het telefoonnummer dat in de Woo-index staat</a> door met de persvoorlichting, en die konden uiteraard niets betekenen. Maar op maandag 16 maart werd ik dan toch plots gebeld door een mij onbekend 06-nummer.</p>
<p>Ik nam de telefoon op (enigszins argwanend vanwege het datalek bij Odido waar ook mijn gegevens in staan, en waarvan de eerste phishing-pogingen al binnengekomen zijn), en werd begroet door iemand die zich introduceerde als "Luitenant". Geen phishing dus, maar het ging over mijn woo-verzoek. Ik verwachte een bevestiging, maar er kwam meer dan alleen dat.</p>
<p>Samengevat komt het hierop neer: er zijn vier documenten gevonden naar aanleiding van mijn woo-verzoek, maar ze kunnen niet geopenbaard worden. Alle vier vallen ze - aldus het Ministerie van Defensie - integraal onder de <a target="_blank" href="https://expertisecentrumspoon.nl/kennisbank/goed-functioneren-van-de-staat-weigergrond-14/">i-grond</a>. De i-grond (verwijzend naar artikel 5.1.2.i van de woo) is een van de uitzonderingsgronden van de woo - simpel gezegd wordt informatie openbaar gemaakt tenzij het onder een van de uitzonderingsgronden valt.</p>
<p>De letterlijke tekst van 5.1.2.i is "het goed functioneren van de Staat, andere publiekrechtelijke lichamen of bestuursorganen". De uitleg die ik via de telefoon hoorde was dat het te maken had met, parafraserend, "de gevoeligheid van de informatie voor de onderhandelingspositie van de Staat". Oftewel dat met het publiceren van deze informatie het mogelijk de staat benadeelt in onderhandelingen als het gaat om vergoedingen, etc. Het voorstel kwam om het woo-verzoek te laten vallen, en het te behandelen als informatieverzoek. Dat betekent geen formele besluiten, documenten, etc., maar een antwoord op mijn vragen als e-mail door degene die ervoor verantwoordelijk is.</p>
<p>De i-grond wordt nogal eens te breed uitgelegd. In principe is het idee van de woo "openbaar, tenzij een van de uitzonderingen van toepassing is". In de praktijk wordt ook wel eens de keuze om het verzoek te weigeren eerst genomen, en daarna een zo passend mogelijke uitzonderingsgrond erbij gezocht. De i-grond is daar populair voor, omdat "het goed functioneren van de Staat" nogal een wendbaar begrip is. Echter kan ik het in deze context ook wel begrijpen, immers, als in één van de stukken zou staan wat <em>het hoogste bod</em> is van de Staat, dan kan ik me voorstellen dat dat beter is om niet te publiceren - dan wordt het als een kaartspel waar een van de twee partijen de kaarten open moet leggen, maar de andere partij niet.</p>
<p>Het lastige is dat ik de stukken dus niet kan zien, dus niet weet of hier de i-grond wel heel breed is toegepast (uit andere woo-verzoeken over dit soort opdrachten zijn alleen delen zwartgelakt, en niet integraal). Ik heb in dat telefoongesprek aangegeven er even over na te denken, vervolgens heb ik met een aantal vrienden en collegae overlegd die ook ervaring hebben met de woo, waarna ik uiteindelijk twee dagen later heb besloten om met het voorstel in te stemmen.</p>
<blockquote>
<p>Het idee van de functie voor vibe/AI developer is om te onderzoeken of versnelling mogelijk is bij het ontwikkelen van OSINT functionaliteiten met behulp van lokale AI/LLM-modellen.</p>
</blockquote>
<p>Op 1 april ontving ik de inhoudelijke reactie van het Ministerie van Defensie. Het idee van de functie voor "vibe/AI developer" is om te "onderzoeken of versnelling mogelijk is bij het ontwikkelen van OSINT functionaliteiten met behulp van lokale AI/LLM-modellen". Dat klinkt, mijns inziens, heel redelijk. OSINT staat hier voor Open Source INTelligence, en ik kan me goed voorstellen dat snel inspelen op nieuwe ontwikkelingen daar van belang is. Sowieso denk ik dat onderzoeken of het meerwaarde kan bieden geen kwaad kan - zelfs als het geen meerwaarde biedt is dat óók een waardevolle uitkomst. En daarnaast gaat het expliciet over lokale taalmodellen, <em>on-premise</em> (dus op eigen servers en niet in de cloud).</p>
<p>Er zit een aantal schakels in de keten tussen de behoeftevraag ("we hebben de behoefte aan een persoon die deze taken uit kan voeren") en de uiteindelijke opdrachttekst. Het begint met het invullen van een formulier - ik heb dit formulier uiteraard niet, maar op basis van <a target="_blank" href="https://www.rijksoverheid.nl/binaries/rijksoverheid/documenten/woo-besluiten/2023/02/01/besluit-op-woo-verzoek-over-search-and-rescue-helicopter-capacity-en-air-reconnaissance-capacity/bijlagen+2+bij+Woo-verzoek++Search-and-Rescue+Helicopter+Capacity+en+Air+Reconnaissance+Capacity.pdf#page=89">een ander woo-verzoek</a> (pagina 89) kan ik een beeld vormen van hoe het behoeftestellingsformulier eruit ziet. Duidelijk het uitvoerigste onderdeel is punt 18, de behoeftestelling, waarin uitgeweid wordt welke mensen, werkzaamheden, vaardigheden, etc. gezocht worden.</p>
<p>De behoeftestelling is een tekst van mogelijk meerdere pagina's lang. Daarin staan ook een heel aantal dingen die voor de vacature niet relevant zijn. De inhuurdesk maakt op basis van die behoeftestelling een opdrachtomschrijving, functieprofiel en takenpakket, en stuurt het vervolgens door naar de marktpartijen (brokers). Die publiceren de tekst op hun beurt, en dat is waar ik de tekst in eerste instantie ook gelezen heb. (O&amp;P Rijk heeft een vrij duidelijk <a target="_blank" href="https://www.oprijk.nl/arbeidsmarkt/inhuur-van-extern-personeel">stappenplan</a> online staan)</p>
<p>In dit geval lijkt het erop dat een kleine sneeuwbal aan het begin van de keten is gaan rollen en groeien, waardoor iets als "we zoeken iemand met ervaring met vibe coden" (waarmee bedoeld werd "gebruik kan maken van lokale kunstmatige intelligentie en grote taalmodellen voor het programmeren") in de behoeftestelling uiteindelijk via de interne inhuurdesk en brokers geworden is tot "we zoeken een senior vibe coder", terwijl dat niet zo bedoeld was.</p>
<p>Dat dit soort dingen kunnen gebeuren herken ik ook bij mijn eigen onderzoek bij de Universiteit Twente, waar "er is bij Nederlandse gemeenten nog ruimte voor verbetering bij het reageren op beveiligingsproblemen" uiteindelijk via de persvoorlichter en daarna de media geworden is tot <a target="_blank" href="https://nos.nl/artikel/2492689-hackers-veel-gemeenten-reageren-niet-adequaat-op-veiligheidslekken">"Veel gemeenten reageren niet adequaat op veiligheidslekken"</a>. Het grote verschil is dat ik uiteraard geen controle heb over de laatste schakel, namelijk de media.</p>
<p>Dat dit op deze manier mis is gegaan is ook wat ik verwacht had. Er was echter ook een kans dat dit een geval van iemand die het woord "vibe coder" heeft gehoord en toen dacht "dat moeten we ook hebben", zonder van de hoed en de rand te weten wat een vibe coder nou eigenlijk is, vergelijkbaar met hoe op een gegeven moment alles een "app" moest hebben (zonder dat duidelijk was wat een app zou toevoegen - de app als doel en niet als middel tot een doel). Gelukkig is dit niet het geval. Ik zou dat ook niet zo bij Defensie vinden passen, maar helemaal uitsluiten deed ik het niet.</p>
<p>Toch vind ik een kritische noot wel op zijn plaats. De opdrachttekst was voor een "senior vibe coder". Ergens in deze keten lijkt me een terugkoppeling naar de afdeling die de behoeftevraag voor een externe kracht heeft opgesteld wel gepast. Anders valt niet te controleren dat de tekst nog steeds aansluit bij wat ze zoeken. Ik ga er niet vanuit dat de inhuurdesk de expertise heeft om dat te kunnen beoordelen. Uit eigen beweging heeft Defensie aangegeven in de toekomst hier beter op te letten, en daar ben ik blij om. Immers, naast dat Defensie de juiste mensen wil werven, wil ook ik dat Defensie de beste mensen vindt - de huidige situatie in de wereld laat duidelijk zien hoe fragiel vrijheid, democratie, en internationaal recht is, en hoewel ik hoop dat het niet nodig zal zijn, ben ik toch blij dat Defensie er is om die vrijheid te beschermen. Daarvoor wil ik mensen met kennis en kunde bij Defensie.</p>
<p>Het probleem met de tekst zoals die gepubliceerd is, is dat er een reëel risico bestaat dat goede kandidaten deze opdracht links hebben laten liggen vanwege de tekst, terwijl mensen die allicht minder geschikt zijn (lees: échte vibe coders, die in de praktijk al voor genoeg <a target="_blank" href="https://escape.tech/blog/methodology-how-we-discovered-vulnerabilities-apps-built-with-vibe-coding/">beveiligingsproblemen</a> hebben gezorgd) juist meer aangetrokken zijn door deze tekst. Kunstmatige intelligentie is niet direct mijn vakgebied, maar als ik een "vibe coder" genoemd zou worden zou ik me toch enigszins beledigd voelen. Ik hoop dat die eerste groep de daadwerkelijke behoeftevraag uit de tekst die online stond heeft kunnen extraheren - reageren kon tot eind februari (en nu dus niet meer). Ik heb er alle vertrouwen in dat het Ministerie van Defensie de geschikte mensen kan uitsorteren.</p>
<p>En daarmee is het mysterie van waarom het Ministerie van Defensie op zoek was naar een senior vibe coder opgelost - ze zochten iemand die kan onderzoeken of lokale grote taalmodellen het ontwikkelen van OSINT-modellen kan versnellen, en dat is uiteindelijk door verschillende schakels in de keten tot "senior vibe coder" omgevormd en vervolgens gepubliceerd.</p>
<p>Dit had net zo goed elders kunnen gebeuren. Vandaar een kleine oproep aan alle lezers (vooral binnen de Rijksoverheid): lees eens de vacatures van je organisatie door en stel jezelf de vraag of de tekst de mensen aantrekt die je graag binnen de organisatie zou willen zien. En zo niet, probeer dat terug te koppelen - het heeft immers nogal wat invloed op je toekomstige collegae.</p>
<p>Tot slot wil ik mijn dank uitspreken aan Defensie. Alle communicatie vanuit het Ministerie van Defensie is ontzettend respectvol en plezierig is verlopen. Het uiteindelijke antwoord was uitgebreid en inhoudelijk. Daar wil ik mijn lof voor uitspreken, want dat is geen vanzelfsprekendheid.</p>
]]></description>
			<link>https://blog.koenvh.nl/waarom-het-ministerie-van-defensie-een-vibe-coder-zocht</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/waarom-het-ministerie-van-defensie-een-vibe-coder-zocht</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Tue, 07 Apr 2026 10:01:32 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Weird clocks and historic time]]></title>
			<description><![CDATA[<p>I've always liked giving a slight twist to everyday objects, and clocks are no exception. Seeing the time is so ubiquitous that it's hardly something you ever think about. This post is about two interesting clocks I made. Admittedly they're more art pieces and I doubt you'd want to use them for your day-to-day life.</p>
<p>The first one shows some (interesting) history facts. Time (in most of the world) is expressed as hours and minutes, generally using 24 hours, like 17:52. Years are generally expressed as a number from 1 and 2026 (at the moment of writing). I was wondering: what if I took the time, e.g. 17:52, and turned it into the year 1752, and would then display an event that happened in 1752?</p>
<iframe src="https://mastodon.nl/@koenvh/116059296238307733/embed" class="mastodon-embed" style="width:100%;max-width:400px;height:820px;max-height:100%;border:1px solid gray;border-radius:15px" height="500px"></iframe>

<p>I know the US, UK, and some other places have a tendency to use AM and PM. AM and PM is a strange system, that, as someone who likes logic, is really annoying to implement. Ever noticed how it starts at 12 AM, then goes to 1 AM up to 11 AM, and then switches to 12 PM? Ironically in this case AM and PM would make this clock easier. The reason is that the years up to 1259 have already past, but everything after 2026 is still the future. This causes my watch to show no facts for over three hours per day, which is a bit of a shame. By the way, the years start at 1, not 0, hence the start of the new millennium was technically 2001 and not 2000, which means I have no facts for 0:00 either. With AM and PM that time does not exist, as it would be 12:00 AM.</p>
<p>Anyway, it's a shame to ignore history past the Middle Ages, so I still went with 24 hours. To get the events, I used Wikipedia's year overview pages, which have a nice short summary for the main events for each year. Thank you Wikipedia volunteers! Parsing that data is slightly annoying, but eventually I had all the data I needed to display some facts for every year. Fixing the occasional errors was simple enough. I used the Dutch Wikipedia - the format for every country is different (and dependent on who maintains it) - so it would take some effort to adopt this to other languages. I also still don't quite know what to do after 20:26. I contemplated using data from BC, but in the end decided against it.</p>
<p>As an aside: Japan has the interesting system where you can go past 24 hours, e.g. 25:30. These times you would be able to find in e.g. a TV guide, meaning it's at 1:30 AM the following day. I think that's quite nifty, because internally a lot of people treat today as "before I go to bed" and tomorrow as "when I wake up".</p>
<p>I made this before I had a smartwatch (or well, a functioning one. I have a first generation Pebble with a broken screen and an ASUS ZenWatch 2 with a battery life now measured in minutes). Initially I wanted to turn this into a wall clock using an e-ink display, but doing that with a font size that is actually useful turned out to be quite tricky. I did turn it into a <a target="_blank" href="https://koenvh.nl/playground/historictime.php">web page</a>. I had written off the idea of using a smartwatch because in my mind they were still quite expensive, but it turns out a second hand Samsung Galaxy Watch4 (which is the one shown above) is actually quite cheap and still supported. I'm quite impressed by that.</p>
<p>The second idea for a clock came from something that has been a quirk of mine for quite some time: I have the tendency to say "good morning" as if it were "good day". I can say "good morning" regardless of the time of day (although I'm now aware of that, so it tends to be "good ... ... ... ... afternoon", during which I try to figure out what time it actually is). There are many ways to solve that, like picking a different greeting or remembering whether it's morning, afternoon, or evening. Those are all sensible. What I could also do is make a watch that always shows 11 AM, so it's always good morning! Every hour the time zone changes to where it is, at that moment, 11 AM in the world. I will freely admit that this is not the most straight-forward solution to this problem, but it is definitely a solution:</p>
<iframe src="https://mastodon.nl/@koenvh/116233325333833474/embed" class="mastodon-embed" style="width:100%;max-width:400px;height:800px;max-height:100%;border:1px solid gray;border-radius:15px" height="500px"></iframe>

<p>I'm quite pleased with how they turned out. As always I posted them online, and definitely the second one gained quite a bit of popularity. I think it not being Dutch helps with that too. I have been using it for a couple of days, and even though I know how it works, I still get confused when I see that it is 11 AM.</p>
<p>Wear OS's format for making watch faces is really limited. For the historic events I barely managed to make it work using a very, very long chained ternary expression that just checks what hour and minute it is. Due to the complexity of time zones, daylight savings time, etc., that would not work for this one. In the end I caved and pre-generated all the time zones for the next 25 years. Hopefully that is enough. They are random in case there is more than one matching time zone, so the time zone for 15 UTC today might be different from 15 UTC tomorrow. I did filter out all the time zones that are not a full hour offset from UTC, like India and Newfoundland - having to work with a 30 minute offset would make this even more cumbersome.</p>
<p>The fun side-effect of these projects is that I have a working smartwatch again. My first smartwatch was the original Pebble, which I loved. Time, date, weather, location, sunrise and sunset, all on the main (and very busy) screen. I still think that due to all the limitations of the original Pebble that it was, ironically, the best user interface design for a smartwatch. Touch screens on smartwatches don't really work well, they feel fiddly and unintuitive. Wear OS handles too much like a phone, which it is not. I find myself swiping through an overfilled app drawer with things I cannot remove to find the one thing you need, at which point I often think "I might as well get my phone". Also, I needed to make sure my watch face was not using too many active pixels to save battery life - all things I did not need to take into account with my Pebble which easily lasted a day.</p>
<p><img loading="lazy" src="posts/weird-clocks-and-historic-time/5097ee18-4a92-48dd-ab89-7fe26cecfda2.jpg" class="image--center mx-auto" /></p>
<p>There is however one thing that is quite cool with my new smartwatch: Google Assistant actually works decently now. I can talk to my watch like Inspector Gadget and it will actually do things. <em>Go go gadget turn the lights off</em>. I must admit I rarely use this functionality, though younger me would have been ecstatic.</p>
<p>Do I actually use these two watch faces in real life? Yes, though mostly as a gimmick for showing others. For when I actually want a watch to know the time I mostly use my old watch, which is a Casio DBC-32-1A. I only really use it for the time and date, but of course I bought it because it has a numpad. Teachers used to say "you aren't always going to have a calculator in your pocket", which was the reason I bought this watch, because it is also a calculator. Unlike all the smartwatches, this one has been going strong for well over 10 years now (the battery actually lasts 10 years like it claims!). It technically has a phone book and some other gizmos, but much like all the settings on a toaster you're never actually going to use them.</p>
<p>Anyway, that is all I have <em>time</em> for (pun intended). I made two clocks that hopefully make you go "huh, that's interesting". In case you are curious, both watch faces are available on GitHub: <a target="_blank" href="https://github.com/Koenvh1/HistorischHorloge">https://github.com/Koenvh1/HistorischHorloge</a> and <a target="_blank" href="https://github.com/Koenvh1/Its11AM">https://github.com/Koenvh1/Its11AM</a>. I have tried uploading the historic time one to Google Play, but my submission keeps getting rejected, and frankly the entire process is such a faff that I can't be bothered, so I'm afraid you will have to "compile" it yourself (it's not really compiling since the watch face format is effectively just an XML file, which makes this entire process even more absurd). The "It's always 11 AM" technically does not adhere to the Wear OS guidelines because "it does not clearly display the time". They're not really wrong about that.</p>
<p>Feel free to steal this idea and make a Pebble version (and let me know when you do), or run it on your own watch.</p>
]]></description>
			<link>https://blog.koenvh.nl/weird-clocks-and-historic-time</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/weird-clocks-and-historic-time</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Tue, 24 Mar 2026 17:54:58 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Wat is er met alle blockchainpilots gebeurd?]]></title>
			<description><![CDATA[<p>Zo'n tien jaar geleden was de blockchain een enorme rage. In talkshows werd gepraat over alle problemen die nu opgelost konden worden met de blockchain. In de krant en op TV werd geprobeerd om de werking van de blockchain begrijpelijk uit te leggen, zoals bij <a target="_blank" href="https://eenvandaag.avrotros.nl/artikelen/dit-moet-je-weten-over-blockchain-109201">EenVandaag</a> en <a target="_blank" href="https://nos.nl/op3/artikel/2200299-dit-is-waarom-ook-jij-over-de-blockchain-wil-leren">NOS op 3</a>. Veel publieke organisaties zijn in die tijd een blockchainpilot gestart. De gemeente Zuidhorn kreeg destijds zelfs <a target="_blank" href="https://www.computable.nl/2018/03/30/blockchain-kindpakket-zuidhorn-wint-prijs/">een prijs</a> voor <em>Meest vernieuwende digitale dienstverlening</em>, vanwege hun "kindpakket in de blockchain". De blockchain maakt dingen sneller, efficiënter, betrouwbaarder, transparanter; of althans, zo werd dat destijds gepresenteerd. Een goed voorbeeld van dat sentiment is te zien in de introvideo van een blockchain hackathon in Groningen genaamd "Blockchaingers" in 2018:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=z7loyODYSNQ">https://www.youtube.com/watch?v=z7loyODYSNQ</a></div>
<p> </p>
<p>Dat was toen. Ondertussen is het 2026 en is de blockchain in de vergetelheid geraakt. Toch is het mijns inziens interessant om te weten:</p>
<ol>
<li><p>Hoe staat het nu eigenlijk met alle blockchainprojecten die destijds gestart zijn?</p>
</li>
<li><p>Zijn er nog blockchaininitiatieven die nog steeds draaien, of zijn ze allemaal in een spreekwoordelijke lade beland?</p>
</li>
<li><p>In veel persberichten stond dat de pilot na een aantal jaar geëvalueerd zou worden - is dat ook gebeurd, en wat was daarvan de conclusie?</p>
</li>
</ol>
<p>Mijn vermoeden is dat deze initiatieven grotendeels in de lade zijn beland en er niets meer mee gedaan wordt. Maar dat is enkel een vermoeden, dus vandaar dat ik een onderzoek ben gestart om te kijken wat er écht mee gebeurd is.</p>
<p>Ik heb bij 32 van die publieke organisaties nu contact opgenomen hoe het nu staat met hun blockchaininitiatief, en gevraagd of er een evaluatie van is (en of ik die kan ontvangen). Hieronder een tabel met de organisaties en mijn héle beknopte samenvatting van waarvoor ze de blockchain ingezet hebben:</p>
<table><colgroup><col></col><col></col></colgroup><tbody><tr><td><p><strong>Organisatie</strong></p></td><td><p><strong>Blockchaininitiatief</strong></p></td></tr><tr><td><p>Gemeente Amsterdam</p></td><td><p>PGB-proces</p></td></tr><tr><td><p>Gemeente Arnhem + 7 andere gemeenten</p></td><td><p>GelrePas</p></td></tr><tr><td><p>Gemeente Barneveld</p></td><td><p>Duurzaamheidslening</p></td></tr><tr><td><p>Belastingdienst</p></td><td><p>Herverdelen geld inkomstenbelasting</p></td></tr><tr><td><p>Gemeente Breda</p></td><td><p>BredaPas</p></td></tr><tr><td><p>Gemeente Bronckhorst</p></td><td><p>Vereenvoudigen administratieve last zorg</p></td></tr><tr><td><p>Ministerie van BZK</p></td><td><p>Stemmen in de blockchain</p></td></tr><tr><td><p>Ministerie van BZK, RvIG e.a.</p></td><td><p>Digitale identiteit</p></td></tr><tr><td><p>CAK</p></td><td><p>Administratie gesubsidieerde gezondheidszorg</p></td></tr><tr><td><p>Ministerie van Defensie</p></td><td><p>Export Control (EC) data</p></td></tr><tr><td><p>Gemeente Den Haag</p></td><td><p>Subsidieproces</p></td></tr><tr><td><p>Gemeente Den Haag, CJIB, e.a.</p></td><td><p>De Rode Knop</p></td></tr><tr><td><p>Gemeente Deventer</p></td><td><p>Toetsen omgevingsvergunning aan bestemmingsplan</p></td></tr><tr><td><p>GR Drechtsteden</p></td><td><p>Gehandicaptenparkeerkaart; interne facturatie</p></td></tr><tr><td><p>Provincie Drenthe</p></td><td><p>Afvalmanagement</p></td></tr><tr><td><p>DUO</p></td><td><p>Educatiedossier</p></td></tr><tr><td><p>Gemeente Eindhoven</p></td><td><p>Vereenvoudigen van de notariële akte</p></td></tr><tr><td><p>Gemeente Emmen</p></td><td><p>EnergyKnip</p></td></tr><tr><td><p>Ministerie van Financiën</p></td><td><p>Financiering schoolgebouwen</p></td></tr><tr><td><p>Gemeente Groningen + GKB</p></td><td><p>Inzicht schulden; digitaal tellen stemmen; stadspas</p></td></tr><tr><td><p>ILT</p></td><td><p>Transport afval</p></td></tr><tr><td><p>Ministerie van J&amp;V</p></td><td><p>HALT-straffen; delen informatie strafzaken</p></td></tr><tr><td><p>Kadaster</p></td><td><p>Registeren schepen</p></td></tr><tr><td><p>Gemeente Medemblik</p></td><td><p>Sloopvergunningen</p></td></tr><tr><td><p>Provincie Noord-Brabant</p></td><td><p>Subsidieverstrekking</p></td></tr><tr><td><p>Raad voor Rechtsbijstand</p></td><td><p>Toekennen juridische bijstand</p></td></tr><tr><td><p>Rijkswaterstaat</p></td><td><p>Strooizout</p></td></tr><tr><td><p>Gemeente Schiedam</p></td><td><p>Gehandicapten parkeerkaart</p></td></tr><tr><td><p>Gemeente Stichtse Vecht</p></td><td><p>ZorgCoin</p></td></tr><tr><td><p>Gemeente Utrecht</p></td><td><p>Afvalregistratie</p></td></tr><tr><td><p>Zorginstituut Nederland</p></td><td><p>Kraamzorg</p></td></tr><tr><td><p>Provincie Zuid-Holland</p></td><td><p>Handel energie</p></td></tr></tbody></table>

<p>Er zijn waarschijnlijk nog veel meer initiatieven die uit dit overzicht ontbreken. Mocht je op de hoogte zijn van nog een blockchainpilot van een Nederlandse publieke organisatie† die in deze lijst ontbreekt, laat het me dan weten (bij voorkeur met bron) - dat mag via <a target="_blank" href="https://koenvh.nl/nl/contact">de e-mail</a>, maar ook via bijvoorbeeld <a target="_blank" href="https://mastodon.nl/@koenvh">Mastodon</a> of <a target="_blank" href="https://www.linkedin.com/in/koen-van-hove/">LinkedIn</a>.</p>
<p>Het kan wel even duren voordat de antwoorden binnenkomen. Zodra ik meer weet zal ik het uiteraard laten weten.</p>
<p>† Hoewel ik deze vragen in eerste instantie als informatieverzoek stuur, is het wel zo handig als de organisatie onder de Wet Open Overheid (WOO) valt. Of een organisatie daaronder valt is <a target="_blank" href="https://organisaties.overheid.nl/woo">hier</a> te vinden.</p>
]]></description>
			<link>https://blog.koenvh.nl/wat-is-er-met-alle-blockchainpilots-gebeurd</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/wat-is-er-met-alle-blockchainpilots-gebeurd</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 13 Mar 2026 11:23:21 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Vibe coding bij het Ministerie van Defensie]]></title>
			<description><![CDATA[<p>Recentelijk kwam ik erachter dat het Ministerie van Defensie op zoek is naar "een gedreven Vibe-Coder en AI-developer specialist die als onmisbare schakel gaat fungeren binnen een OSINT Dev-Ops-team gericht op de ontwikkeling van een schaalbaar, veilig en niet-herleidbaar OSINT-platform".</p>
<p>Ik las het via de Mastodon van Bert Hubert, en mijn wenkbrauwen begonnen te fronsen (en gezien de reacties was ik niet de enige), zeker omdat "security by design" en anonimiteit in de tekst expliciet genoemd worden. Ik kan aanraden <a target="_blank" href="https://web.archive.org/web/20260225162917/https://depubliekepartner.nl/senior-vibe-ai-developer/">de hele tekst te lezen</a>. Sowieso weet ik niet zo goed wat ik me bij een "senior vibe coder" moet voorstellen.</p>
<p>Voor context: "vibecoden" houdt in dat je kunstmatige intelligentie een opdracht geeft en code laat schrijven in plaats van het zelf te doen. De term bestaat pas relatief kort (ongeveer een jaar) vanwege de ontwikkelingen in KI. Het is een stap verder dan KI gebruiken om te helpen met programmeren, met vibecoding neemt KI de leiding. Het is een term die vooral gebruikt wordt om mensen zonder kennis van programmeren die wel een "app" maken te beschrijven - ze weten niet echt wat de code doet, maar kunnen a.d.h.v. instructies aan de KI het resultaat bijschaven.</p>
<p>"Secure by design" is een principe dat staat voor het feit dat beveiliging meegenomen wordt in elke stap van het ontwerp van de software. Denk aan een slot geïntegreerd in een fiets ten opzichte van een hangslot dat later is toegevoegd.</p>
<p>Zodoende is de term "senior vibe coder" eigenlijk al apart. Dat combineren met security by design e.d. maakt het helemaal lastig - een "vibecoder" kan meestal niet herkennen of een door KI aangedragen code überhaupt veilig is, laat staan daarna evalueren of de aangedragen oplossing effectief is.</p>
<p>Ik kan speculeren over wat er gebeurd is (bijvoorbeeld tekst door een recruiter herschreven waardoor de eigenlijke betekenis verloren is gegaan), maar een betere oplossing is denk ik door de documenten op te vragen zodat we kunnen zien wat er is gebeurd - misschien is er wel een hele goede verklaring voor.</p>
<p>Zodoende heb ik vandaag dit verzoek op basis van de <a target="_blank" href="https://www.rijksoverheid.nl/onderwerpen/wet-open-overheid-woo">Wet Open Overheid</a> (WOO) ingediend:</p>
<blockquote>
<p>Beste WOO-commissaris van het Ministerie van Defensie,</p>
<p>Recentelijk kwam ik op verschillende plekken een detacheringsopdracht tegen van het Ministerie van Defensie voor een “Senior Vibe/AI developer” [1, 2, 3, 4], waar, ik citeer: “Je bent een gedreven Vibe-Coder en AI-developer specialist die als onmisbare schakel gaat fungeren binnen een OSINT Dev-Ops-team gericht op de ontwikkeling van een schaalbaar, veilig en niet-herleidbaar OSINT-platform.”.</p>
<p>Ik ben hier niet om op deze opdracht te reageren, maar om erachter te komen hoe deze opdrachttekst tot stand is gekomen, daar deze bij mij en anderen tot verbazing heeft geleid [5].</p>
<p>Zodoende zou ik graag de documenten/communicatie die direct betrekking hebben tot de totstandkoming en publicatie van deze opdrachttekst ontvangen.</p>
<p>Het gaat hier specifiek om bovengenoemde opdracht, en niet om de documenten over het opstellen van opdrachten in het algemeen.</p>
<p>Mochten er vragen over dit verzoek zijn, neem dan gerust contact op. Ik help graag mee om de reikwijdte zo specifiek mogelijk te houden en onnodig werk te voorkomen.</p>
<p>Alvast bedankt.</p>
<p>Met vriendelijke groeten,</p>
<p>Koen van Hove</p>
<p>[1] <a target="_blank" href="https://depubliekepartner.nl/senior-vibe-ai-developer/">https://depubliekepartner.nl/senior-vibe-ai-developer/</a></p>
<p>[2] <a target="_blank" href="https://www.freep.nl/opdracht/senior-vibeai-developer">https://www.freep.nl/opdracht/senior-vibeai-developer</a></p>
<p>[3] <a target="_blank" href="https://striive.com/en/assignments?query=senior%20vibe">https://striive.com/en/assignments?query=senior%20vibe</a></p>
<p>[4] <a target="_blank" href="https://www.opdrachtinformatiemanagement.nl/inhuuropdracht/ministerie-van-defensie/senior-vibe-ai-developer/41DBE0EB-C502-42CD-B3C2-BBB9CF5C0269">https://www.opdrachtinformatiemanagement.nl/inhuuropdracht/ministerie-van-defensie/senior-vibe-ai-developer/41DBE0EB-C502-42CD-B3C2-BBB9CF5C0269</a></p>
<p>[5] <a target="_blank" href="https://mastodon.nl/@bert_hubert/116110333023987256">https://mastodon.nl/@bert_hubert/116110333023987256</a></p>
</blockquote>
<p>Het duurt meestal wel even voordat er antwoord komt op zo'n verzoek, dus met een beetje geluk is er over een paar maanden een antwoord dat niet helemaal zwartgelakt is.</p>
<p>Uiteraard viel me na het verzenden op dat ik per ongeluk "WOO-commissaris" in de aanhef heb gebruikt in plaats van WOO-functionaris. Oeps. De ontvangstbevestiging is aangekomen, en nu komt het lange wachten.</p>
]]></description>
			<link>https://blog.koenvh.nl/vibe-coding-bij-het-ministerie-van-defensie</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/vibe-coding-bij-het-ministerie-van-defensie</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Wed, 25 Feb 2026 16:31:27 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[DVD menus and Tamagotchis]]></title>
			<description><![CDATA[<p>For a long time I’ve been fascinated by DVD menus. VHS tapes and streaming video just start when you insert them (as long as they last person remembered to rewind them), which is functional but boring. DVD menus always felt like tiny works of art. Plus I was curious just how much you could do with them. In the end I made <a target="_blank" href="https://github.com/Koenvh1/DVDgotchi">DVDgotchi</a>:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/vGi-anqQgrg">https://youtu.be/vGi-anqQgrg</a></div>
<p> </p>
<p>Here’s where it started: I try to do fun things with and around DNS. Last year I made <a target="_blank" href="https://blog.koenvh.nl/april-fools-2025-phone-books-and-adverts">a physical book with 600 pages of domain names and IP addresses</a>, <a target="_blank" href="https://blog.koenvh.nl/dialns-a-dns-resolver-over-the-phone">a phone number you can call to resolve DNS A records</a>, and <a target="_blank" href="https://blog.koenvh.nl/what-you-can-but-shouldnt-do-with-rsync">a DNS resolver over rsync</a>. My initial plan was to do something similar with DVDs, and call it DvdNS. Sure, a DVD player cannot connect to the internet, but I could do that locally and just write the known data to the DVD.</p>
<p>First thing I made was a keyboard. That’s where I ran into the first issue: there is a maximum number of displayable buttons in one single menu, and the number row would no longer fit. Storing text input is surprisingly simple. DVDs come with 16 general purpose 16-bit registers, g0 till g15. You can make a button press store that character (e.g. its ASCII code) into one of the registers, and then move a pointer to the next, etc. etc. You can do it more efficiently and use less of the fairly precious registers.</p>
<p>There is however one thing I ran into: I had no real way to display your input. You can branch on the registers, but there is no real way to display the contents dynamically. The only way you can work around that is consider all the possible combinations of variables, and render the outcome for them as some sort of video or menu. As you can imagine, that does not scale well. DVDs also have a limit to the amount of menus and titles, which you will run into very quickly if you try to actually implement a keyboard. You could partially work around this using subpictures, but guess what buttons are? Every possible subpicture you want to display goes in the place of one button, and you could only use ~32 of them on one screen anyway.</p>
<p><img loading="lazy" src="posts/dvd-menus-and-tamagotchis/fef7def5-9273-4a3d-acd9-79560f57400a.png" class="image--center mx-auto" /></p>
<p>I could decide not to show your current input, but then once you press “enter” you want to get some sort of output. In the case of DvdNS that would have been the records for that domain. I considered encoding all possible outcomes as a frame. A standard DVD can hold about two hours of video. ~2 hours × 25 frames per second ≈ 180000 possible outcomes. A lot, but still not <em>that</em> many, especially if you also have to account for all the cases where there is no result. Maybe I will revisit that idea at some point, but for now it’s a dead end.</p>
<p>Back to the drawing board. My second thought was that a <a target="_blank" href="https://en.wikipedia.org/wiki/LucasArts_adventure_games">LucasArts-style adventure game</a> would probably work quite well for this. Limited options, limited items to interact with. Either you have the thing(s) and you can solve the puzzle, or you need more things to solve the puzzle. I think this is still technically feasible, I just don’t have the pixel art skills to pull it off.</p>
<p>I needed another idea. In my exploration of the DVD standard there was one thing that something stood out: the random function. You can generate a (psuedo) random number and use it to jump from. For some reason my mind went to Tamagotchi, which is how I ended up making the DVDgotchi. The idea was simple: some buttons to increase some values, and then randomly decreasing those values over time.</p>
<p>I started looking for the original Tamagotchi animations, but they are surprisingly hard to find. I decided to use Microsoft Rover instead, which was the dog that helped you search in Windows XP. I had access to modern technology after all, like colour, and a nearly HD amount of pixels! Luckily those frames were already ported previously for a project called ClippyJS, which is Clippy and friends in Javascript.</p>
<p><img loading="lazy" src="posts/dvd-menus-and-tamagotchis/8b6ccf0c-25f0-4cc5-a999-02194e1da067.avif" class="image--center mx-auto" /></p>
<p>I only added petting and feeding. I could have added another bar theoretically, but two felt right. I had to do some trickery to get the DVD to compile, because my initial plan of cycling through all animations randomly did not work. Each animation existed as nine video files, for the nine possible states. They are ordered, so I could just generate a random number for the amount of animations I had, and then calculate the offset to make sure you have the version with the right number of food and happiness bars. This is what that looks like:</p>
<pre><code class="lang-c">g2=random(<span class="hljs-number">25</span>); <span class="hljs-comment">// There are a total of twentyfive animations</span>
g3=((g2<span class="hljs-number">-1</span>)*<span class="hljs-number">9</span>)+((g4<span class="hljs-number">-1</span>)*<span class="hljs-number">3</span>)+(g5<span class="hljs-number">-1</span>)+<span class="hljs-number">1</span>;  <span class="hljs-comment">// g4 is food level (1-3), g5 is the happiness level (1-3)</span>
</code></pre>
<p>So far so decent. Sure, everything starts counting at 1 (which I initially forgot), but that’s not the annoying part. You can jump to another menu using <code>jump vmgm menu 4;</code>, but you can’t jump to menu <code>g3 + 2</code> (or <code>g3</code> for that matter) . This means you end up with something like this:</p>
<pre><code class="lang-c"><span class="hljs-keyword">if</span> (g3==<span class="hljs-number">1</span>) { jump vmgm menu <span class="hljs-number">3</span>; }
<span class="hljs-keyword">if</span> (g3==<span class="hljs-number">2</span>) { jump vmgm menu <span class="hljs-number">4</span>; }
<span class="hljs-keyword">if</span> (g3==<span class="hljs-number">3</span>) { jump vmgm menu <span class="hljs-number">5</span>; }
<span class="hljs-keyword">if</span> (g3==<span class="hljs-number">4</span>) { jump vmgm menu <span class="hljs-number">6</span>; }
<span class="hljs-keyword">if</span> (g3==<span class="hljs-number">5</span>) { jump vmgm menu <span class="hljs-number">7</span>; }
<span class="hljs-keyword">if</span> (g3==<span class="hljs-number">6</span>) { jump vmgm menu <span class="hljs-number">8</span>; }
[...]
</code></pre>
<p>There is a maximum number of commands you are allowed to have per menu (although you can jump to other menus to creatively work around that), and this really increases that value. In the end I decided to combine some of the animations so that I ended up with a more manageable set. The errors for this would only appear after compiling, and compiling also involved encoding the video. Basically, it would take several minutes before you can find out whether it’d actually work. I considered trying to max it out, do some interesting trickery, but in the end decided that it was not worth the effort.</p>
<p><img loading="lazy" src="posts/dvd-menus-and-tamagotchis/a1741fbf-cdc7-4876-8a1b-2856a6a9f632.jpeg" class="image--center mx-auto" /></p>
<p>Of course I could not create a DVD without actually burning it onto a DVD. Everyone has a stack of writeable DVDs somewhere… apart from me. Turns out buying those is not as easy as it once was. In the end I had to order them online. Back in the day I had a LightScribe DVD writer, but those DVDs are now very costly, so I opted for DVD stickers instead. I am quite pleased with the result, though I would have liked a more time period correct background picture.</p>
<p>Now I had a DVD… but no DVD player. Again a sign that this project might have been easier 10 or 15 years ago. Luckily my parents had a DVD player (or actually blu-ray player) left over. I was slightly amused to learn that it apparently runs Java:</p>
<p><img loading="lazy" src="posts/dvd-menus-and-tamagotchis/3c98baeb-2844-4a97-9ac4-aabdd3c0c3ed.jpeg" class="image--center mx-auto" /></p>
<p>Even though calling this a “game” is quite a stretch, it’s admittedly still quite fun to “play” something on a DVD player that is not just a video. If your hands are itching to give this a try, you can! <a target="_blank" href="https://github.com/Koenvh1/DVDgotchi">https://github.com/Koenvh1/DVDgotchi</a> has the source code and the ISO you can burn on your own DVD. You might have to convert it to NTSC if your DVD player is region locked and you’re outside the PAL region.</p>
<p>If you know more about how DVDs work, do let me know! I found all information based on forums that are still online and webpages other people made, but sadly half of the links are now dead (much like most Tamagotchis). Maybe I overlooked something that suddenly opens an entire new world of possibilities (like the <a target="_blank" href="https://www.youtube.com/watch?v=RWMkt6WGcL8">DVD angle feature</a>).</p>
]]></description>
			<link>https://blog.koenvh.nl/dvd-menus-and-tamagotchis</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/dvd-menus-and-tamagotchis</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sun, 18 Jan 2026 20:09:09 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[What you can (but shouldn't) do with rsync]]></title>
			<description><![CDATA[<p>rsync is quite an amazing tool to sync files from one location to another remote location, and that for nearly 30 years. Just to be clear, this article is not trying to bash rsync (or /bin/bash rsync for that matter). No, rather it’s a display of how versatile rsync really is… along with the question of whether that versatility makes it a good idea to use it for critical internet infrastructure.</p>
<p>Ever since I started working on RPKI-related things (which uses rsync as its primary URI scheme and transfer protocol) I have been intrigued by rsync. The Resource Public Key Infrastructure (RPKI) is an infrastructure to make cryptographically verifiable statements about internet resources. So think certificates that contain a block of IP addresses or an AS number, and then sign some sort of statement. RPKI is an open standard. rsync is not really an open standard, it’s a program that has its own protocol, which acts as reference implementation. It’s nearly 30-year-old C code which is not the easiest to read. There are some other (partial) implementations, including some commercial ones, but for all intents and purposes there is one rsync. rsync has multiple operating modes, one of the most common ones is that you can use rsync over SSH. RPKI does not do that — it uses rsync where one side is in daemon mode, and any client can connect to it and download from it. That mode has been my focus, and for some reason that mode is generally not implemented in other implementations. I’ll be calling them server and client, but technically they are sender and receiver, as the protocol is bidirectional, but setting up a public rsync daemon that is writeable is not a great idea :-)</p>
<p>rsync heavily relies on the assumption that neither of the two sides tries to fool the other side. That makes sense if you control both sides. In the RPKI, that is not an assumption you can make, otherwise we could just as well not do the certificate shenanigans.</p>
<p>Anyway, without further ado, let’s show what you can do with rsync. Rsync allows you to print arbitrary text (as long as your characters are not before space in the ASCII table, except carriage return and newline, which are allowed at the end of the message), e.g. via the message of the day (MOTD) or just as an “error” response. This means you can play Bad Apple on rsync:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/4z0PIWhO-pg">https://youtu.be/4z0PIWhO-pg</a></div>
<p> </p>
<p>As you can see it is quite choppy at times — that’s because I can’t clear the screen (because that’s an ASCII character before the space), and every line is its own message (as a newline can only appear at the end of the message). Even with those limitations, you can still channel your inner Neo from the Matrix:</p>
<p><img loading="lazy" src="posts/what-you-can-but-shouldnt-do-with-rsync/ead570e7-b294-432b-8775-0796b5289cee.avif" class="image--center mx-auto" /></p>
<p>The MOTD is always downloaded. You can pass <code>--no-motd</code> to tell rsync to not <em>display</em> it, but it will be downloaded. There is no size limit to your MOTD, although on the receiving end it will be stored in memory. You could send the entirety of Wikipedia as MOTD, but then the client will probably run out of memory and crash.</p>
<p>Don’t worry, the rsync client can be mean to the server too. One of the things the rsync client can send are filters, which tell the server which files to include or not to include. The server does the filtering (or not, it’s more of a “could you please” rather than “you must”) by checking all your filters for all the files. There is no maximum number of filters, you could endlessly keep sending filters as well, and they will all be stored in memory — until the server runs out of memory.</p>
<p>The server could of course ignore your filters entirely. Actually, all filters are completely optional. If the server wants, it could just return whatever files it wants, even if you tell it to e.g. not include files over 1 GB, or ending in “.mp4”, etc. etc. The server could also serve so many files your system runs out of inodes without you really being able to do something about it, as there are no options in the default client to prevent that. Though I guess you could use a filesystem without inode limit like XFS… or NTFS if you are feeling adventurous :-)</p>
<p>That is of course if you get the files in the first place. You could open a connection, leave it open, open another one, and another one… A DDoS is not necessary, with ~300 simultaneous connections you can take down pretty much any rsync server, which you could do from a Raspberry Pi. Just make sure to feed data very very slowly.</p>
<p>In summary you can make the client run out of disk space and memory, and you can make the server run out of memory and open connections. <em>But wait there’s more!</em> For a long time you could write outside the destination path provided by rsync, either by using <code>../../../etc/passwd</code> in the file name or by making a symlink to outside the path. Both of those are fixed in the latest version (v3.4.1), which is likely not the version your Linux distribution gives you, and where one of those is likely still present.</p>
<p>All in all, rsync is very versatile, for good and for worse. It’s a great tool to sync files from place A to B, though maybe not the best choice to base your critical internet infrastructure on. But that’s what was picked for RPKI, and it seems we will be stuck with it for the foreseeable time, so rather than moan about it, let’s share the joy! I felt bad for all the DNS people having to miss out on the joy of working with rsync, so I made a DNS resolver over rsync just before Christmas. Feel free to try it out <code>rsync rsync://thuis.koenvanhove.nl/A/koenvh.nl/</code>.</p>
<p><img loading="lazy" src="posts/what-you-can-but-shouldnt-do-with-rsync/ecc11064-74f1-4d86-a22b-24dddde7495b.jpeg" class="image--center mx-auto" /></p>
<p>The records appear as files, and the time is set to when the TTL will expire. Maybe in the future I will encode the flags in the file permissions. <a target="_blank" href="https://mastodon.nl/@koenvh/115734955643662892">Most of the DNS people who saw it were horrified</a>, which I take as a badge of honour :-)</p>
]]></description>
			<link>https://blog.koenvh.nl/what-you-can-but-shouldnt-do-with-rsync</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/what-you-can-but-shouldnt-do-with-rsync</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 10 Jan 2026 19:13:59 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Christmas cards!]]></title>
			<description><![CDATA[<p>For the last six year I have designed and sent Christmas cards to friends, family, and colleagues. I figured I would make a small blog post about them because some thought does go into it every year. I’m not a big Christmas person — I don’t like the music, I don’t decorate, and I like eating well any day of the year (whilst avoiding gluttony). However, Christmas does present the perfect opportunity to send a card to others to remind them that they’re awesome.</p>
<p>One of my main gripes with commercially available Christmas cards is that they all look quite dated. Picture what a Christmas card looks like. You probably think of something like this:</p>
<p><img loading="lazy" src="posts/christmas-cards/b92d3a5c-b3a6-4463-b6eb-f572305498b6.jpeg" class="image--center mx-auto" /></p>
<p>For some reason, even though it’s 2025, Christmas cards by and large look like they could be from 1975. I wanted something a bit different, more modern, so as someone with paper and typesetting as hobby, I drew the logical conclusion that I should probably make them myself. The idea was quite simple: think of what you expect a Christmas card to look like, and then do the opposite. Plus I don’t really need to sponsor Hallmark, especially as in 2019 they pulled an advert showing a lesbian couple from the Hallmark Channel after pressure from a conservative group, and somehow getting your own cards printed is cheaper than buying them in a shop. (Sadly I can’t find the physical versions of the first two cards, so I printed those again for this blog).</p>
<h1 id="heading-2019">2019</h1>
<p><img loading="lazy" src="posts/christmas-cards/674c82d5-0ba5-4773-b2f6-fae5b2faa67e.jpeg" class="image--center mx-auto" /></p>
<p>This was the first card I sent. I initially wanted to make it like a fridge or oven manual in 15 languages but that idea fell through. However, even at just black and white it stood out quite well between the other cards. Originally it was not a postcard but a foldable card. In hindsight I would have not added three languages to the front, but oh well.</p>
<h1 id="heading-2020">2020</h1>
<p><img loading="lazy" src="posts/christmas-cards/ac248467-d750-4d68-8034-2668860b85b9.jpeg" class="image--center mx-auto" /></p>
<p>The next year I wanted something very bold. I used pure RGB-blue and RGB-red for the colours. Somehow in the reprint the right side is slightly cut off. The digital version is a bit more vibrant:</p>
<p><img loading="lazy" src="posts/christmas-cards/6f885d51-8734-4ea7-ad4d-0ae483b975d2.png" class="image--center mx-auto" /></p>
<h1 id="heading-2021">2021</h1>
<p><img loading="lazy" src="posts/christmas-cards/5902ef9d-ad42-4d13-9509-e7f9a797ec0d.jpeg" class="image--center mx-auto" /></p>
<p>Back to black and white. One of the most trippy ones I have made. I do quite like this design though because it really stands out. In many cases I sent these as postcards without envelope, so even when opening the postbox it must have been quite a sight.</p>
<h1 id="heading-2022">2022</h1>
<p><img loading="lazy" src="posts/christmas-cards/f783f12b-8b9d-4501-9140-88ebc43a55fa.jpeg" class="image--center mx-auto" /></p>
<p>Which colour is not associated with Christmas? Yellow! Plus it also happens to be my favourite colour. And around this time I switched my “corporate identity” over to this style. Basically I like the Inter font and minimalist design (also because that’s what I’m good at). You can see this style on my website and blog as well.</p>
<h1 id="heading-2023">2023</h1>
<p><img loading="lazy" src="posts/christmas-cards/02558739-739e-4e0a-943b-0bcd6018d224.jpeg" class="image--center mx-auto" /></p>
<p>Coming up with a new idea every year is not easy. Eventually I came up with the idea of a Snellen chart that anyone who has been at the optician’s should recognise. Again black and white, because somehow less colour stands out more, plus I have never seen those charts in colour.</p>
<p>I also went back to foldable cards. One of the reason I had been using postcards before is because they are cheaper. Turns out a lot of printers make money by selling you overpriced envelopes (which sometimes doubles the total price). I prefer foldable cards normally because it’s a bit easier to write text in them. I also stopped preprinting the contents, and instead I would write everything by hand. That was easier for different languages (half my cards go abroad) and different messages.</p>
<h1 id="heading-2024">2024</h1>
<p><img loading="lazy" src="posts/christmas-cards/59ac4b0b-721a-4944-91cf-a027842e60e0.jpeg" class="image--center mx-auto" /></p>
<p>This was the first card without the word “Kerst” on it. It’s a Christmas tree with the “missing content” texture from the Source Engine, which to gamers of a certain age probably looks recognisable as “someone forgot to instead CS Source”. I also wrote my address on it because rather than my logo, in case someone wanted to send a card back, because I do like receiving them (<em>hint hint nudge nudge</em>).</p>
<h1 id="heading-2025">2025</h1>
<p><img loading="lazy" src="posts/christmas-cards/28af83e9-30f0-4f55-8435-c744c1bf0b78.jpeg" class="image--center mx-auto" /></p>
<p>This year I was really late with coming up with an idea I liked. So late in fact that at the moment of writing (26 December 2025) most cards have not been delivered yet, and I fair quite a few might be delivered next year. I also had no time to get them professionally printed, though luckily my HP printer and stack of ~250 g A6 cards did wonders. I think it would have looked better printed on glossy card paper. The keen eye will recognise it as a parody on the logon screen for Windows XP.</p>
<h1 id="heading-2026">2026?</h1>
<p>With a bit of luck I come up with an idea for 2026 in time. It seems like international mail has become a lot slower, taking a month to get to the USA. That means taking printing time into account the design needs to be done early November. Oh, and stamp prices keep increasing, but complaining about that makes me sound old :-) I do want to see whether I can do something more with different paper types.</p>
<p>Anyway, those are the main ones I have made. And to finish off, here is one specifically about <a target="_blank" href="https://en.wikipedia.org/wiki/Resource_Public_Key_Infrastructure">RPKI</a>. If you know the RPKI and/or computer networking you might like it. If you’re not into that field it’s too long to explain :-)</p>
<p><img loading="lazy" src="posts/christmas-cards/d1e19ef4-1621-493d-8a6a-ea76a38ad8e1.png" class="image--center mx-auto" /></p>
]]></description>
			<link>https://blog.koenvh.nl/christmas-cards</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/christmas-cards</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 26 Dec 2025 20:32:44 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Streaming on a Stream Deck]]></title>
			<description><![CDATA[<p>If you have read my blog before, then you know <a target="_blank" href="https://blog.koenvh.nl/adding-a-button-box-for-my-sim-rig">that I bought a Stream Deck a bit under a year ago</a>. You also probably know that I like doing silly things with software. My Stream Deck was an excellent candidate for this. tl;dr: I ended up turning my Stream Deck into something I can stream video to, here’s a demo and a link to the GitHub repository: <a target="_blank" href="https://github.com/Koenvh1/ScreenDeck">https://github.com/Koenvh1/ScreenDeck</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/1CKVFAwGmTI">https://youtu.be/1CKVFAwGmTI</a></div>
<p> </p>
<p>The tinkerer inside me had been interested in seeing what was possible with the Stream Deck from the moment I bought it. My version consists of fifteen 72×72 pixel LCDs that also act as buttons. I had already previously toyed around with letting multiple buttons work in unison, which worked, but now I was wondering if I could update the image often enough to make smooth video work.</p>
<p>Admittedly in hindsight it was all quite simple, but when developing for the Stream Deck it becomes quite apparent you’re not meant to make multiple buttons interact, so quite a bit of time was spent on setting up the framework to get that to work. The Stream Deck plugins work by starting a program with some parameters, and then communicating to the main application over a websocket. Every button added has its own unique ID, and if you have that ID you can use it to change things like the current image, etc. Is it really fast enough to send 15 images over a websocket, preferably at at least 24 frames per second? That’s 360 calls to change images per second. I was sceptical that it was possible, but to my joy: it was! I initially tried it with a static image that I just moved the position of.</p>
<p>Initially my plan was to turn the Stream Deck into an extra monitor that you could put whatever you wanted on. This plan fell through when I saw that it would likely involve signed drivers, and it would be a kerfuffle to set up. Additionally, the resolution is fine, but would likely be too low to show a desktop on. And scaling it would make it even worse for legibility. I quickly afterwards ended up with video. And luckily StreamLink is another Python library that does most of the heavy lifting of getting an M3U8 stream. Quite a few of the integrations do not seem to work, but the Twitch one did, and that’s all that mattered.</p>
<p>In the beginning I ended up with Pillow, a Python library for manipulating images. This worked fine for the static image, but I also needed something to get the frames from the video. Luckily Python has a lot of libraries, one of them being OpenCV (where CV stands for Computer Vision), which was absolute overkill for this project. I implemented that, rewrote the Pillow parts to OpenCV, and it worked! Again this surprised me, as I was worried I could not cut out and resize 360 calls per second. This is what that looked like:</p>
<p><img loading="lazy" src="posts/streaming-on-a-stream-deck/ae0672b4-c22b-4fad-b4f8-356e789c5dbc.avif" class="image--center mx-auto" /></p>
<p>There was only one problem with this approach: the audio was missing. Turns out OpenCV does not support audio at all, and even suggesting that you want to get the audio stream yourself is not possible. Looking back I should have checked this from the start. So let’s rewrite everything again, though this would be the last time. I went for ffpyplayer. It is another library that is great and awful at the same time. The documentation is lacking, and there is no control over the audio (it just plays once the stream is loaded), but everything works flawlessly. I can even just feed it the M3U8 stream URL. I spent quite a bit of time of trying to get the audio and video to sync up — all this time I thought the video was lagging behind the audio, turned out I was actually getting the frames faster than the audio was playing. Whoops… That was luckily a simple fix, and in hindsight it was in the ffpyplayer documentation.</p>
<p>I also added better support for closing the Stream Deck, opening other tabs, etc. And in the process I also added support for on-the-fly resizing:</p>
<p><img loading="lazy" src="posts/streaming-on-a-stream-deck/49cda9cc-18e5-4f47-bc6f-c04e712418d0.avif" class="image--center mx-auto" /></p>
<p>The StreamDeck SDK that I used was one that someone made for Python. It has a bit of a quirk though: by default it requires you to have Python installed, as it will create a virtual environment, install the dependencies, and then run itself on the fly. I did not like that system, so I rewrote it to compile the Python part with PyInstaller, which worked well after a few tries.</p>
<p>There is however one problem with that: apparently some people make malware in Python (no idea why), and then they use PyInstaller to make an executable of that. As a consequence, some antivirus programs mark <em>every</em> executable made with PyInstaller as virus. The internet provides several potential solutions, such as compiling PyInstaller yourself to get a different signature, but without luck. So I could put all of that effort in the bin, and I ended up shipping an embedded Python. A very ugly solution, but hey, it works, and that’s the important bit. I put it on the <a target="_blank" href="https://marketplace.elgato.com/product/screendeck-c185826c-5191-4a1c-924e-599acd73a625">Elgato Marketplace</a>, which, with the embedded Python version, was quickly accepted.</p>
<p>The response was quite positive, and people actually started using it. Whoops, I made it for fun, but now there are actual feature requests. Nothing too special or difficult to implement: just more generic streaming support and a way to change the volume, which was simple enough.</p>
<p>Unless more feature requests come in that’s probably how this project will stay, because debugging is a bit of a pain and I have other things that have more use. It was still fun to make though. In case you are wondering: I rarely use it myself, but the fact that I <em>can</em> use it myself is good enough.</p>
]]></description>
			<link>https://blog.koenvh.nl/streaming-on-a-stream-deck</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/streaming-on-a-stream-deck</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 13 Dec 2025 16:07:00 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Building a custom HUD for Dakar Desert Rally]]></title>
			<description><![CDATA[<p>This is a story about Dakar Desert Rally, a game I have a love-hate relationship with. It’s not the first time I write about this game, <a target="_blank" href="https://blog.koenvh.nl/how-i-made-and-printed-252-rally-pacenotes">I previously showed the printed roadbooks I made</a> for Dakar 18 (its predecessor) and Dakar Desert Rally. This time it is about the game modding I have done to get an external dashboard for Dakar Desert Rally working, because I found another niche game which is basically abandoned. As I did not find many articles online on how to do this, I figured I might as well write down why and how I did it.</p>
<iframe src="https://mastodon.nl/@koenvh/115239067683181168/embed" class="mastodon-embed" style="width:100%;max-width:400px;height:570px;max-height:100%;border:1px solid gray;border-radius:15px" height="500px"></iframe>

<p>Let’s start with why I have a love-hate relationship with Dakar Desert Rally, as it explains a lot of the things I do for it. Dakar Desert Rally is in my opinion the best rally raid <em>simulation</em> game currently available. That is not giving it a lot of praise, because there is not a lot of competition. There is Big Run from 1989, Paris-Dakar Rally from 2001, and Dakar 2 from 2003. Then there was nothing for a long time (presumably also due to the declining interest in Dakar, health and safety and all that, because looking back at 90s Dakar that was really quite something) and then Dakar 18 came out in 2018.</p>
<p>The problem with both Dakar 18 and Dakar Desert Rally is that they try to appeal to hardcore Dakar fans and more casual players, but in the process fall short for either. The navigation is too difficult for casual players, as you need to learn how to read a Dakar roadbook, and that is non-trivial. There are many initially unintuitive symbols, and most acronyms are in French (e.g. HP for “Hors Piste”, meaning “leave the track”, or E3 for “Etroit”, meaning “narrow”). The game has a built-in lexicon, but it does not hold your hand and instead throws you into the deep end. The navigator is too unreliable to fully trust them to guide you where to go. Sure, Dakar Desert Rally introduced a “sport” mode, but that had next to nothing to do with the real Dakar, and even as an arcade player it just was not very fun.</p>
<p>As a more simulator person I don’t mind the navigation part, in fact, it’s one of my favourite parts of Dakar. My complaints are that, as someone <a target="_blank" href="https://blog.koenvh.nl/adding-a-button-box-for-my-sim-rig">with a simulation set up in my living room</a>, the car handling leaves much to be desired. It feels unintuitive and unresponsive, and the force feedback is unhelpful. Also, having a career mode where simulation mode is locked by default and requires level 25 to be unlocked, and a stage only counting as completed when you finish in a certain position make sense in an arcade game, but for a simulator that feels incredibly odd. It did not help that the AI is very unreliable in performance. Every time a simulator tried to force that (e.g. rFactor, or now Assetto Corsa EVO), the community response has been negative. Lastly, the game does not support any form of telemetry, which would allow third-party apps to read things like current speed, gear, etc. from the game. Normally I use an old smartphone mounted behind my wheel to display this kind of information.</p>
<p>The fact the game appealed to neither, as well as the game being released in a very broken state (which has never been fully resolved, it is not uncommon to get a “fatal error” when loading a stage) meant it was a bit of a flop. Bad reviews, low sales. Development has since halted. I do not expect that there will be someone making a Dakar simulator title any time soon, as it is even more niche than rally simulators, and those are already a rare breed. This is why I have a love-hate relationship with Dakar Desert Rally — but rather than just grumble, I wanted to see if I could make things better.</p>
<p>Whilst it is a shame that Dakar Desert Rally is not going to be updated any more, it also is an opportunity. If I modify the game, then it won’t break in the next release, because there won’t be a next release. I have reverse engineered a lot of things over time, and things randomly breaking was definitely one of my least favourite parts, especially as it always happens at the least opportune times.</p>
<p>Dakar has a general speed limit. 170 km/h for cars, 140 km/h for trucks. Turns out the no speed limit drag race style from the 80s and 90s was too dangerous, which frankly makes sense. For context, here is a by now famous clip from 80s where Jan de Rooy in the DAF truck overtakes Ari Vatanen in the Peugeot, who was then leader of the car class. In that same year another DAF truck would crash — the navigator would not survive the incident, and the driver would end up with severe injuries. <a target="_blank" href="https://anderetijden.nl/aflevering/19/Het-DAF-drama-in-Dakar">Andere Tijden made a documentary</a> about this (in Dutch).</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/fZVyb2xJ-_w">https://youtu.be/fZVyb2xJ-_w</a></div>
<p> </p>
<p>This speed limit makes sense, and is also enforced in the game (but only in simulation mode). In real life cars nowadays tend to have an electronic limiter, but that is not available in the game. The game is also really harsh, immediately penalising you for speeding. The current speed is shown conveniently behind the rim of my wheel, meaning I can’t really see it. The text is also quite small, as is the warning.</p>
<p>Basically I was trying constantly watch what my speed was in an awkward way, only to often fail and accidentally speed (which is easily done when watching your heading, figuring out where to go next, not hitting rocks, and trying to stay below 170 km/h). It’s like going for a nice hike with a stone stuck in your shoe — it’s a small thing but it does sour the experience. If you look closely, you can see the HUD (but not the speed) underneath the rim of the wheel on the screen. Compare that to the very large number on the phone, and I think it is clear why I wanted that instead.</p>
<p><img loading="lazy" src="posts/building-a-custom-hud-for-dakar-desert-rally/1b59987b-67a2-4fbc-ae88-a9693fe6d176.jpeg" class="image--center mx-auto" /></p>
<p>I had been contemplating making something to show the speed on my dashboard for quite some time. When I made the physical roadbook for Dakar Desert Rally, I had already looked into whether I could somehow extract things from the game. When that did not work out, I made a system that would systematically take a screenshot, cut out the roadbook notes, and move the roadbook along. Already then I had the idea of doing that with the HUD as well, extracting the number from the image, and sending that to my phone somehow. I never ended up doing that, though it would probably work.</p>
<p>Recently my interest in Dakar Desert Rally piqued again (and I am not sure why), and this annoyance popped up again. I did find something that claims to add telemetry to Dakar Desert Rally (among other games) <a target="_blank" href="https://github.com/PHARTGAMES/SpaceMonkey">on GitHub</a>, but I never got that working. Given that it was now clear that there were not going to be updates, I thought “maybe I can somehow extract this from memory”.</p>
<p>I had not a lot of experience with this kind of low-level memory access reading, but I figured I might as well give it a shot. One of the first realisations was that there is not a lot of technical difference between a trainer (something to inject something into a game) and what I was trying to do. Both try to find the correct memory address and then do something with it, the only difference is whether that’s reading or writing to it.</p>
<p>To modify values in the game, I had used Cheat Engine before to unlock simulation mode early in Dakar Desert Rally, so that’s where this quest started. It has an unfortunate name, because whilst cheating is one of the things you can use it for, it allows for far more tinkering with games. It is also quite low-level, as it basically edits raw memory values.</p>
<p>In order to read a specific variable (e.g. speed), you first need to know at which address it lives. The tactic to find this is quite simple: start the game, connect Cheat Engine to the game, start a stage, drive a bit and remember the current speed, pause the game and look in memory for a value the same as the current speed. I guessed that the current speed would probably be a 4-byte integer. That resulted in many memory addresses that matched that value, because even if my speed is 106 km/h, other things might also have a value of 106. Then I changed my speed, and scanned those addresses again for the new speed. Repeat that a couple of times, and in the end only one address that seems to represent the speed, because how likely is it to have a variable that happens to always have the value of the current speed?</p>
<p>The problem is that this address changes every time a stage is loaded. What we actually need is a pointer to that address that is static, or a pointer to a pointer to that address, or a pointer to a pointer to a pointer to that address — you get the idea. Luckily Cheat Engine has tooling for this built-in. It can create a pointer map for a certain address, showing all pointer paths (up to a certain length) to that address. I can then restart the stage, find the new address for the speed by repeating the previous step, and looking for a pointer path in the previous set that now refers to the new address. Those paths contain offsets to the next pointer. What that means is “Start at address A, go X bytes down, then go to that address, go Y bytes down, …”, you get the idea. And voila, we have a reliable way to get the address that the speed lives on. This is also the reason why this would break if the game is ever updated, as those paths would change.</p>
<p>Luckily someone on the internet already <a target="_blank" href="https://github.com/Aldaviva/Trainers">made an open source trainer for Dakar Desert Rally</a> (among other games). Their trainer removes the speed limits — seems I am not the only one annoyed by them, though they chose a different solution. Their trainer basically does what I described above, plus some scaffolding like getting the pointer to the game, etc. I would recommend reading their source code — it makes sense once you can wrap your head around it. Plus I was happy I did not need to reinvent the wheel. The only change I needed to make was the new memory path (which, in case you are interested, is <code>[0x5D02198, 0x3F0, 0x10, 0x3F0, 0x670, 0x2E0]</code> for the current speed), and rather than write a value to it, read from it. This is that code:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CurrentSpeed</span>: <span class="hljs-title">BaseCheat</span> {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span>[] CURRENT_SPEED_OFFSETS = [<span class="hljs-number">0x5D02198</span>, <span class="hljs-number">0x3F0</span>, <span class="hljs-number">0x10</span>, <span class="hljs-number">0x3F0</span>, <span class="hljs-number">0x670</span>, <span class="hljs-number">0x2E0</span>];
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span>[] SPEED_LIMIT_OFFSETS = [<span class="hljs-number">0x5854580</span>, <span class="hljs-number">0x10</span>, <span class="hljs-number">0xF0</span>, <span class="hljs-number">0x30C</span>];
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">readonly</span> <span class="hljs-keyword">int</span>[] GEAR_OFFSETS = [<span class="hljs-number">0x05D02198</span>, <span class="hljs-number">0x3F0</span>, <span class="hljs-number">0x10</span>, <span class="hljs-number">0x3F0</span>, <span class="hljs-number">0x670</span>, <span class="hljs-number">0x2CC</span>];

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> name { <span class="hljs-keyword">get</span>; } = <span class="hljs-string">"Speed telemetry"</span>;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">override</span> Combination keyboardShortcut { <span class="hljs-keyword">get</span>; } = Combination.TriggeredBy(Keys.T).Control().Alt();

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">void</span> <span class="hljs-title">apply</span>(<span class="hljs-params">ProcessHandle processHandle, <span class="hljs-keyword">string</span> gameVersionCode</span>)</span> {
        IndirectMemoryAddress currentSpeedAddresses = <span class="hljs-keyword">new</span>(processHandle, <span class="hljs-literal">null</span>, CURRENT_SPEED_OFFSETS);
        <span class="hljs-keyword">var</span> currentSpeed = MemoryEditor.readFromProcessMemory&lt;Int32&gt;(processHandle, currentSpeedAddresses);

        IndirectMemoryAddress speedLimitOffsets = <span class="hljs-keyword">new</span>(processHandle, <span class="hljs-literal">null</span>, SPEED_LIMIT_OFFSETS);
        <span class="hljs-keyword">var</span> speedLimit = MemoryEditor.readFromProcessMemory&lt;Int32&gt;(processHandle, speedLimitOffsets);

        <span class="hljs-comment">//IndirectMemoryAddress gearOffsets = new(processHandle, null, GEAR_OFFSETS);</span>
        <span class="hljs-comment">//var gear = MemoryEditor.readFromProcessMemory&lt;Int32&gt;(processHandle, gearOffsets);</span>
        <span class="hljs-keyword">var</span> gear = <span class="hljs-number">0</span>;

        <span class="hljs-keyword">var</span> ipAddress = <span class="hljs-string">"192.168.1.37"</span>;
        <span class="hljs-keyword">var</span> client = <span class="hljs-keyword">new</span> UdpClient();
        IPEndPoint ep = <span class="hljs-keyword">new</span> IPEndPoint(IPAddress.Parse(ipAddress), <span class="hljs-number">4445</span>);
        client.Connect(ep);
        <span class="hljs-keyword">var</span> data = <span class="hljs-keyword">new</span> <span class="hljs-keyword">byte</span>[<span class="hljs-number">256</span>];
        data[<span class="hljs-number">0</span>] = (<span class="hljs-keyword">byte</span>)currentSpeed;
        data[<span class="hljs-number">1</span>] = (<span class="hljs-keyword">byte</span>)(currentSpeed &gt;&gt; <span class="hljs-number">8</span>);
        data[<span class="hljs-number">2</span>] = (<span class="hljs-keyword">byte</span>)(currentSpeed &gt;&gt; <span class="hljs-number">0x10</span>);
        data[<span class="hljs-number">3</span>] = (<span class="hljs-keyword">byte</span>)(currentSpeed &gt;&gt; <span class="hljs-number">0x18</span>);
        data[<span class="hljs-number">4</span>] = (<span class="hljs-keyword">byte</span>)speedLimit;
        data[<span class="hljs-number">5</span>] = (<span class="hljs-keyword">byte</span>)(speedLimit &gt;&gt; <span class="hljs-number">8</span>);
        data[<span class="hljs-number">6</span>] = (<span class="hljs-keyword">byte</span>)(speedLimit &gt;&gt; <span class="hljs-number">0x10</span>);
        data[<span class="hljs-number">7</span>] = (<span class="hljs-keyword">byte</span>)(speedLimit &gt;&gt; <span class="hljs-number">0x18</span>);
        data[<span class="hljs-number">8</span>] = (<span class="hljs-keyword">byte</span>)gear;
        data[<span class="hljs-number">9</span>] = (<span class="hljs-keyword">byte</span>)(gear &gt;&gt; <span class="hljs-number">8</span>);
        data[<span class="hljs-number">10</span>] = (<span class="hljs-keyword">byte</span>)(gear &gt;&gt; <span class="hljs-number">0x10</span>);
        data[<span class="hljs-number">11</span>] = (<span class="hljs-keyword">byte</span>)(gear &gt;&gt; <span class="hljs-number">0x18</span>);
        client.Send(data, <span class="hljs-number">256</span>);
    }

}
</code></pre>
<p>I could now read the value from memory, but I still needed the dashboard. Years prior I worked on a dashboard for one of the older WRC games. In that game I ran into the issue that I could not see which gear I was in, which, using the sequential gearbox, sometimes resulted in me shifting down to neutral. That game also did not support telemetry output, or at least not at the time. Back then I solved it in a game-agnostic way. The only time a gear changes is when I hit the button for gear up or gear down. I made a program that would listen for those buttons, and keep its own state of the current gear. That would then be sent over a UDP to an Android app where I would display the number. Quite simple, although it did require me to make sure the gears matched up when I started a new stage, as well as set up the maximum number of gears correctly (as shifting up from top gear means you stay in top gear, and shifting down from reverse means you stay in reverse). I figured I could reuse this project to show my current speed. This helped with all the things like keeping the screen of my phone on, removing the menu bar, etc., all small things that together are quite nice not having to do twice.</p>
<p>One thing both of these have in common as well is that I in principle made them for me and only for me. With things I publish online, I make sure they are a bit more polished, and that they work for others as well. That requires things like a configuration file, a nice icon, it working on different screen sizes, etc. Right now things like IP addresses are just hardcoded — if I need to change them I can just recompile everything. Given the Dakar Desert Rally player base of several dozen players, making it universal might not be worth the effort. Though if you do want this for yourself, feel free to message me.</p>
<p>The open source trainer came with an UI — I did not need one, but no need to throw it away either, so I just reused that. The nice thing about UDP is that I do not need to keep state on the sending side, which made it far easier to integrate. Whenever it reads the value from memory, it sends it to the phone. That is probably far more frequent than necessary, and could probably be made far more efficient, but it works fine, and my PC and phone can handle it just fine.</p>
<p>Whilst making this work I did run into the fact that C#, the language the server is in, and Java, the language the Android app is in, do not use the same byte order for integers (technically C# has a variable byte order based on the machine it is compiled on, but if you can find me a big endian Windows machine let me know). Luckily turning that around is not too difficult, though programming in those two languages simultaneously is quite tricky — I often found myself using the wrong functions that would exist in the other language but not in the language I was currently working in. The UDP protocol itself is quite a simple one — for WRC I sent one byte with the current gear. For Dakar Desert Rally, I changed that to four bytes with the current speed. Could hardly be simpler, though I guess technically one byte would suffice for the speed, as it is unlikely to get above 255 km/h anyway.</p>
<p>That was working fairly quickly, but I was not done yet: I wanted to display more than just a number — I also wanted warnings when nearly speeding and actually speeding. The game makes the warning banner yellow when within 20 km/h of the speed limit, and red when over the speed limit. Since the trainer already had the address where the speed limit lived (as it would overwrite that value), I could just read that value and send it as another four bytes. Then I could do the checks (and change the display) on the phone side.</p>
<p><img loading="lazy" src="posts/building-a-custom-hud-for-dakar-desert-rally/50dd6ff5-ac3a-44bc-a7a8-49f27ace4ee0.jpeg" class="image--center mx-auto" /></p>
<p>Initially I changed the text colour just like the game, although with an additional orange when within 10 km/h of the speed limit. This worked but was not really clear. I later changed it so that the background would change in colour rather than the number, and that it would flash red when speeding (rather than just a solid colour). That was a lot clearer. The flashing red was a simple idea, but surprisingly that is the thing that took far longer than getting the number to display. I always find it funny how things that seem simple can take a lot longer than things that seem difficult. Sadly there is no built-in blink feature in Android, or luckily, depending on how you look at it.</p>
<p>It also turned out that I was quite lucky with how easy it was to find the value for the current speed in memory. I tried to add the current gear indicator as well after that, and rather than one address holding the current gear, apparently many addresses hold the current gear. Some of these addresses are car-dependent too — it’s quite a mess, which given the game does not surprise me. I found out one of the addresses is only used by the HUD. By disabling the HUD during the check, one of the values freezes whilst the other keep changing with the gear changes. This would tie the phone dashboard to the HUD being active, but at least it would work for all vehicles. That was not an issue though, as I also found out that way that the address I use for the current speed also depends on the HUD being turned on. If you’re wondering “but I do not see the current gear in your picture”, you would be right: I don’t know where to put it yet, and in practice I do not really miss it.</p>
<p>I posted this to my social media (which is one of the reasons I am writing this blog post), to the <a target="_blank" href="https://discord.com/channels/495171435338268693/495171436327993355/1419088783202193549">Dakar Desert Rally Discord server</a>, as well as to <a target="_blank" href="https://www.reddit.com/r/simrally/comments/1nmz68k/i_am_working_on_an_external_hud_for_dakar_desert/">Reddit</a>. The Discord and Reddit responses sum up the game quite well: it’s mostly quiet, some people saying “I would play this game if X”, and a few hardcore fans, and quite a few people on the fence. This is a video of the “final” version, with me driving a truck. I am still in two minds about releasing this dashboard, but at least I managed to fix a gripe for myself. If anything, I have a new tool in my toolset for tinkering with games.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/slSMbkO5Tsg">https://youtu.be/slSMbkO5Tsg</a></div>
]]></description>
			<link>https://blog.koenvh.nl/building-a-custom-hud-for-dakar-desert-rally</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/building-a-custom-hud-for-dakar-desert-rally</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Thu, 25 Sep 2025 19:06:52 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Let's finally build a new PC]]></title>
			<description><![CDATA[<p>Recently I assembled a new PC. I figured I would take the opportunity to write a bit about how that came to be, as well as reminisce about the good old times, because as my last upgrade has been about 8 years ago, and I think it’s quite an interesting story altogether.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/b3fdda3b-f37b-4ec9-ba73-6ecaf3d9f693.jpeg" class="image--center mx-auto" /></p>
<p>Let’s go back to the beginning: the Big Bang. Many billions later Sinclair would invent the C5 and some years after that I was born, though it would take another 13 years before I would build my first PC, and buy most of the setup I was running until recently. My PC is one of my most important tools (I am writing this blog post on it for example), and as much as I like tinkering with it, it also needs to work, much like my phone and my car. I don’t tend to replace parts that still do what they are supposed to do. For example, I used my Logitech G110 keyboard for about 15 years because it did not want to break. When the Das Keyboard 4 came out in 2014 I had always told myself that I was going to get myself a mechanical keyboard once my G110 would stop working properly. Only a few months ago I decided that I was no longer going to wait for that, and I bought myself a second-hand mechanical keyboard — the Das Keyboard 4 with Cherry MX Brown switches to be precise. The price second-hand was low enough that I was willing to risk it. I’m quite content with it (especially because it is a full-size mechanical keyboard, I never understood why so many mechanical keyboards come without a numpad), but I don’t think I am now going to become a collector of mechanical keyboards or a big modder, though the fact I can easily change the keycaps is nice.</p>
<p>Anyway, back to my computer. Over the last year or so, I started to notice that my PC was getting more and more issues. Programs would no longer runs smoothly, things would stutter, the USB ports on the front were finicky (and some better left unused), sleep mode would cause audio glitches, and a couple of other things. All these things did not happen all at once, but developed slowly over time. None of them single-handedly reason to replace things, but cumulatively it was getting closer to that point. Another factor was the announcement that Windows 10 would be end of life in October 2025. Now as is always the case, that deadline will be extended (the announcement that one can buy extra support was already made, but I have an inkling free extra support will be extended as well). I have nothing against Windows 11, but my hardware was not officially supported. I figured I had reached the threshold to look for new hardware.</p>
<p>Now a quick sidestep on why I run Windows, as given my background a lot of people seem to assume I run Linux. I do run Linux on my work laptop, and whilst it works fine for the most part, it is also clear that it is not the year of Linux on the desktop just yet. Or, to quote a wise man: “People don’t use an operating system, people use programs”. This man happens to be Linus Torvalds, and the quote is from Revolution OS, a documentary about Linux. As many likely already know, <a target="_blank" href="https://blog.koenvh.nl/adding-a-button-box-for-my-sim-rig">I have a simracing setup</a>, and getting that working on Linux would be a real challenge. Sure, you may be able to get it working eventually, but that is not something I want to have to deal with. Additionally, Windows is miles ahead when it comes to things like fractional scaling, or making sure the UI stays responsive when a program in the background is doing a lot of processing. Also tools like Affinity Design and Affinity Photo are not available on Linux. The main tools I miss on Windows that are available on Linux are command-line based, and with Windows Subsystem for Linux, I can even use those fairly effortlessly. That is a really nice project.</p>
<p>Mind you, just like it isn’t the year of Linux on the desktop, using Windows on a server also never <em>felt right</em>. Back in the day I used to do sysadmin work for my local city broadcaster. Part of that was managing some Windows domain controllers. Setting up an RDP session with a domain controller, trying to find the start menu on Windows Server 2012, which decided to use the same start menu as Windows 8 (you know, the one that was made for tablets and required you to hover in the bottom left corner), clicking around the dozen interestingly translated interfaces… Yeah Linux is a breeze in comparison, though I’m sure it’s better now than it was back then (or at least I hope). One of my favourites was the fact that group policies were translated to Dutch, but still sorted according to their original English text. Fun fact: this is still quite common in language selection menus, and once you know you will notice it everywhere, and it might help you find your language quicker. Anyway, I am two digressions deep now, so let’s get back on track.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/193f2feb-0ca2-483c-a983-0f379a11664d.jpeg" class="image--center mx-auto" /></p>
<p>Windows 10 was nearing the end of support, my PC had seen its best, and my hardware was starting to wear out. The first thing I did was see which parts needed upgrading. I have upgraded parts of my PC a couple of times already. To paint a picture, I was running on an Intel i5 9600K, 32 GB DDR4 RAM, and a GTX 1080 Ti — those were not the original parts I used when I first assembled my PC in 2012. My first self-built PC I built (or to be precise, had built for me) contained an Intel i5 3570K, 8 GB of DDR3 RAM, and an AMD Radeon HD 7850. Over time many parts have been replaced many times — I upgraded to 16 GB of RAM, I replaced the graphics card with an AMD Radeon R9 290, added an SSD (yes, originally I did not have one), added a bigger SSD, replaced the hard drives, replaced the PSU several times (once because it died, other times because it was insufficient). That GTX 1080 Ti is the last change I made to it, which must have been around 7 years ago. One of the few things that have always stayed the same were the case and the optical drive.</p>
<p>Fun fact, the stickers on the front are those of the original parts (except for the Samsung SSD one, that one was added when I bought my SSD). Even though it no longer contains those parts, I still liked having the stickers on the front as a reminder. It even got to the point that with all the parts I had replaced over the years I could rebuild my old PC, just in a new case. I decided to do just that. That PC also sits under my desk, although is mostly doing nothing — I only use it for when I need a bare metal Linux machine for one reason or another, like firing off many TCP requests to show that if one 13 year old machine can bring down your service that is critical for routing security, it may not be the best choice for resilience… but others have disagreed even after the demonstration, so I will leave it at that :-)</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/635410f6-421e-4d03-be40-065f491b8764.jpeg" class="image--center mx-auto" /></p>
<p>Anyway, I quickly realised that there were effectively no parts I wanted to take with me to a new PC (except for the hard drives and optical drive), and that I would be building an entire new PC. The USB ports on the case were not great any more, and I also felt like a new case would be nice regardless — even cases have changed quite a bit in 13 years. I still was not entirely sure whether I wanted to build it myself, but one thing I did know is that I did not want a prebuilt. I know too well what I want (and don’t want) to buy a prebuilt PC where the parts don’t quite line up with what I want. And that meant looking for parts.</p>
<p>I had been procrastinating that picking parts for quite some time. When I bought my GTX 1080 Ti, I did so when its successor — the RTX 20-series — was just announced. It allowed me to buy it at quite a discount. I figured I might as well do that again (admittedly that was also just a nice excuse to it <em>later</em>), so I waited for the announcement of the RTX 50-series. Well, that went wrong. Apart from the announcement not being terribly impressive, new stock was limited while old stock was already depleted, resulting in prices skyrocketing. Clearly I had to wait a few months for prices to become reasonable again.</p>
<p>Even though I like software, many people are surprised to learn I do not go jumping up and down with joy when it comes to a new PC. I have nothing against building computers per se — for me a PC is a tool, and something I would like to “just work”. When I was younger I would customise my PCs (at least software-wise) until they were unrecognisable and barely functioning. I stopped doing that quite some time ago.</p>
<p>As I was trying to decide on parts, I noticed that a lot had changed. I was used to just buying the best Intel i5 processor on the market. I knew that Intel had gone downhill, and that their latest generations were having a lot of issues — that I had read in the news, and my searches confirmed that. Shame, really, but also a good reminder things can quite quickly change. I had no idea what any of the new AMD Ryzen names meant. Finding an answer to that was more difficult than I expected. I was used to only Intel being relevant and just picking between the i3, i5, and i7 (last time I checked AMD they made the Bulldozer processor, the FX 8350, which was great if you needed many cores, but had some other downsides).</p>
<p>I now understand it as following: Ryzen is the name, after that is a number, 5, 7, or 9, which is like the i3, i5, i7 the “class” the CPU belongs to, 5 being normal users, 7 “prosumers”, and 9 professional users with specific use cases (it mainly has more cores than the 7). Then the four numbers after that identify the generation (9xxx being the newest), then the model type (e.g. x800), then the special features (e.g. X, or X3D, where X3D means it has 3D V-Cache technology, which sounds very impressive, but just means a larger L3 cache). If you look online you find a lot of other schemes as well how those numbers supposedly work, but those do not match the CPU I ended up with, the AMD Ryzen 7 9800X3D. Needless to say, I think those numbers are a bit of a mess and could be a lot clearer. Comparisons with my current processor are difficult, as my hardware was generally too old to be considered for any comparisons. All of them would be an improvement, but how do I interpret those numbers? 100% faster and 150% faster at some point just become “much faster”. In the end I just picked something I knew would be fine, and probably slightly overkill.</p>
<p>By the way, it would not be my first AMD processor — before my Intel i5 3570K I used to have an AMD Athlon X2. The i5 was a real upgrade, because it had double the amount of cores of my Athlon (4 instead of 2), and all of those cores were a lot faster too. Mind you, that Athlon was a real upgrade from my Pentium 4 too, and also double the amount of cores. My second i5 had six cores, which is a bit of a shame, because now the development is 1 → 2 → 4 → 6 → 16 (well, 8 cores and 8 threads). Another metric I used to decide on a CPU was “what processor can I get for €X?”. I quickly found out that no longer works because in my mind processors are still the prices from 2012. Turns out prices have increased quite a bit since ten years ago. Also, as strangely as it sounds, I did not really have a budget per se. My PC is one of my most important tools, and I’m happy to spend a bit more on it if that means I do not get annoyed by it, especially as it is a lot of money anyway, and thus the difference in price is relatively small. What is €100 more over the course of 8 years?</p>
<p>One of the other reasons the difference in price for processors is relatively small is the graphics card. I had been wanting a new graphics card for quite some time, as I noticed more recent games had started to stutter: my graphics card could not keep up. Especially things like Microsoft Flight Simulator in virtual reality were sometimes a bit of a nauseating experience when it would constantly stutter. Luckily the numbering is still more or less the same as when I bought my GTX 1080 Ti, although the choice was far greater now. One of the main changes is that it feels like the higher segment is far broader now. The RTX 5090 (which has a suggested retail price of ~€2300) has a lot of power I do not know what you would use it for, except maybe AI. For games it is definitely overkill, even on a 4K monitor. Anyway, I spent €650 on the, back then, top of the line GTX 1080 Ti. That amount of money leaves you in the lower segment nowadays. I ended up settling for an RTX 5080 for ~€1000. I have no compelling reasons why not an RTX 5070 Ti, in the end I just decided that it was probably overkill, and definitely good enough — no matter what I picked it would be a chunk of money anyway. The price of the GPU makes the CPU <em>relatively</em> cheap.</p>
<p>One of the things I noticed when looking for graphics cards is that most of them only had four ports, most of the time 3× DisplayPort and 1× HDMI. My GTX 1080 Ti had five ports (3× DisplayPort, 1× HDMI, and 1× DVI), and I was using all of them. Three for the monitors on my desk, one for my simracing setup, and one for VR. I had had the idea to upgrade my monitors for a long time as well — my oldest monitor still has a “Windows 7 compatible” sticker on it (whatever that means, it’s a monitor), and is now around 15 years old. I started looking into what it would cost to replace those three monitors on my desk with two new ones, as graphics cards with five ports would cost around €400 more for the same performance. There were three reasons I wanted to upgrade my monitors already anyway: 1) I noticed that when working from home on my laptop, I would only use two monitors anyway, so it made sense to go back from three to two monitors. My original reason for three monitors was simracing, but I had a separate setup for that now; 2) the maximum brightness on my middle and oldest monitor was rather low, making it very difficult to see when the sun was shining. The contrast (pun intended) with my laptop was vast, and 3) in the office I used a 4K monitor, and even though I did not need three monitors (as I would mostly focus on two at a time anyway), having the extra space of a 4K monitor was a welcome addition.</p>
<p>So I briefly interrupted my search for PC parts to look for new monitors. Mainly to see how expensive that would be. I was pleasantly surprised. I won’t say they are cheap, but I found a 27” Dell 4K 120 Hz IPS monitor (the Dell S2725QS) for €270 each. I think that is really quite affordable, especially compared to what I was expecting for 4K <em>and</em> 120 Hz. I wasn’t looking for the latter, but it is a nice addition. I ordered those monitors surprisingly quickly, especially for how long I normally spend on making these kinds of decisions. This is the before and after:</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/02f57edd-905b-490b-9db9-f5ebe2c166c3.jpeg" class="image--center mx-auto" /></p>
<p>The monitors themselves were pretty smooth sailing, but the things around it were not. I use my Dell XPS 15 9520 for work. It supports external monitors and hubs via USB-C. I bought a new hub to connect my two monitors to, so I could use one hub to connect the monitors to my laptop. The hub said it was compatible with my laptop, so obviously it… didn’t work. USB-C is a mirage of different protocols (apparently the ports on the left of my laptop support something other than the port on the right, and you can only find that deep in the documentation), and things-that-should-work-but-do-not. I bought a USB-C to DisplayPort cable, which… did not work. I bought an HDMI to DisplayPort cable, which also did not work. I managed to get the monitors working with two hubs, one monitor connected to each hub. Not ideal, but it worked. I was ready to send all the non-functioning parts back, but decided to give it one more try.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/f55980d7-6f79-4506-b068-721a0aec7275.jpeg" class="image--center mx-auto" /></p>
<p>Apparently my new UGREEN USB-C hub that claims to support 2× 4K at 120 Hz over HDMI does not support that, but only one monitor at 4K at 120 Hz, but it does support 2× 1440p at 60 Hz. This is not documented anywhere, I found this out using trial and error. Increasing the resolution or refresh rate would cause a black screen. Admittedly this was fine, I would run it at 1440p anyway, because fractional scaling in Ubuntu is still not great, and 4K at 100% scaling is too small for my liking. The 60 Hz is a bit of a shame, but I have never been the person to really care about the 60 Hz vs 120 Hz difference. I never really noticed the 120 Hz on my ultrawide for my simracing setup, and this was mainly for documents, programming, and email, not something that really benefits from a high refresh rate. Plus, I was very happy that it would work with just one cable, because that was a lot simpler and neater.</p>
<p>Anyway, back to parts. Slowly but surely I had settled on a CPU and graphics card. A lot of other components were a lot simpler, for example I knew I wanted 64 GB of RAM (or actually 128 GB, but then I saw the prices and decided against it). One of the parts that was a difficult choice was the case. I had been reusing my first case from 2012, and it turns out new cases are quite different from cases back then. For example, space for a hard drive is no longer a given — my old case had space for six or seven, and one space for a 2.5” SSD, which was already quite modern at the time. Nowadays with NVMe SSDs you no longer even need space for those. My main SSD in my new PC is an NVMe SSD as well.</p>
<p>Initially I was also looking for a case that would still accommodate an optical drive. I don’t use it often, but it is annoying to not have one when you need one. I actually found a case that met all those requirements, was affordable, and did not look like a fun fair with large glass panes and RGB lighting. It was a nice, sleek, black case with a small orange highlight. It had the ports on the front that I wanted (namely headset and USB, because even though PC cases are still massive apparently having usable ports on the front has become a novelty — I guess they expect you to connect USB devices via <em>The Cloud®</em> now too or something). Anyway, it was not fully bland, but not something that terribly stands out either. It did have a window, and even though I would rather not have one (because my PC is under my desk on the left, which makes the window rather pointless as it is on the wrong side, plus it requires me to do neat cable management), but I could live with that. It was the <a target="_blank" href="https://www.bequiet.com/en/case/938">be quiet! Pure Base 600</a>, for those interested.</p>
<p><em>Case closed</em>, until I decided to check that my graphics card would fit, and found out it would not. Those things have become huge too. I ended up looking for another case, and ended up going for the <a target="_blank" href="https://www.fractal-design.com/products/cases/north/north-xl/chalk-white/">Fractal Design North XL</a> that you can see in the opening picture. I saw it before, but decided to give the white case a closer look. I was a bit hesitant to look at white cases, because that generally only looks good with white components (my white GTX 1080 Ti stood out a bit in my old black case), but because the inside was still black, and there was no window, that was not an issue. I did previously look at the black one, but the gold accents were not really my taste. I liked the wood touch (though I wonder how well it will age — time will tell I guess), and decided I could live without optical drive and I could always buy a USB one. I also thought it would look good in my living room. Funnily enough that was never a consideration for my old case, but I have reached the point in life where it does. I also recently bought a small storage container to store my batteries, I keep my used bread bags in a bread bag (because they might be useful later!), and I felt intense satisfaction by how smoothly the freezer drawers moved in and out after I defrosted it last week. Different phase of life I guess, maybe I will go for RGB again when I reach my midlife crisis :-)</p>
<p>In the meantime another “computer” in my home started complaining: My Home Assistant started notifying me that the Raspberry Pi 2 it was running on would no longer be a supported platform starting from 2026, because it runs on a 32-bit ARM architecture. I do not blame the Home Assistant developers for that decision, but it was still annoying. My Raspberry Pi 2 was not quick (far from), but it was fast enough. It did the job. I again halted my PC part quest to look for a replacement for my Raspberry Pi. I always liked the Raspberry Pi as a cheap, simple, and low-power computer. It seems however that with later Raspberry Pi versions, and especially the 5, they completely removed themselves from that. The new Raspberry Pi 5 is, everything together, around €100. That is no longer “if this breaks it’s a shame but not a big deal” territory to me. It seems like it wants to be a serious computer. The specs show that too: 4 cores, up to 16 GB RAM… this was supposed to be a cheap computer. The power usage is also several times that of my Raspberry Pi 2. I find that a bit of a shame. I am not saying the Raspberry Pi 5 is not a good computer, on the contrary, it is <em>too good</em> as a computer. It is cheaper to buy a second-hand thin client than to buy a new Raspberry Pi, which seems to be against its original purpose. However, this digression quickly resolved itself when a colleague still had a Raspberry Pi 4 left over — sometimes solutions really are that simple. Home Assistant now happily runs on that.</p>
<p>Anyway, back to the new PC. I was slowly converging on the final parts list. With some small adjustments from the wisdom of the crowd (e.g. a better PSU for the same price, and a different motherboard with more USB ports) and small changes based on availability, I was almost ready to order. One of the things I did still have to decide on was whether I was going to build it myself or have it built for me. I can build a PC just fine, but with new parts, especially expensive parts, I was really tempted to spend the extra money to have someone else do it for me. I wrote an email inquiring for the price. In the end I decided to build it myself — not due to the price, but because it would take ~2 weeks to get it built, and I thought that was too long. Surely I could manage to not mess this up, and worst case I could replace the part. These are the parts I settled on, along with their lowest prices in shops:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Category</td><td>Product</td><td>Price</td></tr>
</thead>
<tbody>
<tr>
<td>Processor</td><td>AMD Ryzen 7 9800X3D Boxed</td><td>€ 449,-</td></tr>
<tr>
<td>Motherboard</td><td>ASUS ROG Strix B650E-F GAMING WIFI</td><td>€ 197,-</td></tr>
<tr>
<td>Graphics card</td><td>MSI GeForce RTX 5080 16G VENTUS 3X OC PLUS</td><td>€ 1 029,-</td></tr>
<tr>
<td>Case</td><td>Fractal Design North XL - Mesh Chalk White</td><td>€ 165.99</td></tr>
<tr>
<td>Processor cooling</td><td>Arctic Freezer 36 (Black)</td><td>€ 30.67</td></tr>
<tr>
<td>RAM</td><td>Kingston Fury Beast KF560C30BBEK2-64</td><td>€ 229.90</td></tr>
<tr>
<td>PSU</td><td>be quiet! Pure Power 13 M 1000W</td><td>€ 149.-</td></tr>
<tr>
<td>SSD</td><td>Lexar NM790 (without heatsink) 4TB</td><td>€ 214.90</td></tr>
<tr>
<td></td><td><strong>Total price:</strong></td><td><strong>€ 2 465.46</strong></td></tr>
</tbody>
</table>
</div><p>What I should add is that the motherboard came with a €25 “back to school” cashback. Now I wonder what kind of school-going person is going to spend €200 on a motherboard, but hey. I always fill those cashback forms out, partially for the money, but mostly out of spite. This cashback form, like many others, turned out to also be made so that as few people as possible would actually claim it. It was hidden from Google, the time the form was “open” was several weeks later than the actual cashback period, so you had to mark in your calendar to do the cashback, filling it out required far more information than really necessary, and oh, you had to confirm you were 18+, which most people in school I guess are not. Then they need about a month to process the cashback, because <em>reasons</em>.</p>
<p>After what is now many months I bit the bullet and ordered all the parts. I ordered them from one shop, which was slightly more expensive, but not by much (less than €100), and they were shipped in two boxes. Annoyingly one box was delayed, so on day one I only ended up with the case. It was a nice case, but still annoying that building would have to wait. Building ended up being quite fun: phone turned off, music in the background, just slowly bit by bit putting things together.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/51502edf-c5f8-49c7-92d9-a89c06f9ef8e.jpeg" class="image--center mx-auto" /></p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/8eadc06d-12e5-4971-b69a-3ea7c6b2edf7.jpeg" class="image--center mx-auto" /></p>
<p>More building ensued, and everything was pretty smooth sailing. I only needed some extra time for the NVMe SSD (this was the first time I installed one), and the CPU fan (and which way it should point). I was also quite surprised how few cables there are in a modern PC: the graphics card is only one cable to the PSU nowadays rather than two or three, the NVMe SSD removes the SATA and power cable of the older style SSD. The front of the case does not have many ports and status lights, so not many cables there. The HDDs are mounted on the bottom and out of sight. It felt very empty inside, although I do not mind that. I almost regretted not having a window to show the neatness of my case (though it would still not be really visible under my desk). At some point comes the scary part: turning it on for the first time. Luckily all worked as it should.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/3bc4eb3f-f31c-475c-b889-1c27a91c8517.jpeg" class="image--center mx-auto" /></p>
<p>The building part is the quick part, setting everything up again in software was more work. I did do it systematically, with list of previously installed programs, moving over configurations, fixing file permissions, etc. It is still incredibly tedious though, especially when things do not work. Whilst it is pragmatic, it also is not really <em>fun</em> to do things that way. Frankly picking parts is stressful, because it is difficult to tell if they are the right parts, building it is stressful because if you break something it gets expensive, and reinstalling it is stressful, because even though I have backups (<a target="_blank" href="https://blog.koenvh.nl/a-tale-of-backups">I have written about those</a> and <a target="_blank" href="https://blog.koenvh.nl/the-tale-of-backups-continues">then once more</a>), I still do not want the hassle of having to use them. But the end result is rewarding in all cases.</p>
<p>In many ways it is like moving house, but digitally. You move to a new place that is ultimately better, but the moving part is hardly fun. It is a lot of tedious waiting, being careful with the virtual Ming vase, and trying to get everything working the way it should (which some software is more finicky about than others). Especially because this move was not just to a new PC, but also from Windows 10 to 11, and it was a fresh install. I do not remember the last time I did a fresh install on my main machine, but it might have very well been 2012. It was a deliberate decision — my Windows installation had seen so much I did not think it was wise to bring it to a new system.</p>
<p>Windows 10 and 11 are very similar yet still different enough that it is annoying sometimes, especially when things don’t quite work, like my audio initially. Turns out it just needed a few restarts and the driver was working. Also the new right-click menu is not an improvement. I also ran into some things I ultimately did not initially remember setting up, like a lot of applications that wanted to notify me of everything. Plus there is always that lingering feeling “did I not forget something?”, and the running into all the issues later that you have with a new installation, like when I wanted to print something, and suddenly realising I never copied those printer profiles I set up a long time ago.</p>
<p>Not all issues have been fully solved yet: one of them was that Affinity Photo apparently corrupts the JPEG export with hardware acceleration turned on — something I found out whilst writing this blog post, and another was the fact that the Dutch keyboard layout (that I have never seen a single Dutch person use) was added again by default, which ironically can only be removed by <em>adding</em> the Dutch keyboard and then removing it. Just Windows things I guess. My right monitor does not wake up properly after it goes into sleep mode, and only works when I turn it off and on again (which is luckily easy enough to do, and Windows remembers where my windows were, that part works marvellously). Oh, and remember <a target="_blank" href="https://blog.koenvh.nl/the-tale-of-backups-continues">that backup thing I made for myself</a>? Well, that broke again, because on my previous computer I disabled automatic sleep mode because it would break my audio, so I never took into account that it could go into sleep mode <em>during</em> the backup. Admittedly that was fully on me, though still annoying. I made a bodge for that that starts a program to prevent it from going into sleep mode. It works, but I probably want to replace it with something more permanent. Though this might very well become that one unpacked box after moving that I will always do later, but never end up doing. Speaking of the sleep mode — well, my network card kept waking up my machine a minute after it went to sleep. That took quite some time to figure out what was happening.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/eb46c515-39c1-4d0d-8f10-ca60d329cd8b.jpeg" class="image--center mx-auto" /></p>
<p>In case I forgot setting up something else, I decided to make a backup of my AppData, because too many programs store important things in there (like save games) — I booted into an Ubuntu LiveUSB on my old PC, hooked up an external hard drive, and copied over the files. At least, that was the plan. Making a bootable USB was difficult enough, turns out my one modern-ish USB stick is having some issues. I should really order a new one. Apparently new USB sticks start at around 128 GB for ~€10. Oh how times change, I was already quite chuffed with my 128 GB SSD back in the day, and now a small USB stick has the same capacity. Getting my old PC to boot from my USB properly was a kerfuffle. I initially wanted to do the copying before removing the hard drives, but booting into Ubuntu was giving me so much trouble, I ended up just moving them and backing up the AppData later. I had already installed and set up most programs anyway. I am not sure if it has to do with the 4K monitor or something else that caused Ubuntu those issues. Regardless, I tried it again later with my old monitor attached and that worked. I set it to copy from my SSD to the hard drive, and went to bed. The morning after I was greeted by a kernel panic. I tried it again, and went to work. When I came back from work I was greeted by another kernel panic. I tried it again with a Fedora LiveUSB, and then it worked. Maybe it also helped I disabled sleep mode that time around. At least my AppData was now copied over.</p>
<p>But despite those minor things, everything works smoothly. It is surprising how quickly it becomes normal that things do not stutter, that games run smoothly with a high frame rate even at high graphics settings, and that the PC starts up in seconds rather than minutes.</p>
<p>Oh, and one last thing I did on a whim: I was annoyed by the fact that my headset would generally be somewhere in the way on my desk, especially when working from how, so I decided that if I was going to upgrade, I might as well get rid of this annoyance, and buy a headset mount. It’s made out of aluminium and quite sturdy.</p>
<p><img loading="lazy" src="posts/lets-finally-build-a-new-pc/58cc455f-2a8b-4926-8ff6-f76758230381.jpeg" class="image--center mx-auto" /></p>
<p>Now with all of that together — new monitors, new PC, headset mount, and miscellaneous things, my working from home spot feels a lot nicer. Of course this story is not finished yet, because I will always come up with something new. For example, now with my new PC, I have noticed just how slow HDDs are, and I am tempted to replace my two 4 TB HDDs with an 8 TB SSD. I am also contemplating moving my landline (yes, I still have one) to my desk, now that I have the space for it. It helps the slightly uncomfortable feeling of pulling the cord too far that you currently have when calling, as it is on a sideboard next to my desk. Yes, it is a corded landline phone. But it still receives firmware updates, as I found out recently. But all of those are improvements for later. Right now I am just going to enjoy jumping seven years into the future hardware-wise.</p>
]]></description>
			<link>https://blog.koenvh.nl/lets-finally-build-a-new-pc</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/lets-finally-build-a-new-pc</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Wed, 03 Sep 2025 13:30:48 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Het verhaal achter het Rijkspodcastregister]]></title>
			<description><![CDATA[<p>Voor de verandering schrijf ik een keer in het Nederlands, want dit onderwerp leent zich niet zo goed voor het Engels: het gaat namelijk over de Rijksoverheid en over podcasts (ironisch genoeg is er geen goede Nederlandse vertaling voor “podcast” — misschien komt “audio-uitzending op aanvraag” het dichtste bij, maar echt lekker klinkt het niet), en het <a target="_blank" href="https://rijkspodcast.koenvh.nl">Rijkspodcastregister</a> dat ik opgezet heb.</p>
<p>Enfin — correctie, afijn, laten we bij het begin beginnen. Op een dag zat ik in de auto op weg naar mijn werk. Dat is de auto waar ondertussen geen tracker meer inzit, overigens, maar dat verhaal kun je <a target="_blank" href="https://blog.koenvh.nl/what-does-this-button-do">hier</a> lezen. Er zijn een aantal podcasts waar ik graag naar luister <a target="_blank" href="https://www.nrc.nl/podcast/vandaag/">NRC Vandaag</a>. De NRC is een commerciële krant, dus er zitten advertenties voor de podcasts. Normaliter spoel ik die door — twee keer op de doorspoelknop op het stuur met de rechterduim drukken, daar ben ik ondertussen vrij getraind in — maar dit keer deed ik dat niet. Zodoende hoorde ik een reclame voor de podcast <a target="_blank" href="https://www.ncsc.nl/wat-doet-het-ncsc-voor-jou/enter-de-podcast">Enter van het NCSC</a>, en dat is waar dit verhaal begint.</p>
<p>Ik kende de podcast Enter al voor deze reclame. Sowieso heb ik verrassend veel contact met de mensen van het NCSC, en ook mensen die meedoen aan deze podcast. Het bestaan van deze podcast verbaasde me dus niet, maar dat er reclame voor werd gemaakt wel. Dat zette me aan het denken: hoe veel podcasts zijn er eigenlijk door de overheid gemaakt?</p>
<p>Ik had eigenlijk geen tijd om dat uit te zoeken toen deze vraag in me opkwam. Een korte zoekopdracht gaf me wel <a target="_blank" href="https://www.rijksoverheid.nl/ministeries/ministerie-van-binnenlandse-zaken-en-koninkrijksrelaties/verhaal-van-bzk/podcasts-bzk">een overzicht van BZK</a> (Binnenlandse Zaken en Koninkrijksrelaties), maar niet van de andere ministeries. Ik zat te wikken en wegen of ik elk ministerie apart zou aanschrijven — dat leek me wel veel werk, en vooral ook veel werk om op alles te antwoorden als de reacties binnen zouden komen. Uiteindelijk heb ik een email gestuurd naar EZ (Economische Zaken zonder Klimaat), en naar het algemene contactadres van de <a target="_blank" href="https://www.rijksoverheid.nl/contact/informatie-rijksoverheid">Rijksoverheid (1400)</a>.</p>
<p>Ik had helemaal geen grote verwachtingen van 1400 — ze zijn er voor algemene informatie over de Rijksoverheid voor de gemiddelde burger, en deze aanvraag valt daar duidelijk buiten. Maar niet geschoten is altijd mis, dus we kunnen het gewoon proberen. Na twee dagen kreeg ik het antwoord dat zij het ook niet wisten (gôh), maar dat ze de vraag door hadden gezet naar DPC (Dienst Publiek en Communicatie), onderdeel van AZ (Algemene Zaken), beter bekend als “dat ministerie van de minister-president”. Die e-mail bij DPC kwam binnen op een vrijdag. Ik denk dat dat geholpen heeft, want hoewel ze geen overzicht hadden (wat ik al verwacht had), hadden ze wel de moeite genomen om een heel aantal links te sturen. Heel leuk, daar kon ik wat mee.</p>
<p>Toen ik weer tijd had ben ik begonnen om de podcasts te verzamelen. Mijn insteek was vrij simpel: link zoeken via Apple Podcasts (Apple heeft een vrij beschikbare API voor podcasts, die is ontzettend handig hiervoor), en dan via de API de details ophalen. Niks bijzonders, wel makkelijk. Die code was vrij snel geschreven. Helaas liep ik vrij snel tegen een probleem aan: een heel aantal podcasts zijn alleen gepubliceerd op Spotify. Waarom dat precies zo is ontgaat me, want Spotify heeft gewoon een podcastplatform waarmee ook op andere platforms gepubliceerd kan worden (daar maakt Enter ook gebruik van), maar goed, dan maar ook ondersteuning voor Spotify toevoegen.</p>
<p>De lijst van podcasts die alleen op Spotify beschikbaar is werd langer en langer. Daarnaast staan alle niet-Spotify-exclusieve podcasts óók op Spotify. Om die reden heb ik uiteindelijk besloten om maar Spotify voor alles te gebruiken — wel zo makkelijk, al doet het mijn hart voor open standaarden wel pijn. Er zijn daarnaast ook podcasts die alleen maar MP3’s op een website zijn.</p>
<p>De data verzamelen was soms nog wel uitdagend. “Podcasts die gemaakt zijn door of in opdracht van de Rijksoverheid” is een vrij duidelijke definitie, maar er is ook zeker een grijs gebied. Mijn innerlijke ambtenaar heeft zin om er een beslisboom voor te maken als het niet zo veel moeite was. Ook later bleek dat een lastige vraag: is een podcast van een onafhankelijke organisatie met mensen vanuit de overheid wel of niet een podcast van de Rijksoverheid? Toegegeven, het is niet heel erg als het niet helemaal consistent is.</p>
<p>De naam was er één die ik al langer in mijn hoofd had: het Rijkspodcastregister, te vinden op <a target="_blank" href="https://rijkspodcast.koenvh.nl">rijkspodcast.koenvh.nl</a>. Geïnspireerd door de <a target="_blank" href="https://services.belastingdienst.nl/rijksvideodienst/">Rijksvideodienst</a> (da’s gewoon Cisco WebEx) en het <a target="_blank" href="https://algoritmes.overheid.nl/nl">Algoritmeregister</a> past deze naam binnen het productportfolio van de Rijksoverheid. Dit is hoe het er op dag één uitziet:</p>
<p><img loading="lazy" src="posts/het-verhaal-achter-het-rijkspodcastregister/14d59977-fff7-4801-872c-d12f7ae260f8.png" class="image--center mx-auto" /></p>
<p>Het is vrij simpel maar vrij functioneel — een lijst van podcasts, met een knop om de afleveringen weer te geven, die elk individueel afgespeeld kunnen worden. De gedachte is dat voor afspeellijsten e.d. kun je Spotify of een andere podcastspeler gebruiken. Het integreren met de Spotify-speler was vrij simpel, en heel veel opties zijn er niet, dus ook niet veel om je zorgen over te maken. Op de eerste dag staan er 106 podcasts in de lijst. In de geest van opensourcewerken staat alle code op <a target="_blank" href="https://github.com/Koenvh1/Rijkspodcast">GitHub</a> — daarnaast is dat ook gewoon makkelijk.</p>
<p>Zoals altijd deelde ik dit met collegae, met wie ik het sowieso al vaker had gehad over dit idee. Die kwamen met een paar kleine suggesties. Niet lang daarna deelde ik het op sociale media, <a target="_blank" href="https://www.linkedin.com/feed/update/urn:li:activity:7350868440271753218/">waaronder LinkedIn</a>, want zodra het goed genoeg is om te delen hoor ik graag wat anderen ervan vinden zodat ik iets met die suggesties kan (en als er geen aandacht voor komt, dan besteed ik er liever ook niet onnodig veel tijd aan). Ik blijf me verbazen dat LinkedIn mijn meest populaire sociale mediaplatform is. Blijkbaar worden mijn posts graag gelezen — en daar ben ik uiteraard blij om. Mastodon staat steevast op plaats twee, en Twitter en Bluesky krijgen eigenlijk nul aandacht. Het leuke van LinkedIn is dat het ook weer gedeeld wordt door anderen, waardoor het uiteindelijk bij de mensen binnen de overheid terecht komt. Dat enthousiasme word ik altijd blij van.</p>
<p>Volgens mij is het feit dat deze podcasts normaliter zo onvindbaar zijn wel exemplarisch voor de Rijksoverheid: anonieme gebouwen, geen namen en contactpersonen op de website — alleen als je de mensen kent kom je er makkelijk mee in contact. Overigens zijn dat niet mijn woorden, maar die van toenmalig topambtenaar bij Justitie en Veiligheid Dick Schoof in 2022 in de podcast “<a target="_blank" href="https://www.algemenebestuursdienst.nl/actueel/nieuws/2022/07/04/podcast-tussen-publiek-en-politiek-topambtenaren-over-wat-nodig-is">Tussen Publiek en Politiek</a>” gemaakt in opdracht van Binnenlandse Zaken. Hetzelfde geldt voor deze podcasts: als je weet dat ze bestaan kun je ze vinden, als je het niet weet dan vind je ze ook niet. En dat is best jammer. Het algemene vertrouwen in de overheid is niet heel erg groot, terwijl er toch best veel goede mensen werken die goede dingen doen. Die zie je alleen niet. In sommige van deze podcasts hoor je ze echter wel.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://open.spotify.com/episode/3URthxGtT8810929AdNdR7">https://open.spotify.com/episode/3URthxGtT8810929AdNdR7</a></div>
<p> </p>
<p>Ik was ondertussen ook wel benieuwd wat nou het verhaal was achter die MP3’s op een website. Tot nu toe had ik ze niet toegevoegd, want dat ging vrij lastig. Ik heb de vraag maar gewoon gesteld, en binnen een dag kreeg ik antwoord. Lang verhaal kort: het kost geld om het in de lucht te houden, en dat was niet de moeite waard, dus dan maar als losse bestanden op een website. Zonde eigenlijk. In mijn zoektocht viel het me namelijk al op dat elke organisatie podcasts op haar eigen manier publiceert, vaak met betaalde diensten van derde partijen. Het is dus goed mogelijk dat deze op den duur voor goed verloren gaan zodra gestopt wordt met betalen voor die diensten.</p>
<p>Even een klein zijspoor. Podcasts gemaakt door de Rijksoverheid mogen niet zomaar verwijderd worden. Podcasts vallen onder “publiekscommunicatie” volgens de Archiefwet. Dat betekent dat ze gearchiveerd moeten worden, en na 20 jaar de podcasts in het Nationaal Archief komen. Nog geen enkele podcast is 20 jaar oud, dus nu zijn ze daar nog niet te vinden. Ik heb met het Nationaal Archief contact opgenomen, en die bevestigen dit. Tot die tijd moet de organisatie zelf erop toezien dat dit fatsoenlijk gearchiveerd wordt. Ik ben benieuwd of de realisatie dat podcasts ook onder publiekscommunicatie vallen bij elke organisatie doorgedrongen is — ik vrees van niet. Ze mogen overigens wel offline worden gehaald (zolang ze maar gearchiveerd worden), maar dan kunnen ze met een WOO-verzoek (Wet Open Overheid) weer opgevraagd worden — immers is geen van de uitzonderingsgronden hier van toepassing — en WOO-verzoeken moeten in principe gepubliceerd worden. Kortom: offline halen heeft niet zo veel zin, behalve voor de kosten.</p>
<p><img loading="lazy" src="posts/het-verhaal-achter-het-rijkspodcastregister/e0895b2c-8b37-4c33-92de-fae0a503edf0.png" class="image--center mx-auto" /></p>
<p>Er maar gewoon op vertrouwen dat de overheid dit goed voor elkaar heeft vind ik onverstandig — ik heb al eerder gezien dat de informatiehuishouding niet altijd op orde is, en als iets weg is, is het weg. De Inspectie Overheidsinformatie en Erfgoed gaf in een antwoord aan me aan dat die zorg van mij, dat niet elke organisatie zal herkennen dat podcasts onder publiekscommunicatie vallen, legitiem is. Met een beetje geluk zorgt die extra aandacht dat die realisatie er iets meer komt. Daarnaast heb ik op advies van het Nationaal Archief contact opgenomen met Beeld &amp; Geluid om te kijken of we proactief podcasts kunnen archiveren. Die gaven aan interesse te hebben.</p>
<p>Archivering is één ding, maar er zijn nog meer zaken waar iets mee gedaan moet worden. Het feit dat veel podcasts exclusief op Spotify staan bijvoorbeeld. Die kan ik namelijk niet archiveren, want die staat online met DRM, naast het feit dat het sowieso gek is podcasts gemaakt met publiek geld exclusief op één commercieel platform staan (terwijl het ook anders kan).</p>
<p>Ik heb verschillende organisaties, waaronder De Academie van DPC en ICTU (de uitvoerders van <a target="_blank" href="https://social.overheid.nl">Mastodon Overheid</a>), voorgesteld om een centraal "Rijkspodcastplatform" op te zetten. Dit lost namelijk het vraagstuk met kosten, archivering, en open standaarden in één keer op, en creëert direct een overzicht — dan hoef ik dat niet meer te doen. ICTU antwoordde dat ze helaas geen eigen initiatief kunnen nemen, maar heeft wel aangegeven de suggestie door te sturen naar de opdrachtgever bij BZK. Heel fijn, want dat lukte me niet om die zelf te benaderen.</p>
<p>In de tussentijd was ik ook nog aan de slag om wat losse eindjes van de website op te schonen. Zo ontbraken er van sommige podcasts afleveringen als er meer dan 50 waren, ging het updaten nog handmatig. Dat is nu geautomatiseerd, en je kunt nu sorteren op datum, wat ook een stuk handiger is. Je kunt nu ook zoeken, want ondertussen is de lijst al meer dan 150 podcasts lang, en werd scrollen vervelend. Toen het er nog maar 50 waren ging dat nog wel.</p>
<p>Een van de redenen dat er zo veel nieuwe podcasts bijkwamen was <a target="_blank" href="https://www.reddit.com/r/thenetherlands/comments/1m7ezdp/ik_ben_bezig_met_het_maken_van_een/">Reddit</a>. Ik had het daar gedeeld, en dat werd populairder dan ik van tevoren had verwacht. Grappig hoe veel meer er is dan je van tevoren denkt. De dag daarna ontving ik een e-mail van iemand van WODC (Wetenschappelijk Onderzoeks- en Datacentrum, onderdeel van het Ministerie van Justitie en Veiligheid) dat hun podcast nog ontbrak. Dat was erg leuk, want ik kende het WODC nog niet — blijkbaar wordt het ook daar gezien.</p>
<p><img loading="lazy" src="posts/het-verhaal-achter-het-rijkspodcastregister/be136e9a-39f6-46a6-a9cb-2708cbdddb7c.png" class="image--center mx-auto" /></p>
<p>Mijn doel was om op veel verschillende plekken dit onder de aandacht te brengen, zodat er langzaam maar zeker tandwielen kunnen gaan draaien om de huidige situatie te verbeteren. Een aantal organisaties heb ik al genoemd, maar ik zal je niet met de hele lijst vervelen — misschien doe ik dat later nog als er nog nieuwe ontwikkelingen zijn. Al deze processen duren lang — ik verwacht dus ook geen direct resultaat. Sowieso is de feedback van veel organisaties positief over dit initiatief, en anders komt er gewoon geen antwoord, dat gebeurt ook.</p>
<p>Uiteraard kwam tijdens het verspreiden hiervan ook de opmerking die ik al verwacht had: “gaat daar ons belastinggeld nou naartoe?”. Een terechte vraag, maar ik denk op het hele overheidsbudget een druppel op een gloeiende plaat. En hé, als dat geld toch al is uitgegeven, kun je er maar beter gebruik van maken. Deze podcasts zijn technisch gezien een vorm van propaganda — onafhankelijke journalistiek is het niet, maar dat hoeft ook niet. Soms merk je dat ook duidelijk terug, en is de podcast duidelijk gemaakt met een doel, maar vaak vinden mensen het ook gewoon leuk om over hun werk te praten.</p>
<p>En als je nu denkt: welke van deze 100+ podcasts moet ik nou luisteren? Ik kan zelf <a target="_blank" href="https://www.aivd.nl/onderwerpen/werken-bij-de-aivd/podcast-de-dienst">De Dienst</a>, <a target="_blank" href="https://www.rijksoverheid.nl/ministeries/ministerie-van-binnenlandse-zaken-en-koninkrijksrelaties/verhaal-van-bzk/podcasts-bzk/tussen-publiek-en-politiek">Tussen Publiek en Politiek</a>, <a target="_blank" href="https://www.nvwa.nl/nieuws-en-media/nieuws/2022/09/05/nvwa-iod-geeft-luisteraars-inkijk-in-opsporingsonderzoeken">Roet in het Eten</a>, en <a target="_blank" href="https://achter-de-schermen-bij-nederland.castos.com/">Achter de schermen bij Nederland</a> aanbevelen, maar ik heb natuurlijk niet naar alle 100+ podcasts geluisterd.</p>
]]></description>
			<link>https://blog.koenvh.nl/het-verhaal-achter-het-rijkspodcastregister</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/het-verhaal-achter-het-rijkspodcastregister</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 02 Aug 2025 13:50:09 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[The tale of backups continues]]></title>
			<description><![CDATA[<p><a target="_blank" href="https://blog.koenvh.nl/a-tale-of-backups">Some time ago I wrote about backups</a>, my love-hate relationship with most back-up software available, how Duplicati had let me down when it came to rebuilding my database, and I switched to my own solution. Let’s have a look at what happened since then.</p>
<p>My own solution works remarkably well. I thought it was quite fast at first… and I still do. That part of the story is very short — it just worked. I had to get used to starting it rather than just shutting down my computer though. I still had Duplicati running on the side, which ran once per week, or at least, it should.</p>
<p>According to the release notes, some of my grievances were fixed in the latest canary release version, so I gave that a try. That sadly resulted in me having to nurture my backup every time it ran. I do not blame testing versions for being unstable, but it was annoying nonetheless. Generally a repair would fix it. That was until the end of June, when those attempts did not work either:</p>
<p><img loading="lazy" src="posts/the-tale-of-backups-continues/bfe3e958-18fe-4510-8f30-266022c18f1b.png" class="image--center mx-auto" /></p>
<p>As the dates and time of day also give away, any time I try anything, it takes several minutes before I see the result. Apart from some live logging, what goes on in Duplicati is a black box. That was quite frustrating to work with.</p>
<p>In the meantime, my gripes with backup software had resulted in a suggestion I had seen popping up a few times already: restic with Backrest. Restic seemed very sensible, but the lack of a Windows installer and the heavy reliance on the command line made me wonder if it was the right choice. Luckily Backrest solves that issue by providing a nice web GUI over restic, and an installer for Windows as well. In a way it’s small things, but they were just enough to make me give it a try.</p>
<p>I think this shows both the bad and the beauty with a lot of open source software. The bad is that because, presumably, the developers don’t really want to make a GUI (which, having developed GUIs, is fully understandable), they didn’t. The beauty is that someone else can come along and decide they will just make one. That’s what Backrest in essence is.</p>
<p>Still, none of this helps with discoverability, which admittedly commercial software tends to excel at. Flashy webpages promoting the product that can be installed in one go, rather than some side-project on a GitHub page. Mind you, that’s not critique of Backrest — I just wish I had found it myself. It reminds me a bit of trying to get people on Mastodon. If “How do I sign up for Mastodon?” already does not have a straight-forward answer (well, it’s a federation of servers etc. etc.) you’re going to lose a lot of people, which is a shame, because Mastodon is a great concept, and so is Backrest.</p>
<p>One of the reasons I like a GUI for backups is because I always feel a bit uneasy when configuring them. You only <em>really</em> know whether you did it correctly when you need it, so you can’t really afford mistakes. I have a fire proof safe for important documents, or at least, it was sold as fire proof and I hope it is, because I never tested it. Backups are the same. Also, configuring them that way is a lot easier, as well as monitoring progress. Command lines are great, but for tools like backups or video conversion I still far prefer GUIs (the ffmpeg command line syntax feels like witchcraft).</p>
<p>I started the Restic backup and… it worked. I know that is not a very high bar, but still not something I take for granted. It was rather quick too, admittedly, having Backblaze B2 as a back-end means I can use my entire 1 Gbps upload — hurray for modern internet. It ran for several hours, seemingly everything was going fine. I let it run, and when I came back it showed a non-descript error at 1.88 TiB out of 2.91 TiB. The only real error was a docx file it could not back up because I had it open in Microsoft Word at the time, but that should not prevent the other 1.5 million files from being backed up. Annoying, but I was not ready to give up that fast.</p>
<p>I decided to run it again, maybe it was just a fluke. Sadly that meant another 12+ hours waiting for things to complete. That one however also failed, <em>grmbl grmbl</em>. Because it had not completed a snapshot yet, it would check all files again. It did not need to upload them, but checking the hashes took plenty of time already. In this case another 11 hours, give or take.</p>
<p><img loading="lazy" src="posts/the-tale-of-backups-continues/95c02764-11a8-4eb8-b3a1-c27006fc9ff7.png" class="image--center mx-auto" /></p>
<p>But after that it finally worked — hurray! Because now the first backup was done, every subsequent backup was a lot quicker too. Whereas attempt 3 still took over 11 hours to upload ~300 GB of data, the next run only took 20 minutes. Those times were more reasonable. This was so fast and performant, I might as well run it daily again rather than weekly.</p>
<p>Unrelated to my desktop, but my laptop’s backup also randomly failed. A notification popped up “backup failed”, I clicked on it, and it showed me a window with something along these lines on it:</p>
<blockquote>
<p>Backup Failed</p>
<p>Invalid data - SHA1 hash mismatch for file:<br />duplicity-inc.20130124T230054Z.to.20130201T225108Z.vol1.difftar.gpg</p>
<p>Calculated hash: 7726f55012e1e26cc762c9982e7c6c54ca7bb303<br />Manifest hash: 205ecad0a91f8a11967b70d2d3fbc8e4d06231f5</p>
</blockquote>
<p>I really doubt someone like my mother could make heads or tails with this information (apart from the “Backup Failed” part). Even worse, there was only one button: “Close”. Running the backup again resulted in the same error. I could probably somehow fix it, but I decided to just nuke my backup and start over again, it’s small enough for that, but I am still very annoyed that I had to do that. Apart from taking time, for a layperson this is absolutely not set-and-forget.</p>
<p>Anyway, back to my desktop. Now that my backups have been running for a couple weeks, I have next to no complaints. It is still performant. I added Healthchecks.io for monitoring (which Backrest has built-in support for) and that, apart from a few things when setting things up, it worked alright. The scheduling was a bit annoying — it turns out it expects my PC to be on at the time it is scheduled, or else it will just skip it, rather than schedule it whenever my PC turns on. I “fixed” this by setting it to an 18-hours-after-the-last-backup schedule, which does run as soon as the 18 hours are over. This is good enough for what I want to do — run it daily at sometime at the start of the day.</p>
<p>With the new things set up and working, it was time to get rid of the old. I had made a new bucket on Backblaze B2 for the restic backup, so that I could always revert to Duplicati. After a week I figured it was stable enough, plus paying for double the space was not cheap. Turns out you can’t just delete all files, but you can set a policy that removes all files in 24 to 48 hours. With that done, all my Duplicati files were gone. I also uninstalled Duplicati, and removed the database.</p>
<p>I really hope that this ends my backup story, and that it can be “and they backed up happily ever after”. If you don’t hear me moan about this in the future, then assume that this worked out alright :-)</p>
]]></description>
			<link>https://blog.koenvh.nl/the-tale-of-backups-continues</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/the-tale-of-backups-continues</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Tue, 22 Jul 2025 13:08:56 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[April Fools 2025: Phone books and adverts]]></title>
			<description><![CDATA[<p>I am a big fan of doing silly things seriously. Going through with a plan fully, where the motivation is pretty much “because I can” is a blast. They make people laugh. Most April Fools “jokes” are quite terrible in that regard, generally just an announcement to then say “Ho ho, April Fools!” — and the audience goes <em>mild</em>. Real jokes take some effort, and make everyone laugh in the end. They can be silly, or slightly satirical, but they are not mean. Luckily they also exist — every year Google Japan used to bring out a keyboard video, like this one for example (see https://yt.be/keyboards for a full list):</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=9G3DWHf1xX0">https://www.youtube.com/watch?v=9G3DWHf1xX0</a></div>
<p> </p>
<p>This year I also decided to take some effort and make some of my silly ideas real (and hopefully cause a smile in the process). Actually, I had some silly ideas that happened to surface again just before April Fools. Had they happened another time of the year, I’d have done them regardless, just that now there was a good reason to get them done before a certain time.</p>
<h1 id="heading-ad-fund-em">Ad Fund ‘Em</h1>
<p><img loading="lazy" src="posts/april-fools-2025-phone-books-and-adverts/eb9cd90c-1e2e-4766-9682-e6ccf19f3205.jpeg" class="image--center mx-auto" /></p>
<p>The idea for this was one that I had some years ago. During a discussion about funding for academia, I jokingly suggested adding adverts to research papers. After all, the internet is littered with them (but not this blog!). That was received very well, but never made it past the “funny idea” phase. Recently <a target="_blank" href="https://www.volkskrant.nl/wetenschap/megabezuiniging-op-wetenschap-betekent-1-200-ontslagen-sluiting-dreigt-voor-beroemde-school-voor-astronomie~bb607fbc/?referrer=https%3A%2F%2Fwww.google.com%2F">funding in academia came under threat even more</a>, which triggered this idea again. It slightly critiques the current stance (I don’t think the funding cuts are a great idea, especially in the current manner), and also comments on the ubiquitous nature of adverts in current public space. It’s also just funny.</p>
<p>Additionally, <a target="_blank" href="https://sigbovik.org">SIGBOVIK</a> was coming up. SIGBOVIK is a conference I have loved for a long time, and admittedly consider one of the highlights of the year. It’s a conference held around April 1st organised by computer science students of Carnegie Mellon University. It is a satirical take on academia and academic conferences and publishing. The most important part is that people do things you would never get published in a real journal, as every reviewer would ask “… but why?”. It is silly ideas done seriously, serious ideas done silly, and everything in between.</p>
<p>Given that I had had this curiosity on how to make LaTeX packages anyway, and the fact that I had time, I decided to just give it a go. Of course every scientific paper needs a name, and I quickly came up with Ad Fund ‘Em, a pun on <em>Ad fundum</em>, which in student life means “drinking an entire glass of beer (or other alcohol) at one time”. I don’t drink, but I couldn’t let a good pun go to waste.</p>
<p>The result <a target="_blank" href="https://raw.githubusercontent.com/Koenvh1/adfundem/refs/heads/main/paper.pdf">can be found here</a> or in the <a target="_blank" href="https://sigbovik.org/2025/proceedings.pdf#page=103">proceedings on page 99</a>. Making the package itself went rather smoothly, and the responses I got afterwards were all what I was hoping for: people were both amused and horrified. One of the fun things about writing something like this is that I can slightly let the academic rigour slack, and add remarks in figures that offer no real value other than bringing a smile to the reader. I often have these thoughts when writing actual scientific work as well, but then I keep them to myself.</p>
<h1 id="heading-dialns-redux">DialNS redux</h1>
<p>I have already written about <a target="_blank" href="https://blog.koenvh.nl/dialns-a-dns-resolver-over-the-phone">DialNS</a> earlier, so this was not a new idea. However, as I was making the paper submission for SIGBOVIK, I realised I might as well re-record DialNS and submit it as a presentation. Where’s Ad Fund ‘Em does not work in presentation form, DialNS does not really work in text.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/_vgXi80y7Us">https://youtu.be/_vgXi80y7Us</a></div>
<p> </p>
<p>I submitted it to SIGBOVIK, and then I waited for the conference to come around. Most other years they had a livestream from the room at Carnegie Mellon University (generally the quality was not great, but hey). I sent an email beforehand if there would be one this year as well, but I received no reply. When the conference came around, there was no livestream. That was a real let-down — I assume my submission was shown, but I have no idea whether it was, or how the audience reacted to it. That feels really strange.</p>
<p>At least some strangers on the internet found it — presumably because they, like me, were looking for any footage from SIGBOVIK 2025 on the internet (at that point I was not even sure it had taken place). All positive reactions. You might also spot a subtle reference to my SIGBOVIK paper laying on top of the printer: that was not coincidence :-) Apart from that it did not take much work to record: the video limit was 3 minutes, and most of the humour is the functional-yet-inefficient method of doing this via the phone, so apart from the opening I had not much to prepare. You can still call it yourself at the moment of writing (May 2025): +31 85 369 5573.</p>
<h1 id="heading-phone-book-of-the-internet">Phone book of the Internet</h1>
<p>The saying “the DNS is the phone book of the internet” is one I have heard many, many times already. It is not difficult to see how that would translate into turning it into a real book. A few weeks before April Fools the idea came up again (although I do not quite remember how, though probably related to DialNS). My mind started thinking about how I could actually do it. Ideally I would make a Dutch phone book, but the .nl zone is sadly not open, so that was not possible (let alone the size it would be — it would be about a metre thick).</p>
<p>For my actual research, I had been using the <a target="_blank" href="https://radar.cloudflare.com">Cloudflare Radar</a> dataset of top 1,000,000 most resolved domain names (according to Cloudflare’s own public resolver, 1.1.1.1). This was a good enough list of domain names to use for this silly idea as well. I set about thinking about how I was going to resolve all those 1,000,000 domain names (my simple sequential Python scripts would not work for that), but when discussing it with a colleague he mentioned that <a target="_blank" href="https://openintel.nl/">OpenINTEL</a> probably has that data. That would make things a lot easier, plus, you know, it is a project where I know the people working on it. A quick look, and he was right. Additionally, it allowed me to find out how to work with parquet files (which I had never done before) — it is remarkably simple in Python to load those into a Pandas DataFrame.</p>
<p>The format I wanted was quite simple: left aligned domain name, right aligned IP address, and something in between to fill the space. I based this on images I could find from old phone books, which were surprisingly difficult to find online. Though to be fair, there generally does not seem to be a good reason to photograph the content of a phone book. I based myself on pictures like <a target="_blank" href="https://i.regiogroei.cloud/6e37f31a-ebd0-3ca8-99fa-52462a640d94.jpg?width=1000&amp;height=523&amp;aspect_ratio=1000:523">this</a>. Initially I had the plan to use HTML, and then turn the HTML into a PDF (similarly to <a target="_blank" href="https://blog.koenvh.nl/how-i-made-and-printed-252-rally-pacenotes">how I printed 252 pacenote booklets</a>). However, I was not pleased with the result I got, and using more columns in print in HTML with CSS was a mess. I also made a text-based version, where I calculated the amount of dots necessary to fill the space between the domain name and IP address, but real phone books were not using monospace characters (at least not in Europe), so it felt “off” to have that here.</p>
<p>I quickly decided to use LaTeX. I have a love-hate relationship with LaTeX: it’s slow, the syntax is awful to work with if you try to do anything special with the formatting, but the end result looks incredibly pretty. I would have to generate most LaTeX code, and that I did with some good old fashioned Python string replacement. I tried something out, went to compile it, and <em>compiler out of memory.</em> Oh. I have learned a lot about the memory management of the three different LaTeX compilers (pdflatex, xelatex, and lualatex) I have on my machine. Some more fiddling and I got a version working that compiled. The LaTeX to make this work is not pretty, but it works and that’s what counts in print. Some more tinkering, making sure the fonts are correct (I do really like Inter, especially when it’s small print), and I had a layout I was happy with. I also discarded all domain names that do not fit on the line together with their IP address, because dealing with overflowing columns was not on the list of things I wanted to deal with.</p>
<p><img loading="lazy" src="posts/april-fools-2025-phone-books-and-adverts/e1aa096a-c2d5-483d-9117-e6563af759bc.jpeg" class="image--center mx-auto" /></p>
<p>Initially I had every new letter start wherever it wanted in the column. It was just a large section header. This worked fine, except compile times went up to around 5 hours, which was a bit more than I wanted. A colleague suggested compiling every letter separately and in parallel, and then including the PDF back into the original. It felt like a hack, so I did that, and it worked marvellously. I also got to use all my 32 cores in my laptop for once. A side-effect was that now every letter started on a new page, not that that was an issue.</p>
<p>April Fools was getting closer, and when I showed at work what I had done on April Fools itself, they were so enthusiastic that we decided to turn it into a work thing. <a target="_blank" href="https://nlnetlabs.nl/news/2025/Apr/01/phonebook-of-the-internet/">Article written</a>, picture made, PDF online. It got far more attention than I expected — turns out seeing your own domain name among thousands of other domain names is quite popular. Many comments about how funny it was, the puns, also DialNS got some more attention again (because I added it to the article).</p>
<p>One comment I got a lot was about the lack of IPv6 support. I can show you why it only uses IPv4:</p>
<p><img loading="lazy" src="https://media.licdn.com/dms/image/v2/D4E2CAQEpaNz3EIhwnw/comment-image-shrink_8192_800/B4EZaIEnD0HoAY-/0/1746039633461?e=1747350000&amp;v=beta&amp;t=lEF4yO_wD5gKXREU14UZhOnDaJPdgsqyiM6b2qy8uqg" /></p>
<p>Another comment that popped up quite often was “I’d buy a book if you made one”. That did not sound like a bad idea, so strengthened by the many positive comments, I went looking on the internet how much it would cost to get it printed. I sent an email to a printed that claimed it could print the original ~3200 page version, after a week I got the reply that they <em>could</em>, but that they generally only do up to 800 pages. A bummer, but understandable (even more so after I received the actual physical version, it’s heavy enough). I reduced it to the top 200,000 domain names, which reduced it to 602 pages. The price also dropped significantly to just over €20 (instead of ~€80), which was a price I was willing to do this for.</p>
<p><img loading="lazy" src="posts/april-fools-2025-phone-books-and-adverts/06a54496-78fd-4dc3-a07c-7ea8dcb97102.jpeg" class="image--center mx-auto" /></p>
<p>The most work was designing a cover. I wanted it to look like a book about the internet from the 90s or early 2000s. I used <a target="_blank" href="https://i.redd.it/d0mojqvbku741.jpg">this picture</a> of the Internet Yellow Pages from 1996 for a lot of my inspiration, as well as similar “Internet” books from the same era. I installed Microsoft Publisher 2003 to get period correct images for the front and back. The quote on the front was provided without asking by my university supervisor (and one of the founders of OpenINTEL) Roland — thanks Roland!</p>
<p>Another things that took quite a bit of work was making the tabs on the side work. I wanted the “which letter are you at now” style tabs that you had in the phone book back in the day as well. I found some LaTeX things online that created tabs, and with some bodging I managed to make it work. Again, the LaTeX code looks horrible, but the end result is pretty, and that’s what matters. A nice side-effect (that I did not account for) is that you can see the thickness of each section on the side of the book:</p>
<p><img loading="lazy" src="posts/april-fools-2025-phone-books-and-adverts/3d0d6acf-bcc8-4598-948a-c0af156e9f89.jpeg" class="image--center mx-auto" /></p>
<p>I had already found the printer I wanted to use. Their systems were sadly a bit slow, especially if you are playing around with things. It would often take hours before their automated system would tell me whether the file I had provided was correct.</p>
<p>I also found that they had a button to request an ISBN, and that free of charge. That was an opportunity I could not let pass — so yes, this book has an ISBN: 978-9-46-511631-0 to be precise. I looked up whether that meant I now had to use a fixed book price, but that’s only for Dutch and Frisian works, and this one is <em>technically</em> English due to the title page. A relief, otherwise I would have had to register this book at the <em>Commissariaat voor de Media</em>.</p>
<p>I was finally happy with the way it looked, I ordered one, and then I waited for it to arrive. It was both shipped later and delayed, which was quite annoying. I expected it to be delivered in my letter box, but the book is too thick to fit. It was a lot bigger and heavier than I anticipated, even though I knew the dimensions. It only really dawns on you when you have it in hand. Of course every first print contains a mistake — this one was no different. I had made the tabs slightly too close to the edge. According to their documentation it should be within the margins, but it was not. Of course I took it to the office, and my colleagues thought the books was very funny as well. I ordered one for Roland too, and a few other friends and colleagues now have one as well. I also added the buy link online. That’s not me trying to sell you this, just saying that if you want one, you can buy one. The source files are also available, if you prefer that option.</p>
<p>All in all this year was a very busy April Fools. One that was a lot of fun for me and others. I think I accomplished the goal I initially set out of making people smile. I don’t have any plans for next year, but I am sure I will be able to come up with something.</p>
]]></description>
			<link>https://blog.koenvh.nl/april-fools-2025-phone-books-and-adverts</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/april-fools-2025-phone-books-and-adverts</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 09 May 2025 22:13:51 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[How I made and printed 252 rally pacenotes]]></title>
			<description><![CDATA[<p>In <a target="_blank" href="https://blog.koenvh.nl/splitsing-drie-links-niet-snijden-500">Splitsing drie links niet snijden 500!</a> I wrote about how I ended up making a Dutch co-driver voice for EA WRC. Whilst I was doing that, I ended up rummaging through the game files. It’s always interesting to see how the game works internally. Whilst I was going through all the assets, I was wondering if I could do something with it — I ended up printing the pacenotes for all 252 stages in EA WRC.</p>
<p><img loading="lazy" src="posts/how-i-made-and-printed-252-rally-pacenotes/186674cf-6ceb-47a4-95c9-301783b236f7.jpeg" class="image--center mx-auto" /></p>
<p>Let’s back up slightly to 2022. At that time I was playing a game called Dakar 18, which aimed to simulate (or approach, at least, it was not a full on simulator) the famous Dakar race from 2018. Dakar works slightly differently than WRC - the roadbook gives directions of where to go, rather than highlighting every corner. It’s more the directions of a scavenger hunt, telling you to follow a compass heading for the next 5 kilometres. The game had quite a few stages, and looking at the game files, I found that the roadbook notes were actually stored in an easily machine-readable format. Given the central role of the roadbook (and admittedly also the annoyance of the codriver voice, who was overenthusiastic and not focussing on giving me the right information in time and then berating me for going the wrong way), I decided to make a physical version. This was very well received by the developers as well.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Koenvh/status/1483533138974416898">https://twitter.com/Koenvh/status/1483533138974416898</a></div>
<p> </p>
<p>Later that year the next version of the game came out. The stages were different, and the roadbook format had changed (it now had colour!). The amount of stages and the number of pages also would no longer fit in a single book, so I ended up printing it as a separate booklet for every stage. I had them printed as “print samples” to save on money, and frankly I don’t mind the watermark. Again this was well-received by the developers and the community.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/Koenvh/status/1595205019854426113">https://twitter.com/Koenvh/status/1595205019854426113</a></div>
<p> </p>
<p>That was 2022. Forward again to 2025. I was reminded of the Dakar booklets when I was browsing through the assets for EA WRC. I decided that it might be nice to have pacenotes for EA WRC printed on paper as well. I looked through the assets with FModel, a community tool to browse through Unreal Engine assets, to see if I could find some definition for the stages. It also has a JSON export function that came in really handy. After some searching I found the asset, which I thought was just one stage, but actually contained every stage from that location. Searching through that file was sometimes a bit tedious, as it is ~500 MB of JSON file. However, with some searching I found an array with co-driver calls.</p>
<p>Now these were the co-driver calls, but where were the actual calls that the co-driver says in-game? Initially I found a property called <code>m_scriptLineModeComplex</code> which seemed to contain what I was looking for. I printed an entire stage that way, and tried to match it up with an existing stage based on a video I found on the internet. The calls were very close, but not quite what I was looking for. This caught me slightly off-guard at first, although I now believe these were notes made by the developers what the calls should be, and later they were changed slightly.</p>
<p>I also noticed that often there would be large gaps where there was no data. Upon some inspection, I found out that there is a <code>parentId</code> property, that, when not 0, means that the calls should be used from the parent call instead. I believe this is because when EA advertises with, e.g., 12 stages in Chile, what is actually happening is that they have one long stage in Chile, that same stage in reverse, and then 10 subsections of that one stage as their own separate stages. This is also very apparent from the stage maps visible in-game.</p>
<p>Now, I had data, but I still did not know <em>where</em> the actual calls were. It took some time before I realised the property <code>m_segmentIdList</code>, which just contains a list of integers, actually refers to the IDs of the different calls. Once I exported the master call list and matched up the IDs in that list with the textual representation of that call, it suddenly fit like a glove. Eureka! All of this was written using many loops and parsing in Python — by hand this would have taken ages.</p>
<p>I decided to log all the stages in that file, and, for for example Chile, I nicely got an output with all 14 stages. Hold on, 14? According to the game there were only 12… Hmm… It seems that there are stage variations made for the game that are not in the final game, and the annoying thing is that it does not tell me which stage is which — to debug my system I looked up a stage video on YouTube, and tried to match the calls to one of the 14 stages in order to find out which one it was. Given that, at the time of making, there were 252 stages in EA WRC, that was not a sustainable method. Also, there were clearly some imposter stages in there as well that made things even more difficult. It just told me that it was stage 0, 1, 2, 3, …, 11, 12, 13. After digging through some more files, I found that the translation files actually used those numbers as a key as well. This allowed me to also filter out which ones were not being used, and I could thus discard.</p>
<p>As a quick intermezzo: every call also contains four booleans: <code>m_usedInWinter</code>, <code>m_usedInSpring</code>, <code>m_usedInSummer</code>, <code>m_usedInAutumn</code>. I knew Monaco was slightly different in Winter due to the ice on the road, but I was curious to see if there were any other differences based on the season. I can conclude that no, there are not. This is only used for Monaco in winter.</p>
<p>A question I still had not solved was how I was going to present the pacenotes. Dakar is nice in the sense that the roadbook format is standardised, and bar some minor details most of it is the same. WRC on the other hand is not — if you ask 10 different co-drivers their pacenote system, you get 11 different answers. I needed something that I could make work with the data I had (I was not going to edit it all by hand, that was infeasible).</p>
<p>Most co-drivers have a notebook that they scribble on by hand. I initially wanted to see whether I could get that aesthetic, but I quickly realised that the data I had was not suitable for that. The call data I had was too detailed for that, plus the spacing etc. would be difficult to get right, as the handwritten version is all based on feel and flow, and computers don’t really understand that concept. Pictures and general data on how people do it in real life is also just really quite scarce. Often I found myself staring at pixels to find out how a team did it. In my search I did however find a computer-generated format I quite liked, the <a target="_blank" href="https://jemba.se/inertia.htm">Jemba Inertia Notes System</a> (<a target="_blank" href="https://jemba.se/Sigdalsrally%202010%20SS2%20Page1.GIF">example</a>). The nice thing is that it worked line by line.</p>
<p>I still had to come up with shorthands for the actual calls though. The master call list contains the text as spoken, which is far too verbose to print. I looked for examples, but again there were only small bits and pieces I could find online. There is no uniform rally terminology, and whilst at first that was annoying, the positive side of that is that doing it wrong is quite difficult. It took a lot of tinkering, but in the end I managed to get something I was happy with.</p>
<p><img loading="lazy" src="posts/how-i-made-and-printed-252-rally-pacenotes/42125c54-b775-4cd2-bb9c-6782e4aab0bc.png" class="image--center mx-auto" /></p>
<p>Another feature that the Jemba system has that I thought was quite neat was the distance since the start a certain call appears. Looking at the data I had of EA WRC, I did not have that directly. I did however have a percentage of progress for each call, but the file did not contain the actual total distance per stage. Luckily that was not too big of an issue, as the EA WRC website and the game itself do somehow list that (not sure where they store that information). I manually made that list, let it do the calculations, and voila: a distance indicator as well.</p>
<p>At this point I had the data I wanted, but not yet the representation of how it would be printed. With the Dakar booklets I generated long HTML tables, that I then used wkhtmltopdf on to turn into a PDF. That system, whilst sometimes frustrating, worked well for Dakar, so I decided to also use it for EA WRC. Admittedly it’s quite simple - just a table, a table header, and some rows with data.</p>
<p>Compiling this into one PDF resulted in a total of 2908 A4 pages. That was not printable. Again, given the good experiences I had with the previous booklets, I decided to just do it per stage again. Sure, 252 is a bit more than 36, but surely that would not be too big of an issue, right? (Some foreshadowing: it was a bit more of a kerfuffle than expected)</p>
<p>But first: one of the things I did wrong with the Dakar booklets was that even though I designed fancy fronts, I did not design a rear or anything else. Some booklets just had empty white pages at the end. I wanted to avoid that this time. Given that the page number is always a multiple of four, I had to figure out what to do with the one, two, or three empty pages at the end. Someone helpfully suggested making them pages for notes, which I quite liked.</p>
<p>For Dakar, I designed the 36 fronts by hand (albeit systematically). For 252, I thought some more automation was a good idea. I really struggled getting the CSS just right to make the background and text work properly, and every compilation was not exactly quick, but in the end it worked out. For some reason making the background cover but not overflow in wkhtmltopdf was rather tricky. In the game’s assets I found the image the game uses for every location, and I figured I might as well reuse that on the front. Along with the logo for the location, a map of the stage, and of course the name, every booklet felt unique, but I could still generate them easily. All of this was slightly more work than initially expected, but that always seems to be the case.</p>
<p>I printed the Dakar booklets at a printer in Germany that sends out samples for free. I contacted them and asked them whether there was any limit to that, and their customer service kindly replied “No”. That was a clear answer. Now adding all the booklets by hand, uploading the PDF, etc. is tedious, so I did the sensible thing and scripted that. That script took quite some time to run, until the website became so slow it could not handle it anymore… whoops. I had to order them in four separate batches.</p>
<p>This German printer is relatively cheap, and one of the reasons for that is that most of it is on-demand printing that is highly automated. Orders are not necessarily grouped, and no human really looks at it. For that reason they charge shipping per individual item, which in my case was 252 × €7.95. Shipping within Germany is free however, and 252 × €0 is still €0. So I asked a friend if they could receive a package for me.</p>
<p>A couple of days later I receive an email that my order has been shipped… and about a hundred other emails that the order has been shipped. Scrolling through them, I quickly notice that a lot of them have a DPD tracking code, and all of them seem different from each other (but not all, there were some DHL ones in there as well). Oh dear, I had a small suspicion, but was still hoping that it would be batched. I was wrong. Apparently DPD arrived on their doorstep with 100+ individually packaged packages. Luckily they took it in good humour :-) A couple weeks later I came around to visit them (and take home my stack of cardboard). This is what my car looked like: the boot and back seat were filled with boxes.</p>
<p><img loading="lazy" src="posts/how-i-made-and-printed-252-rally-pacenotes/b5635fd8-e6db-475c-87d3-1b938ef75571.jpeg" class="image--center mx-auto" /></p>
<p>I had the luck that a couple of days after I arrived home, the paper collection was due. So I spent a total of 1½ hours on the parking lot, boot lid open, ripping open packages (and sometimes putting plasters on my fingers — cardboard can cut). I ended up with a stack of cardboard that the recycling people picked up the next day.</p>
<p><img loading="lazy" src="posts/how-i-made-and-printed-252-rally-pacenotes/8a400ad3-7fca-45cd-9676-487686d7766a.jpeg" class="image--center mx-auto" /></p>
<p>Most of that was packaging (and rather inefficient packaging at that). When laying it all out on the floor, this is what it actually looks like. Size-wise it is about three magazine racks on my shelf, so about ~21 cm wide. I also <a target="_blank" href="https://www.reddit.com/r/EASPORTSWRC/comments/1k52xpg/i_printed_the_pacenotes_for_all_stages_in_ea_wrc/">posted this on Reddit</a>, and the responses, also from the developers, were very positive. That was really great to hear. It is always a bit nerve-wrecking to upload this. In the meantime I had also <a target="_blank" href="https://koenvh.nl/wrc/">uploaded all the PDFs to my website</a>, and based on some of the Reddit comments people were actually using them. That was amazing! Quotes like “We've just had the most fun we've ever had simracing together” really make my day (and are quite a contrast to the comments I received for doing other things). Sadly I have not yet seen any footage of it being used. Maybe some day (and if you have found some footage, please send it).</p>
<p><img loading="lazy" src="posts/how-i-made-and-printed-252-rally-pacenotes/16238e55-4755-45b6-844f-04bc7787af30.jpeg" class="image--center mx-auto" /></p>
<p>I have shown this to friends and family as well, and a question that keeps popping up is “are you going to actually use this?”. Now the fun part is that I <em>can</em> use it if I wanted to, even though I don’t really plan on it. The fun is in making it, but the chance that I’m actually going to use it for a practical purpose is unlikely. The combination of working with a lot of data, typesetting, generating, automating, and graphics design is just a particular combination that scratches a certain itch. I had the same <a target="_blank" href="https://nlnetlabs.nl/news/2025/Apr/01/phonebook-of-the-internet/">when making a certain phone book</a>.</p>
<p>So that is how I ended up with a lot of paper booklets, a fun project, and a lovely story to tell later. I hope you enjoy this (very abridged) story as well. If you are interested, all the files are on the website, and if you end up using it, send me a message — I like hearing those stories.</p>
]]></description>
			<link>https://blog.koenvh.nl/how-i-made-and-printed-252-rally-pacenotes</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/how-i-made-and-printed-252-rally-pacenotes</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Wed, 07 May 2025 20:23:00 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[A tale of backups]]></title>
			<description><![CDATA[<p>I have data. Admittedly most of that data does not have a lot of value, such as a list of motorway mile markers in the Netherlands and their coordinates from 2021. Still, I have no reason to throw it away, thus I just keep it on my disk just in case I ever need it. I have been doing this since the very start, and I still have some of my earliest files. If I lose those files nothing bad would happen per se, but I still would rather not. More than once I have had a hard drive fail on me, so the logical solution is: backups. Backups are something incredibly ubiquitous, yet somehow my — what I consider a fairly common use case — seems to not fit most solutions, so this story is about my search for backup solutions.</p>
<p>I do not like throwing things away. That’s true for real life as well. Anyone who has ever seen my living room knows that I have a simple solution for that in real life: not buying things in the first place. My living room (and house in general) is minimalist, clean, and due to the large amount of flat surfaces sometimes a literal echo chamber. Luckily digital data is much easier to store, and as a consequence I have a lot more things that “might be useful some time”. Additionally, storage capacities have grown more than my data has — the total size of the files of my first computer is several gigabytes, probably small enough for most modern USB drives (not the variety that you get for free with some sort of logo printed on them; those still seem to use the same technology as 15 years ago). I was amazed when I bought my first 1 TB external hard drive, I mean, 1 terabyte! It’s big enough to get its own SI unit prefix! I still have that hard drive, as again I don’t like throwing things away.</p>
<p>My data is thus, really, my data. Files that I somehow collected over the years. It’s a collection of small files (documents, programming projects, configuration files), slightly larger files (photos, art projects), and large files (videos, virtual machines). Nowadays it is about 2.5 TB of things spread over about 1.5 million files. I do not consider that too strange or “a lot”, but we will come back to that later.</p>
<p>My first backups were simply copying files from my computer to an external hard drive. This worked well, except that it got a bit cumbersome after a while. After a long search I settled on SyncBack (Free) to sync my files to my hard drive. I know a second copy does not offer all the capabilities of an incremental backup, but even though I had 1 TB of external hard drive space, I did not have enough space for incremental backups.</p>
<p>When that 1 TB really ran out, I bought a 2 TB hard drive. Another neat feature was that it supports USB 3.0 with a USB B cable (the bulky one). Later I also bought a 3 TB one (with a micro USB 3.0 cable that was so short it was almost comical), as my internal drives became larger and larger as well. I actually still have a picture of what my drives looked like at the time — Bravo, Charlie, and Foxtrot were my internal drives, and Lima, Mike, and November were my external drives for backups.</p>
<p><img loading="lazy" src="posts/a-tale-of-backups/8f65f6c2-c508-4449-bbff-7db9e8fbbda3.png" class="image--center mx-auto" /></p>
<p>The speed up of USB 3.0 was a nice additional feature, because I had a tendency to start the backup at the end of the day, and I slept in the same room as my computer. That meant trying to sleep with the noise and light of the turned on computer, as I tended to underestimate the time the backup would take. I also would make a backup whenever I thought of it, which was about every two-ish months. That’s fine for most files, but annoyingly the most recent files are generally the ones you want to lose least, and whilst some were on Dropbox, SkyDrive, Google Drive, or whatever cloud service would give me a decent amount of free space, a lot of files were not, so I decided that I needed a better solution.</p>
<p>In 2014 I signed up for CrashPlan Home. I did this after looking at all the other options. One of the reasons I chose them was that the price was reasonable, and they offered <em>unlimited</em> storage. A lot of other options provided storage that even at that time I thought was rather limited — 100 GB did not cut it for me in 2014 either. Another reason I did it then is because we switched from ADSL to cable. Over ADSL we had 0.5 mbit/s up (I still have the speedtest.net history showing that), and over cable that turned into a whopping 10 mbit/s! That was 20 times faster, and more importantly, enough to actually consider an online backup. I selected (at this point around 300 GB) worth of files in the Java-based CrashPlan home application, that would fully redraw itself on every resize and was about as performant as trying to run Windows 11 on an ASUS Eee PC. It was also not very friendly on resources, but hey, it was worth it for the backups.</p>
<p>In 2017 CrashPlan Home stopped, but you could switch to CrashPlan for Small Business for quite a bit more, and get features you definitely don’t need as home user. At least they gave me a discount at the start. Begrudgingly I did that. Slowly my backup there started growing to 500 GB in 2018, 600 GB in 2019, 1.5 TB in 2020 (by that time I still had cable, albeit using a new upload speed of 25 mbit/s, so that took quite some time to upload), and 1.9 TB in 2021. At this point I had fibre and uploading a lot of data was no longer a problem.</p>
<p>In 2022 I finally used the thing that I had paid CrashPlan for all that time: I tried to restore some files after a hard drive had failed. Annoyingly when I did that I ran into some very slow restore speeds, even with my fibre connection. Restoring the 1 TB of files would take between 9 and 30 days(!) on my 1 Gbps fibre connection. According to their support, that was within their expected range — they aimed at being able to restore 30 to 150 GB per day. 30 GB in 2022! Needless to say I was not satisfied with that, so I decided to look for something else. Luckily I also still had the copy on my external hard drives that I could use instead.</p>
<p>After another long search I ended up using Duplicati with Backblaze B2 as back-end. I ended up using Duplicati because it did what I wanted to do, is open source, and seemed reasonably performant (in the context of backup software). I chose Backblaze B2 because of the price, reliability, and the fact they can physically send you a hard drive with your files (as long as you pay for it), which would avoid the situation I had with CrashPlan. Backing up went fine, and restoring went reasonably quickly as well.</p>
<p>Because Backblaze B2 bills based on usage, I was always a bit scared to use my backups. I had been eyeing at replacing it with a Hetzner Storage Box, because it had a fixed price. Sadly my fibre had terrible performance with Hetzner — it was like I was using ADSL again with less that 8 mbit/s speeds. Via some digging I contacted some engineers at my ISP and some engineers at Hetzner about it, and after some rummaging around, even though neither claims to have done something, <em>suddenly</em> it worked properly again - truly a coincidence :-) I decided to just give it a go, and I got a Hetzner Storage Box subscription as well. I had both configured in Duplicati: Hetzner would run daily when I booted up my PC, and Backblaze B2 would run every Tuesday.</p>
<p>For years this worked fine, until a couple of weeks ago my Hetzner backup suddenly no longer worked. My database had somehow gotten corrupt. Basically Duplicati keeps a local database of where files are, also for increments. This is basically a cache, and this database can be recreated from the backup itself. Oh well, I thought, database corrupt: let’s just recreate it from Hetzner. After 5 days it was still recreating the database, and because I was going away for the weekend, I did not just want to leave my computer running. Even though I had tested restoring, I never tested recreating the database, which apparently can take several days or weeks to complete. According to the forums, it might take days if you have “a large backup like 100 GB of small-ish files”. Well great, it’s 2025 and apparently having more than 100 GB of files is still an issue.</p>
<p>Basically once you have more than, let’s say, 500 GB, it seems that backups become difficult. A lot of off-the-shelf backup solutions aimed for normal desktop users don’t really support this amount of data. Backup solutions that do support it seem to be aimed at servers first, specifically Linux — I guess they do not expect people with Windows laptops and desktops to have some files they don’t want to lose. Duplicati was one of the few with a Windows installer. It also seems that the focus of a lot of backup solutions is quite different than what I care about. They boast about compression, data deduplication, etc., but frankly: if I decide to have the same file twice on my hard drive, then I don’t mind it ending up in my backup twice either, especially if that means I can do a restore in hours rather than days or weeks. I had plenty of time to look into alternatives as my database was slowly being recreated in the background.</p>
<p>Admittedly because I spent quite some time searching last time round as well, I quickly decided to give up on trying to find something that does what I want out of the box, and I decided to build something myself. Rather than wait for the backup to be recreated, I decided to get rid of it, the irony of fibre being that reuploading 2 TB of data was quicker than recreating the database. As much as making my own backup thing scares me, at least it would have no surprises. In the end I ended up scripting something around rclone. It is just a synchronisation of my files, no versioning or “advanced” backup technology. What I realised is that all the day-to-day versioning is nice to have, but not essential, whereas quick recovery of files in case of disaster is important to me, and something that a lot of backup solutions don’t seem to care about. I keep the versioning with the Backblaze B2 backup, that I keep using Duplicati for, because I want a backup for my backup, but I decided to move Hetzner to just a copy of my files. The only extra step I took is that rclone encrypts the files before they are uploaded.</p>
<p>The end result is just a simple script that calls rclone a couple of times for different folders. It can do a normal run in about 30 minutes for all my files, and in that process it also checks all remote files for integrity. This is quite a bit better than I had with Duplicati, which would take about 45 minutes.</p>
<p>This system worked really rather well - backups were relatively performant, and so were restores. There were still two open issues: monitoring and scheduling. Duplicati ran at startup. My solution did not have anything like that — it was just a command line prompt when it was running. I was visiting a friend who had a solution so simple I don’t quite understand how I did not come up with it myself earlier: I modified the script to make it shut down my computer after it was done. Then I could just execute the script before shutting down for the day, which had the additional advantage that the performance of my machine would not be impacted as was previously the case with the backup running when my computer booted up. At this point I was no longer sleeping in the same room as my computer, so that was no longer an issue. That was the scheduling part done.</p>
<p>Duplicati’s system tray icon would tell me if something was awry, and of course my solution does not have that, so I needed monitoring. I settled on the beautifully simple Healthchecks. I modified the script to curl a specific URL. If that does not happen for three days, I will get an alert via email and as a notification on my phone. Basically enough to tell me to remind me to look at my backups. This seems to be working well so far, and I’m really quite happy with the current setup. I added a small check to see if the rclone log contains “ERROR”, and if it does, it signals to Healthchecks that the last run ended with a failure. I get notified about that, which is great.</p>
<p>All in all I think that, for now at least, my backup situation is sorted. What still irks me however is that even in 2025 my backup is considered large by most software and platforms. A million files and 2 TB of data should not be an issue. Additionally, none of the things I found and tried were simple and intuitive. I have always pressed the importance of backups, also to friends and families, but it does leave a slightly bitter taste that I can’t just tell them “install this thing and you’re done”. All of them need configuration and more knowledge of the internals than I would like.</p>
<p>Duplicati definitely belongs to this category, where half of the things require looking at the logs or using the “command line interface” (which is a web page that makes commands, it’s an interesting thing that manages to confuse both command line users and web users). My own rclone scripting is definitely not beginner friendly. The ones that are beginner friendly expect you to have basically no files, or a second hard drive, or anything that is not “some files in C:\Users”.</p>
<p>So far the most simple desktop end-user back-up solution I have come across is Déjà Dup, which uses Duplicity under the hood (but that is not important) and is shipped with Ubuntu by default. It gets a long way there, but still, it has its own issues that make it not quite as smooth as it in my opinion should be. The main issue I have run into is that it sometimes creates another snapshot (which is fine), and because a snapshot basically adds all files anew to the cloud storage, that runs out of space. The fact cloud storage can run out of space seems to be a case not considered by the developers, and I get an unhelpful “403 Forbidden” error pop-up. I can, by a bit of looking, deduce that it has run out of space. Sure, increasing the amount of cloud storage is the simple solution if that’s available, but a bit more control over when to make a snapshot and how much space to use for it, as well as some detection to prevent it from running out of space in the first place, and then reporting that that is what is happening would be nice for laypeople. Additionally, when you try to restore files it takes ages - I spent an hour restoring a single file via Déjà Dup, all the time a loading spinner shows giving you no idea of how long it’s going to take (or whether it’s still doing something in the first place). And this is a small-ish, 100 GB backup of my work laptop. <em>Sigh.</em></p>
<p>It seems all backup software has its problems. Maybe I will create my own backup solution in the future with its own unique set of different problems - I have, for example, always found it strange how incremental backups pile mutation on mutation on top of an old backup, making recovering the most recent version (which is the one I want most of the time) the most processing power intensive, rather than doing it the other way around, but maybe I will explore that later. At least right now my data is backed up, secure, and available, so that hopefully when I turn 80, and many drives have failed me in between, I can still access my list of motorway mile markers from 2021.</p>
]]></description>
			<link>https://blog.koenvh.nl/a-tale-of-backups</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/a-tale-of-backups</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Mon, 05 May 2025 22:00:11 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Adding a button box for my sim rig]]></title>
			<description><![CDATA[<p>I will preface this by saying that this will mostly be pictures. I think there’s more to show than there is to say.</p>
<p>I like simracing (driving virtual race cars). Becoming one with this machine, even if it’s virtual, is a blast. Over the years I’ve expanded my setup - from a wheel clamped to my desk to a dedicated setup with bells and whistles.</p>
<p><img loading="lazy" src="posts/adding-a-button-box-for-my-sim-rig/e101ff09-4d1d-4e38-bceb-2db9843e77a3.jpeg" class="image--center mx-auto" /></p>
<p>I like my setup, but I always still had one thing on my wish list: a button box. For all actions in the virtual world, you need a button. I have a couple on the wheel, but often that is not enough. I can always use the keyboard, but it’s difficult to use that blindly. And when going very fast, you want to be able to hit it blindly.</p>
<p>During my Christmas break, I decided to finally make this a reality. I spent a long time finding a button box - there are many available, and I could also make one myself. What kind of buttons do I need? What do I want? Where will I mount it?</p>
<p>That last one was quite simple: I wanted something behind my gear shifter. I had a similar thing with my G27 shifter, and I quite liked that. I figured I could DIY a mount myself.</p>
<p><img loading="lazy" src="posts/adding-a-button-box-for-my-sim-rig/6d1dd4e9-2945-4924-8ff6-9a72ec9ffe88.jpeg" class="image--center mx-auto" /></p>
<p>It’s surprisingly difficult to find online what part in the hardware store would work for this. In the end I found this: these are two wood corner connectors - not the first thing that would come to my mind, but they work really well. The mounts on the back were there already.</p>
<p><img loading="lazy" src="posts/adding-a-button-box-for-my-sim-rig/a29b59cb-df22-42f8-977b-43e851147a1d.jpeg" class="image--center mx-auto" /></p>
<p>As you can see it’s quite closely packed in that area so there is not that much room to play with. Still, I had not decided on a button box yet. Then I found the Elgato Stream Deck, measured it, and: it would fit perfectly. The price compared to other pre-made button boxes is quite cheap, and the fact it is an LCD that allows me to display my own thing was a nice touch as well. I ended up buying one second hand.</p>
<p>In order to mount it I used two straight wood connectors again, some rubber protectors, and two screws and bolts to clamp it onto the stand. A bit of cardboard makes it a nice plateau (although I don’t think it’s technically necessary). It was far more solid than I initially expected. I was really happy how this small DIY attempt turned out.</p>
<p><img loading="lazy" src="posts/adding-a-button-box-for-my-sim-rig/7a7e0d93-16dd-4d61-ade4-ff66778e7439.jpeg" class="image--center mx-auto" /></p>
<p>And then I had my button box, and I could assign buttons to actions, but surely it could do more. I decided to see how difficult it would be to display other things as well. I decided to see if I could turn the entire button box into a giant race flag display.</p>
<p>It turns out the plug-ins for the Elgato Stream Deck work by creating a WebSocket connection to the main application, so it is not dependent on a specific language. I decided to create mine in Python.</p>
<p>Fairly quickly it worked, and this is the result:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=lQ90jhHhH3k">https://www.youtube.com/watch?v=lQ90jhHhH3k</a></div>
<p> </p>
<p>All in all I would call it a success. And like <a target="_blank" href="https://blog.koenvh.nl/dialns-a-dns-resolver-over-the-phone">DialNS</a> it was a fun Christmas project.</p>
]]></description>
			<link>https://blog.koenvh.nl/adding-a-button-box-for-my-sim-rig</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/adding-a-button-box-for-my-sim-rig</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Thu, 13 Mar 2025 22:03:41 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[The end of my FOI adventure with the UK Cabinet Office]]></title>
			<description><![CDATA[<p><em>(this is a continuation of the</em> <a target="_blank" href="https://blog.koenvh.nl/my-continued-foi-adventure-with-the-uk-cabinet-office"><em>previous part</em></a><em>, and the</em> <a target="_blank" href="https://blog.koenvh.nl/my-foi-adventure-with-the-uk-cabinet-office"><em>first part</em></a><em>)</em></p>
<p>Previously I received the result of how my request was handled, which was, in summary, only blacked-out text. I have now received the final response, which ends this adventure with the UK Cabinet Office.</p>
<p>When I received the blacked-out text about how my FOI request was handled I requested an internal review, because I thought it was a bit silly. Of course this response was delayed as well, as were all previous ones:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/lwbmG6L9">https://jmp.sh/lwbmG6L9</a></div>
<p> </p>
<p>That was not really surprising. It was annoying, but at this point quite expected. I did not expect them to change their opinion anyway, this was just so I could lodge a complaint at the ICO in the future. My expectation was not incorrect, as the response later came:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jumpshare.com/v/kSsbKN1Q4SqBFUT4TfDx?b=IYmSUtkYGyMiNLd0rybP">https://jumpshare.com/v/kSsbKN1Q4SqBFUT4TfDx?b=IYmSUtkYGyMiNLd0rybP</a></div>
<p> </p>
<p>In the meantime I contacted the ICO regarding the original request - I was wondering how long it would take. They came back to me saying they would have an answer around January. In practice it took a bit longer though, but I decided I would wait with appealing the other FOI request until I had this answer. It came in February:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/CeOaMJ5c">https://jmp.sh/CeOaMJ5c</a></div>
<p> </p>
<p><strong>The short version is that the ICO considers that section 36 was correctly applied by the Cabinet Office and that publication would not be in the public interest.</strong> I was surprised by this outcome - I would have expected at least <em>some</em> information to be publishable, but I am not going to appeal this result at tribunal.</p>
<p>Sadly that means we will never know exactly what went down before the current version was published, which, given that there is a real chance Kemi Badenoch will become the next prime minister, her approach to this would have been interesting to learn. However, there is something we did learn:</p>
<blockquote>
<p>Found no evidence that the Cabinet Office held information under parts 3 &amp; 4 of the request</p>
</blockquote>
<p>Which, if you recall, was regarding the sources they used regarding “being wrongly suggested that people have a legal right to access single-sex spaces and services according to their self-identified gender is a problem” and “adverse effects of access to single-sex spaces by individuals where their self-identified gender does not match their sex”.</p>
<p>Based on the information I now have, I can only conclude that there was no factual basis for the call-for-input. The Cabinet Office may happily prove me wrong by releasing the documents I asked for, but this is the only logical conclusion I can draw from the little information I have.</p>
<p>Back then, <a target="_blank" href="https://www.standard.co.uk/news/politics/kemi-badenoch-department-of-health-and-social-care-lbc-government-times-radio-b1154923.html">she said</a> to The Standard “with cases where organisations believe they are required to allow access to such services to self-identifying transgender people”:</p>
<blockquote>
<p>Single-sex spaces are essential for ensuring privacy and dignity for women. I do recognise, however, that the law in this area is complex, and I know that some organisations are confused and afraid of backlash if they are seen to get it wrong.</p>
</blockquote>
<p>I can only conclude that this knowledge never made it into the department as those sources were never found. Either that or these organisations don’t exist and it wasn’t a true story, but I find that unlikely as politicians don’t lie.</p>
<p>It is tempting to speculate why they might have done it, but I do not believe I can provide a conclusive answer without those documents. Let’s assume they did it with the best of their intentions, and not to make a political point or to distract from other issues, right?</p>
]]></description>
			<link>https://blog.koenvh.nl/the-end-of-my-foi-adventure-with-the-uk-cabinet-office</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/the-end-of-my-foi-adventure-with-the-uk-cabinet-office</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 15 Feb 2025 22:30:20 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Splitsing drie links niet snijden 500!]]></title>
			<description><![CDATA[<p>I’m Dutch, and even though this blog post is written in English, I’m a big proponent of the Dutch language. I also like rallying, not in the political sense, but in the “going very fast in a car through the forest” sense. Traditional rallying consists of a car, a driver, and a co-driver - the latter reads out the pacenotes for the stage you are on, giving the driver an idea what the road ahead is going to look like (think turns, jumps, water, etc.).</p>
<p>There are several simulators that try to recreate that rally feeling, but although all of them have a variety of co-driver voices available, none of them supported Dutch. For a long time I have wanted to have a Dutch voice - both because it’s easier to comprehend when going 160 km/h down a dirt road, and also because, well, it’s Dutch. Waiting for someone else to make one (which is what I have done for the past 10 years) turned out to be unfruitful, so I decided to take matters into my own hands. First question was picking a simulator. I currently use two: Richard Burns Rally and EA Sports WRC.</p>
<p>Richard Burns Rally is quite old at this point, initially being released in 2004 it’s now nearly 21 years old, but on the simulator aspect still pretty much on top. It being an older game also means it’s simpler - the audio is just OGG Vorbis files in a folder. I figured replacing those should be simple enough, right? Well, wrong. One of the things that has kept Richard Burns Rally alive for all that time is the vibrant modding scene. At the moment of writing, Rallysimfans (also known as RBR Hungary) is the most popular mod pack for Richard Burns Rally. Apart from redoing the entire interface and including a lot more stages than the original game, it also redoes the physics, the HUD, and the pacenotes.</p>
<p>The new pacenote system is exactly what you would expect from a community-made modification: it is very extensive, very configurable, and not greatly documented. It’s very impressive. I decided I would disable it for the moment to go back to the simple system the original game uses. They’re all separate plugins, so disabling should be simple, or so I thought. When I did that however, I found out that the HUD plugin relies on the pacenote plugin, which in turn relies on the physics plugin. Much like systemd everything is connected one way or another. The lack of understanding how it works, which files it uses and more importantly which ones it does not made me decided to look further.</p>
<p>The other simulator I use is EA Sports WRC. It’s made like a modern triple-A title, and modding support was not really on their list. However, it is possible - other people did it first and successfully so. There are no modded stages or cars, but the ones that are available are quite extensive, and the detail on the stages is much higher than Richard Burns Rally’s 2.5D models.</p>
<p>Before starting out, I tried one of the existing co-driver mods for it. It worked really well and installation was remarkably simple. Recreating that though was a bit more tricky. It consists of roughly three stages:</p>
<ol>
<li><p>Normalising the audio files’ volume</p>
</li>
<li><p>Converting the audio files to the proprietary WEM format</p>
</li>
<li><p>Bundling all audio files into the Unreal Engine’s PAK format</p>
</li>
</ol>
<p>Even though there is not a very large EA Sports WRC modding scene, there luckily is a lot of tooling for Unreal Engine 4. So by scavenging around on the internet and finding the excellent tools other people made, after some tinkering I managed to get my custom sound loaded in the game. Eureka!</p>
<p>The difficult part was still to come of course: creating the audio files. Luckily the names of the audio files gave a very good idea of what was said in that pacenote. “and_acute_hairpin_left.wem” leaves little to its imagination. The game stitches these audio files together to create the entire pacenotes.</p>
<p>I started out by creating translations for the individual components: “and”, “acute”, “hairpin”, “left”. Initially my idea was to use one of the many AI voices to generate the individual components, and then to stitch those together (and maybe create overrides where necessary). This proved to be quite janky, and did not work well at all. I tried to make it work better, but quickly abandoned it.</p>
<p>I then decided to use those translations to create a giant table of file name and translation. I could then tweak each line individually as necessary. I used a Google Cloud TTS voice to generate every voice line - there are only ~2000 in total, so it was not too much to generate, and I already knew how to do that and what the result would be. It does not sound as natural as a real or good AI voice, but it is very clear. The first attempt was made.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/JNuF8Z9pSp0">https://youtu.be/JNuF8Z9pSp0</a></div>
<p> </p>
<p>From this point on the main thing was changing the terminology. One of the peculiarities of rally pacenotes is that there is no universal standard, or a universal Dutch vocabulary. I had to make up my own using my own knowledge, some inspiration from Dutch rally videos from the Hellendoornrally, and the English and German versions of the original pacenotes. I also got some help from friends on the internet.</p>
<p>Making more iterations took quite some time. The original voice has multiple versions for each line, with small differences in intonation etc. The generated voice does not have those, but because it technically overrides the original voice, it does need to cover those files as well. That means that I basically had to copy the same line 16 times.</p>
<p>Some more tweaking and tweaking, and I finally got a version I was happy with. I mean, hey, I now have a Dutch co-driver, something I had wanted for many years. I also <a target="_blank" href="https://www.overtake.gg/downloads/ea-wrc-nederlandse-bijrijder.74578/">published it on the internet</a>, because even though I mostly made it for myself, if anyone else has been waiting for 10 years for a co-driver like that, I want them to be able to use it as well.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/j7Q2_tfYwdk">https://youtu.be/j7Q2_tfYwdk</a></div>
<p> </p>
<p>On a slight tangent: I have made quite a few modifications for quite a few games. I mostly do it because I like doing it. As much as I like modding, some of the community members can really make me doubt publishing those modifications to other people. People like me make modifications to do what we want it to do, and because we like doing it, and because we like giving others access to it for free. That means you are not entitled to anything. Nothing against constructive criticism, but just writing “this sucks 🤣”, demanding we do things for you, or making fun of things really brings down the mood.</p>
<p>It does not hurt me too much - I know there are people out there who do like it, most of them who stay silent. But if your only contribution is to spread negativity then just don't. People create things because they like to, then they're nice enough to share it with others. That means you now have more options - nothing is taken away from you. Rather than take the path of least effort and not respond, by bringing negativity people who like creating modifications will at some point not want to share what they make any more, even things you do want to use.</p>
<p>Luckily a lot of other responses have been positive. I am very happy that the developers of EA Sports WRC make modding relatively simple still. I also like how the community has created a lot of tooling, for example to convert the WAV files to WEM.</p>
<p>All in all I am very happy that I managed to do something I wanted to have for quite some time, and that the result is pretty good too. Hopefully I can replace the TTS voice with a real co-driver voice in the future.</p>
]]></description>
			<link>https://blog.koenvh.nl/splitsing-drie-links-niet-snijden-500</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/splitsing-drie-links-niet-snijden-500</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Thu, 23 Jan 2025 21:04:15 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[DialNS - a DNS resolver over the phone]]></title>
			<description><![CDATA[<p>I picked up a Christmas project. For quite some time I had wanted to toy around with telephones, because I’ve always been intrigued by the world of automated phone numbers. The fact you could dial a number, and the response from the other end could be dynamically generated? Magical. Plus even though I have unlimited calls it still feels special in a way. Anyway, at some point during the last year, the idea came up to resolve DNS records over the phone. Does it serve any practical function? No, not at all. But it is fun.</p>
<p>Let’s start by showcasing the result. <strong>You can now dial +31853695573 and resolve your records over the phone!</strong></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/szlnM3kD-Bo">https://youtu.be/szlnM3kD-Bo</a></div>
<p> </p>
<p>Like all software development, it’s never as straight-forward as one would like. The most common protocol to do digital telephony is SIP (Session Initiation Protocol) as defined in <a target="_blank" href="https://www.rfc-editor.org/rfc/rfc3261.html">RFC 3261</a>, which combined with some other protocols make voice-over-IP, VoIP, happen. Looking through the SIP RFC, seeing that it has 269 pages and I don’t care that much about the years of “it has to work with this wonky implementation of this device from 2000”, I decided to look for a library to handle this for me instead.</p>
<p>Finding a library was easier said than done. Even though SIP is old and ubiquitous, libraries don’t seem plentiful. I started of using <a target="_blank" href="https://github.com/tayler6000/pyVoIP">pyVoIP</a>, a great effort that never really seemed to work for me. My SIP provider, CheapConnect, did not want to work with it. The reason I chose my provider is because, as the name already makes apparent: they’re cheap. This funny project costs me less than €1/month, which is worth it for the joke.</p>
<p>There are several codecs out there for telephony - some modern ones for better audio quality like Opus. Naturally my provider supports none of them, and just supports G.711a, otherwise known as PCMA or G.711 A-law. It turns out pyVoIP only supports G.711μ, which is used in the USA. You can’t have a standard if the USA doesn’t do things slightly differently :-)</p>
<p>I looked further and found another library, for C# this time: <a target="_blank" href="https://github.com/sipsorcery-org/sipsorcery">sipsorcery</a>. It does a lot of things including the thing I want - receiving phone calls. Figuring out how that worked was a bit of a hassle, because the documentation works very well if you know what you are looking for, but otherwise it can be a bit of a mystery. It’s also very Windows-focused, and this runs on a Raspberry Pi.</p>
<p>Once the scaffolding was set up based on examples provided by sipsorcery, I could do the fun part. Audio and implementing the T9 keyboard. The audio part was pretty simple: it wants 16 KHz raw PCM audio (or 8 KHz, but I opted for 16). Luckily ffmpeg is great at converting any audio to that. For the voice itself, I just used an online text to speech API - even though the responses are dynamic, the text spoken never really needs to be. I can just generate an audio file for A-Z and 0-9 and use that.</p>
<p>The T9 keyboard was somewhat tricky because it relies on timing. I decided to take some shortcuts, and just use a timer of one second. That time was based on my old phone with a T9 keyboard. That does mean for the quick T9 typers that you cannot start with the next letter after the first letter is done, but in practice I don’t think that is too much of a disturbance. They are simply ignored.</p>
<p>This is what that code looks like:</p>
<pre><code class="lang-csharp">        <span class="hljs-keyword">private</span> System.Timers.Timer timer = <span class="hljs-keyword">new</span> System.Timers.Timer(<span class="hljs-number">1000</span>);


        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">ButtonPress</span>(<span class="hljs-params"><span class="hljs-keyword">byte</span> key, <span class="hljs-keyword">int</span> duration</span>)</span>
        {
            <span class="hljs-keyword">await</span> Task.Run(() =&gt;
            {
                buffer.Add(key);
                timer.Stop();
                timer.Start();
            });
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">AddLetter</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> letter</span>)</span>
        {
            timer.Stop();
            textBuffer += letter;
            <span class="hljs-keyword">await</span> PlayLetter(letter);
        }

        <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">FlushBuffer</span>(<span class="hljs-params"></span>)</span>
        {
            <span class="hljs-keyword">var</span> buf = <span class="hljs-keyword">new</span> List&lt;<span class="hljs-keyword">byte</span>&gt;(<span class="hljs-keyword">this</span>.buffer);
            <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;
            <span class="hljs-keyword">if</span> (!buf.All(x =&gt; x == buf[<span class="hljs-number">0</span>]))
            {
                <span class="hljs-keyword">this</span>.buffer.Clear();
                <span class="hljs-keyword">return</span>;
            }
            <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">0</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"-"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"0"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">1</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"."</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"1"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">2</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"A"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"B"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"C"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"2"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">3</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"D"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"E"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"F"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"3"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">4</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"G"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"H"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"I"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"4"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">5</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"J"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"K"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"L"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"5"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">6</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"M"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"N"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"O"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"6"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">7</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"P"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"Q"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"R"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"S"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">5</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"7"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">8</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"T"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"U"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"V"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"8"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">9</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"W"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">2</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"X"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">3</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"Y"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">4</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"Z"</span>);
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">5</span>) <span class="hljs-keyword">await</span> AddLetter(<span class="hljs-string">"9"</span>);
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">10</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span> &amp;&amp; textBuffer.Length &gt;= <span class="hljs-number">1</span>)
                {
                    <span class="hljs-keyword">await</span> PlayAudio(<span class="hljs-string">"Tones/removed.pcm"</span>);
                    textBuffer = textBuffer.Remove(textBuffer.Length - <span class="hljs-number">1</span>);
                }
            }
            <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (buf[<span class="hljs-number">0</span>] == <span class="hljs-number">11</span>)
            {
                <span class="hljs-keyword">if</span> (buf.Count == <span class="hljs-number">1</span>)
                {
                    timer.Stop();
                    <span class="hljs-keyword">await</span> Continue();
                }
            }
            <span class="hljs-keyword">this</span>.buffer.Clear(); 
        }
</code></pre>
<p>I now have a text buffer with the domain name, and now I need to do the DNS part too. For a project revolving around DNS, this was frankly the easiest part, as I just used C#’s built-in <code>Dns.GetHostEntry</code>. Some slight filtering later (reading out IPv6 over the phone takes ages, so let’s not do that), and by reusing the sounds for the input methods, and voila: DNS resolution over the phone.</p>
<p>Then it was a matter of squashing some bugs that had to do with sipsorcery, such as handling more than one call (for which they luckily provide examples). There are probably still some bugs, but luckily this does not need to be production-grade software.</p>
<p>I might play around with more phone-related things in the future. Who knows, maybe it’s capable of text-based.. err.. phone-based adventures. Or maybe I can use a phone as remote control, or as a Tamagotchi. On the more practical spectrum of things I’ve also played around with hooking it up to a Discord voice call. There will probably be more phone-based shenanigans in the future.</p>
]]></description>
			<link>https://blog.koenvh.nl/dialns-a-dns-resolver-over-the-phone</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/dialns-a-dns-resolver-over-the-phone</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sun, 29 Dec 2024 15:40:21 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[What the button in my car did]]></title>
			<description><![CDATA[<p><a target="_blank" href="https://blog.koenvh.nl/what-does-this-button-do">Previously I wrote about a mysterious switch I found in the car I had recently bought</a>, and my search to see what it did. It got quite some attention, including on <a target="_blank" href="https://news.ycombinator.com/item?id=42276620">Hacker News</a> (although in true HN fashion, most of the comments are not about the previous blog post, but about the mandatory data connectivity that is included in every car, such as for example the mandatory <a target="_blank" href="https://europa.eu/youreurope/citizens/travel/security-and-emergencies/emergency-assistance-vehicles-ecall/index_en.htm">eCall system</a> in new cars in the European Union, where the car can call the emergency services on its own).</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/4f45f625-47ee-4f33-beff-efe0ad6e90e2.jpeg" class="image--center mx-auto" /></p>
<p>Anyhow, back to the car. My car to be precise. I could not resist the urge to dig deeper, so I made a Subject Access Request to the third-party company that I was told managed the fleet for the company where this car was originally used. Their privacy policy stated I would get a reply in 24 hours, which is in range of my patience (the privacy policy of the company where this car came from says they will reply within 3 months - no idea why they need that much time or why that would be reasonable, and although I did also send them an email I don’t expect a response back any time soon).</p>
<p>Anyway, 24 hours go by and no response, so let’s call them. They would ask and they would get back to me the next day, and true to their word they did. I will summarise the email as “good on you for using your rights, but we do not have that data, why did you think we had it in the first place, and go bother someone else”.</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/81d9a487-04a0-45b2-8046-9d0eec22e202.jpeg" class="image--center mx-auto" /></p>
<p>The date the mechanics were going to remove the equipment was only in a couple of weeks, and I could not resist the urge to check again. Let’s remove the fuse box cover again and have another look. This time I brought a light, and by using my phone to look up, I spotted something: a buzzer, and if you look very carefully you can also see the black box. I presume the buzzer was used to notify the driver that they did not use their iButton yet. It also interferes with the data module in the black box creating that annoying sound any time it tries to send or receive data. Strikes me as a design flaw, but alas.</p>
<p>I decided to call the company the car came from again, but this time a different office than the head office. I told the story again, and told that the other company had told me they were not responsible, and if they perhaps knew another company they used for tracking their fleet. I got a different name this time, and even though they could not provide a contact person the name was enough.</p>
<p>I called the phone number on the tracking provider’s website, told the story again, and the person on the other end helpfully told me the tracker in my car had been disabled five months before I bought it. That’s a relief - at least my whereabouts were not stored, and maybe the tracker was actually disabled. Later I received an email from the telecom provider that confirmed this. Phew.</p>
<p>The relief was short-lived when, during the phone call with the tracking provider, they continued that they could reactivate the tracker remotely if I wanted them to. I told them that I’d rather not, and thanked them for the information. So basically someone behind a dashboard can press a button to re-enable tracking on my car without me knowing. Amazing.</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/ddb82788-325f-48a6-bb56-372c8828df09.jpeg" class="image--center mx-auto" /></p>
<p>The date of the mechanics came around, and I present to you: no switch or button. I must say I am very pleased with how my dealership handled this. I asked them to put the stuff they removed in the boot (or trunk for the Americans), because I was of course still intrigued. Let’s see what was removed:</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/5319a9c2-4c9b-424e-b15d-e1fb2fe6a3b2.jpeg" class="image--center mx-auto" /></p>
<p>It’s the panel, along with some wiring to a buzzer and the black box. No immobiliser luckily. The black box retrieved some information from the OBD2 port about the car’s speed, mileage, etc., but let’s see what else is inside that box:</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/df8f9ce3-2a64-488a-a030-8b0015baeab8.jpeg" class="image--center mx-auto" /></p>
<p>It’s a battery and SIM card. I could hear the interference with the buzzer after I took this picture, so I assume the battery is there to make the black box work when the car is off too. Anyway, let’s have a closer look at the SIM card. Does it still work? First quest is finding a device that still takes that size of SIM card. Luckily I found an old phone.</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/32bc32f0-cc08-4214-a140-29274532bb8b.jpeg" class="image--center mx-auto" /></p>
<p>Excellent, it does. It is roaming and still has signal. Of course I tried to call myself, after which I heard a robot voice read me the following:</p>
<blockquote>
<p>The line has been disconnected for administrative reasons. For information, please contact your service provider - you find the telephone number on your telephone bill.</p>
</blockquote>
<p>Bummer, no free calls for me then. I’m assuming data does not work either, but I cannot check that as this phone does not exactly do data. But hey, at least it is no longer connected. I was curious so I tried to find more about where the SIM comes from, but that seems very opaque. Something I might look into later.</p>
<p><img loading="lazy" src="posts/what-the-button-in-my-car-did/74938d25-7bc5-4848-b0b9-96b4c9e6aefd.jpeg" class="image--center mx-auto" /></p>
<p>So I now know what it is, how it was used, that it was all disabled but it could have been re-enabled with the click of a button. My car is now again how it was delivered from the factory, without any third-party tracking or mysterious switches.</p>
]]></description>
			<link>https://blog.koenvh.nl/what-the-button-in-my-car-did</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/what-the-button-in-my-car-did</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 20 Dec 2024 20:57:12 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[What does this button do?]]></title>
			<description><![CDATA[<p>Last week I bought a car. After twelve years of service, my old trusty Peugeot 107 in blue has had its best. Expensive repairs were coming <em>at some point</em>, and I did not feel like waiting around for them to come. Plus the existing list of faults (like the high oil usage of about a litre per month, a brake that sometimes blocked without reason, or the smell of exhaust fumes that sometimes came into the car when the fan was on high) also started getting longer and longer.</p>
<p><img loading="lazy" src="posts/what-does-this-button-do/91643929-8f55-468a-8568-97d1a64862da.jpeg" class="image--center mx-auto" /></p>
<p>Anyway, new car time! After a lot of research I ended up with an Opel Corsa from 2020. To be precise, it’s an Opel Corsa Edition with 101 HP, and most importantly, it’s mine.</p>
<p>Unlike the Peugeot, the Opel has gadgets - quite a few of them. Of course it being my car, I want to know what all buttons do, so I read the entire manual (which is very annoying to read, as they make one manual for every version of the car, so half of it does not apply to this car, but I digress). One of those buttons was the following below the lighting controls.</p>
<p><img loading="lazy" src="posts/what-does-this-button-do/f39bda1d-77c1-4186-862b-c566cce517bb.jpeg" class="image--center mx-auto" /></p>
<p>Those do not appear in the manual, or the website, or anywhere. What could they be? Just flipping the switch does nothing, apart from turning off the light on the switch. So let’s look where the button goes. I can see that part of it is wired to the back of the OBD2 port (a port that retrieves data from the onboard computer about the car, such as pedal position, temperature, lights, rev count, speed - basically if you can see it on your dashboard it can be read using the OBD2 port), so it is getting information from the car, but apart from that the wires go to places that I can’t see without taking the car further apart.</p>
<p><img loading="lazy" src="posts/what-does-this-button-do/00372c30-63b8-4807-9ae3-4b15727304e5.jpeg" class="image--center mx-auto" /></p>
<p>Something else that I noticed before is that I heard the typical inference noise coming from that area of the car when putting the ignition on. You know, <a target="_blank" href="https://youtu.be/FYjs7vsaSEw">this</a> sound. <em>Ti-ti ta, ti-ti ta, ti-ti ta, ti-ti ta, ti-ti ta, ti-ti ta, ti-ti ta, TAAAAAAAAAAA</em>. That gave me the ominous feeling there might be something sending data in there. Sure, my car does that too so I can see in the app where I parked it (seriously, it does that), but at least I gave permission for that.</p>
<p>I asked the wisdom of the crowd. A lot of ideas came up: nitrous (would have been fun), LPG switch (it’s only petrol), flame kit (I wish), front parking sensors (those are standard on the car). No avail.</p>
<p>I called my dealership and asked. They guessed it might have been an immobiliser - bit strange on a car this young and type, and also strange for it to be a switch like this. I called the dealership that previously maintained the vehicle based on the phone number in the service history. They did not install it, and guessed it might be a black box. They did tell me who it previously belonged to (a large company), so I called their headquarters. They told me they don’t do their own cars any more, but that they outsourced it, and gave me a phone number. I called them, they told me they don’t do that, but he speculated it might be a GPS tracker.</p>
<p>Some more searching, and I figured I would just drive to my dealership. More speculating with the salespeople, who told me to make an appointment with the mechanics. I did, they had a quick look at it and I now have an answer:</p>
<p>The metal part is something to hold an iButton or Dallas key, a magnet-like thing, next to. Via a wire it can communicate which key is held next to it to identify a user. It registers who's driving to a fleet tracker via a device also mounted in the car, which sends it to a fleet manager via the internet. That way it can be tracked which employee did what (and potentially who to send the fine to). That would also explain the mobile phone interference noises I've heard from that area of the car. So it’s a black box, a GPS tracker, and maybe also an immobiliser? I am not sure about the last one (and why it has a switch, though some sources suggest a private use/company use mode).</p>
<p>I'm getting it removed because now I'm basically driving around with a foreign GPS tracker. Some lease company somewhere is getting data on wherever I go. Kind of spooky if you think of it, especially as I assume I am one of the few actually looking into what this is. Most people would have probably driven around for years with a foreign GPS tracker.</p>
<p><img loading="lazy" src="posts/what-does-this-button-do/1353bcc6-c25c-482b-9a75-67686582c479.jpeg" class="image--center mx-auto" /></p>
<p>And that’s how the search comes to an end. After a bit of perseverance I figured out what it is. I now know my car is being tracked still, and that they know I did try out what the car’s acceleration is like at full throttle.</p>
<p>There are more interesting angles to this, like “can I request my data from the fleet manager thing that has been tracking my whereabouts under the GDPR?”, and “can I get free data from the SIM card embedded in the device that I now technically own?” but I will leave those for another day.</p>
]]></description>
			<link>https://blog.koenvh.nl/what-does-this-button-do</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/what-does-this-button-do</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 29 Nov 2024 14:23:05 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[My continued FOI adventure with the UK Cabinet Office]]></title>
			<description><![CDATA[<p><em>(this is a continuation of the</em> <a target="_blank" href="https://blog.koenvh.nl/my-foi-adventure-with-the-uk-cabinet-office"><em>previous part</em></a><em>)</em></p>
<p>The Information Commissioner’s Office (ICO) has not come back to me yet regarding the original freedom of information (FOI) request. Admittedly that was not fully unexpected given the usual timelines they work with (on average 6 months), although I would not have minded being an exception. In the meantime I did get an update on the meta-FOI request, which this blog post will be about.</p>
<p>Quick recap what this is about: on 1 May 2024 the UK Cabinet Office published an interestingly-worded Call for Input. I wanted to know how that came to be and why it was published in its current form, so I sent a FOI request for that on the same day. After several delays I got a response that rejects the request because it would, all factors considered, be against the public’s interest for this information to be released. I disagree. When I received that refusal I did two things: requested an internal review, and sent out a second - meta - FOI request for all the information about the handling of that first FOI request.</p>
<p>After the deadline of 20 working days I got a letter with an extension, because they would have to do a public interest test. That was unsurprising, I already expected there to be another 20 working days added.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/hlRAVTVx">https://jmp.sh/hlRAVTVx</a></div>
<p> </p>
<p>After that 20 working days I received… another extension letter? This is not really allowed, and also kind of surprising since the content is identical with the first (except for the date). Alas, let’s wait another 20 working days.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/PwfMybrO">https://jmp.sh/PwfMybrO</a></div>
<p> </p>
<p>Another 20 working days pass and I receive… yet another extension letter with identical contents. This is getting a bit silly.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/ipjmXHX8">https://jmp.sh/ipjmXHX8</a></div>
<p> </p>
<p>At this point I am too impatient to create a quartet of extension letters and I contact the ICO. They reply back to me that this is not how it is supposed to go and only one extension is allowed (I know), that they have contacted the UK Cabinet Office, and to write back to the ICO if the Cabinet Office does not reply within 10 working days.</p>
<p>The good news is that I won’t need to wait that long as I do get a response. They state that they hold the information (obviously), but that some things were redacted based on Section 36 and Section 40.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/4vJ0eptT">https://jmp.sh/4vJ0eptT</a></div>
<p> </p>
<p>The Section 40 redactions were not unexpected as they are about personal information, and frankly fair as the names of the people are not really relevant.</p>
<p>The Section 36 redactions were after receiving the extension letters to be expected, it’s about the good functioning of government, and the free and frank discussion among civil servants and ministers. Let’s see how they applied them…</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/xOizcLgQ">https://jmp.sh/xOizcLgQ</a></div>
<p> </p>
<p>I think it’s fair to say that no black marker was spared. All information bar meta-data, “Hi”, and “Many thanks” was redacted. Apparently all other information might do harm if the public gets to read it. I find that very peculiar.</p>
<p>I decided to ask for an internal review:</p>
<blockquote>
<p>I accept the redactions based on section 40. However, I challenge the weights given to the individual components in the public interest test that have resulted in nearly all content being redacted under section 36. I believe insufficient weight is given to the inherent interest of the public in seeing how the Cabinet Office handles an FOI request and executes the public interest test. I further believe too much weight is given to the impact releasing the discussions about FOI would have on free and frank discussions. Those handling FOI requests should absolutely be aware of the fact that what is said might and likely will become public.</p>
</blockquote>
<p>Which again starts the waiting game…</p>
]]></description>
			<link>https://blog.koenvh.nl/my-continued-foi-adventure-with-the-uk-cabinet-office</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/my-continued-foi-adventure-with-the-uk-cabinet-office</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Fri, 01 Nov 2024 18:55:30 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[My FOI adventure with the UK Cabinet Office]]></title>
			<description><![CDATA[<p>A friend of me alerted me on 1 May 2024 that a Call for Input was published by the Minister for Women and Equalities, back then Kemi Badenoch. The call was titled: <a target="_blank" href="https://www.gov.uk/government/publications/call-for-input-incorrect-guidance-on-single-sex-spaces-and-gender-self-identification"><em>Call for input: Incorrect guidance on single-sex spaces and gender self-identification</em></a>. This title is, in my opinion, quite suggestive. But let's not fill in the blanks for them. They probably identified a problem. Let's see <a target="_blank" href="https://www.gov.uk/government/news/kemi-badenoch-asks-for-examples-of-bad-guidance-on-single-sex-spaces">what Kemi Badenoch says about it herself</a>:</p>
<blockquote>
<p>In some cases organisations believe they are required to allow self-identifying transgender people to access these services. Now, as part of raising awareness and to understand how well single-sex spaces are maintained, Kemi Badenoch is calling on the public to submit their examples to the Government.</p>
<p>"Single-sex spaces are essential for ensuring privacy and dignity for women. I do recognise, however, that the law in this area is complex, and I know that some organisations are confused and afraid of backlash if they are seen to get it wrong.</p>
<p>So I am asking people to submit real-world examples of organisations using incorrect guidance, so that our policymaking continues to tackle any confusion and we ensure single-sex spaces are maintained."</p>
</blockquote>
<p>Apparently the problem is that certain organisations are confused. I must admit that I do not see how admitting transwomen (who are women too) would damage the dignity of women. However, rather than make assumptions, I requested background information on this call for input. Luckily the UK has a quite decent Freedom of Information Act (FOI).</p>
<p>The Freedom of Information Act, in simple terms, allows any person to request information from bodies that fall under the act, of which the Cabinet Office (where this call for input was published) is one. I used that right to send them the following request on the same day as the call for input was published:</p>
<blockquote>
<p>I wish to learn how this call for input came to be, and which deliberations were made which culminated in it being published in its current form. For that reason, I hereby request the following information:</p>
<ol>
<li><p>Any document that relates to the realisation and publication of aforementioned call for input, including, but not limited to drafts, emails, notes, audio and video recordings, including any document regarding whether the call for input should be created in the first place;</p>
</li>
<li><p>Any expert consultation, or any other input by an internal or external party regarding this call for input in any recorded form, including, but not limited to drafts, emails, notes, audio and video recordings;</p>
</li>
<li><p>Any document that relates to cases relating to it being wrongly suggested that people have a legal right to access single-sex spaces and services according to their self-identified gender is a problem, in any recorded form, including, but not limited to drafts, emails, notes, audio and video recordings;</p>
</li>
<li><p>Any document that relates to adverse effects of access to single-sex spaces by individuals where their self-identified gender does not match their sex which was used as a foundation, base, or input for aforementioned call for input.</p>
</li>
</ol>
</blockquote>
<p>It looks like a long request, but it basically asks for any communication regarding the call for input, the expert reviews, and documents regarding this being a problem in the first place.</p>
<p>This is when the waiting game begins. After 20 working days I got an extension for another 20 working days:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/4ShBvZtZ">https://jmp.sh/4ShBvZtZ</a></div>
<p> </p>
<p>Okay, some part may be subject to Section 36 of the Act. Basically, Section 36 concerns: <em>might this damage the State so much that the public interest is in favour of withholding this information rather than publishing it?</em> Maybe parts of it are. The extension was not fully unexpected, albeit slightly annoying.</p>
<p>Another 20 working days and the response comes in: all information falls under Section 36. That was a bit of a surprise, and I disagree with the assessment. The response is also rather vague with the reasons why this would fall under Section 36, whereas the Information Commissioner's Office (ICO) is quite clear that <a target="_blank" href="https://ico.org.uk/for-organisations/foi/freedom-of-information-and-environmental-information-regulations/section-36-prejudice-to-the-effective-conduct-of-public-affairs/">reasons for engaging Section 36 need to be specific, and not used as blanket rulings</a>.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/dCvGIlWJ">https://jmp.sh/dCvGIlWJ</a></div>
<p> </p>
<p>I disagreed with this assessment, and I requested an internal review:</p>
<blockquote>
<p>I disagree with the refusal based on section 36(2)(b)(i), 36(2)(b)(ii), and 36(2)(c) for the following reasons:</p>
<ol>
<li><p>I do not believe that the publication of the deliberations to publish or not to publish this call for input would have a chilling effect, i.e. inhibit the processes of providing advice and exchanging views. As the call for input is now published, I believe the issue can no longer be considered "live" and the process is no longer ongoing. Assuming the publication followed the appropriate procedures, I do not see how its publication could have a chilling effect on future processes. Citing the Information Commissioner's guidance: "civil servants and other public oﬃcials are expected to be impartial and robust when giving advice, and not be easily deterred from expressing their views by the possibility of future disclosure. The possibility of future disclosure could actually lead to better quality advice." I strongly believe that applies here as well.</p>
</li>
<li><p>Assuming the call for input was published according to the appropriate procedures, the disclosure of the communication surrounding that should not prejudice the effective conduct of public affairs on any body or the wider public sector. I thus believe the balance of the public interest is thus in favour for this information to published.</p>
</li>
</ol>
<p>Please carry out an internal review of the handling of my request and consider changing your position.</p>
</blockquote>
<p>I also made another FOI request, a so-called "<a target="_blank" href="https://ico.org.uk/for-organisations/foi/freedom-of-information-and-environmental-information-regulations/requests-about-previous-information-requests-meta-requests/">meta-FOI request</a>", about the handling of the previous FOI request. After 20 working days that request got an extension because they have to do a public interest test as Section 36 might apply. I have an inkling I already know where this is going...</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/lKj5V2jl">https://jmp.sh/lKj5V2jl</a></div>
<p> </p>
<p>A couple more working days pass by and I get a response to my request for internal review. They uphold the original refusal, but a couple of new things are now included in the response as well:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/9UvULwtd">https://jmp.sh/9UvULwtd</a></div>
<p> </p>
<ol>
<li><p>They now claim they do not have the information requested in point 3 and 4 of the original request;</p>
</li>
<li><p>There has been involvement from special advisors and legal;</p>
</li>
<li><p>They claim there is less public interest due to it being done under the Sunak administration;</p>
</li>
<li><p>They claim it has less public interest as it does not concern the forming of new policy;</p>
</li>
<li><p>Most of the arguments regarding why not to publish it seem to revolve around the chilling effect;</p>
</li>
<li><p>Other departments have been involved as well.</p>
</li>
</ol>
<p>These are all interesting factoids, but not reasons to not publish any information. I also have my severe doubts there is not any document for point 3 and 4 - if that is indeed the case, then I think that would be even more interesting, as that would imply that they have no documents relating to there being a problem in the first place.</p>
<p>Because after the internal review the process at the Cabinet Office is now done (although, if there were another option, I doubt it would be fruitful), I made a complaint at the ICO:</p>
<blockquote>
<p>I accept that parts are exempt under section 40(2). However, I challenge the application of the exemption of section 36 to the entirety of the requested information. I also challenge the notion that the Cabinet Office does not hold information on part 3 and 4 of the request.</p>
<p>I believe the weights attached to the public interest test insufficiently take the general public's interest in transparency into account. I do not believe that the call being made by a previous government would strengthen the argument to withhold information, nor do I see how the fact that it was not meant for the formation of government policy is particularly relevant. I also believe that too much weight is given to a potential chilling effect, as civil servants, officials, and other government departments should be aware that what they are doing could become public. I understand that parts such as names may be omitted under exemption 40(2) – personal data is not the goal of this request. The response to the request for internal review seems to imply that only by remaining opaque public affairs can be effectively conducted, which goes against the aim of the Act.</p>
<p>The claim that no information is held for part 3 (and 4) of the request strikes me as unlikely, as it requests the basis on which the call for input was created and published. Deducing the kind of communications that must have taken place based on the response to the request for internal review (communication between ministers and special advisors, legal advisors, and civil servants), I find it highly likely that at least some information must exist within the Cabinet Office. If this information does in fact not exist, I think it strengthens the public’s interest in the release of the information requested in parts 1 and 2.</p>
<p>Concluding, I believe the outcome of the PIT insufficiently takes the general interest into account, and I believe there is at least some data for parts 3 and/or 4 of the request.</p>
<p>Simultaneously with the request for an internal review, I made a meta-FOI request to the Cabinet Office requesting documents regarding the handling of this FOI request. That meta-request has identifier FOI2024/08728. The deadline has been extended on grounds that some parts may be subject to Section 36.</p>
<p>Throughout the response in the internal review, it strikes me that the exemptions are applied rather liberally. I see arguments being made regarding why the public interest is reduced rather than why the exemptions mean this information cannot be made public. The only real argument against publication seems to be the chilling effect and it impeding free and frank discussion and decision making, but without clear specification how that would apply to this case specifically (it seems that the explanation given for applying Section 36(2)(b)(i), (ii), and (2)(c) can apply to any case pretty much verbatim). I believe that from a “publish, unless” point of view, there is insufficient argumentation why this information should be withheld. I also do not believe all documents in the, in my opinion, quite clearly specified request would fall under this exemption.</p>
<p>I fully understand the withholding information under Section 40(2). I cannot form an opinion on the application of Section 42, as I do not know which documents this would concern.</p>
</blockquote>
<p>The ICO's guidance for the application of the chilling effect argument is quite clear, which makes me hopeful of my case:</p>
<blockquote>
<p>Arguments under s36(2)(b)(i) and (ii) are usually based on the concept of a ‘chilling effect’. The chilling effect argument is that disclosure of discussions would inhibit free and frank discussions in the future, and that the loss of frankness and candour would damage the quality of advice and deliberation and lead to poorer decision-making. Tribunals are generally sceptical of such arguments.</p>
</blockquote>
<p>Five days after my complaint to the ICO I got a confirmation. Now it's a waiting game to see what the case officer at the ICO thinks of my case.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://jmp.sh/9tRaKVQW">https://jmp.sh/9tRaKVQW</a></div>
]]></description>
			<link>https://blog.koenvh.nl/my-foi-adventure-with-the-uk-cabinet-office</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/my-foi-adventure-with-the-uk-cabinet-office</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Tue, 13 Aug 2024 22:00:00 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Improving the resiliency of RPKI Relying Party software]]></title>
			<description><![CDATA[<p>For the last months I have been working on RPKI Relying Party security. The question I asked myself was: is it possible to disrupt RPKI Relying Party software by introducing a malicious CA/repository into the tree? The short answer is: yes. For a more indepth look into what I did, I wrote the following blog post at RIPE Labs: <a target="_blank" href="https://labs.ripe.net/author/koen-van-hove/improving-the-resiliency-of-rpki-relying-party-software/">https://labs.ripe.net/author/koen-van-hove/improving-the-resiliency-of-rpki-relying-party-software/</a></p>
]]></description>
			<link>https://blog.koenvh.nl/improving-the-resiliency-of-rpki-relying-party-software</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/improving-the-resiliency-of-rpki-relying-party-software</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 13 Nov 2021 15:48:56 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[The asterisk around the corner]]></title>
			<description><![CDATA[<p>It seems that a lot of hyperboles get thrown around nowadays. I have read a lot of cases where people say you should <em>never</em> do this, or you <em>must</em> do that, as if there are absolutely no exceptions ever. A few cases I have seen:</p>
<ul>
<li>You must always use HTTPS</li>
<li>You should never expose MySQL to the outside world</li>
<li>You must use two-factor authentication</li>
<li>Your program must be unit-testable</li>
</ul>
<p>The list can go on and on. My point is not that the points are invalid, just that they are worded as if the world is always black and white, and it is not. I would however like people to stop with the hyperboles. There are many cases where HTTPS is great, and if possible, use it, but that does not mean that there are no valid cases where HTTPS in not possible.</p>
<p>As an exercise to you, dear reader, try to think of a case where the points above don't hold. There's no single right answer, but you can probably come up with something. These are the scenarios I came up with:</p>
<p><strong>You must always use HTTPS</strong>
Except for when you try to include resources that are not served over HTTPS, such as  <a target="_blank" href="http://koenvh.nl/radio/">web radio streams</a>. If I serve that page over HTTPS, around half of the radio streams would stop working. You can proxy everything behind a HTTPS, but that is a lot more expensive, plus creates a lot of legal questions. But okay, let's say that we can proxy everything with a HTTPS proxy, then what are you going to do when you are trying to connect to a local server on the user's computer? You cannot get a valid certificate for 192.168.x.x, so it won't be accessible over HTTPS. </p>
<p><strong>You should never expose MySQL to the outside world</strong>
The argument is generally that you should write an API front-end that translates that to the MySQL database behind the scenes. That's great for apps and other things you fully control, but what if you want to use it in combination with an application that only speaks SQL? </p>
<p><strong>You must use two-factor authentication</strong>
Security is not some absolute. I in fact would sometimes prefer to not have yet another password to keep care of, especially for things I don't intend to use frequently. A magic link (basically sending an email with a link) works just as well in a lot of cases, and is still only one factor (namely what you have, that being access to your email). Also, your audience may not have access to a good</p>
<p><strong>Your program must be unit-testable</strong>
This is great if your program has logic that you can test. That's not always the case. For example, I created an application that works in conjunction with a game server application. If you abstract away all the parts that require the game server, you are left with no logic. You can mock the game server, in which case you are testing whether your mock works, whereas most if not all errors come from unexpected behaviour from the server. </p>
<p>There are other examples too. Just to reiterate, I am not against them, I do however think it is good to put them into perspective, and not treat them as absolutes.</p>
]]></description>
			<link>https://blog.koenvh.nl/the-asterisk-around-the-corner</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/the-asterisk-around-the-corner</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Tue, 01 Sep 2020 22:52:47 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[A month of Astiga's new synchronisation system]]></title>
			<description><![CDATA[<p><em>This is a follow-up to the previous blog post, which you can find <a target="_blank" href="https://blog.koenvh.nl/astigas-new-synchronisation-system-ck6my2w6101y8dfs189ucbay">here</a>.</em></p>
<p>Astiga's new synchronisation system has run for about a month now. Which seems like a nice time to evaluate it. Some stats:
<img loading="lazy" src="posts/a-month-of-astigas-new-synchronisation-system/UPWdxkJGz.png" alt="stats.png" /></p>
<p>About 50 TiB has been synced in a month, which is a bit under 2TiB per day. That also has to do with the start, as I expect to break the 60TiB barrier for March 2020. Compared with the old system, that is about double the data it used to process in a month. Apart from the first initial hickups (nothing too noteworthy), everything now works fully as it should. </p>
<p>Something I have noticed is that the original configuration was a bit on the careful side - there is still room for an increase in the amount of slots (and thus an increase in speed). This will be gradually done in the future. </p>
<p>Nothing really special happened, everything just works as it should.
Things that just work as they should don't tend to make for interesting blog posts, so my apologies for that - next time I will make sure something crashes in spectacular fashion :-)</p>
]]></description>
			<link>https://blog.koenvh.nl/a-month-of-astigas-new-synchronisation-system</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/a-month-of-astigas-new-synchronisation-system</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sun, 08 Mar 2020 17:44:45 GMT</pubDate>
		</item>
		<item>
			<title><![CDATA[Astiga's new synchronisation system]]></title>
			<description><![CDATA[<p>Lectori salutem.</p>
<p>If you are reading this, then I am going to presume that you know that <a target="_blank" href="https://asti.ga">Astiga</a> is a music streaming service that streams music from your cloud storage (WebDAV, OneDrive, Google Drive, Dropbox, etc.) to your browser, phone, television, terminal or whatever you use. </p>
<p>You may have noticed that Astiga got a new synchronisation system (the system that gets the files from your cloud storage, extracts the metadata and stores it in the database) on the 8th of February 2020. Either you noticed the blazing speed at which it is able to synchronise now, or you noticed the few teething problems it faced (especially on 12 and 13 February). With this blog post I want to give you a background into why the new system was introduced, how it differs from the old system, and what went wrong on the harrowing day that was 12 February 2020 (just kidding, it was not that bad). </p>
<p><strong>Let's start with the why.</strong>
This question is rather easy to answer. First of all, syncing was always a slow cumbersome process. Now, most did not mind waiting a bit longer for it to complete, but it's still annoying nevertheless. Secondly, I wanted to include audio classification in Astiga Vibe. Problem is that it requires quite a bit of CPU to do that. The fact that it takes a bit longer is not an issue, however, it should not affect the performance of Astiga on the whole in a significant way. Thirdly, the previous system did not scale too well.</p>
<p><strong>And with that, we segue into what the old system was, and how it differs from the new system.</strong>
The old system consisted of one server. As soon as a synchronisation was started, it would spawn a new process that would first list all the files to add, and then add them in fairly linear fashion. It would use a pool (whose size was 1 for normal users, and 3 for premium users), and irregardless of what happened around it, it would just execute that and be done with it. It did not look at what other syncs were running - all of them were completely independent. With infinite hardware, this scales really well. Problem is that the server has finite resources, and when you have to account for peaks as well, that means that on the whole a lot of resources are underused most of the time. The opposite is true as well: Some peaks would exhaust the resources, and cause the server to slow down.</p>
<p>The new system uses a custom global scheduler that runs on a server separate from the "main" server. Once the synchronisation is started, it would add it to a queue. The second server would then fetch the queue, retrieve the tasks (i.e. the files to be synced) for each of them, and add them to another queue specific to the user. The scheduler has a fixed set of slots that could execute a task. So let's say we have two slots 1 and 2, and three users with files: Anna, Bob and Charlie. What the new scheduler does is that it goes to Anna, gets the first task belong to Anna (file to be synchronised) from the list and assigns that to the first available slot (e.g. slot 1). It would then go to Bob, get the first task belonging to Bob, and assign it to the first available slot (slot 1 was taken by Anna, so it will be assigned to slot 2). For Charlie, it does the same, but because no slots are available, it waits until one of the slots becomes available again, and then assigns it. After Charlie it goes back to Anna, and the circle is complete. This means that it fairly distributes the available processing power across all users. In case Charlie was a premium user (which he of course should be :-) ), the first three tasks will be assigned to a slot instead of just the first slot. This means that the processing power can be used much more efficiently. If you are the only one syncing, then you get to use all processing power; if millions of people are syncing, it will still only use a set maximum of processing power. The former case is much more likely than the latter.</p>
<p><strong>So what went wrong?</strong>
The first thing that went wrong is pretty simple: the new system is a lot quicker, and can handle a lot more simultaneous synchronisations. Every task is in principle independent, which means it makes it own connection to the database to add the file your library. More simultaneous tasks means more connections. The database had a limit on the maximum simultaneous connections that was too low, which would cause the connection to fail, which in turn was treated like a database error, which triggered the reconnect mechanism. As you might imagine, this caused the amount of connections to rise again. A relatively simple fix.</p>
<p>The tricky bit with a global scheduler is that it is global. Things go wrong, intentionally or unintentionally. For example, someone could turn off their NAS during a sync (which is a bad idea, but it happens). This causes some errors (which are caught). However, in the old system there was no dependency on other syncs whatsoever. In the global system, this is not the case. This went wrong in two places: 1) The files for newly added synchronisations were being gathered independently, but it still waited for all of them to be finished before continuing. This did not so much cause an error, though it did slow things down considerably. 2) Tasks that got stuck (for example because a file would retry downloading, or the file was offline, or some other miscellaneous error) would still occupy a slot, eventually starving the amount of available slots. This has been solved by releasing a slot after a fixed amount of time no matter what. This was what really caused an issue, as at some point it would simply stop and not continue (technically, it would continue after a timeout of three days, but that is not desirable).</p>
<p>The fix was relatively simple, but because I was asleep when the problem occurred, it still caused the sync process to be stalled for a couple of hours. </p>
<p>Personally I believe transparency is key when it comes to these issues, so I <a target="_blank" href="https://twitter.com/astigamusic/status/1227639077555654656">tweeted</a> about it, plus I sent an email to all those affected explaining what happened and what I was doing about it. I also restarted all syncs that got stuck. Oh, and I wrote a blog post (see what I did there?).</p>
<p>The amount of Astiga users keeps growing at an increasing rate, and that is absolutely great! There are plenty of plans for the future (and you can of course add your own suggestions on the feedback forum). If you want to stay up to date with what happens, consider following Astiga on <a target="_blank" href="https://twitter.com/astigamusic">Twitter</a>. This is also the right time to consider getting <a target="_blank" href="https://play.asti.ga/premium">Astiga Premium</a>. I am not saying that you should, just that you could.</p>
]]></description>
			<link>https://blog.koenvh.nl/astigas-new-synchronisation-system</link>
			<guid isPermaLink="true">https://blog.koenvh.nl/astigas-new-synchronisation-system</guid>
			<dc:creator><![CDATA[Koen van Hove]]></dc:creator>
			<pubDate>Sat, 15 Feb 2020 01:51:13 GMT</pubDate>
		</item>
	</channel>
</rss>