<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>ClickedyClick</title>
	<atom:link href="https://gergely.imreh.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>https://gergely.imreh.net/blog/</link>
	<description>Life in real, complex and digital.</description>
	<lastBuildDate>Wed, 25 Mar 2026 06:01:03 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>Tableau&#8217;s 3834D197 error: Pivot Tables in Google Sheets</title>
		<link>https://gergely.imreh.net/blog/2026/03/tableaus-3834d197-error-pivot-tables-in-google-sheets/</link>
					<comments>https://gergely.imreh.net/blog/2026/03/tableaus-3834d197-error-pivot-tables-in-google-sheets/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Wed, 25 Mar 2026 06:01:00 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Google Sheets]]></category>
		<category><![CDATA[tableau]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=3014</guid>

					<description><![CDATA[<p>Fixing an error where one has to both find the issue, and find that Tableau was getting in the way of seeing when the issue was actually fixed.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2026/03/tableaus-3834d197-error-pivot-tables-in-google-sheets/">Tableau&#8217;s 3834D197 error: Pivot Tables in Google Sheets</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Today&#8217;s story is a little note to self, something to remind me when I next hit the same issue.</p>



<p>At <code>$DAYJOB</code> I was just working on some Google Sheets-backed dashboards using <a href="https://www.tableau.com/">Tableau</a>, when the team hit the following error message:</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="548" height="255" src="https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222036.png" alt="A tableau error dialog, saying: Unable to complete action Bad Connection: Tableau could not connect to the data source. Error Code: 3834D197 Errors occurred while loading the data source. With a Go to Support link in the bottom left, and a &quot;Copy Error Message&quot; on the bottom right." class="wp-image-3015" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222036.png 548w, https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222036-500x233.png 500w" sizes="(max-width: 548px) 100vw, 548px" /><figcaption class="wp-element-caption">Tableau error message for the code 3834D197</figcaption></figure>



<p>It says:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Unable to complete action<br>Bad Connection: Tableau could not connect to the data source.<br>Error Code: 3834D197<br>Errors occurred while loading the data source.</p>
</blockquote>



<p>While debugging this, there were actually <em>two</em> aspects to take into account, that complicated things: one is the source of the error, and the other is Tableau&#8217;s behaviour when encountering the error. Both are good to keep in mind for future development.</p>



<span id="more-3014"></span>



<p>Searching Tableau&#8217;s support didn&#8217;t yield much useful info: not that many people seem to have hit this issue, and when someone did, people were just guessing, and throwing things at the wall, see what sticks&#8230;</p>



<p>While trying to debug, managed to figure out that if I made a copy of the offending Google Sheet (spreadsheet), but removed one specific sheet (tab), then the data connection worked. On the other hand, when I removed the same tab from the original spreadsheet, and kept trying to connect, the error persisted&#8230;</p>



<h3 class="wp-block-heading">Scrub Those Pivot Tables</h3>



<p>First things first: what was missing in the copy compared to the original that allowed the connection to work?</p>



<p>Searching Tableau&#8217;s support didn&#8217;t yield much useful info: not that many people seem to have hit this issue, and when someone did, people were just guessing, and throwing things at the wall, see what sticks&#8230; Similar throwing stuff out happened on <a href="https://www.reddit.com/r/tableau/comments/1hbck48/help_tableau_error_3834d197_when_refreshing/">Reddit</a> too.</p>



<p>Going through the suggestions, the first suspect was a chart, as the offending sheet had a <a href="https://en.wikipedia.org/wiki/Waterfall_chart">waterfall chart</a> near the top, but after a quick trial of whipping up a chart and connecting to Tableau (which worked), that hypothesis was refuted.</p>



<p>Buried in some of the other comments, however, there was a <a href="https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000kQlJh0AK">mention</a> of pivot tables:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Was breaking my head over this error and as soon as I deleted the <strong>Pivot Table</strong> the error was fixed.</p>
</blockquote>



<p>Checking for that, scrolling further down on our offending sheet (the one that we had to remove in the copy to make it work), there <em>was</em> indeed a pivot table. </p>



<p>For a test, creating a simple sheet with a pivot table in it, indeed I could trigger the error on demand, so this seems to be the proximate cause of our issue!</p>



<figure class="wp-block-image size-full"><img decoding="async" width="834" height="368" src="https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222139.png" alt="A simple pivot table in Google Sheets, where there are two data columns on the left, and the created pivot table highlighted in cells on the right." class="wp-image-3016" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222139.png 834w, https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222139-500x221.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2026/03/screenshot-2026-03-23-at-222139-768x339.png 768w" sizes="(max-width: 834px) 100vw, 834px" /><figcaption class="wp-element-caption">Here&#8217;s a basic pivot table; not the one that caused my grief.</figcaption></figure>



<p>One lesson learned: Google Sheets<sup data-fn="f878ffad-0f33-400f-9b06-1c542b0568f2" class="fn"><a href="#f878ffad-0f33-400f-9b06-1c542b0568f2" id="f878ffad-0f33-400f-9b06-1c542b0568f2-link">1</a></sup> that are connected to Tableau directly shouldn&#8217;t have pivot tables in it.</p>



<h3 class="wp-block-heading">Restart Tableau to Try Again</h3>



<p>But this was only half of the story. The nagging feeling remained, why did my modified <em>copy</em> of the spreadsheet work, but if I&#8217;ve fixed the original sheet (deleted the pivot table) still throw the error when I tried to reconnect?</p>



<p>I&#8217;ve downloaded both sheets (as <code>xlsx</code>) and tried to compare, but couldn&#8217;t really see any material differences&#8230; Is it in the history? Is it something more subtle that is hidden in the structure of the doc, or how Google exposes the spreadsheets over the network connection versus the downloaded files?</p>



<p>For simplicity, I tried things out with intentionally triggering this error with a simple sheet, then fixing it, and trying to re-add. The error was again showing even through the sheet no longer had any pivot table&#8230; </p>



<p>The breakthrough came when I shut down Tableau, started up again, loaded the fixed sheet &#8212; and then it worked.</p>



<p>Seems like Tableau caches something between the attempts of connecting to the sheet (either the whole content, which I would doubt, or just the connection results?), so when we hit an error loading the spreadsheet, that error on the same error persisted while the Tableau sesson was running. And not knowing about that caching, and because our original dashboard took a couple of minutes to click through various data connector access requests when started up, I obviously wasn&#8217;t starting fresh whenever we made a chage to the breaking sheet. So we were just banging our head on the wall, while changing stuff to see if things worked, but Tableau&#8217;s caching hid it from us that we&#8217;ve fixed stuff&#8230;</p>



<p>That was an hour or two well spent! The lesson will definitely remain: if you hit an error loading a connector, just restart Tableau before trying again.</p>



<p>And the developer experience corollary: for goodness sake make Dashboards that do not take 3-5 minutes to reconnect to all the sources whenever starting up<sup data-fn="f4c12485-f5b6-4044-95f9-315e9164cc80" class="fn"><a href="#f4c12485-f5b6-4044-95f9-315e9164cc80" id="f4c12485-f5b6-4044-95f9-315e9164cc80-link">2</a></sup>.</p>


<ol class="wp-block-footnotes"><li id="f878ffad-0f33-400f-9b06-1c542b0568f2">I guess that applies to Microsoft Office Spreadsheets too as Tableau uses the similar code internally, but I haven&#8217;t tested that. <a href="#f878ffad-0f33-400f-9b06-1c542b0568f2-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="f4c12485-f5b6-4044-95f9-315e9164cc80">Easier said than done, I&#8217;m aware. <a href="#f4c12485-f5b6-4044-95f9-315e9164cc80-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2026/03/tableaus-3834d197-error-pivot-tables-in-google-sheets/">Tableau&#8217;s 3834D197 error: Pivot Tables in Google Sheets</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2026/03/tableaus-3834d197-error-pivot-tables-in-google-sheets/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to Think Differently When Internet Searches Are Metered</title>
		<link>https://gergely.imreh.net/blog/2026/02/how-to-think-differently-when-internet-searches-are-metered/</link>
					<comments>https://gergely.imreh.net/blog/2026/02/how-to-think-differently-when-internet-searches-are-metered/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Sat, 07 Feb 2026 08:38:17 +0000</pubDate>
				<category><![CDATA[Admin]]></category>
		<category><![CDATA[Thinking]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[internet search]]></category>
		<category><![CDATA[Kagi]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=3003</guid>

					<description><![CDATA[<p>A few months ago I&#8217;ve tried to up to Kagi, a subscription-based search engine, that I heard a lot of good stuff of. Since I was in a country where their payment system didn&#8217;t work yet, I couldn&#8217;t actually complete the signup. They&#8217;ve generously given a Starter plan me for free while their system was [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2026/02/how-to-think-differently-when-internet-searches-are-metered/">How to Think Differently When Internet Searches Are Metered</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>A few months ago I&#8217;ve tried to up to <a href="https://kagi.com">Kagi</a>, a subscription-based search engine, that I heard a lot of good stuff of. Since I was in a country where their payment system didn&#8217;t work yet, I couldn&#8217;t actually complete the signup. They&#8217;ve generously given a Starter plan me for free while their system was being sorted out (nice, thank you!), however that plan comes with a &#8220;300 search per month&#8221; limit &#8212; which I&#8217;ve quickly seen to really matter.</p>



<p>It was around a time when I had a new laptop, and been trying to get things right installing <a href="https://archlinux.org/">ArchLinux</a>, where both the processes and the hardware changed a lot since I&#8217;ve last had done it in&#8230; 2011-ish. So that involved many queries to the Internet. So much so, that my 300 searches were <em>done</em> on the 3rd day or so.</p>



<p>So 3 days into a month I already used up my quota, and couldn&#8217;t just upgrade to unlimited (yet), since it was a gift from the Kagi. So what could I do different in the future for a better experience?</p>



<span id="more-3003"></span>



<h1 class="wp-block-heading">Habit Changes</h1>



<p>What does that better experience really mean? Doing a retrospective of how was I searching, it seemed to me a really mindless, throwaway process:</p>



<ul class="wp-block-list">
<li>plopping in keywords, scrolling quickly around, not really clicking on any link necessarily, but changing the word and re-running the search (total shotgun approach)</li>



<li>running the same query again and again across days</li>



<li>relying on the search engine to get slowly changing or unchanging information</li>
</ul>



<p>&#8230; and more. I feel like these are the habits from other search engines, where I didn&#8217;t find things, when most pages are &#8220;sponsored&#8221;, when I didn&#8217;t put in effort to check the queries I run just sent them off, and could do because it was &#8220;all you can eat&#8221;, and that made me not really pay attention to the &#8220;taste &amp; flavour&#8221; of the results&#8230;</p>



<h2 class="wp-block-heading">Bookmark more &amp; better</h2>



<p>One of the most obvious idea I had was that since I go to the same pages all the time, why not <em>save</em> those links in bookmarks?</p>



<p>I used to bookmark a lot, and organise those bookmarks, etc&#8230; Then I gave it all up, because &#8230; I could just search things? And because organisation wasn&#8217;t that simple either. Which folder does this link go to? Is it under Programming > Languages > Python? Or under Professional Development? Or under Useful Libraries?&#8230;</p>



<p>The same perfectionist &#8220;it doesn&#8217;t just need to be bookmarked, it has to be filed away correctly too!&#8221; was really not doing me any good service&#8230; It took a little while to work through this, and just settle on putting every bookmark in a flat hierarchy, and use tagging to help me find them (rather than folders). Difference being, that a single link can have multiple tags, but can exist in only one folder. I do believe that&#8217;s the only scalable way, ever since I was trying to do the other way around (and failing)<sup data-fn="f8dd80ed-9269-4d69-810d-90eb92b9ea2d" class="fn"><a href="#f8dd80ed-9269-4d69-810d-90eb92b9ea2d" id="f8dd80ed-9269-4d69-810d-90eb92b9ea2d-link">1</a></sup>.</p>



<p>So bookmark:</p>



<ul class="wp-block-list">
<li>the (book) library catalogues that I borrow books from</li>



<li>the forums and docs pages of projects I&#8217;m using<sup data-fn="481de0c2-87dc-4fe1-bc86-ae6461319aab" class="fn"><a href="#481de0c2-87dc-4fe1-bc86-ae6461319aab" id="481de0c2-87dc-4fe1-bc86-ae6461319aab-link">2</a></sup> </li>



<li>the useful tools available online, from <a href="https://www.timeanddate.com/">timezone wrangling</a> to <a href="https://www.xe.com/">currency conversion</a>&#8230;</li>



<li>blogs, publications, web comics that I frequent&#8230;</li>
</ul>



<p>and so on&#8230; Now whenever I visit a page, I do stop for a moment and think: could I imagine myself wanting to come back here in the future? If yes, let&#8217;s bookmark.</p>



<p>It&#8217;s not all perfect, but it&#8217;s more about the tools than the process: Firefox on Android doesn&#8217;t seem to handle the same &#8220;search in bookmarks&#8221; shortcuts, or make it more difficult to do. Oh, well, eventually&#8230;</p>



<h2 class="wp-block-heading">Add custom search engines</h2>



<p>Sometimes I the page I want to go to is within a large collection, such as a wiki, or a forum. I know it&#8217;s there, but unsure where exactly. Bookmarks take me to the page, but then I have to use the search as a second step. This can be made a lot more ergonomic in pretty much every current browser by adding custom search engines<sup data-fn="32fe7398-0359-4ead-8261-d3f4239af710" class="fn"><a href="#32fe7398-0359-4ead-8261-d3f4239af710" id="32fe7398-0359-4ead-8261-d3f4239af710-link">3</a></sup>.</p>



<p>Adding Wikipedia&#8217;s search? It might already be there. Adding ArchLinux Wiki? It takes 15 seconds to do, and I have a shortcut to so much Linux system admin knowledge that is riddiculous. Whatever site has a search, can be added just as simple.</p>



<p>Here the kicker is to remember that shortcut (and the fact that I&#8217;ve added that shortcut), but after that, it&#8217;s off to the races.</p>



<h2 class="wp-block-heading">Change how I search</h2>



<p>While I was auditing what sort of searches I like to do, one type that stood out was when I was asking for something like &#8220;what&#8217;s the website of this-or-that company or project or organisation?&#8221; More often than not, these are companies, projects, and organisations that are notable enough to be in Wikipedia. Here&#8217;s the new process: search Wikipedia or the company/project/organisation and use the link from their page.</p>



<p>This also feel more to the point than search for &#8220;&lt;companyname> website&#8221; which is just &#8220;close enough&#8221; in meaning, and will still get me many results, even if the answers should be a single value.</p>



<p>This is caveated by that for programming / open source projects, the better search is probably GitHub / GitLab / CodeBerg, where they are likely hosted (in decreasing probability, currently), and switch to search engine search when that fails.</p>



<p>This is aling the lines that if I already know an authoritive source for the information, I should probably go there directly?</p>



<h2 class="wp-block-heading">Misc</h2>



<p>Bookmarks &amp; custom searches brought down my search count already. One that is more of a housekeeping change is that my browser was reopening pages from my previous session whenever I started it. If I had any Kagi search results open, that just used up another in the quota, and there often there were more than one open&#8230; Setting my browser to start afresh on each time I open it helped with that &#8212; and also helped with me not being distracted every time I open my browser by whatever I was doing last time, as opposed to what I wanted to do <em>now</em>.</p>



<h1 class="wp-block-heading">What did I Learn?</h1>



<p>Now that I&#8217;m on a proper paid plan, I will up it to the Professional plan, where searches are not metered. It doesn&#8217;t feel like just a lazy release valve<sup data-fn="ab17392b-0fc4-4261-b010-409482095f2b" class="fn"><a href="#ab17392b-0fc4-4261-b010-409482095f2b" id="ab17392b-0fc4-4261-b010-409482095f2b-link">4</a></sup>, rather because I don&#8217;t believe this sorts of limit to my access information is productive. &#8220;Limited&#8221; limitations, when there&#8217;s a purpose, can indeed be &#8220;cretive limitations&#8221;. </p>



<p>If I believe that Kagi does a good job, then there&#8217;s no point sticking to the quota; if I don&#8217;t, then why am I using it in the first place, instead of any of the alternatives?</p>



<p>And if I want to use the power of creative limitations, I can always do my own quest with rules like no search, it&#8217;s within my power.</p>



<p>I do feel that the changes to my thinking due to this experience &#8212; being more deliberate of what am I looking for and thoughful about where might I find them; choosing and rewarding sources I find useful and reliable; using the little gray cells more &#8212; I want to keep and even cultivate. These changes also brought back a more old-school internet vibes (old as in when I bought printed magazines that came with collected links of what you can find on the  World Wide Web, something more tangible and purposeful). I guess I&#8217;m getting old as well. :)</p>


<ol class="wp-block-footnotes"><li id="f8dd80ed-9269-4d69-810d-90eb92b9ea2d">Tagging also got a big push from and also due to a push from the book <a href="https://app.thestorygraph.com/books/22c416f5-4a94-4c7a-add3-5161c9d3a4d7">Everything is Miscellanious</a>. It&#8217;s also why e.g. Gmail was awesome to have tags while other clients were still just doing folders. Fastmail goes even further, lets you choose <a href="https://www.fastmail.help/hc/en-us/articles/360058753554-Setting-up-and-using-labels#what">labels</a> (tags) or folders, which is pretty awesome of them. <a href="#f8dd80ed-9269-4d69-810d-90eb92b9ea2d-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="481de0c2-87dc-4fe1-bc86-ae6461319aab">Almost every good project has a good forum/docs, or maybe a good forum/docs contributes to the project being good? Here&#8217;s looking at you <a href="https://wiki.archlinux.org/title/Main_page">ArchWiki</a>, <a href="https://help.obsidian.md/">Obsidian Help</a>,&#8230;  <a href="#481de0c2-87dc-4fe1-bc86-ae6461319aab-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="32fe7398-0359-4ead-8261-d3f4239af710">For example <a href="https://support.mozilla.org/en-US/kb/add-or-remove-search-engine-firefox">in Firefox</a>. <a href="#32fe7398-0359-4ead-8261-d3f4239af710-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="ab17392b-0fc4-4261-b010-409482095f2b">As in &#8220;if I&#8217;m unlimited, I don&#8217;t have to care about all the effort I&#8217;ve described so far, I can go back to my old habits&#8221;. <a href="#ab17392b-0fc4-4261-b010-409482095f2b-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2026/02/how-to-think-differently-when-internet-searches-are-metered/">How to Think Differently When Internet Searches Are Metered</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2026/02/how-to-think-differently-when-internet-searches-are-metered/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Installing Arch Linux as an incremental game</title>
		<link>https://gergely.imreh.net/blog/2025/09/installing-arch-linux-as-an-incremental-game/</link>
					<comments>https://gergely.imreh.net/blog/2025/09/installing-arch-linux-as-an-incremental-game/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Tue, 30 Sep 2025 07:25:43 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Thinking]]></category>
		<category><![CDATA[linux]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2990</guid>

					<description><![CDATA[<p>Once in a decade or so the time comes to install (or reinstall) my personal computer. This time the occasion is getting a new laptop, something more modern, something more capable. The time passed and the changes in technology since the last time I had to do this means that the installation is likely familiar, [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/09/installing-arch-linux-as-an-incremental-game/">Installing Arch Linux as an incremental game</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Once in a decade or so the time comes to install (or reinstall) my personal computer. This time the occasion is getting a new laptop, something more modern, something more capable. The time passed and the changes in technology since the last time I had to do this means that the installation is likely familiar, but still different &#8211; sometimes subtly, sometimes in unrecognisable ways.</p>



<p>Setting up my laptop is now in its 3rd day, and the experience made me think of incremental games. Incrementals<sup data-fn="ac3a5a53-0f71-416a-bc5e-6af94cf6bb35" class="fn"><a id="ac3a5a53-0f71-416a-bc5e-6af94cf6bb35-link" href="#ac3a5a53-0f71-416a-bc5e-6af94cf6bb35">1</a></sup> are where you make some progress in gathering some resource, then have to reset your progress that gives you some small buff or gain. You then start again &#8211; but better. The cycle repeats and you might have the same experiences countless times, but after a while the game can be much faster or even unrecognisable due to the accumulated effects.</p>



<p>While I was installing <a href="https://archlinux.org/">Arch Linux</a> this time I went through the following cycle:</p>



<ul class="wp-block-list">
<li>Partitioned my disk and installed the system, but then couldn&#8217;t boot into it</li>



<li>Redo the partitioning, system, and bootloader install, now I can boot into it, but don&#8217;t have any network access</li>



<li>Redo the system config, now I have network access, but have to figure out what desktop environment am I going to run, and what do I need for that. A hot mess ensues after trying out all main desktop environments for kicks</li>



<li>Reinstall the system, cleaner, with my desktop environment of choice, now the high resolution environment makes everything either: tiny, gigantic, and/or blurred.</li>



<li>Sorted out most of the sizes of things, have network, have Bluetooth, but the sound and the media buttons don&#8217;t work</li>



<li>Sorted out sound, now the power management locks me out while watching a video&#8230;.</li>
</ul>



<p>&#8230; and so on. I know I have a few more rounds (few more &#8220;crunches&#8221;) to do get there, and the multilingual typing input setup will be a doozie with <a href="https://en.wikipedia.org/wiki/Bopomofo">bopomofo</a>, but I&#8217;m getting better and <em>feeling</em> better every round &#8211; that&#8217;s how a good incremental game goes. A power up there, a know-how of a semi-obscure, quality of life config here<sup data-fn="90f16848-f9b2-441b-99e2-13891011da2f" class="fn"><a href="#90f16848-f9b2-441b-99e2-13891011da2f" id="90f16848-f9b2-441b-99e2-13891011da2f-link">2</a></sup>.</p>



<p>Of course I could have gotten an <a href="https://ubuntu.com/">Ubuntu</a> or <a href="https://www.fedoraproject.org/">Fedora</a> image, installed everything in <em>way less time</em> than writing about the experience, and could already be using it &#8212; but that&#8217;s <em>a whole different</em> game<sup data-fn="1c1f44b1-25dc-4971-905b-5dcbffbc516f" class="fn"><a href="#1c1f44b1-25dc-4971-905b-5dcbffbc516f" id="1c1f44b1-25dc-4971-905b-5dcbffbc516f-link">3</a></sup>.</p>



<p>The games we choose to play show our values. On an optimistic day I feel I value increased knowledge; on more realistic days it certainly seems like procrastination. Now let&#8217;s keep this in mind when I finally have high enough level of Linux buffs that they change the game mechanics and I get to <em>do</em> something with my computer.</p>


<ol class="wp-block-footnotes"><li id="ac3a5a53-0f71-416a-bc5e-6af94cf6bb35">Keep away from Universal Paperclips and give a very wide berth to Antimatter Dimensions <a href="#ac3a5a53-0f71-416a-bc5e-6af94cf6bb35-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="90f16848-f9b2-441b-99e2-13891011da2f">Such as enabling tapping on the touch pad for a click, without needing to actually press&#8230;. <a href="#90f16848-f9b2-441b-99e2-13891011da2f-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="1c1f44b1-25dc-4971-905b-5dcbffbc516f">A game that might be won, while I&#8217;m not sure whether the one I&#8217;ve chosen has a win condition. <a href="#1c1f44b1-25dc-4971-905b-5dcbffbc516f-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol>


<p></p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/09/installing-arch-linux-as-an-incremental-game/">Installing Arch Linux as an incremental game</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2025/09/installing-arch-linux-as-an-incremental-game/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>The curious case of binfmt for x86 emulation for ARM Docker</title>
		<link>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/</link>
					<comments>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Fri, 04 Apr 2025 05:15:48 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[ArchiveTeam]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[QEMU]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2967</guid>

					<description><![CDATA[<p>Seemingly identical configurations, different results. When two methods for setting up x86 emulation on ARM showed the exact same system configuration but behaved completely differently in Docker, I began questioning my system administration knowledge and my sanity &#8211; and briefly contemplated a new career as a blacksmith. This is a debugging tale for those working [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/">The curious case of binfmt for x86 emulation for ARM Docker</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Seemingly identical configurations, different results. When two methods for setting up x86 emulation on ARM showed the exact same system configuration but behaved completely differently in Docker, I began questioning my system administration knowledge and my sanity &#8211; and briefly contemplated a new career as a blacksmith.</p>



<p>This is a debugging tale for those working with containers, and a reminder that things aren&#8217;t always what they seem in Linux, all with a big pinch reminder to Read the Fine Manual, Always!</p>



<span id="more-2967"></span>



<h2 class="wp-block-heading">ARM with Achiveteam v2</h2>



<p>Recently I&#8217;ve got an email from a reader of the <a href="https://gergely.imreh.net/blog/2021/04/arm-images-to-help-the-archive-team/">ARM images to help the Archive Team</a> blogpost from years ago, asking me about refreshing that project to use again. There I was recompiling the ArchiveTeam&#8217;s Docker images to support ARM, and thus I was looking how things changed in the intervening time. I also got more <s>lazy</s> pragmatic since then, I was was wondering if the <a href="https://wiki.archiveteam.org/">Archiveteam</a> just made some ARM or multi-arch images as I believe(d) they should. That lead me to <a href="https://wiki.archiveteam.org/index.php/ArchiveTeam_Warrior#Can_I_run_the_Warrior_on_ARM_or_some_other_unusual_architecture?">their FAQ entry about ARM images</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Can I run the Warrior on ARM or some other unusual architecture?</strong></p>



<p>Not directly. We currently do not allow ARM (used on Raspberry Pi and M1 Macs) or other non-x86 architectures. This is because we have previously discovered questionable practices in the Wget archive-creating components and are not confident that they run correctly under (among other things) different endiannesses. [&#8230;]</p>



<p>Set up <a href="https://docs.docker.com/build/building/multi-platform/#qemu">QEMU with your Docker install</a> and add &#8211;platform linux/amd64 to your docker run command.</p>
</blockquote>



<p>This actually seems like a sensible thing &#8211; if they dug that deep that they&#8217;ve seen issues in <a href="https://www.gnu.org/software/wget/">wget</a>, I&#8217;ve definitely been doing things naively before.</p>



<p>The guidance of installing QEMU seems sensible as well (we were doing a lot of those at <a href="https://www.balena.io/">balena</a>), and it goes roughly like.</p>



<ol class="wp-block-list">
<li>install <code>binfmt</code></li>



<li>install QEMU with statically compiled binaries</li>



<li>load those binaries to emulate the platforms you want with the <code>F</code> / <code>fix_binary</code> flag</li>
</ol>



<p>For those unfamiliar, <code>binfmt_misc</code> is a <a href="https://www.kernel.org/doc/html/latest/admin-guide/binfmt-misc.html">Linux kernel feature</a> that allows non-native binary formats to be recognized and passed to user space applications. It&#8217;s what makes it possible to run ARM binaries on x86 systems and vice versa through emulation. The various flags are how the actual behaviour of <code>binfmt</code> is adjusted (F, P, C, O)</p>



<p>Docker advised to use a image to set things up, that is, for example for the <code>x86_64</code>/<code>amd64</code> platform like this:</p>



<pre class="wp-block-code"><code class="">docker run --privileged --rm tonistiigi/binfmt --install amd4</code></pre>



<p>My Raspberry Pi is running <a href="https://archlinuxarm.org/">ArchLinuxARM</a> which installs <a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-binfmt.service.html">systemd-binfmt</a> to load the relevant emulation settings at boot time, which seemed handy: with the docker method I had to run that every time before I could run an emulated container, with systemd I would have thing ready by every time the time Docker is ready to run (ie. keeping the Archiveteam containers always on and restarting after reboot.) So I have a strong incentive to use the systemd-based approach instead of the <code>docker run</code> based one.</p>



<p>Now comes the kicker <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92f.png" alt="🤯" class="wp-smiley" style="height: 1em; max-height: 1em;" />:</p>



<ul class="wp-block-list">
<li>the <code>docker</code> installed <code>binfmt</code> setup worked and allowed to run <code>linux/amd64</code> containers</li>



<li><code>systemd-binfmt</code> initiated <code>binfmt</code> setup worked for the <code>x86_64</code> binaries in the file system, but not in Docker where the binaries just failed to run</li>



<li>both setups had identical output when looking at the config in <code>/proc/sys/fs/binfmt_misc</code></li>
</ul>



<h2 class="wp-block-heading">When Same&#8217;s Not the Same</h2>



<p>To see whether emulation works, the <code>tonistiigi/binfmt</code> container can be invoked without any arguments and it shows the status. For example setting things up with <code>docker</code> would show:</p>



<pre class="wp-block-code"><code class="">$ docker run --privileged --rm tonistiigi/binfmt
{
  "supported": [
    "linux/arm64",
    "linux/amd64",
    "linux/amd64/v2",
    "linux/arm/v7",
    "linux/arm/v6"
  ],
  "emulators": [
    "qemu-x86_64"
  ]
}</code></pre>



<p>Here the <code>supported</code> section shows <code>amd64</code> as it should, and their test of running an amd64 image to check if the binaries are run has the expected output:</p>



<pre class="wp-block-code"><code class="">$ docker run --rm --platform linux/amd64 -t alpine uname -m
x86_64</code></pre>



<p>Going back to the alternative, after uninstalling that emulatior I start up <code>systemd-binfmt</code>I can test the status again:</p>



<pre class="wp-block-code"><code class="">$ docker run --privileged --rm tonistiigi/binfmt
{
  "supported": [
    "linux/arm64",
    "linux/arm/v7",
    "linux/arm/v6"
  ],
  "emulators": [
[...snip...]
    "qemu-x86_64",
[...snip...]
  ]
}</code></pre>



<p>This shows that while the emulator is installed, Docker doesn&#8217;t find that the <code>linux/amd64</code> platform is supported, and this checks out with running the <code>alpine</code> image again as above:</p>



<pre class="wp-block-code"><code class="">$ docker run --rm --platform linux/amd64 -t alpine uname -m
exec /bin/uname: exec format error</code></pre>



<p>Well, this doesn&#8217;t work.</p>



<p>The <a href="https://docs.kernel.org/admin-guide/binfmt-misc.html">binfmt_misc docs in the Linux Kernel wiki</a> do have plenty of info on the setup and use of the that emulation function. For example to check the configuration of the emulation setup, we can look at the contents of a file in <code>/proc</code> filesystem:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64
enabled
interpreter /usr/bin/qemu-x86_64
flags: POCF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p>This was the almost the same whether I the <code>docker</code> based setup or used <code>systemd-binfmt</code> with a slight difference: the <code>flags</code> bit is only <code>PF</code> when run with <code>systemd-binfmt</code>, and <code>POCF</code> when set things up with <code>docker run</code>. Even if the Docker docs are asking for the <code>F</code> flag, I wanted to make sure we are on equal footing, so I&#8217;ve tried to modify  the QEMU setup to be the same. This means overriding the <code>qemu-x86_64.conf</code> that is shipped by default:</p>



<ul class="wp-block-list">
<li>Copy the config from <code>/usr/lib/binfmt.d/qemu-x86_64.conf</code> to <code>/etc/binfmt.d/qemu-x86_64.conf</code> (make sure the file has the same name to ensure this new file overrides the one from the lib folder)</li>



<li>Edit the end of the line from <code>:FP</code> to <code>:FPOC</code> </li>



<li>restart <code>systemd-binfmt</code></li>
</ul>



<p>After this the output of the the runtime info in <code>/proc/sys/fs/binfmt_misc/qemu-x86_64</code> was completely the same. Why&#8217;s the difference?</p>



<p>More debugging steps ensued:</p>



<h3 class="wp-block-heading">More Debugging Ensued</h3>



<p>I&#8217;ve <strong>read through the source code</strong> of <code>tonistiigi/binfmt</code> on <a href="https://github.com/tonistiigi/binfmt/tree/master/cmd/binfmt">GitHub</a> and seen that it doesn&#8217;t do anything fancy, it&#8217;s quite clear implementation of the `binfmt_misc` usage docs and the same magic values as QEMU shipped on my system. Good that no surprise, but no hints of any difference</p>



<p>I&#8217;ve tried to <strong>replicate that process</strong> of setting up QEMU through translating it into Python and running, still the same</p>



<p>I&#8217;ve <strong>recompiled the binary</strong> on my system, and run it outside of docker: it worked the same way as the <code>systemd-binfmt</code> setup: <code>x86_64</code> static binaries<sup data-fn="edde2512-e0d8-4c40-8fc7-32ce3232f660" class="fn"><a id="edde2512-e0d8-4c40-8fc7-32ce3232f660-link" href="#edde2512-e0d8-4c40-8fc7-32ce3232f660">1</a></sup> worked outside of Docker but not inside of it</p>



<p>A sort-of <strong>breakthrough</strong> came when I&#8217;ve tried out <a href="https://github.com/dbhi/qus">dbhi/qus</a> Docker images, that promises &#8220;qemu-user-static (qus) and containers, non-invasive minimal working setups&#8221;, and can do the similar emulator &amp; platform support setup with:</p>



<pre class="wp-block-code"><code class="">docker run --rm --privileged aptman/qus -s -- -p x86_64</code></pre>



<p>It was a lot slower to run (coming back to this later), but worked like the charm, just like Docker&#8217;s own recommendation. However there was a difference in the outcome when I checked the runtime config info:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64
enabled
interpreter /qus/bin/qemu-x86_64-static
flags: F
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p>It has just the apparently required <code>F</code> flag, but the interpreter points to <code>/qus/bin/qemu-x86_64-static</code> &#8230; which is not in the regular file system. Nevertheless <code>alpine</code> happily runs, just as my local static binaries.</p>



<p>How does this actually work, then?</p>



<h2 class="wp-block-heading">Everything&#8217;s Illuminated</h2>



<p>With this above, and with a better understanding what the docs say, we have everything in place to understand the all the behaviours above, things we had pointers throughout, but not enough experience to put them together:</p>



<p>So, the <code>F</code> flag was required by the Docker docs, what does that <em>actually</em> do?</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><code>F</code> &#8211; fix binary</p>



<p>The usual behaviour of binfmt_misc is to spawn the binary lazily when the misc format file is invoked. However, this doesn’t work very well in the face of mount namespaces and changeroots, so the <code>F</code> mode opens the binary as soon as the emulation is installed and uses the opened image to spawn the emulator, meaning it is always available once installed, regardless of how the environment changes.</p>
</blockquote>



<p>Because of this, if <code>F</code> is set, the <code>interpreter</code> entry in the runtime settings doesn&#8217;t mean the path of the interpreter it <em>will</em> be called, but where it <em>was</em> called at the time &#8211; ie. it&#8217;s irrelevant for the actual runtime.</p>



<p>The <code>tonistiigi/binfmt</code> image ships its own static-compiled <code>qemu-*</code> binarlies, as well as  <code>aptman/qus</code> container gets the right ones at runtime (hence the slowness), and the <code>interpreter</code> path is the binary <em>inside the container when the command is run</em>. The binary is then kept in memory, and the container can go away, the <code>interpreter</code> path&#8217;s not refering anything that exists any longer.</p>



<p>Why does <code>systemd-binfmt</code> fail then? Well of course because it&#8217;s a <code>dynamically</code> linked binary:</p>



<pre class="wp-block-code"><code class="">$ file /usr/bin/qemu-x86_64
/usr/bin/qemu-x86_64: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=a4b8a93a4361be61dfa34a0eab40083325853839, for GNU/Linux 3.7.0, stripped</code></pre>



<p>&#8230; and because it&#8217;s dynamically linked, even if the <code>F</code> flag makes it stay in memory, its lib dependencies aren&#8217;t, so when in run in Docker (which uses namespaces) it doesn&#8217;t have everything to run&#8230;</p>



<p>And of course, ArchLinux <a href="https://wiki.archlinux.org/title/QEMU">spells</a> this out:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Note:</strong> At present, Arch does not offer a full-system mode and statically linked variant (neither officially nor via AUR), as this is usually not needed.</p>
</blockquote>



<p>Yes, &#8220;as this is usually not needed&#8221;. :)</p>



<h2 class="wp-block-heading">Updated Setup and Looking Forward</h2>



<p>Sort of lobbying ArchLinux to have static QEMU<sup data-fn="f5c8a1a6-6844-4956-adf5-488644168f92" class="fn"><a id="f5c8a1a6-6844-4956-adf5-488644168f92-link" href="#f5c8a1a6-6844-4956-adf5-488644168f92">2</a></sup> what options do I have?</p>



<ul class="wp-block-list">
<li>set up a <code>systemd</code> service to run the <code>tonistiigi/binfmt</code> container on startup (which is <a href="https://blog.container-solutions.com/running-docker-containers-with-systemd">possible</a>)</li>



<li>get some static QEMU binaries and override the settings that <code>systemd-binfmt</code> uses</li>



<li>switch to anothe Linux Distro that supports the Pi, the software I run, but also ships static QEMU builds</li>
</ul>



<p>All three are suboptimal, potentially fragile, and the third is way too much work. Still the second one was kinda fine:</p>



<pre class="wp-block-code"><code class="">cd $(mktemp -d)
docker create --name="tmp_$$"  tonistiigi/binfmt
docker export tmp_$$ -o tonistiigi.tar.gz
docker rm tmp_$$
tar -xf tonistiigi.tar.gz --wildcards "*/qemu-x86_64"
# Copy along the binaries folder:
sudo cp usr/bin/qemu-x86_64 /usr/bin/qemu-x86_64-static</code></pre>



<p>Then just like we&#8217;ve overridden the upstream <code>qemu-x86_64.conf</code> we do it again:</p>



<ul class="wp-block-list">
<li>Copy the config from <code>/usr/lib/binfmt.d/qemu-x86_64.conf</code> to <code>/etc/binfmt.d/qemu-x86_64.conf</code> (make sure the file has the same name to ensure this new file overrides the one from the lib folder)</li>



<li>Edit the end of the line from <code>:/usr/bin/qemu-x86_64:FP</code> to <code>:/usr/bin/qemu-x86_64-static:FPOC</code> (that is updating the binary it points at and the flags for good measure too</li>



<li>As a bonus, can update the <code>:qemu-x86_64:</code> in the front too, say to <code>:qemu-x86_64-static:</code>, to change the display name of the emulator without affecting any of the functionality, it will just rename the entrin in <code>/proc/sys/fs/binfmt_misc</code> </li>



<li>restart <code>systemd-binfmt</code></li>
</ul>



<p>Then the check again:</p>



<pre class="wp-block-code"><code class="">$ cat /proc/sys/fs/binfmt_misc/qemu-x86_64-static
enabled
interpreter /usr/bin/qemu-x86_64-static
flags: POCF
offset 0
magic 7f454c4602010100000000000000000002003e00
mask fffffffffffefe00fffffffffffffffffeffffff</code></pre>



<p>And the <code>alpine</code>-based checks work once more.</p>



<h2 class="wp-block-heading">Lessons Learned</h2>



<p>The details were all in plain sight, but not enough experience to piece these things together. The Docker-recommended image ships its own QEMU? What does that <code>F</code> flag actually do? Can you run binaries while you don&#8217;t have them anymore? Dynamic and static linking and the signs of their misbehaviours to provide hints&#8230; However this is coupled with confusion when expectations are broken (say the <code>interpreter</code> doesn&#8217;t have to refer to an actual file path that exists <em>now</em>), until I started to question my expectations. Also, just being a heavy user of Docker doesn&#8217;t mean I&#8217;m knowledgeable of the relevant kernel functionality, and probably I should be more…</p>



<p>This whole process underlined my previous thoughts on <a href="https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/">Software Engineering when AI seems Everywhere</a>, as I did try to debug things by rubber ducking with <a href="https://claude.ai">Claude</a>: this time the hallucinations were through the roof (a metric tonne of non-existent systemd funcionality, non-existent command line flags), definitely got me on a wild goose chase in a few cases. So even more care&#8217;s needed, maybe a version of <a href="https://en.wikipedia.org/wiki/Hofstadter%27s_law">Hofstadter&#8217;s Law</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Imreh&#8217;s Law<sup data-fn="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc" class="fn"><a id="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc-link" href="#f632dbc3-dd26-4b73-b6cc-0b53e878e3dc">3</a></sup>: LLMs are always more wrong than you expect, even when you take into account Imreh&#8217;s Law.</p>
</blockquote>



<p>In the end, Don&#8217;t Panic, make theories and try to prove them, and talk with anyone who listens, even when they are wrong, and you are more likely to get there<sup data-fn="692118e9-bc19-43d4-8ac0-1603d91facdf" class="fn"><a id="692118e9-bc19-43d4-8ac0-1603d91facdf-link" href="#692118e9-bc19-43d4-8ac0-1603d91facdf">4</a></sup>.</p>


<ol class="wp-block-footnotes"><li id="edde2512-e0d8-4c40-8fc7-32ce3232f660">I&#8217;ve download static binaries from <a href="https://github.com/andrew-d/static-binaries">andrew-d/static-binaries</a>, recommend <code>strings</code> as something that&#8217;s quick and simple to use <code>./strings /bin/sh | head</code> for example, allowing fast iteration. <a href="#edde2512-e0d8-4c40-8fc7-32ce3232f660-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="f5c8a1a6-6844-4956-adf5-488644168f92">ArchLinux is x86 by default, for them it would be to emulate <code>linux/arm64</code>, <code>linux/arm/v7</code>, <code>linux/arm/v6</code> images. For ArchLinux ARM it would be a different work the other direction. If only the main Arch would support ARM, it would be a happier world (even if even more complex). <a href="#f5c8a1a6-6844-4956-adf5-488644168f92-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="f632dbc3-dd26-4b73-b6cc-0b53e878e3dc">Tongue-in-cheek, of course. <a href="#f632dbc3-dd26-4b73-b6cc-0b53e878e3dc-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="692118e9-bc19-43d4-8ac0-1603d91facdf">And with this we just rediscovered the <a href="https://wiki.c2.com/?FeynmanAlgorithm">Feynman Algorithm</a>, I guess. <a href="#692118e9-bc19-43d4-8ac0-1603d91facdf-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/">The curious case of binfmt for x86 emulation for ARM Docker</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2025/04/the-curious-case-of-binfmt-for-x86-emulation-for-arm-docker/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Software Engineering when AI seems Everywhere</title>
		<link>https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/</link>
					<comments>https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Sat, 22 Mar 2025 10:29:59 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Thinking]]></category>
		<category><![CDATA[coding assistant]]></category>
		<category><![CDATA[llm]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2959</guid>

					<description><![CDATA[<p>It&#8217;s pretty much impossible to miss the big push to use AI/LLM (Large Language Model) coding assistants for software engineers. Individual engineers, small and large companies seem to be going &#8220;all in&#8221; on this1. I&#8217;m generally wary of things that are this popular, as those often turn out more cargo cult than genuinely positive. So [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/">Software Engineering when AI seems Everywhere</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s pretty much impossible to miss the big push to use AI/LLM (Large Language Model) coding assistants for software engineers. Individual engineers, small and large companies seem to be going &#8220;all in&#8221; on this<sup data-fn="1ce21cd2-18db-4974-a7e1-d9e5e71e2305" class="fn"><a id="1ce21cd2-18db-4974-a7e1-d9e5e71e2305-link" href="#1ce21cd2-18db-4974-a7e1-d9e5e71e2305">1</a></sup>. I&#8217;m generally wary of things that are <em>this</em> popular, as those often turn out more cargo cult than genuinely positive. So what&#8217;s a prudent thing to do as a software engineer? I believe the way ahead is a boring piece of advice, taht applies almost everywhere: <em>instead of going easy, do more of the difficult stuff</em>.</p>



<p>I genuinely think that putting the AI/LLM genie back into the bottle is unlikely (the same way as some people want the Internet, or smartphones, or cryptocurrencies put back into the bottle, which also not really gonna happen). That doesn&#8217;t mean that uncritical acceptance of the coding assistant tools should be the norm, <em>au contraire</em>, just like any tool, one needs to discover when they are fit for for the job, and when they are not. I have used <a href="https://github.com/features/copilot">GitHub CoPilot</a> for a while, now digging into <a href="https://www.cursor.com/en">Cursor</a> as it starts to conquer the workplace, and <a href="https://chatgpt.com/">ChatGPT</a> &amp; <a href="https://claude.ai/">Claude</a> for individual coding questions. I don&#8217;t think it&#8217;s controversial to say that all these tools have their &#8220;strengths and weaknesses&#8221;, and that <em>currently</em> the more complex, more &#8220;production&#8221; the problem is, the further away it is from a proof-of-concept, the less likely these tools are of any help. They are help, they can be a large force multiplier, but they are big multiplier when one goes in with the least amount of input (knowledge, awailable time, reqirements for the result&#8230;)</p>



<span id="more-2959"></span>



<p>On the day-to-day use I&#8217;ve ended up usually in one of these cases:</p>



<ul class="wp-block-list">
<li>the suggested results were wrong: clearly when I was lucky, and subtly when I wasn&#8217;t;</li>



<li>when the results were correct I had to spend tuning the prompts, questions, and guidance until the invested effort was similar to doing the work myself;</li>



<li>when the results were correct and they weren&#8217;t too much work they addressed simple stuff (such as writing structured documentation), in which case the tools indeed got me into a nice flow state;</li>
</ul>



<p>The proportion of the above cases was about 70/20/10%, respectively<sup data-fn="ce69d46f-92ad-4f91-9c2c-f66d8ca89fe7" class="fn"><a id="ce69d46f-92ad-4f91-9c2c-f66d8ca89fe7-link" href="#ce69d46f-92ad-4f91-9c2c-f66d8ca89fe7">2</a></sup>.</p>



<p>This experience made me pause and think about what influences this status quo in the areas that I can control? For example I cannot (really) control how capable the various models are, they will be improving over time for sure. On the other hand, is there something that I could do differently to have better outcomes?</p>



<p>Stepping all the way back, the universal rule of thumb of <em>garbage in, garbage out</em> hasn&#8217;t failed me yet, so working on better quality input seems like a good place to start. And better input only happens when <em>I&#8217;m</em> better at the things I&#8217;m doing. Wherever I look, that getting better is coming from focused, significant, and directed <em>effort</em>.<sup data-fn="7c1fde34-4940-462d-b3d9-e54569ac3a8a" class="fn"><a id="7c1fde34-4940-462d-b3d9-e54569ac3a8a-link" href="#7c1fde34-4940-462d-b3d9-e54569ac3a8a">3</a></sup></p>



<p>What can I do be more effortful &#8211; in the right way? What are those software engineer activities that require effort, but not <a href="https://leaddev.com/velocity/what-toil-and-why-it-damaging-your-engineering-org">toil</a>? Looking back my past experience, so far I&#8217;ve found three main areas:</p>



<p>Work on <strong>debugging skills</strong>. How to read code, how to approach a brand new software system and build up model of how it works? <a href="https://pages.cs.wisc.edu/~remzi/Naur.pdf">Programming is theory building</a>, and so the better (more correctly, faster, with more insights) you can build your theories, the better the ultimate outcomes will be.</p>



<p>Work on <strong>testing skills</strong>: once you know how things work, you can know better how they can go wrong, and better, you can prevent things going wrong. However not all tests are created equal: coding assistants these days can create tests very easily (tests often need a lot of boilerplate, and boilerplate is easy to generalise, exactly the coding assistants bread &amp; butter). They seem less good figuring out what tests really move the neadle increase our certainty that things work well &#8211; and those are the tests that are really needed.</p>



<p>Work on <strong>software architecture skills</strong>: making something complex and complicated is less work, than making something simple and elegant. Simplicity and elegance is bot just for its own sake. Simple is easier to reason about, easier to maintain and evolve, more likely to be correct. Can coding assistants do this? So far they tend towards verbocity which breeds complexity. They are very local and thus much less holistic. They learn from the entire Internet, but that also means that they have to have extremely good <a href="https://en.wikipedia.org/wiki/Attention_(machine_learning)">attention</a> to find what&#8217;s relevant&#8230;</p>



<p>These three are programming language, business context, job role agnosic, and I find it difficult to imagine an engineer role that doesn&#8217;t need them. I guess as long as humans do software engineering, these shall serve people right.</p>



<p>At the end of the day, instead of speeding up with coding assistants, I&#8217;ll slow down. Instead of the volume, I try focusing on quality. The assistants still have potential space there: rubber ducking, as an advanced (even if flawed) search engine, and to code the parts that have less value for a given set of goal. There <a href="https://youtu.be/_IZw2CoYztk?si=NVxvQ5-i_Uwjiljj">machines can do the work</a> so people have time to think<sup data-fn="7536a7ea-4000-46ff-a30e-b09724b489a6" class="fn"><a id="7536a7ea-4000-46ff-a30e-b09724b489a6-link" href="#7536a7ea-4000-46ff-a30e-b09724b489a6">4</a></sup>.</p>


<ol class="wp-block-footnotes"><li id="1ce21cd2-18db-4974-a7e1-d9e5e71e2305">It might be my own bubble, being a machine learning engineer by the day. <a href="#1ce21cd2-18db-4974-a7e1-d9e5e71e2305-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="ce69d46f-92ad-4f91-9c2c-f66d8ca89fe7">With better models I&#8217;d guess the cases will be the same, but the proportions will shift. <a href="#ce69d46f-92ad-4f91-9c2c-f66d8ca89fe7-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="7c1fde34-4940-462d-b3d9-e54569ac3a8a">Let this be physical such as exercise, or mental like it&#8217;s advocated in <a href="https://en.wikipedia.org/wiki/How_to_Read_a_Book">How to Read a Book</a>. <a href="#7c1fde34-4940-462d-b3d9-e54569ac3a8a-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="7536a7ea-4000-46ff-a30e-b09724b489a6">Rather than <a href="https://www.youtube.com/watch?v=955dZpR7QwY">the other way around</a>. <a href="#7536a7ea-4000-46ff-a30e-b09724b489a6-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/">Software Engineering when AI seems Everywhere</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2025/03/software-engineering-when-ai-seems-everywhere/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Refreshing Airplane Tracking Software With and Without AI</title>
		<link>https://gergely.imreh.net/blog/2025/01/refreshing-airplane-tracking-software-with-and-without-ai/</link>
					<comments>https://gergely.imreh.net/blog/2025/01/refreshing-airplane-tracking-software-with-and-without-ai/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Sun, 19 Jan 2025 06:13:59 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[ADS-B]]></category>
		<category><![CDATA[DVB-T]]></category>
		<category><![CDATA[SDR]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2925</guid>

					<description><![CDATA[<p>Bug fixing with LLMs won't get you far if you don't speak the (programming) language, so better use your head.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/01/refreshing-airplane-tracking-software-with-and-without-ai/">Refreshing Airplane Tracking Software With and Without AI</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>A bit like last time this post is about a bit of programmer hubris, a bit of AI, a bit of failure&#8230; Though I also took away more lessons this time about software engineering, with or without fancy tools. This is about rabbit-holing myself into an old software project that I had very little knowhow to go on&#8230;</p>



<p>The story starts with me rediscovering a <a href="https://en.wikipedia.org/wiki/DVB-T">DVB-T</a> receiver USB stick, that I had for probably close to decade. It&#8217;s been &#8220;barnacled&#8221; by time spent in the Taiwanese climate, so I wasn&#8217;t sure if it still works, but it&#8217;s such a versatile tool, that it was worth trying to revive it.</p>



<p>When these receivers function, they can receive digital TV (that&#8217;s the DVB-T), but also FM radio, DAB, and also they can act as software defined radio (SDR). This last thing makes them able to receive all kinds of transitions that are immediately quite high on the fun level, in particular airplane (ADS-B transmission) and ship (AIS) tracking. Naturally, there are websites to do both if you just want to see it (for example <a href="https://www.flightradar24.com">Flightradar24</a> and <a href="https://www.marinetraffic.com/">MarineTraffic</a>, respectively, are popular aggregators for that data but there are tons), but doing your own data collection opens doors to all kinds of other use cases.</p>



<p>So on I go, trying to find, what software tools people use these days to use these receivers. Mine is a pretty simple one (find out everything about it by following the &#8220;RTL-SDR&#8221; keywords wherever you like to do that :) and so I remembered there were many tools. However also time passed, I forgot most that I knew, and also there were new projects coming and going.</p>



<h2 class="wp-block-heading">ADSBox</h2>



<p>While I was searching, I found the <a href="https://github.com/netdisciple/adsbox">adsbox</a> project, that was interesting both kinda working straight out of box for me, while it was also last updated some 9 years ago, so it&#8217;s an old code base that tickles my &#8220;let&#8217;s maintain all the things!&#8221; drive&#8230;</p>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="336" src="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox-1024x336.png" alt="The GitHub repo information of ADSBox, last commits overall have been 9 years ago, and there are very few of them." class="wp-image-2933" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox-1024x336.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox-500x164.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox-768x252.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox-1200x393.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox.png 1269w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The tool is written mostly in C, while it also hosts its own server for a web interface, for listing flights, and (back in the day) supporting things like Google Maps and Google Earth.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="491" src="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1-1024x491.png" alt="The ADSBox interface showing a bunch of airplane information." class="wp-image-2929" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1-1024x491.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1-500x240.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1-768x368.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1-1200x576.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_frontend1.png 1280w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">The adsbox plane listing interface.</figcaption></figure>



<p>Both the Google Maps and Earth parts seem completely: Maps has changed a lot since, as I also had to update my <a href="https://gergely.imreh.net/blog/2015/11/taiwan-wwii-map-overlays/">Taiwan WWII Map Overlays</a> project over time too (the requirement of using API keys to even load the map, changes to the JavaScript API&#8230;). Earth I haven&#8217;t tried, but I&#8217;m thinking that went the way of the dodo on the the desktop?</p>



<span id="more-2925"></span>



<p>So in the supur of the this-is-the-weekend-and-I-have-energy-to-code moment, I started to think of the options:</p>



<ul class="wp-block-list">
<li>could fix up the map, either with the Google Maps changes, or bring in some other map?</li>



<li>the project has barely any readme, and I mainly managed to make it work by looking at <a href="https://www.rtl-sdr.com/adsbox-new-ads-b-decoding-software-for-linux/">old articles from the time when <code>adsbox</code> waas new</a>, could fix those up?</li>



<li>during the compilation, loads of warnings happened, that seem to call for some &#8220;better quality&#8221; coding, let&#8217;s fix stuff until <code>-Werror</code> (making all warnings errors) passes too! This would be a learning experience</li>



<li>I&#8217;m sure I can find other tasks to do as well, like an error message here, a strange behaviour there&#8230;</li>
</ul>



<p>Here&#8217;s the kicker though: I don&#8217;t really know C. I spend most of my time in Python-land, and haven&#8217;t done a C project in anger yet. Is it worth trying to dig in, while there are other ADS-B projects that a) work better, b) are in languages that I&#8217;m more looking to learn, such as Rust?</p>



<p>There was an additional drive of curiosity, just like in my last post: <em>can I use Large Language Models (LLMs) to complement me on things I lack, such as knowledge of the exact programming language at hand?</em></p>



<p>With this I thought let&#8217;s dig in, and let&#8217;s dig into the C code: that seemed immediately tractable, more limited in scope, and thus would help build up (hopefully) some successes and I&#8217;ll learn my way around the codebase better.</p>



<p>On the LLM side I have <a href="https://github.com/features/copilot">GitHub Copilot</a> &#8211; though it seems somewhat crippled in my open source <a href="https://github.com/coder/code-server">Code Server</a> installation of VS Code, rather than the official VS Code, in particular the context menus and Copilot Chat seems missing, and thus it was only communicating with me through TAB-completions and me adding comments to guide or suggest. That&#8217;s not very practical, so didn&#8217;t push it too far for the relevant tasks of explanation and exploration of options that I wanted to do.</p>



<p>I also have <a href="https://claude.ai/">Claude</a> that I can chat with. If I wasn&#8217;t working on my 13 year old <a href="https://en.wikipedia.org/wiki/ThinkPad_X_series">Lenovo ThinkPad X201</a>, I&#8217;d probably set up <a href="https://ollama.com/">Ollama</a>, but that&#8217;s just excruciating with even the smallest models on this machine (until I upgrade something newer, or run the questions on my work M1 MacBook). So Claude it is for now.</p>



<h3 class="wp-block-heading">Hello Fixes</h3>



<p>I guess it&#8217;s one sign of hubris (or unlimited optimism), to jump into fixing compilation warnings, without knowing anything much of the codebase yet. This started in areas where the airplane tracking interacts with <a href="https://www.sqlite.org/index.html">SQLite</a>, for example had warnings about <strong>casting pointers to integers of different size</strong> while shuffling around SQLite query results:</p>



<pre class="wp-block-code"><code class="">int * t = (int *) sqlite3_value_int(argv[0]);</code></pre>



<p>This was also part of a larger code section (formatting integers into hexadecimal or octal strings, for example for the ICAO codes&#8230;), and thus had to play around how much context to give to Claude to actually have something useful.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="740" height="509" src="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_claude.png" alt="Chatting with Claude about the code snippet, my question and their answer shown in part." class="wp-image-2928" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_claude.png 740w, https://gergely.imreh.net/blog/wp-content/uploads/2025/01/adsbox_claude-500x344.png 500w" sizes="auto, (max-width: 740px) 100vw, 740px" /><figcaption class="wp-element-caption">A segment from a discussion with Claude.</figcaption></figure>



<p>A bit of mocking around there seemed to have worked, and while I should have asked more software architecture &amp; best practices questions, probably knew about it enough to be dangerous, and left it as it was so far.</p>



<p>Having said that, after this change it turned out that some part of the interface now displaying stuff differently: the 24-bit ICAO airplane registration codes had useless leading zeros for 8 hex digits, rather than the expected 6 digits &#8211; since the fix was done without this context. Here we go, manual adaptation on this regression.</p>



<p>Now there were cases when &#8220;<strong>sprintf may write a terminating nul past the end of the destination</strong>&#8220;, as the code seems to have written its data back into the same place as this:</p>



<pre class="wp-block-code"><code class="">sprintf(data-&gt;avr_data, "*%s", data-&gt;avr_data + 13);</code></pre>



<p>This ended up being again about a much bigger context (interative reading, processing, and passing on of recorded ADS-B packets), where based on the Claude&#8217;s suggestions I couldn&#8217;t really get to anything useful. The real point was always one step further:</p>



<ul class="wp-block-list">
<li>instead of the line look at the nearby lines</li>



<li>instead of the near by lines, look at the whole funciton</li>



<li>instead of the whole function, look at the wider codebase with its configuration</li>
</ul>



<p>These are of course no-brainers. However Claude with its chat interface cannot really do that, while Copilot without its chat interface also cannot do this digging. Catch-22? Since in the end I admitted myself (for the <em>n</em>th time) that I need to understand the purpose of the code better before &#8220;fixing&#8221; it. Then due to the lack of comments in the codebase + lack of natural intuition of the built in C functions&#8217; behaviour, I&#8217;ve just left them as they were for now, since they do work.</p>



<p>From here I turned to other parts. The <strong>webserver was not serving some files with the correct MIME type</strong>, due to its hand-rolled file extension extraction (splitting filenames at the first <code>.</code> rather than the last), this was easy to fix &#8211; with a bit of StackOverflow this time, rather than asking Claude.</p>



<p>Then there was an issue with <strong>the tool apparently not playing back the recorded packet data</strong>, which I fixed with a combo of regular ol&#8217; debug printouts, StackOverflow, and just thinking about how it could work (it&#8217;s the issue of explicitly filling in daylight saving data in the relevant <code>tm</code> struct &#8211; <code>tm_isdst</code> &#8211; and thus IMHO it&#8217;s doing a regular &#8220;undefined&#8221; behaviour: in this case jumped the first timestamp&#8217;s time ahead by an hour, and thus would have needed to wait an hour to pass as the playback (following the real timingof the packets) catch up and start actually replaying. Still weird, why only the first packet&#8217;s data was shifted, and could I do a more solid fix than setting it once as the code never seem to overwrite it? These are the questions that are more addressing C-knowledge or potential best practice of the code&#8217;s structure overall&#8230;</p>



<p>Finally I&#8217;ve started on <strong>replacing Google Maps</strong> with <a href="https://openfreemap.org/">OpenFreeMap</a> and got as far as displaying the map (which is the easy step:). The whole replacement would likely be a lot more, also given the amount of barely documented JavaScript code in the project &#8211; but hopefully I have more working knowledge of JS than C.</p>



<h3 class="wp-block-heading">Lessons Learned</h3>



<p>First lesson is that I likely have a &#8220;saviour complex&#8221;, trying to fix up every code I see being imperferct in some way, whether or not I am capable of doing it or not. This is something to meditate further on for sure.</p>



<p>When using LLMs for code work, they are just as useful as another mid-level coder without much context &#8211; almost not at all. The context of code is <em>always</em> relevant, so either the LLM would have to get it itself, or the person pairing with the LLM would have to provide it. Thus the work is always there, just not always possible.</p>



<p>It&#8217;s very nice that I can do things in programming languages that I don&#8217;t really understand, but that&#8217;s only the case if I either spend much-much time actually getting to know things so I can start to judge whether the changes even have a chance to be correct or not; or I don&#8217;t care whether they are correct or not (but is this really an option?)</p>



<p>Overall the LLMs need the same things as humans to do a good job, and cannot pretend that they really can do work without these (even if they might appear being able to do without these for some time):</p>



<ul class="wp-block-list">
<li>good comment in the code so the intention can be ascertained as well</li>



<li>tests that show what the correct behaviour should be, and catch regressions or unintentional breakages</li>



<li>have domain knowledge to form better mental models about what should happen</li>
</ul>



<p>The first two wasn&#8217;t true in this project. The last point is likely where LLMs are ahead in cases like this (having been trained on &#8220;all the Internet&#8217;s data&#8221;), though wouldn&#8217;t be the same for some niche, or work internal projects.</p>



<p>The LLMs suggestions are still ver much localised, thus they cannot really fix up the structure of the code too much &#8211; or maybe I&#8217;m not using the right tools, of course. And this is where my future big ask would lie: don&#8217;t just tell me how to fix this line, rather tell me that the entire block is no longer needed / could be merged with another part of the code / could be broken out to its own module that would help over there&#8230; Of course, this is moving the goal post a bit of what LLM programmers&#8217; look like, though I also think that the current &#8220;fix this line&#8221; is something I most definitely want to have enough practice with that I don&#8217;t really need to ask (though it could suggest if there are good practices I haven&#8217;t picked up yet).</p>



<h2 class="wp-block-heading">Where do I go from here?</h2>



<p>This adsbox project is mostly obsolete, as I&#8217;ve found a bunch of other tools that are better, and better supported now (<a href="https://github.com/rsadsb/adsb_deku">adsb_deku</a>, <a href="https://github.com/wiedehopf/tar1090">tar1090</a>), but surprising it still have stuff that are better here and in other tools (the plane&#8217;s status icons, some data displayed here that is not in others, showing what sort of packets (what Downlink Format or DF numbers) were received for the aircraft, etc&#8230; So there might be still value in using it occasionally, so there might be value. </p>



<p>Even if I could get a kick out of it, it&#8217;s likely useful to keep things time-boxed or constrained to some topics: change the map; add comments as I find them; fix issues if they arise; package it up for ArchLinux. That&#8217;s about it, but these should be generally useful (e.g. using OpenFreeMap for other projets in the future or rewriting the aforementioned <a href="https://imrehg.github.io/taiwanmap/">Taiwan WWII Map project</a> to use that).</p>



<p>My current fixes live in my fork in GitHub <a href="https://github.com/imrehg/adsbox">imrehg/adsbox</a>, with no guarantees. Since the project also doesn&#8217;t have a license (just a note of &#8220;free for non-commercial use&#8221;, which doesn&#8217;t cover modifications), I&#8217;m probably keeping it simple for now.</p>



<p>I also got the hang of software defined radio again, and there&#8217;s just so much fun to have&#8230;</p>



<p>What&#8217;s the most useful is seeing in practice, what does software need to be maintainable almost a decade later, and what&#8217;s missing in most projects: explanatory comments to understand what is being done and why, and tests to know whether things are running correctly or not. And maube then both my future self, my colleagues, and any potential AI pair programmer would have a better chance of succeeding at &#8220;maintain all the things!&#8221;</p>
<p>The post <a href="https://gergely.imreh.net/blog/2025/01/refreshing-airplane-tracking-software-with-and-without-ai/">Refreshing Airplane Tracking Software With and Without AI</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2025/01/refreshing-airplane-tracking-software-with-and-without-ai/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Adventures into Code Age with an LLM</title>
		<link>https://gergely.imreh.net/blog/2024/11/adventures-into-code-age-with-an-llm/</link>
					<comments>https://gergely.imreh.net/blog/2024/11/adventures-into-code-age-with-an-llm/#respond</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Sat, 09 Nov 2024 09:50:20 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[Claude]]></category>
		<category><![CDATA[llm]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2910</guid>

					<description><![CDATA[<p>It&#8217;s a relaxed Saturday afternoon, and I just remembered some nerdy plots I&#8217;ve seen online for various projects, depicting &#8220;code age&#8221; over time: how does your repository change over the months and years, how much code still survives from the beginning till now, etc&#8230; Something like this made by the author of curl: It looks [&#8230;]</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/11/adventures-into-code-age-with-an-llm/">Adventures into Code Age with an LLM</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>It&#8217;s a relaxed Saturday afternoon, and I just remembered some nerdy plots I&#8217;ve seen online for various projects, depicting &#8220;code age&#8221; over time: how does your repository change over the months and years, how much code still survives from the beginning till now, etc&#8230; Something like <a href="https://daniel.haxx.se/blog/2024/10/31/curl-source-code-age/">this made by the author of curl</a>:</p>



<figure class="wp-block-image size-large is-style-default"><img loading="lazy" decoding="async" width="1024" height="644" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-1024x644.png" alt="" class="wp-image-2914" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-1024x644.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-500x314.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-768x483.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-1536x966.png 1536w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-2048x1288.png 2048w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-1200x754.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/screenshot-2024-10-31-at-13-39-29-gnuplot-1980x1245.png 1980w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Curl&#8217;s code age distribution</figcaption></figure>



<p>It looks interesting and informative. And even though I don&#8217;t have codebases that have been around this long, there are plenty of codebases around me that are fast moving, so something like a month (or in some cases week) level cohorts could be interesting.</p>



<p>One way to take this challenge on is to actually sit down and write the code. Another is to take a <a href="https://en.wikipedia.org/wiki/Large_language_model">Large Language Model</a>, say <a href="https://claude.ai/">Claude</a> and try to get that to make it. Of course the challenge is different in nature. For this case, let&#8217;s put myself in the shoes of someone who says</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>I am more interested in the results than the process, and want to get to the results quicker.</p>
</blockquote>



<p>See how far we can get with this attitude, and where does it break down (probably no spoiler: it breaks down very quickly.).</p>



<p>Note on the selection of the model: I&#8217;ve chosen Claude just because generally I have good experience with it these days, and it can share generated artefacts (like the relevant Python code) which is nice. And it&#8217;s a short afternoon. :) Otherwise anything else could work as well, though surely with varying results. </p>



<h3 class="wp-block-heading">Version 1</h3>



<p>Let&#8217;s kick it off with a quick prompt.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Prompt</strong>: How would you generate a chart from a git repository to show the age of the code? That is when the code was written and how much of it survives over time?</p>
</blockquote>



<p>Claude quickly picked it up and made me a Python script, which is nice (that being my day-to-day programming language). I guess that&#8217;s generally a good assumption these days if one does data analytics anyways (asking for another language is left for another experiment).</p>



<span id="more-2910"></span>



<p>The result is this <a href="https://claude.site/artifacts/d7c4b1f6-e6c4-4d74-82dd-97e0d40d0c5e">this code</a>. I&#8217;ve skimmed it that it doesn&#8217;t just delete all my repo or does something completely batshit, but otherwise saved in a repo that I have at hand. To make it easier on myself, added some <a href="https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata">inline metadata</a> with the dependencies:</p>



<pre class="wp-block-code"><code class=""># /// script
# dependencies = [
#   "pandas",
#   "matplotlib",
# ]
# ///</code></pre>



<p>and from there I can just run the script with <a href="https://docs.astral.sh/uv/">uv</a>.</p>



<p>First it checked too few files (my repository is a mixture of Python and SQL scripts managed by <a href="https://www.getdbt.com/">dbt</a>), so had to go in and change those filters, expanding them.</p>



<p>Then the thought struck me to remove the filter altogether (since it already checks only files that are checked in git, so it should be fine &#8211; but then it broke on a step where it reads a file as if it was text to find the line counts. I guess there could be a better way of filtering (say &#8220;do not read binary files&#8221;, if there&#8217;s a way to do that), but just went with catching the problems:</p>



<pre class="wp-block-code"><code class=""># ....
    for file_path in tracked_files:
        try:
            timestamps = get_file_blame_data(file_path)
            for timestamp in timestamps:
                blame_data[timestamp] += 1
                total_lines += 1
        except UnicodeDecodeError:
            print(f"Error reading file: {file_path}")
            continue
#....</code></pre>



<p>(hance I know that a favicon PNG was causting those <code>UnicodeDecodeError</code> hubbub in earlier runs. Now we are getting somewhere, and we have a graph like this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="512" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_age_distribution-1024x512.png" alt="" class="wp-image-2911" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_age_distribution-1024x512.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_age_distribution-500x250.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_age_distribution-768x384.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_age_distribution.png 1200w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Version 1</figcaption></figure>



<p>This is already quite fun to see. There are the sudden accelerations of development, there are the plateaus of me working on other projects, and generally feel like &#8220;wow, productive!&#8221; (with no facts backing that feeling <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f602.png" alt="😂" class="wp-smiley" style="height: 1em; max-height: 1em;" />). Also pretty good ROI on maybe 15 mins of effort.</p>



<p>Having said that, this is still fair from what I wanted.</p>



<h3 class="wp-block-heading">Version 2</h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Promt:</strong> Could we change the code to have cohorts of time, that is configurable, say monthly, or yearly cohoorts, and colour the chart to see how long each cohort survives?</p>
</blockquote>



<p>This came back with another <a href="https://claude.site/artifacts/34b65023-9ba4-4abb-b214-1c10d82eacfd">set of code</a>. Adding the metadata, skimming it (it has the filter on the file extensions again, never mind), and running it once more to see the output, I get this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="542" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-1024x542.png" alt="" class="wp-image-2912" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-1024x542.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-500x265.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-768x406.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-1536x813.png 1536w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-2048x1083.png 2048w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-1200x635.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_survival_monthly-1980x1048.png 1980w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Version 2</figcaption></figure>



<p>Because of the file extension filter in place, the numbers are obviously not aligning with the above, but it does something. The something is a bit unclear, bit it <em>feels</em> like progress, so let&#8217;s give it a benefit of the doubt, and just change once more. </p>



<h3 class="wp-block-heading">Version 3</h3>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p><strong>Promt:</strong> Now change this into a cummulative graph, please.</p>
</blockquote>



<p>One more time Claude came back with <a href="https://claude.site/artifacts/a391c5dc-743c-439f-aa09-70ec9b86a19a">this code</a>. Adding the metadata again, same drill. Running this has failed with errors in <code>numpy</code>, though:</p>



<pre class="wp-block-code"><code class="">TypeError: ufunc 'isfinite' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''</code></pre>



<p>Now this needed some debugging. It turns out a column the code is trying to plot is actually numbers as strings rather than numbers as, you know, say floats&#8230;</p>



<pre class="wp-block-code"><code class=""># my "fix"
        df['cumulative_percentage'] = df['cumulative_percentage'].astype(float)
# end

        # Plot cumulative area
        plt.fill_between(df.index, df['cumulative_percentage'],
                        alpha=0.6, color='royalblue',
                        label='Cumulative Code')</code></pre>



<p>It didn&#8217;t take too many tries, but it was confusing at first &#8211; why shouldn&#8217;t be, if I didn&#8217;t actually <em>read</em> just skim the code&#8230;</p>



<p>The result is then like this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="543" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-1024x543.png" alt="" class="wp-image-2913" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-1024x543.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-500x265.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-768x407.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-1536x814.png 1536w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-2048x1085.png 2048w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-1200x636.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2024/11/code_growth_monthly-1980x1049.png 1980w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Version 3</figcaption></figure>



<p>Sort of <em>meh</em>, it feels like it&#8217;s not going to the right direction overall.</p>



<p>But while debugging the above issues, I first tried tried to ask Claude about the error (maybe it can fix it itself), but came back with &#8220;Your message exceeds the <a href="https://support.anthropic.com/en/articles/7996848-how-large-is-claude-s-context-window">length limit</a>. &#8230;&#8221; (for free users, that is). So I kinda stopped here for the time being.</p>



<h2 class="wp-block-heading">Lessons learned</h2>



<p>The first lesson is very much re-learned:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Garbage in, garbage out.</p>
</blockquote>



<p>If I cannot express what I really want, it&#8217;s very difficult to make it happen. And my prompts were by no means expressing my wishes correctly, no wonder Claude wasn&#8217;t really hitting the mark. Whether or not a human engineer would have faired better, I don&#8217;t know. I know however, that this kind of &#8220;tell me exceedingly clearly what&#8217;s your idea&#8221; is an everyday conversation for me as an engineer (and being on both end of the convo).</p>



<p>The code provided by the model wasn&#8217;t really far off for <em>some</em> solution, so that was fun! On the other hand, when it hit any issues, I really had to have domain and language knowledge to fix things. This seems like an interesting place to be:</p>



<ul class="wp-block-list">
<li>the results are quick and on the surface good-enough for a non/less technical person, probably</li>



<li>but they would also be the ones who couldn&#8217;t do anything if something goes wrong.</li>
</ul>



<p>Even myself I feel that it would be hard to support the code as a software engineer if it was just generated like this. But that&#8217;s also a strange thought: so many times I have to support (debug, extend, explain, refactor) code that I haven&#8217;t had anything to do with before.</p>



<p>It seems to me that now that since Claude comes across as an eager junior engineer, writing decent code that always needs some adjustments, the trade-off is really in the dimension of spending time to get better at prompting vs better at coding. </p>



<p>If there&#8217;s a person with some amount of programming skills, mostly interested in the results not the process, and doubling down on prompting: they likely could get loads further than I did here. Good quality prompts and small amount of code adjustments being the sweet spot for them.</p>



<p>For others who have more programming expertise, and maybe more interested in the process, spending time on getting better at programming rather than getting really better at prompting: keeping to smaller snippets might be the sweet spot, or learning new languages, &#8230; Something as a starting point for digging in, a seed, is what this process can help with.</p>



<h2 class="wp-block-heading">Future</h2>



<p>Given the above notes on how this generated code is like a new codebase that I suddenly neet to support, here&#8217;s a different, fun exercise <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> to actually improve engineering skills:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Take AI generated code that is &#8220;good enough&#8221; for a small problem and refactor, extent, productionise it.</p>
</blockquote>



<p>I&#8217;m not sure if this would work, or would get me into wrong habits, but if I wanted do have some quick ways of doing <a href="https://en.wikipedia.org/wiki/Peak:_Secrets_from_the_New_Science_of_Expertise">deliberate practice</a> &#8211; and not <a href="https://exercism.org/">Exercism</a>, <a href="https://gergely.imreh.net/blog/2023/08/doing-the-easy-problems-on-leetcode/">LeetCode</a>, or somilar, rather something that can be custom made, then this seems a way to get started.</p>



<p>Also, now that I&#8217;ve gotten even more interested in the problem, I&#8217;ll likely just dig into how to actually define that chart I was looking for and what kind of data I would need to get from <code>git</code> to make it happen. The example code made me pretty confident, that &#8220;all I need is Python&#8221; really, even though while prepping for this I found other useful tools like <a href="https://github.com/mergestat/mergestat-lite">one allowing you to write SQL queries for your repo</a>, that might be some further way to expand my understanding.</p>



<p>Either way, it&#8217;s just fun to mess with code on a lazy Saturday.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/11/adventures-into-code-age-with-an-llm/">Adventures into Code Age with an LLM</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2024/11/adventures-into-code-age-with-an-llm/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Git login and commit signing with security</title>
		<link>https://gergely.imreh.net/blog/2024/05/git-login-and-commit-signing-with-security/</link>
					<comments>https://gergely.imreh.net/blog/2024/05/git-login-and-commit-signing-with-security/#comments</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Thu, 23 May 2024 14:24:42 +0000</pubDate>
				<category><![CDATA[Computers]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[password manager]]></category>
		<category><![CDATA[ssh]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2870</guid>

					<description><![CDATA[<p>Evaluating SSH keys for both access and authorship, and making use of password managers to store and serve those keys.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/05/git-login-and-commit-signing-with-security/">Git login and commit signing with security</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Doing software engineering (well-ish) is pretty hard to imagine without working in <a href="https://en.wikipedia.org/wiki/Version_control">version control,</a> which most of the time means <a href="https://git-scm.com/">git</a>. In a practical setup of git there&#8217;s the question of how do I get access to the code it stores &#8212; <em>how do I &#8220;check things out&#8221;?</em> &#8212; and optionally how can others verify that it was indeed me who did the changes &#8212; <em>how do I &#8220;sign&#8221; my commits?</em> Recently I&#8217;ve changed my mind about what&#8217;s a good combination for these two aspects, and what tools am I using to do them.</p>



<h2 class="wp-block-heading">Access Options</h2>



<p>In broad terms git repositories can be checked out either though the <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_http_protocols">HTTP protocol</a>, or through the <a href="https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_ssh_protocol">SSH protocol</a>. Both have pros and cons.</p>



<p>Having two-factor authentication (2FA) made the HTTP access more secure but also more setup (no more direct username/password usage, rather needing to create extra access keys used in place of passwords). Credentials were still in plain text (as far as I know) on the machine in some git config files.</p>



<p>The SSH setup was in some sense more practical one (creating keys on your own machine, and just passing in the public key portion), though there were still secrets in plain text on my machine (as I don&#8217;t think the majority of people used password-protected SSH keys, due to their user experience). This is what I&#8217;ve used for years: add a new SSH key for a new machine that I&#8217;m working on, check code out through ssh+git, and work away.</p>



<p>When I&#8217;ve recently came across the <a href="https://github.com/git-ecosystem/git-credential-manager">git-credential-manager</a> tool that supposed to make HTTP access nicer (for various git servers and services), and get rid of plain text secrets. Of course this is not the first or only one of the tools that does <a href="https://git-scm.com/docs/gitcredentials">git credentials</a>, but being made by GitHub, it had some more clout. This made me re-evaulate what options do I have for SSH as well for similar security improvements.</p>



<p>Thus I&#8217;ve found that both <a href="https://developer.1password.com/docs/ssh/">1Password</a> and <a href="https://keepassxc.org/docs/">KeePassXC</a> (the two main password managers I use) have ssh-agent integration, and thus can store SSH keys + give access to them as needed. No more plain text (or password protected) private keys on disk with these either!</p>



<p>Now it seems there are two good, new options to evaulate, and for the full picture I looked at how the code signing options work in this context as well.</p>



<span id="more-2870"></span>



<h2 class="wp-block-heading">Code Signing Options</h2>



<p>When <a href="https://git-scm.com/book/en/v2/Git-Tools-Signing-Your-Work">signing my commits</a> to authenticate authorship, it&#8217;s <a href="https://git-scm.com/docs/gitformat-signature">possible to use</a> PGP/GPG (the &#8220;classic way&#8221;), or now also SSH keys (as detailed, for example <a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits">here</a> or <a href="https://docs.gitlab.com/ee/user/project/repository/signed_commits/ssh.html">here</a>).</p>



<p>The GPG setup is well established, and also links my commits to my identities used elsewhere (e.g. signed emails sent to mailing lists that care about it, with the key linked from this site&#8217;s <a href="/">frontpage</a>). This of course is not always needed or desired, but it decouples the identity from the code hosting platform. There&#8217;s some serious downsides as well, though: GPG signing keys are not supposed to be numerous (just a single one), and thus if I use multiple machines to work on, I will have to take my private keys with me between machines, for example making copies of them. Or if not making copies, then have them on hardware keys (that have other problems with backups and all that, if I got it right the last time I tried to understand the process).</p>



<p>The SSH key commit signing is much newer (need git version at least 2.34), but it&#8217;s also simpler: add a key to my git hosting service, sign commits with that key, and thus the service can match things up and show that match. I can add as many keys as machines I&#8217;m working on if needed, no need to transfer or copy keys between machines, and I can also choose use some keys for login only or code signing only.</p>



<p>A third party trying to verify these signatures, though, would need to get the keys from the hosting service (I&#8217;d find it surprising if people would distribute their commit signing keys out of band the same way as they do with GPG public keys, since there are likely more of them). Hence it git hosting services will need to make the user&#8217;s keys available (as they do at the relevant <code>username.keys</code> URLs, e.g. mine on <a href="https://github.com/imrehg.keys">GitHub</a> and <a href="https://gitlab.com/imrehg.keys">GitLab</a>).</p>



<p>Also can&#8217;t forget to add the relevant keys to the list of allowed signing keys locally, and all the other relevant setup (see e.g. the <a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key">GitHub</a> and <a href="https://docs.gitlab.com/ee/user/project/repository/signed_commits/gpg.html">GitLab</a> docs). There are a bit too many places to update, but it&#8217;s mostly set-it-and-forget-it. After that, once started to sign commits, adding the <code>--show-signatures</code> flag to the commands that support it (<code>git log</code>, <code>git show</code> for example), should show the signatures.</p>



<h2 class="wp-block-heading">My Winning Combo</h2>



<p>Looking at the opions above, there&#8217;s a matrix of options that we can use, and here&#8217;s what I think about them:</p>



<figure class="wp-block-table"><table><tbody><tr><td></td><td><strong>GPG signature</strong></td><td><strong>SSH key signature</strong></td></tr><tr><td><strong>Git Credential Helper</strong></td><td>Extra setup</td><td>Simpler</td></tr><tr><td><strong>SSH clone</strong></td><td>The usual</td><td>Most convenience <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44d.png" alt="👍" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td></tr></tbody></table><figcaption class="wp-element-caption">Convenience matrix of Git access (rows) and commit signing (columns) options</figcaption></figure>



<p>Really, where I want to be is just SSH keys for everything, even if they are imperfect, but they have the most number of puzzle pieces to fit.</p>



<h2 class="wp-block-heading">SSH Key Security</h2>



<p>While previously SSH keys were really just held as files in your <code>~/.ssh</code> folder, most likely, recently I&#8217;ve found (tada!) that the password managers I use can also store &amp; serve SSH keys: see in particular <a href="https://developer.1password.com/docs/ssh/">1Password&#8217;s SSH documentation</a> and <a href="https://keepassxc.org/docs/">KeePassXC docs</a> (scroll to SSH Agent Integration on that page), though I&#8217;m sure other password managers can do this too.</p>



<h4 class="wp-block-heading">1Password</h4>



<p>The two password managers listed above handle things quite similarly. 1Password is a bit less hands-on, though, the default settings work pretty well.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="954" height="778" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-27-at-11.55.43.png" alt="" class="wp-image-2886" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-27-at-11.55.43.png 954w, https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-27-at-11.55.43-500x408.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-27-at-11.55.43-768x626.png 768w" sizes="auto, (max-width: 954px) 100vw, 954px" /></figure>



<p>One important bit is that 1Password runs its own SSH agent, so that has to be configured in the relevant places, but it&#8217;s easy enough. The approvals are also useful, so it&#8217;s more transparent when something accesses the key.</p>


<div class="wp-block-image">
<figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="1024" height="516" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-07-at-16.41.56-1024x516.png" alt="1Password pop-up for SSH key usage" class="wp-image-2871" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-07-at-16.41.56-1024x516.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-07-at-16.41.56-500x252.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-07-at-16.41.56-768x387.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/04/Screenshot-2024-04-07-at-16.41.56.png 1064w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">1Password pop-up for SSH key usage</figcaption></figure>
</div>


<p>With this things generally work, and relatively easy to reason about things. When things are less clear, it might be just a debug check-away away from seeing the keys added to this alternate agent:</p>



<pre class="wp-block-code"><code class="">$ export SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock<br>$ ssh-add -l<br>256 SHA256:XfRsbxRMm+CN[...snip...]</code></pre>



<h4 class="wp-block-heading">KeePassXC</h4>



<p>KeePassXC, being open source, is my preferred solution of the two, though unsurprisingly it&#8217;s the more awkward one to set up. The main differences from 1Password include:</p>



<ul class="wp-block-list">
<li>needing to generate the keys externally to the password manager (rather than having built-in ssh keygen) &#8211; this is a con on usability but a strong pro on basing security on the established tool, rather than potentially questionably reimplement it</li>



<li>uses the main SSH agent, so no extra setup is necessary in most of the tools &#8211; this is a potential pro on usability for configurations, but a potential con that the worflow and config of loading keys into the agent needs a bit more understanding to be both ergonomic and safe to one&#8217;s level of paranoia</li>



<li>the key use confirmation defaults to &#8220;ok&#8221; on pressing Enter on the pop-up (rather than Cancel) &#8211; this is a pro on usability, but con on &#8220;failing open&#8221; rather than closed</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="438" height="150" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/04/SSH_confirmation_Screenshot_2024-04-03_11-20-46.png" alt="SSH key usage confirmation with KeePassXC" class="wp-image-2872"/><figcaption class="wp-element-caption">SSH key usage confirmation with KeePassXC</figcaption></figure>
</div>


<p>It&#8217;s still a pretty simple workflow, and it&#8217;s quite interesting to see how many things KeePassXC learned to do as well.</p>



<h3 class="wp-block-heading">Experience</h3>



<p>Thinking about the various threat models to my SSH crendentials, this setup adds one more layer to the defence in depth, and it does feel more relaxed already (relaxed from a point of stress I didn&#8217;t quite know I had before).</p>



<p>Picking the SSH key based login and signing feels like using the most appropriate tech for the moment, and there are still knobs for people to adapt it to their security levels (different SSH keys for login and signing, passwords on the keys themselves, etc&#8230;)</p>



<p>This setup works very well when I want to be notified whenever a tool&#8217;s using the SSH key so it would be more obvious if a stray process is trying, say exiltrate the keys. On the other hand this breaks down when git itself is running background processes, such as <a href="https://git-scm.com/docs/git-maintenance">git-maintenance</a>, so that&#8217;s not something that I could use here. So far out of (literally) thousands of codebases &amp; repos I&#8217;ve used that maintenance setup exactly once, for convenience. For me it is not a major loss, then.</p>



<p>The one bit that <em>feels</em> a step backwards is that having the SSH keys in the password manager and carrying it around counteracts the &#8220;separate key for each system&#8221; arrangement. This might just be part of getting used to new processes, and not an actual downside.</p>



<h3 class="wp-block-heading">Further Thoughts</h3>



<p>In cybersecurity yesterday&#8217;s best practices might be inadecvate today and &#8220;last week&#8217;s&#8221; practices might be outright dangerous&#8230; Gonna keep revisiting this setup more broadly and in terms of details, as I learn more.</p>



<p>It&#8217;s a good question why even do code signing (besides having a &#8220;verified&#8221; check mark, which alone doesn&#8217;t mean much if not part of a verification process), though this needs some more space to unpack. For the time being I&#8217;ll assume that signing is better than not signing, if nothing else than as a forward looking prep for better audit processes down the line.</p>



<p>There&#8217;s really a question around having too many things in a single password manager: nowadays it can be the complete &#8220;royal flush&#8221; of password, TOTP, SSH key, recovery codes, <a href="https://en.wikipedia.org/wiki/WebAuthn">passkey</a>&#8230; and likely more bits that I might not be using yet? This does make me uneasy, and likely a scale on which usability and security will adjust over time (such as. bundling and unbundling various cybersecurity aspects).</p>



<p>I might also actually misunderstand various things above, if so, I&#8217;d be <em>very</em> keen to hear, just drop me a line!</p>



<p><strong>Edit (2024-09-15)</strong>: Looks like MacOS 14.6 Sonoma changes have <a href="https://github.com/keepassxreboot/keepassxc/issues/11134">broken</a> the KeePassXC behaviour when confirmation is requested before using the key. I was looking at the <a href="https://github.com/theseal/ssh-askpass/issues/54#issuecomment-2268376357">workaround posted</a> (some manual reconfiguring of <code>ssh-askpass</code>, but it seems pretty fragile&#8230; So I&#8217;d probably keep to 1Password on MacOS (<em>sigh</em>), while on Linux Bob continues to be your uncle&#8230;</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/05/git-login-and-commit-signing-with-security/">Git login and commit signing with security</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2024/05/git-login-and-commit-signing-with-security/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>ZFS on a Raspberry Pi</title>
		<link>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/</link>
					<comments>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/#comments</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Wed, 28 Feb 2024 11:48:50 +0000</pubDate>
				<category><![CDATA[Admin]]></category>
		<category><![CDATA[Computers]]></category>
		<category><![CDATA[Arch Linux]]></category>
		<category><![CDATA[Raspberry Pi]]></category>
		<category><![CDATA[SysAdmin]]></category>
		<category><![CDATA[ZFS]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2862</guid>

					<description><![CDATA[<p>Using ZFS to make the most an assorted bunch of SD cards and USB sticks for storage, and hitting Linux kernel licensing issues along the way.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/">ZFS on a Raspberry Pi</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I have a little home server, just like mike many other geeks / nerds / programmers / technical people&#8230; It can be both <em>useful</em>, a learning experience, as well as <em>a real chore</em>; most of the time the balance is shifting between these two ends. Today I&#8217;m taking notes here on one aspect of that home server that is widely swing between those two use cases.</p>



<p>When I say I have a home server, that might be too generous description of the status quo: I have a pretty banged up <a href="https://www.raspberrypi.com/products/raspberry-pi-3-model-b/">Raspberry Pi 3B</a>. It&#8217;s running <a href="https://archlinuxarm.org/">ArchLinux ARM</a>, the 64-bit, AAarch64 version, looking a bit more retro on the hardware front while pushing for more modernity on the software side &#8211; a mix that I find fun.</p>



<p>There are a handful of services running on the device &#8212; not <em>that</em> many, mostly limited by it&#8217;s <em>*gulp*</em> 1GB memory; plenty of things I&#8217;d love to run, doesn&#8217;t well co-locate in such a tiny compartment. Besides the memory, it&#8217;s also limited by storage: the Raspberry Pi runs off an SD card, and those are both fragile, and limited in size. If one wants to run a home file server, say using a handful of other SD cards lying around, to expand the available storage, that will be awkward very soon. To make that task less awkward (or replace one kind of awkward with a more interesting one), I&#8217;ve set out to set up a ZFS storage pool, using <a href="https://openzfs.readthedocs.io/en/latest/introduction.html">OpenZFS</a>. </p>



<h2 class="wp-block-heading">The idea</h2>



<p>Why ZFS? In big part, to be able to credibly answer that question. </p>



<p>But with a single, more concrete reason: being able to build a more solid and expandable storage unit. ZFS cancombine different storage units</p>



<ul class="wp-block-list">
<li>in a way that combats data errors, e.g. mirroring: this addresses SD cards fragility</li>



<li>in a way that data can expand across all of them in a single file system: this addresses the SD cards size limitations</li>
</ul>



<p>This sounds great in theory and after a bit of trial-and-error, I&#8217;ve made the following setup, relying on dynamic kernel modules for support for flexibility, and a hodgepodge of drives at hand for the storage</p>



<p>The file system supports needs is provided by the <a href="https://aur.archlinux.org/packages/zfs-dkms">zfs-dkms package</a> dynamic kernel module (<a href="https://wiki.archlinux.org/title/Dynamic_Kernel_Module_Support">DKMS</a>), which means the kernel module required for being able to manage that file system is recompiled for each new Linux kernel version as it is updated. This is handy in theory, as I can use the main kernel packages provided by the ArchLinux ARM team.</p>



<p>For storage, I&#8217;ve started off with two SD cards in mirror mode (going for data integrity first). Later I&#8217;ve found &#8212; and invested in &#8212; some large capacity USB sticks that bumped the storage size quite a bit. With these, the currentl ZFS pool looks like this:</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="365" src="https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1024x365.png" alt="Terminal screenshot of the 'zpool status' command." class="wp-image-2863" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1024x365.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-500x178.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-768x274.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage-1200x428.png 1200w, https://gergely.imreh.net/blog/wp-content/uploads/2024/02/pooling-that-external-storage.png 1454w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p>It already saved me &#8212; or rather my data &#8212; once where an SD card was acting up, though that&#8217;s par for the course. One very large benefit is that the main system card is being used less, so hopefully will last longer.</p>



<h2 class="wp-block-heading">The complications</h2>



<p>Of course, it&#8217;s never this easy&#8230; With non-mainline kernel modules and with DKMS, every update is a bit of a gamble, that can suddenly not pay off. That&#8217;s exactly what happened last year, when suddenly the module didn&#8217;t compile anymore on a new kernel version, and thus all that storage was sitting dump and inaccessible. After <a href="https://github.com/openzfs/zfs/issues/14555">digging into</a> the issue, it down to:</p>



<ol class="wp-block-list">
<li>the OpenZFS project being under <a href="https://opensource.org/license/cddl-1-0">Common Development and Distribution License</a> (CDDL)</li>



<li>the Linux kernel <a href="https://github.com/torvalds/linux/commit/aaeca98456431a8d9382ecf48ac4843e252c07b3">deliberately breaking non-GPL licensed code</a> by starting to withold certain floating point capabilities, because &#8220;this is not expected to be disruptive to existing users&#8221;.</li>
</ol>



<p>This wasn&#8217;t great, as user being between pretty much a rock &amp; a hard place, even if this is a hobby and not strictly speaking a production use case on my side. </p>



<p>Nonetheless, it worked by <a href="https://wiki.archlinux.org/title/Downgrading_packages">downgrading</a> to a working version and <a href="https://wiki.archlinux.org/title/Pacman#Skip_package_from_being_upgraded">skipping updates</a> to the kernel packages.</p>



<p>Then based on a <a href="https://github.com/openzfs/zfs/issues/14555#issuecomment-1475119774">suggestion</a>, patching the zfs-dkms package (rewriting the license entry in the META file) to make it look like it&#8217;s a GPL-licensed module &#8212; which is fair game for one doing on their own machine. This is hacky, or let&#8217;s call it pragmatic.</p>



<pre class="wp-block-code"><code class="">--- META.prev   2024-02-28 08:42:21.526641154 +0800
+++ META        2024-02-28 08:42:36.435569959 +0800
@@ -4,7 +4,7 @@
 Version:       2.2.3
 Release:       1
 Release-Tags:  relext
-License:       CDDL
+License:       GPL
 Author:        OpenZFS
 Linux-Maximum: 6.7
 Linux-Minimum: 3.10</code></pre>



<p>Now, with the current 2.2.3 version, it seems like there&#8217;s an official f<a href="https://github.com/openzfs/zfs/pull/15711">ix-slash-workaround</a> for being able to get the module to compile, even if it&#8217;s not a full fix. From the linked merge request message I&#8217;m not fully convinced that this is not a fragile status quo, but it&#8217;s at least front of mind &#8211; good going for wider ARM hardware usage that brings out people&#8217;s willingness to fix things! </p>



<h2 class="wp-block-heading">Future development</h2>



<p>Some while back, while working at <a href="https://www.balena.io/">an IoT software deploument &amp; management company</a>, I had a lot of interesting hardware at hand, naturally, to build things with (or wrestle with&#8230;). Nowadays I have things I best describe as <em>spare parts</em>, and thus loads of thingss are more fragile than they need to be, as well as gosh-it-takes-a-long-time to compile things on a Raspberry Pi 3 &#8211; making every kernel update some half-an-hour longer!</p>



<p>Likely the best move would be to upgrade to a (much more powerful) <a href="https://www.raspberrypi.com/products/raspberry-pi-5/">Raspberry Pi 5</a> and use an <a href="https://learn.pimoroni.com/article/getting-started-with-nvme-base">external NVMe drive</a>, where I&#8217;d have much less need for ZFS, at least for the original reasons. It would likely be still useful for other aspects (such as <a href="https://wiki.archlinux.org/title/ZFS#Automatic_snapshots">snapshotting</a>, or sending/receiving the drive data, compression, deduplication, etc&#8230;), changing the learning path away from multi-device support to the file system features.</p>



<p>If I wanted to use more storage in the existing system, I could also get rid of the mirrored SD cards and just just 4 large USB sticks (maybe in a <a href="https://openzfs.github.io/openzfs-docs/Basic%20Concepts/RAIDZ.html">RAIDZ</a> setup), a poor-man&#8217;s NAS, I guess. Though there I&#8217;d worry a bit about using the sticks with the same sizes for this to work (unlike pooling, which has no same-size requirements), given the differences in the supposedly same sized products from different companies (likely locking me into a having the same brand and model across the board).</p>



<p>I also feel like I&#8217;m not using ZFS to its full potential. If I know enough just to be dangerous&#8230; maybe that&#8217;s the generalists natural habitat?</p>
<p>The post <a href="https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/">ZFS on a Raspberry Pi</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2024/02/zfs-on-a-raspberry-pi/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Making a USB Mute Button for Online Meetings</title>
		<link>https://gergely.imreh.net/blog/2023/08/making-a-usb-mute-button-for-online-meetings/</link>
					<comments>https://gergely.imreh.net/blog/2023/08/making-a-usb-mute-button-for-online-meetings/#comments</comments>
		
		<dc:creator><![CDATA[Gergely Imreh]]></dc:creator>
		<pubDate>Sat, 19 Aug 2023 04:28:00 +0000</pubDate>
				<category><![CDATA[Maker]]></category>
		<category><![CDATA[Programming]]></category>
		<category><![CDATA[arduino]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Seeed Studio]]></category>
		<category><![CDATA[USB]]></category>
		<category><![CDATA[USB HID]]></category>
		<guid isPermaLink="false">https://gergely.imreh.net/blog/?p=2801</guid>

					<description><![CDATA[<p>Since Google Meet teases us to add a USB device to controll calls, let's learn how to teach an Arduino clone to act like a phone mute button, and thus ultimately being capable to act like an infinite variety of peripheral devices.</p>
<p>The post <a href="https://gergely.imreh.net/blog/2023/08/making-a-usb-mute-button-for-online-meetings/">Making a USB Mute Button for Online Meetings</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>I use Google Meet every day for (potentially hours of) online meetings at work, so it&#8217;s very easy to notice when things change and for example new features are available. Recently I&#8217;ve found a new &#8220;Call Control&#8221; section in the settings that promised a lot of fun, <a href="https://support.google.com/meet/answer/12562325?hl=en">connecting USB devices to control my calls</a>.</p>



<figure class="wp-block-image size-full is-style-default"><img decoding="async" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/Screenshot-2023-08-19-at-07.05.34.png" alt="Screenshot of the Google Meet Settings menu during calls, showing the call control menu and a call-out to connect my USB device." class="wp-image-2803"/><figcaption class="wp-element-caption">Google Meet Settings menu during a call, witht the Call control section</figcaption></figure>



<p>As someone who enjoys (or drawn to, or sort-of obscessed with) <a href="https://gergely.imreh.net/blog/category/maker/">hacking on hardware</a>, this was a nice call of action: let&#8217;s cobble together a custom USB button that can do some kind of call control<sup data-fn="7509dd7d-2867-4955-808e-d9bd3a9e8cd4" class="fn"><a href="#7509dd7d-2867-4955-808e-d9bd3a9e8cd4" id="7509dd7d-2867-4955-808e-d9bd3a9e8cd4-link">1</a></sup>: say muting myself in the call, showing mute status, hanging up, etc.</p>



<p>This kicked off such a deep rabbit hole that I barely made it back up to the top, but one that seeded a crazy amount of future opportunities. </p>



<span id="more-2801"></span>



<p>And as a shortcut, there&#8217;s a <a href="#demo">demo</a> below to showcase where I got to.</p>



<h2 class="wp-block-heading">Finding suitable hardware</h2>



<p>This step was harder than I&#8217;ve expected, given that I have drawers and drawers of gadgets, but I&#8217;m likely a bit out of practice, and also out of date. What I was looking for is</p>



<ul class="wp-block-list">
<li>Being able to show up as a USB device (must)</li>



<li>Have built in button (optional) or easy connectivity of buttons without breadboard for now</li>



<li>Have built in LED (optional) or some other way of showing 1 bit of information</li>
</ul>



<p>This doesn&#8217;t sound hard, right?</p>



<h3 class="wp-block-heading">ReSpeaker</h3>



<p>The first option that came up was Seeed Studio&#8217;s <a href="https://wiki.seeedstudio.com/ReSpeaker_Core/">ReSpeaker Core</a> that I had two of at hand: Arduino Leonardo compatibility, touch sensors for buttons, and an LED ring (the &#8220;Pixel Ring&#8221;). Turns out that they have been <em>discontinued</em> &#8211; which should be fine for now; but also my models are two different <em>pre-release prototypes</em> Seeed gave away for testers. Thus they are not quite like the final version, have different hardware on board here and there, so an experimental experience is expected.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/respeaker_core_prototypes-1024x572.jpg" alt="Photo of two ReSpeaker Core boards" class="wp-image-2809"/><figcaption class="wp-element-caption">ReSpeaker core samples to work with</figcaption></figure>



<p>The earlier prototype only has touch sensors on one side, the pixel ring lights up, but I couldn&#8217;t control it with <a href="https://github.com/respeaker/respeaker_arduino_library">Seeed&#8217;s ReSpeaker Arduino library</a>. The later prototype has two sides of sensors (effectively two buttons), but the lights don&#8217;t seem to work<sup data-fn="17ce950a-5b32-45de-8a5c-2464146a1840" class="fn"><a href="#17ce950a-5b32-45de-8a5c-2464146a1840" id="17ce950a-5b32-45de-8a5c-2464146a1840-link">2</a></sup>. Regardless this </p>



<h3 class="wp-block-heading">Aside: alternatives considered</h3>



<p>It was illuminating to see how much abandoned, obsolete, discontinued, or not quite useful hardware boards do I have.</p>



<p>One is RFDuino, that I got from <a href="https://www.kickstarter.com/projects/1608192864/rfduino-iphone-bluetooth-40-arduino-compatible-boa/">Kickstarter</a>, I&#8217;m yet to use, and all the project&#8217;s websites have already disappeared &#8211; fortunately not the <a href="https://github.com/RFduino/RFduino">code repo</a>. This would have been a more complex solution anyways, but wireless! Use one RFDuino to expose a USB Telephony device, and communicate wirelessly to another that operates the light and button on battery. Pretty cool. Also, it might not have worked if the chip used cannot do the cruicial &#8220;expose a USB [device]&#8221; part of the plan.</p>



<p>Other option that popped up was an <a href="https://store.arduino.cc/products/arduino-nano">Arduino Nano</a> + my own made <a href="https://www.tindie.com/products/imrehg/grovehat-for-arduino-nano/">GroveHat</a> + a <a href="https://wiki.seeedstudio.com/Grove-Button/">Grove Button</a>. Except, the Nano definitely cannot be a custom USB device, so there goes nothing.</p>



<p>Besides these, I&#8217;ve found plenty of:</p>



<ul class="wp-block-list">
<li>single board computers (old or obsolete),</li>



<li>FPGAs (never used, and would be a whole different project to implement something on them), and </li>



<li>other microcontrollers that all have interesting specialties, but don&#8217;t tick the mandatory boxes&#8230;</li>
</ul>



<p>These boards might not be right for now, but definitely there are projects in store for them (if only thre&#8217;s time).</p>



<p>Back to ReSpeaker then&#8230;</p>



<h2 class="wp-block-heading">Plugging in the USB</h2>



<p>The next thing is to figure out what&#8217;s really happening when an USB device is plugged in and it shows the operating system that it can do certain things. That is, how does Meet know that there&#8217;s a compatible device to connect to?</p>



<h3 class="wp-block-heading">The USB HID docs</h3>



<p>This is answered by the <a href="https://www.usb.org/hid">USB Human Interface Devices (HID)</a> specs &#8212; one that is pretty complicated, has a lot of legacy bits, and need a different kind of mindset. In a nutshell, though, with my current, partial understanding:</p>



<p>On connection the device sends a &#8220;report&#8221; to the OS that details on what can it do, including:</p>



<ul class="wp-block-list">
<li>what kind (or kinds!) of device it is?</li>



<li>what functionality of the kind is available in this particular implementation?</li>



<li>what&#8217;s the data layout to pass control information back-and-forth for this implementation?</li>
</ul>



<p>In our example, a very minimal setup would would be:</p>



<ul class="wp-block-list">
<li>I&#8217;m a Telephony Device (Usage page <code>0x0B</code>)</li>



<li>I implement a generic &#8220;Phone&#8221; (Usage ID <code>0x01</code>)</li>



<li>I have capability to do a &#8220;Phone Mute&#8221; (Usage ID <code>0x2F</code>)</li>



<li>Here&#8217;s the 1 bit of a 1 byte payload that conveys that phone mute status</li>
</ul>



<figure class="wp-block-image size-large"><img decoding="async" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/Screenshot-2023-08-19-at-07.55.31-1024x920.png" alt="Screenshot of the beginning of the Telephony Device section from the HID Usage Tables" class="wp-image-2808"/><figcaption class="wp-element-caption">Getting started with Telephony devices from the HID Usage Tables</figcaption></figure>



<p>This course does not take into account other functionality, e.g.</p>



<ul class="wp-block-list">
<li>I can also hang up &#8211; Hook Switch, Usage ID <code>0x20</code>;</li>



<li>I have status LEDs &#8211; that&#8217;s a whole fun of redefining functions on the LED Page <code>0x08</code>;</li>
</ul>



<p>and so on. But for the time being this should be enough.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/Screenshot-2023-08-19-at-06.59.56-1024x924.png" alt="The Call Control usages abd their description from the HID Usage Tables document" class="wp-image-2806"/><figcaption class="wp-element-caption">Call Control functionality for Telefony devices</figcaption></figure>



<h3 class="wp-block-heading">Device implementation</h3>



<p>Fortunately we can stand on the shoulders of giants, that is the <a href="https://github.com/NicoHood/HID">Arduino HID Project</a> which implemented a bunch of different devices. And even though a &#8220;phone&#8221; like this is not among them, we can make some reasonable guesses how it would work.</p>



<p>Having said that, from a forum post that was also trying to do something similar (but based on the TinyUSB library):</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>HID report descriptor is very difficult thing to come up by oneself. You should google around, or dump report descriptor from existing device to copy/follow it.</p>
<cite><a href="https://github.com/hathach/tinyusb/discussions/667#discussioncomment-392454">hathach @ TinyUSB discussion 667</a></cite></blockquote>



<p>Okay, then do not come up with this stuff, instead let&#8217;s look for tools. The USB HID homepage links to the <a href="https://github.com/microsoft/hidtools">Microsoft HID Tools</a> to generate HID reports from a TOML-like language. Except it needs C# and I just wasn&#8217;t ready to dive in a side-quest to install &amp; learn a new toolchain.</p>



<p>So being lazy this way, a bit more sleuthing turned up <a href="https://web.archive.org/web/20210614160735/https://blog.noser.com/first-steps-with-an-usb-hid-report/">someone&#8217;s example HID report</a> for a device very close to what I&#8217;m trying to do, hurray!</p>



<p>I took this and started to poke around the HID project to see how other devices are implemented. Troubleshooting by using the ReSpeaker&#8217;s touch to adjust screen brightness up / down (as a &#8220;Consumer Device&#8221;) was also pretty neat! In the end I took the system buttons example and run with that one.</p>



<p>Having said that, the HID report is really just the interface. The devil is in how to implement actually creating the data packages that passes data according to the report definition. And this is the case when I wish I knew more C++ but copy-paste and some guesswork will have to do.</p>



<figure class="wp-block-image size-large"><img decoding="async" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/Screenshot-2023-08-19-at-08.48.30-1024x519.png" alt="Screenshot of the Phone HID report definition code" class="wp-image-2814"/><figcaption class="wp-element-caption">Our minimal viable mute button&#8217;s HID report (<a href="https://github.com/imrehg/HID/blob/1851b68b008111734287f6ce4f894173ad21bb3f/src/MultiReport/Phone.cpp#L26C1-L41C1">source</a>)</figcaption></figure>



<p>The current result lives in the &#8220;<a href="https://github.com/imrehg/HID/tree/phone">phone&#8221; branch of my HID Project fork</a>, check for the &#8220;Phone&#8221; bits in &#8220;src/HID-APIs&#8221; and &#8220;MultiReport&#8221; folders, if interested.</p>



<h2 class="wp-block-heading">Minimal viable mute</h2>



<p>The implementation from this point on was pretty straightforward &#8211; since we cut back the scope so much&#8230;</p>



<p>The code to run on the ReSpeaker then just has to do the following:</p>



<ul class="wp-block-list">
<li>when touching one side, send a report with &#8220;Phone Mute&#8221; on</li>



<li>when touching the other, send a report with &#8220;Phone Mute&#8221; off</li>
</ul>



<p>And this is sort of simple<sup data-fn="c40b4257-a075-44fc-aefb-e036c5cb3543" class="fn"><a href="#c40b4257-a075-44fc-aefb-e036c5cb3543" id="c40b4257-a075-44fc-aefb-e036c5cb3543-link">3</a></sup> :</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="870" src="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/arduino-code-for-phone-mute-events-1024x870.png" alt="Screenshot of the Arduino code to send the right mute events" class="wp-image-2821" srcset="https://gergely.imreh.net/blog/wp-content/uploads/2023/08/arduino-code-for-phone-mute-events-1024x870.png 1024w, https://gergely.imreh.net/blog/wp-content/uploads/2023/08/arduino-code-for-phone-mute-events-500x425.png 500w, https://gergely.imreh.net/blog/wp-content/uploads/2023/08/arduino-code-for-phone-mute-events-768x653.png 768w, https://gergely.imreh.net/blog/wp-content/uploads/2023/08/arduino-code-for-phone-mute-events.png 1198w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /><figcaption class="wp-element-caption">Sending data on touch events in the simplest way</figcaption></figure>



<p id="demo">For the full use case there would be a lot more complexity for both reading and writing data from the host, controlling multiple peripherals (LEDs and buttons) and the whole logic around it. But for now, it&#8217;s good enough for a demo:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe loading="lazy" title="A custom USB HID mute button for Google Meet" width="580" height="435" src="https://www.youtube.com/embed/Mz-B7sYGLUI?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
</div><figcaption class="wp-element-caption">A very quick demo</figcaption></figure>



<p>The code repository is available on Github at <a href="https://github.com/imrehg/arduino-usb-phone-hid">imrehg/arduino-usb-phone-hid</a>.</p>



<h2 class="wp-block-heading">Notes and Future work</h2>



<h3 class="wp-block-heading">The specs</h3>



<p>It&#8217;s great that stuff from 20+ years ago still works mostly the same way. The <a href="https://www.usb.org/sites/default/files/hut1_4.pdf">latest 1.4 version</a> of the HID Tables is nicely formatted, has a lot more device typed defined, but has much less support text. Originally I&#8217;ve read the <a href="https://www.usb.org/sites/default/files/documents/hut1_12v2.pdf">1.12v2 version</a> as that showed up in my search. Back then in 2004 they had an &#8220;examples&#8221; section (see the Telephone at Appendix 10!) which is useful to grok more of the fundamentals. </p>



<p>The newer version also has some devices types that looked suitable, but weren&#8217;t really: Generic Desktop Page (<code>0x01</code>) and <code>0xE0-E2</code> Usage IDs for Call Active LED, Call Mute Toggle, and Call Mute LED respectively. These didn&#8217;t seem to work with Meet, so it might be interesting to try implementing a device that does both and try other online call software.</p>



<p>I should also have read the spec more before diving into hacking on the HID implementation fork, as there&#8217;s a lot more information in the <a href="https://www.usb.org/document-library/device-class-definition-hid-111">HID Device Class Definition</a>, including how to construct the values for many of the fields (I&#8217;m looking at you &#8220;<code>INPUT (Cnst,Var,Abs)</code>&#8220;). RTFM is and remains a solid advice &#8211; and not just when one thinks there&#8217;s time.</p>



<p>Also regarding the specs: some of them I only find in the Internet Archive&#8217;s <a href="https://web.archive.org/">Wayback Machine</a>. If you encounter a good source that should be kept, always add it to the Wayback Machine and preserve it for your future selves and others!</p>



<p>This exploration of USB HID pulled on so many threads, and left so much unfinished, that it&#8217;s a fertile ground for the future, even more than most previous projects.</p>



<h3 class="wp-block-heading">More call functionality</h3>



<p>The most obvious thing is to implement the whole setup with the buttons. I&#8217;ve tried Hook Switch to hang up a call, that works too. Could add status lights, maybe throw in some &#8220;Active Call&#8221; LEDs, or so on. This requires better understanding how data is sent over the wire for USB and how to handle incoming data. The Arduino examples rarely seem to use the &#8220;Output&#8221; fields (ie. incoming data, output from the host&#8217;s point of view, but maybe <a href="https://github.com/hathach/tinyusb">TinyUSB</a> does ?</p>



<p>For this, it would be nice to find a different hardware platform that would make this more seamless (so I can concentrate on the software side more). If that platform would lend itself to be reproduced or made stand alone, that would be even nicer: imaging brining my little call control box that can be used with other computers easily as well&#8230;</p>



<h3 class="wp-block-heading">Implement more USB HID devices</h3>



<p>The <a href="https://github.com/NicoHood/HID">Arduino HID project</a> has a bunch of devices implemented, but there are an infinite numbers that could be added. Unfortunately for Arduino it is harder to add more device types as an add-on to this library versus the current &#8220;forked&#8221; approach<sup data-fn="d18cdbdc-1733-40b6-a512-81c55a158ce0" class="fn"><a href="#d18cdbdc-1733-40b6-a512-81c55a158ce0" id="d18cdbdc-1733-40b6-a512-81c55a158ce0-link">4</a></sup>, so new decices should be in the main project, eventually.</p>



<p>So far there&#8217;s no Telephony device implemented there and it would be nice to find the right level of abstraction that works. The library doesn&#8217;t implement specific HID table pages, but specific usages or a subset of a usage. Thus like always, the hardest part would likely be setting the right interface (the right specs and &#8220;API&#8221;) for a new device to implement both the HID reports and the functions that manipulate what&#8217;s being sent and when.</p>



<p>On the other hand, that does sound like a fun experiment, and I&#8217;d look forward to adding 3D Game Controllers (Game Controls Page <code>0x05</code>), Environmental Sensors (Sensors Page <code>0x20</code>, Usage ID <code>0x30-3B</code>), &#8230; or even a Submarine Simulation Device (Simulation Device page <code>0x02</code>, usage id <code>0x05</code>). These are stuff I go to Hackerspaces for&#8230;</p>



<h3 class="wp-block-heading">WebHID for internet plus USB</h3>



<p>While debugging this HID device behaviour, I found also <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebHID_API">WebHID</a> that brings such devices to the web. This feature seems to be behind Meet&#8217;s and other phone systems like <a href="https://www.3cx.com/blog/docs/webhid-headset-integration/">3CX</a> expanding USB support outside of the OS and into the browser. And no, <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1835412">Firefox does not support it</a>, furthermore <a href="https://mozilla.github.io/standards-positions/#webhid">declined supporting it</a>.</p>



<p>Nonetheless it&#8217;s very cool that (if I upskill a bit), I can create a web page that would help me debug such HID development:</p>



<ul class="wp-block-list">
<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HID/requestDevice">request devices</a> that are filtered in various ways (vendor, product is standard, but usage page and explicit usage is the main key). This is likely what Meet does as well, &#8220;just gimme devices with Telephony usage page (or Phone usage? Need to check exactly) </li>



<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HIDDevice/collections">read the HID report collections</a> sent by the device, so the results can be debugged, and</li>



<li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HIDInputReportEvent">read device input events</a> that we can then either log for debugging or in an application react to to it</li>
</ul>



<p>This opens a lot more mashup opportunities by the dozen.</p>



<h2 class="wp-block-heading">Finally</h2>



<p>Unlike most other projects I had where I&#8217;m focused on one specific outcome, this turned out to be more focusing on getting a new toolkit (custom USB devices) up and running, so I can think about a wider <em>types</em> of projects to do. In that sense, this feels a big success, even if I know how little I know about programming outside of my day-to-day environment. But ignorance is not a bliss.</p>



<p>And now, going on mute.</p>


<ol class="wp-block-footnotes"><li id="7509dd7d-2867-4955-808e-d9bd3a9e8cd4">Many moons past I used to use a Jabra Evolve 80, that has <a href="https://www.manualslib.com/manual/1021017/Jabra-Evolve-80.html?page=16#manual">a USB accessory</a> controlling call features, so I did have first hand example of what sort of experience I&#8217;d like. <a href="#7509dd7d-2867-4955-808e-d9bd3a9e8cd4-link" aria-label="Jump to footnote reference 1"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="17ce950a-5b32-45de-8a5c-2464146a1840">I&#8217;ve tried reviewing the <a href="https://github.com/respeaker/get_started_with_respeaker/blob/master/files/RespeakerCorev1.0_Schematic.pdf">hardware schematics</a>, looking into the <a href="https://github.com/respeaker/respeaker_arduino_library/blob/4e04f8c72fa53beeb27dacd8d729a90f79202db7/pixels.cpp">pixel ring control functions</a>, and given that the LEDs seems standard I&#8217;ve also attempted to use the <a href="http://fastled.io/">FastLED library</a> to drive them instead, so far nothing. I still bet on hardware differences from final schematic + my inability to debug it, but it can be faulty hardware just as well. Needs more effort &#8211; in the future. <a href="#17ce950a-5b32-45de-8a5c-2464146a1840-link" aria-label="Jump to footnote reference 2"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="c40b4257-a075-44fc-aefb-e036c5cb3543">The Arduino code became more &#8220;simple&#8221; once I realised that things set up this way do not need debouncing for the touch sensors. In other cases that would be essential, there&#8217;s sooo much flaky signal to use those terminals as momentary switches or similar. <a href="#c40b4257-a075-44fc-aefb-e036c5cb3543-link" aria-label="Jump to footnote reference 3"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li><li id="d18cdbdc-1733-40b6-a512-81c55a158ce0">At least I don&#8217;t know how nicely extend a library for C++, if that&#8217;s even possible. Keen to learn, though. <a href="#d18cdbdc-1733-40b6-a512-81c55a158ce0-link" aria-label="Jump to footnote reference 4"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/21a9.png" alt="↩" class="wp-smiley" style="height: 1em; max-height: 1em;" />︎</a></li></ol><p>The post <a href="https://gergely.imreh.net/blog/2023/08/making-a-usb-mute-button-for-online-meetings/">Making a USB Mute Button for Online Meetings</a> appeared first on <a href="https://gergely.imreh.net/blog">ClickedyClick</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://gergely.imreh.net/blog/2023/08/making-a-usb-mute-button-for-online-meetings/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Object Caching 11/152 objects using APC
Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: gergely.imreh.net @ 2026-04-07 17:44:59 by W3 Total Cache
-->