<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>djh</title>
    <link>https://djharper.dev/</link>
    <description>Recent content on djh</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-gb</language>
    <lastBuildDate>Fri, 06 Mar 2026 00:00:00 +0000</lastBuildDate>
    
        <atom:link href="https://djharper.dev/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Claude Code Questionnaires</title>
      <link>https://djharper.dev/post/2026/01/10/claude-code-questionnaires/</link>
      <pubDate>Sat, 10 Jan 2026 20:55:34 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2026/01/10/claude-code-questionnaires/</guid>
      <description>One thing I&amp;rsquo;ve noticed about Claude Code is it will sometimes ask you questions, especially at the start of new projects, some clarifying things it wants to know so it can feed into the plan input, for example if you was creating a frontend project it might ask you what framework(s) you want to use.
I thought that was a neat feature but didn&amp;rsquo;t realise you can get the assistant to do this for your own stuff just by specifying it in prompts.</description>
      <content:encoded>
      <![CDATA[
        <figure>
    <a href="/img/questions.png"><img src="/img/questions.png" /></a>
</figure>

<p>One thing I&rsquo;ve noticed about Claude Code is it will sometimes ask you questions, especially at the start of new projects, some clarifying things it wants to know so it can feed into the plan input, for example if you was creating a frontend project it might ask you what framework(s) you want to use.</p>

<p>I thought that was a neat feature but didn&rsquo;t realise you can get the assistant to do this for your own stuff just by specifying it in prompts.</p>

<p>This came apparent to me when I was looking at automating adding new self-hosted services to my server. I have a bunch of configuration files for my homelab (nixos) and adding a new service requires a modifying a few things like reverse proxy configuration, docker-compose and some other minor bits. I wanted to automate these steps with Claude Code but there&rsquo;s obviously a bunch of context it needs, but just by adding this to my <code>CLAUDE.md</code>&hellip;</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">When deploying a new self-hosted service, ask the user:

1. What domain the service should be accessible at (e.g., `foo.bar.com`)
2. Which server it should be hosted on (typically `myserver`)
3. What port the service listens on inside its container (the conta
iner port)
4. What dashboard section to add it to (Apps, Home, Finance, Media,
 or Infrastructure)
5. Does the container need any volumes for persistent data (e.g., `
/opt/data/servicename:/data`)
6. The docker image name (e.g. `foo:latest`)</code></pre></div>
</figure>

<p>and saying in my prompt that I wanted to deploy my service to the server - it popped up the same lil&rsquo; survey interface with the questions I want the assistant to ask.</p>

<p>That&rsquo;s pretty cool, I can see myself using this pattern quite a lot when automating some workflows.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Using LLMs to turn scripts into applications</title>
      <link>https://djharper.dev/post/2025/08/19/using-llms-to-turn-scripts-into-applications/</link>
      <pubDate>Tue, 19 Aug 2025 21:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2025/08/19/using-llms-to-turn-scripts-into-applications/</guid>
      <description>I&amp;rsquo;ve built a bunch of scripts and tools over the years that are largely hodge-podge, &amp;ldquo;good enough&amp;rdquo; monstrosities that should never see the light of day, but mostly useful, albeit awkward to use.
One of those tools was something I built to help me maintain my beancount file.
The tooling downloads transaction data from various financial institutions and then converts the contents into beancount format so that I can copy/paste the contents into my file.</description>
      <content:encoded>
      <![CDATA[
        

<p>I&rsquo;ve built a bunch of scripts and tools over the years that are largely hodge-podge, &ldquo;good enough&rdquo; monstrosities that should never see the light of day, but mostly useful, albeit awkward to use.</p>

<p>One of those tools was something I built to help me maintain my <a href="https://github.com/beancount/beancount">beancount</a> file.</p>

<p>The tooling downloads transaction data from various financial institutions and then converts the contents into beancount format so that I can copy/paste the contents into my file.</p>

<p>As part of the processing pipeline it also included a rule-engine of sorts that formatted and categorised transactions depending on various conditions defined in the rules and this was all configured through one big, cumbersome JSON file.</p>

<p>It&rsquo;s worked fine for a long time but frustratingly the &ldquo;rule engine&rdquo; part was becoming a pain to maintain, or even remember how to use in the first place. A small bit of JSON is cute, but JSON left unattended becomes something unspeakable, especially if you feed it after midnight.</p>

<p>I&rsquo;ve never really taken the time to improve on it though, as ever time seems to evaporate as you grow older.</p>

<p>So I thought I&rsquo;d look into LLMs to help with this and move the rules engine into an application to help me manage the rules in a saner way.</p>

<h2 id="hello-beanengine">Hello BeanEngine</h2>

<figure>
    <a href="/img/bean-engine.png"><img src="/img/bean-engine.png" /></a>
</figure>

<p>This is what Claude (and I?) built, we called it BeanEngine.</p>

<p>It&rsquo;s a web application that implements my rules engine and allows you to configure it through a nice UI.</p>

<p>The application has two functions</p>

<ul>
<li>An API where I can pass transactions, and then it returns everything I need to convert them into a beancount entries.</li>
<li>A UI to configure rules on how to process the transactions and classify what they are for</li>
</ul>

<p>Over the course of a sessions on a few days (maybe 2-3 hours) Claude completed this for me.</p>

<p>Anyone who tells you these AI tools are one-shot miracle machines are lying to you, from my experience building this tool, there were a lot of iterations, but it felt more akin to supervising someone sometimes telling them where they were wrong.</p>

<h3 id="the-ui">The UI</h3>

<p>I made claude build me a number of pieces of functionality</p>

<ul>
<li>A rule engine to process transactions, complete with search functionality and CRUD actions for rules.</li>
<li>An account mapping engine to map accoutns to beancount accounts</li>
<li>A transfer rules engine to identify transactions that are transfers between personal accounts</li>
</ul>

<p>Here are some screenshots of the screens Claude built for me, notice how the UI is fairly consistent between screens. All of this was designed by Claude, I did tell it to implement dark mode.</p>

<p>Rules engine</p>

<figure>
    <a href="/img/rule-mgmt-1.png"><img src="/img/rule-mgmt-1.png" /></a>
    <a href="/img/rule-mgmt-2.png"><img src="/img/rule-mgmt-2.png" /></a>
    <br/>
    <br/>
    <figcaption>Claude even added some import/export functionality which I didn't even ask for but kept anyway</figcaption>
</figure>

<p>Account mappings engine</p>

<figure>
    <a href="/img/account-mappings-1.png"><img src="/img/account-mappings-1.png" /></a>
</figure>

<p>Transfer rules engine</p>

<figure>
    <a href="/img/transfer-rules-1.png"><img src="/img/transfer-rules-1.png" /></a>
</figure>

<p>All of these systems are not that complicated, just CRUD screens backed by a SQLite database. But these would have taken me <em>days</em> to build and lets be honest, building CRUD apps is <em>tedious</em>. With LLMs they&rsquo;re fun to build because you get the LLM to do all the work.</p>

<p>What&rsquo;s all this in service of?</p>

<p>Well, the API uses all these rules engines to process transactions</p>

<p>For example I can send this to the API</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">[
  {
    <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-01&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;PATREON&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">4.8</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;PAYPAL *PATREON MEMBERS 1234&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>
  },
  {
    <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-01&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;GOOGLE SERVICES&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">12.99</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;GOOGLE*YOUTUBE 1231231&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>
  },
  {
    <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-09&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;WAITROSE&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">75</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;WAITROSE 123423     ONLINE&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>
  },
  {
    <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-08&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;foo-bank:12345&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;Daniel Harper&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">200</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;bar-bank savings account&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>
  }
]</code></pre></div>
</figure>

<p>and it will respond with</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">[
    {
        <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-01&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;Patreon&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;Podcast sub&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;Liabilities:CreditCard:CreditCardProvider&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;account&#34;</span>: <span style="color:#2aa198">&#34;Expenses:Fun:Subscriptions&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">4.8</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;classification_type&#34;</span>: <span style="color:#2aa198">&#34;ml_prediction&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;confidence&#34;</span>: <span style="color:#2aa198;font-weight:bold">0.9993922710418701</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_recipient&#34;</span>: <span style="color:#2aa198">&#34;PATREON&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_reference&#34;</span>: <span style="color:#2aa198">&#34;PAYPAL *PATREON MEMBERS 1234&#34;</span>
    },
    {
        <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-01&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;YouTube&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;Youtube Premium&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;Liabilities:CreditCard:CreditCardProvider&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;account&#34;</span>: <span style="color:#2aa198">&#34;Expenses:Fun:Subscriptions&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">12.99</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;classification_type&#34;</span>: <span style="color:#2aa198">&#34;ml_prediction&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;confidence&#34;</span>: <span style="color:#2aa198;font-weight:bold">0.9989938139915466</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_recipient&#34;</span>: <span style="color:#2aa198">&#34;GOOGLE SERVICES&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_reference&#34;</span>: <span style="color:#2aa198">&#34;GOOGLE*YOUTUBE 1231231&#34;</span>
    },
    {
        <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-09&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;Waitrose&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;Liabilities:CreditCard:CreditCardProvider&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;account&#34;</span>: <span style="color:#2aa198">&#34;Expenses:Groceries&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">75.0</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;classification_type&#34;</span>: <span style="color:#2aa198">&#34;rule_override&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;confidence&#34;</span>: <span style="color:#2aa198;font-weight:bold">1.0</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_from_account&#34;</span>: <span style="color:#2aa198">&#34;creditcardprovider:-x12434&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_recipient&#34;</span>: <span style="color:#2aa198">&#34;WAITROSE&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_reference&#34;</span>: <span style="color:#2aa198">&#34;WAITROSE 123423     ONLINE&#34;</span>
    },
    {
        <span style="color:#268bd2;font-weight:bold">&#34;date&#34;</span>: <span style="color:#2aa198">&#34;2025-08-08&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;recipient&#34;</span>: <span style="color:#2aa198">&#34;Daniel Harper&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;reference&#34;</span>: <span style="color:#2aa198">&#34;bar-bank savings account&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;from_account&#34;</span>: <span style="color:#2aa198">&#34;Assets:Bank:Current:FooBank:Current&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;account&#34;</span>: <span style="color:#2aa198">&#34;Assets:Bank:Savings:BarBank:Savings&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;amount&#34;</span>: <span style="color:#2aa198;font-weight:bold">200.0</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;currency&#34;</span>: <span style="color:#2aa198">&#34;GBP&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;classification_type&#34;</span>: <span style="color:#2aa198">&#34;transfer&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;confidence&#34;</span>: <span style="color:#2aa198;font-weight:bold">1.0</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_from_account&#34;</span>: <span style="color:#2aa198">&#34;foo-bank:12345&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_recipient&#34;</span>: <span style="color:#2aa198">&#34;Daniel Harper&#34;</span>,
        <span style="color:#268bd2;font-weight:bold">&#34;original_reference&#34;</span>: <span style="color:#2aa198">&#34;bar-bank savings account&#34;</span>
    }
]</code></pre></div>
</figure>

<p>You may notice that the response contains a few things</p>

<ul>
<li>The recipient is sometimes rewritten to something cleaner (e.g. &ldquo;Google Cloud&rdquo;)</li>
<li>The reference is sometimes rewritten to something nicer (e.g. &ldquo;Podcast Sub&rdquo;)</li>
<li>The beancount <code>Expense</code> account the transaction should be for is returned</li>
<li>There&rsquo;s some indication of what type of transaction it is (transfer between personal accounts or &ldquo;ML classification&rdquo;)</li>
</ul>

<p>My script can then convert these into nice, clean beancount transactions</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">2025-08-01 * &#34;Patreon&#34; &#34;Podcast sub&#34; ; confidence: 1.00
  Liabilities:CreditCard:CreditCardProvider -4.80 GBP
  Expenses:Fun:Subscriptions

2025-08-01 * &#34;YouTube&#34; &#34;Youtube Premium&#34; ; confidence: 1.00
  Liabilities:CreditCard:CreditCardProvider -12.99 GBP
  Expenses:Fun:Subscriptions

2025-08-09 * &#34;Waitrose&#34; &#34;&#34; ; confidence: 1.00
  Liabilities:CreditCard:CreditCardProvider -75.00 GBP
  Expenses:Groceries

2025-08-08 * &#34;Daniel Harper&#34; &#34;bar-bank savings account&#34; ; transfer
  Assets:Bank:Current:FooBank:Current      -200.00 GBP
  Assets:Bank:Savings:BarBank:Savings</code></pre></div>
</figure>

<h3 id="wait-what-ml-model">Wait what, <em>ML model</em>?</h3>

<p>You may also notice the response contains some mention of ML prediciton.</p>

<p>I&rsquo;m not an ML engineer and do not pretend to be, but I was having so much fun with this I decided to let Claude loose on building a really simple classification model for me. Overkill? Yes. Probably a terrible model? Almost certainly. But hey, while we are here&hellip;.</p>

<p>The model accepts</p>

<ul>
<li>a recipient (e.g. <code>GOOGLE SERVICES</code>)</li>
<li>an amount (e.g. <code>12.99</code>)</li>
</ul>

<p>and attempts to predict the target account e.g. <code>Expenses:Fun:Subscriptions</code></p>

<p>The model is trained on all my past beancount transactions. I got Claude to write the model trainer to accept a beancount file, extract the data, clean it up and then train a really really simple neural network, which is probably overkill - it did say better classifiers exist.</p>

<p>This model is then stored in a PKL file, and I got claude to write a simple system so I can upload new versions of the model via the UI and switch models without having to restart the application.</p>

<figure>
    <a href="/img/models.png"><img src="/img/models.png" /></a>
</figure>

<p>Did I need the ML model? No. You can acheive the same result just defining the rules properly in the rules engine. But I figured it might be handy for the transactions that are less frequent.</p>

<h2 id="anyway">Anyway</h2>

<p>Just a blog post about something I found pretty neat.</p>

<p>I think what&rsquo;s interesting to me is these sorts of tools and things that I have in my toolbox are mostly personal to me. I think with the era of these LLM things, it gives the opportunity to unlock more more from them. Maybe at the expense of the ball of spaghetti Claude has come up with, but I&rsquo;m willing to make that trade off for the time being, I&rsquo;m the only user that has to live with it.</p>

<p>Cheers xxx</p>

<p><small><em>Side note: I&rsquo;m aware that beancount has importing functionality, I don&rsquo;t really use these though I prefer to maintain my beancount file myself, I&rsquo;ve honed a bunch of techniques to speed this up over the years and I&rsquo;m too stubborn to change.</em> 🙂</small></p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Syncing a file to a remote server when it changes on OSX</title>
      <link>https://djharper.dev/post/2025/02/22/syncing-a-file-to-a-remote-server-when-it-changes-on-osx/</link>
      <pubDate>Sat, 22 Feb 2025 20:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2025/02/22/syncing-a-file-to-a-remote-server-when-it-changes-on-osx/</guid>
      <description>I&amp;rsquo;m a big fan of plain text accounting and have been for years.
My tooling of choice has always been beancount with fava as the web application to render my beancount file.
I&amp;rsquo;ve always had one problem though, I edit my beancount file on my Mac using emacs1, but my fava instance runs on a server in my homelab. So if I want to see the latest changes, I&amp;rsquo;d do stuff like manually copy the file (annoying) or implement stupidly overkill solutions like syncthing - which was a pain to maintain for just one file.</description>
      <content:encoded>
      <![CDATA[
        

<p>I&rsquo;m a big fan of <a href="https://plaintextaccounting.org/">plain text accounting</a> and have been for years.</p>

<p>My tooling of choice has always been <a href="https://beancount.github.io/">beancount</a> with <a href="https://beancount.github.io/fava/">fava</a> as the web application to render my beancount file.</p>

<p>I&rsquo;ve always had one problem though, I edit my beancount file on my Mac using emacs<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup>, but my fava instance runs on <a href="/post/2024/11/24/my-home-network/">a server</a> in my homelab. So if I want to see the latest changes, I&rsquo;d do stuff like manually copy the file (annoying) or implement stupidly overkill solutions like <a href="https://syncthing.net/">syncthing</a> - which was a pain to maintain for just one file.</p>

<p>So I was looking for something simpler, my requirements were</p>

<ol>
<li>Copy the file to my server when it changes</li>
<li>Do this automatically and be enabled all the time, even after reboots</li>
</ol>

<p>After some very basic research and testing, I settled on a neat solution, using <a href="https://github.com/emcrisostomo/fswatch">fswatch</a> and <a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html">launchd</a> to basically monitor my beancount file and sync to my server on change events.</p>

<p>The solution is just a few lines of XML that runs a bash one-liner</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-xml" data-lang="xml"><span style="color:#93a1a1;font-style:italic">&lt;?xml version=&#34;1.0&#34; encoding=&#34;UTF-8&#34;?&gt;</span>
<span style="color:#93a1a1;font-style:italic">&lt;!DOCTYPE plist PUBLIC &#34;-//Apple//DTD PLIST 1.0//EN&#34; &#34;http://www.apple.com/DTDs/PropertyList-1.0.dtd&#34;&gt;</span>
<span style="color:#268bd2;font-weight:bold">&lt;plist</span> <span style="color:#268bd2">version=</span><span style="color:#2aa198">&#34;1.0&#34;</span><span style="color:#268bd2;font-weight:bold">&gt;</span>
<span style="color:#268bd2;font-weight:bold">&lt;dict&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>Label<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>beancount.sync<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>ProgramArguments<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;array&gt;</span>
        <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>/bin/bash<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
        <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>-c<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
        <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>/opt/homebrew/bin/fswatch -o beancount/finances.beancount  | xargs -n1 -I{} /usr/bin/rsync -avz beancount/finances.beancount server:/opt/data/beancount/<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;/array&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>RunAtLoad<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;true/&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>KeepAlive<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;true/&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>StandardOutPath<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>/tmp/beancount.log<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>StandardErrorPath<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>/tmp/beancount.err<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;key&gt;</span>UserName<span style="color:#268bd2;font-weight:bold">&lt;/key&gt;</span>
    <span style="color:#268bd2;font-weight:bold">&lt;string&gt;</span>daniel<span style="color:#268bd2;font-weight:bold">&lt;/string&gt;</span>
<span style="color:#268bd2;font-weight:bold">&lt;/dict&gt;</span>
<span style="color:#268bd2;font-weight:bold">&lt;/plist&gt;</span></code></pre></div>
</figure>

<p>It works by <code>fswatch</code> firing some output every time the file changes and <code>rsync</code> does the sync up to the server.</p>

<p>Enabling the service was easy, I just ran this command about 6 months ago</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">launchctl load ~/Library/LaunchAgents/beancount.sync.plist</code></pre></div>
</figure>

<p>&hellip;and that&rsquo;s it!</p>

<p>A remarkably simple solution that&rsquo;s been working solidly, and given Fava monitors changes to the files it&rsquo;s rendering - you see your updates reflected in the UI almost instantly or a few seconds at most.</p>

<h2 id="why-fswatch">Why fswatch</h2>

<p>I&rsquo;m aware launchd has a directive called <a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLaunchdJobs.html"><code>WatchPaths</code></a>, I tried using this initially but it never seemed to work properly and debugging why was depressing. I&rsquo;ve been a Mac user for nearly 20 years and barely understand most of its internals.</p>

<p>The <code>fswatch</code> solution worked first time and has worked every time, so it gets a 👍 from me.</p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1">This is the only thing I use emacs for. I run it with <a href="https://github.com/beancount/beancount-mode">beancount-mode</a> and <a href="https://github.com/emacs-evil/evil">evil</a> for vim keybindings. It&rsquo;s the best editor for beancount data but I use Vim for everything else!
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>My home network</title>
      <link>https://djharper.dev/post/2024/11/24/my-home-network/</link>
      <pubDate>Sun, 24 Nov 2024 12:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2024/11/24/my-home-network/</guid>
      <description>I&amp;rsquo;ve become a rack guy.
This has been my obsession over the past few years. Whilst I&amp;rsquo;ve had various bits of network equipment, SoHo cabinets and a whole lot of chaos going on in the house over the past few years, this is the first time I&amp;rsquo;ve actually gotten myself a proper rack and I&amp;rsquo;m really pleased with it.
But this post I want to talk about my home network and all the bits I&amp;rsquo;ve put together to make it work, it&amp;rsquo;s not very interesting or novel but this sort of setup is something I&amp;rsquo;ve wanted for a long time and I just wanted to write about it and how I got here.</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<a href="/img/homelab-1-30.png"><img src="/img/homelab-1.png" class="center-img" title="Photo of an open frame 12U network rack with a fake plant on the top" /></a>
</figure>

<p>I&rsquo;ve become a rack guy.</p>

<p>This has been my obsession over the past few years. Whilst I&rsquo;ve had various bits of network equipment, SoHo cabinets and a whole lot of chaos going on in the house over the past few years, this is the first time I&rsquo;ve actually gotten myself a proper rack and I&rsquo;m really pleased with it.</p>

<p>But this post I want to talk about my home network and all the bits I&rsquo;ve put together to make it work, it&rsquo;s not very interesting or novel but this sort of setup is something I&rsquo;ve wanted for a long time and I just wanted to write about it and how I got here.</p>

<p>If you want a more informative post about home network racks, I highly recommend <a href="https://mtlynch.io/building-first-homelab-rack/">this post</a> from Michael Lynch, he goes into way more detail and I found it useful.</p>

<h2 id="the-network">The network</h2>

<p>I&rsquo;m lucky enough to have ethernet in some of the rooms of my house. This is not common in the UK, but newer build properties like mine are being built with CAT6 in the walls. Obviously it&rsquo;s possible to retrofit cabling in UK houses but it requires some chopping and chasing to get everything in place, so it&rsquo;s nice to have had that already done when I moved in. In hindsight I wish I&rsquo;d requested more drops throughout the house but oh well, it&rsquo;s good enough.</p>

<p>The drops all end at a point in a small cupboard on the ground floor, 4 ports in total one for each area, this is where the rack lives. Here&rsquo;s a crude drawing of what the network looks like right now:</p>

<figure>
<a href="/img/topology.png"><img src="/img/topology.png" class="center-img" title="Photo of an open frame 12U network rack with a fake plant on the top" /></a>
</figure>

<p>It took a while to get to this point though, so I&rsquo;ll document the journey&hellip;</p>

<h3 id="a-bit-of-history">A bit of history&hellip;</h3>

<p>When I moved in the ISP just provided a shitty router, which suits most people, but I wanted take advantage of the full setup with PoE access points etc and use all the ports in the rooms. This led me to the &ldquo;pro-sumer&rdquo; market which is a minefield of expense, and made a bunch of mistakes a long the way.</p>

<p>My initial foray was to not bother with a full size rack and purchase something smaller, the 10&rdquo; &ldquo;SoHo&rdquo; cabinets seemed much cheaper, along with the gear that fits in it. The first thing I bought was a 6U cabinet, fitted with some shelves. For networking I settled on some <a href="https://www.tp-link.com/uk/business-networking/">TP-Link Omada</a> gear, mainly the ER605 router, OC200 controller, 10 port PoE switch along with a few WiFi access points and mini-switches to put around the house, powered by PoE.</p>

<figure>
<a href="/img/soho-cabinet.png"><img src="/img/soho-cabinet.png" class="center-img" title="Photo of small network cabinet containing router, switch and patch panel" /></a>
</figure>

<p>&hellip;and honestly? This served me well for a few years, it was great having everything nice and neatly tucked way in a little box in the cupboard, and outside of the Omada controller software/web interface being dogshit slow to navigate it worked well enough, and the experience of being able manage the router, switch and APs from one control surface was good.</p>

<p>But then the first problem arrived, soon after I purchased a 1L mini PC for self hosting/homelab purposes, to replace a set of Raspberry Pi&rsquo;s that were becoming cumbersome to manage and constrained by RAM.</p>

<figure>
<a href="/img/think-center.png"><img src="/img/think-center.png" class="center-img" title="Photo of small Lenovo 1L Mini PC" /></a>
</figure>

<p>The PC did not fit into the little SoHo cabinet, I&rsquo;d run out of room already and the bottom layer was consumed by surplus cabling. Maybe I could have rejigged some things around to make more room but it was becoming a hassle. So the PC ended up being put in the lounge upstairs and the fan in it was annoyingly loud.</p>

<p>So mistake no.1: <em>don&rsquo;t under-estimate or cheap out on your U&rsquo;s, in hindsight I should have gone for a 9U or 12U cabinet</em></p>

<p>This, compounded with the fact that I had plans to get <a href="/post/2024/07/25/installing-unraid-on-a-terramaster-f4-424/">a NAS</a>. I needed something bigger to accomodate all my things. I didn&rsquo;t want all these devices dotted around my house because I&rsquo;d run out of room downstairs. So the next expense was to upgrade to a 19&rdquo; 12U open frame &ldquo;network rack&rdquo;<sup>*</sup> <a href="https://www.kenable.co.uk/en/networking/network-cabinets/cabinets-rack-data/12363-flat-packed-12u-open-data-cabinet-desktopfloor-mount-19-inch-depth-482mm-5054338123639.html">for about £60</a> with the power distribution strip. The bits for it arrived in flatpack format and just required some assembly.</p>

<p><sup>* <i>note there&rsquo;s a key difference between &ldquo;network rack&rdquo; and &ldquo;server rack&rdquo;. Server racks tend to be a lot deeper, whereas my network rack is only 482mm deep (from what I understand)</i></sup></p>

<figure>
<a href="/img/IMG_0625.png"><img src="/img/IMG_0625.png" class="center-img" title="Photo of an open frame network rack with nothing in it" /></a>
</figure>

<p>Afterwards I bought some more shelves to put my things on, and because I&rsquo;m a cheap git I decided to re-use the 8 port patch panel from my old cabinet and designed/3D printed some &ldquo;extenders&rdquo; for the left/right parts so it could fit into the wider rack.</p>

<figure>
<a href="/img/patch-panel.png"><img src="/img/patch-panel.png" class="center-img" title="Photo of a patch panel in a network rack, with 8 ports. 3D printed parts we used to extend this" /></a>
</figure>

<p>With that out of the way the Mini PC could be moved back downstairs and into the rack, along with the Omada gear and recently purchased NAS. Unfortunately the NAS takes up about 5U of space, but there&rsquo;s still plenty of room on the shelves.</p>

<p>I even designed and 3D printed a custom mount for the Omada controller and router so I could mount it in 1U of space which took <em>hours</em> and had to be glued together in sections because the printer bed on my printer was too small. Turns out 19&rdquo; is quite a large amount of space to fill. In hindight this was a massive waste of time because I got the sizing slightly wrong so the franken-rackmount did not fit into the rack properly, I should have just used a shelf and not tried to be clever.</p>

<figure>
<a href="/img/mounted.png"><img src="/img/mounted.png" class="center-img" title="Photo of a network rack with custome 3D printed parts to mount a router and network controller" /></a>
</figure>

<p>So mistake no.2: <em>don&rsquo;t try to be clever</em></p>

<h3 id="making-it-look-nice">Making it look nice</h3>

<p>So, with all the stuff in place along with the janky 3D printed parts, there was one remaining niggle that bugged me to no end. The open frame leaves a big open gap at the top with all the wiring and power supplies and shame exposed to the open for you to look down on when you open the cupboard. I wanted to put something on top to make it look at least presentable.</p>

<p>This led me to an annoying quest of trying to get some nice wood cut to size, the options were surprisingly expensive. In the end, I just happened to be in IKEA one day looking in their recycled/returns/re-use section and spotted a cupboard door with a slight mark on the bottom selling for as-is for just £5. It was 23&rdquo; square and I got my Dad to help me cut two sections off to get it down to size.</p>

<figure>
<a href="/img/ikea-wood.png"><img src="/img/ikea-wood.png" class="center-img" title="Photo of an IKEA cupboard door selling for £5" /></a>
</figure>

<p>I quickly designed some 3D printed &ldquo;pegs&rdquo; to stick on the corners so they would fit into some bolt holes on the top of the rack (so the top can easily be lifted it off you need to access anything underneath)</p>

<figure>
<a href="/img/peg.png"><img src="/img/peg.png" class="center-img" title="Photo of 3D printed peg stuck onto the corner of a wood piece" /></a>
</figure>

<p>&hellip;and the result was very satisfying! Yes it&rsquo;s not super high quality and 2 of the 4 edges are rough from the cutting, but the good sides are the ones and show and it looks miles better than just a mess of cables.</p>

<figure>
<a href="/img/hat.png"><img src="/img/hat.png" class="center-img" title="Photo of a network rack with an IKEA cupboard door attached to the top" /></a>
</figure>

<p>At this point I thought I was done, but the homelab quest never seems to end, and quite naturally, my next side quest led me to investigate IPv6, which led me down the completely irrational path of replacing all my Omada gear entirely.</p>

<h3 id="brief-interlude-ipv6-woes">Brief interlude: IPv6 woes</h3>

<p>You see, it all started when looking into renting a cheap VPS from Hetzner. One of their options is you can cheap out and get an IPv6 address for free or pay extra to rent an IPv4. As you may have gathered with the running theme of this post, I&rsquo;m not one to open my wallet more than what I have to&hellip;</p>

<p>So after setting up IPv6 on my network and everything was running in the future, alarm bells set in when I could connect to one of my services running on my homelab from the VPS by just hitting an IP address. Uh-oh, all my devices and services were exposed to the internet 😱!</p>

<p><em>Isn&rsquo;t my router a firewall though?</em> was my first immediate question, and yes it is, but that only works for IPv4. The <em>version</em> of my router was too old to have the firmware upgrade to support IPv6 firewalling. So every device on the network is exposed all the time when you enable IPv6 on this device. While scanning the IPv6 space is probably unfeasible anyway, I didn&rsquo;t like the idea of all my stuff being out there so I came crawling back to the sweet, comforting embrace of IPv4.</p>

<p>This experience left a niggle in my brain that was difficult to shake, if the router doesn&rsquo;t have the right firmware to support some features what else is missing? The rational and cheaper move would have just been to upgrade the router to a more recent model and be done with it - the modular nature of the Omada setup makes this relatively simple.</p>

<p>However&hellip;</p>

<h3 id="irrational-changes">Irrational changes</h3>

<p>It was time to see what else was out there. I remember back in the day looking at Ubiquiti gear but was put off by the expense along with stock shortages that were going on at the time (2022-ish) it was terribly off putting. The cost could have been stomached somewhat but what really put me of was their flagship router - the Dream Machine Pro did not have PoE ports, so you had to purchase a separate switch. This easily pushed the cost to over £800 once you factored in access points in and other accessories.</p>

<p>But while researching I noticed Ubiquiti had released a new version of their Dream Machine Pro, the &ldquo;SE&rdquo; special edition version, which upgrades all the ports to PoE. This was quite a compelling case to me, my thinking was, this would allow me to replace the 3 TP-Link devices in my rack plus janky 3D printed parts, with just 1 device actually designed to be rack mounted and possibly - better hardware?</p>

<p>So I pulled the trigger and purchased</p>

<ul>
<li>1x Dream Machine Pro</li>
<li>2x U6+ WiFi 6 access points</li>
</ul>

<p>Wise? No. Expensive? Definitely. Makes me happy? Yes.</p>

<figure>
<a href="/img/udm.png"><img src="/img/udm.png" class="center-img" title="Photo of Unifi Dream Machine Pro SE in a network rack" /></a>
</figure>

<p>Outside of the IPv6 issue, which I think could just be resolved by upgrading the router, there really isn&rsquo;t much wrong with the Omada setup so the takeaway shouldn&rsquo;t be &ldquo;Omada bad, Ubiquiti good&rdquo; - it&rsquo;s really just a few things fell into place for me to make the switch more compelling to me.</p>

<h3 id="the-switchover">The switchover</h3>

<p>Switching between ecosystems seemed daunting but actually it ended up being surprisingly smooth.</p>

<p>Initially I just setup the dream machine on a desk, connecting just one of the APs, and configured the WiFi network to have the same SSID+password as my existing one. When my iPhone connected to it straight away it gave me confidence that everything else should be able to connect just fine.</p>

<p>As for the wired stuff, most things had static IPs configured on the devices themselves or via DHCP reservations, so after whipping up a quick spreadsheet to map out all the important stuff, I got on with the task of bascially tearing out the Omada gear and getting the dream machine racked up, plugged in and all patch cables transferred over.</p>

<p>&hellip;and everything just worked? It was honestly surprising how seamless it was.</p>

<p>Well, OK, there were a few niggles. One thing that broke was my EV charger on the wall outside, while the device was present in the Unifi web interface it just wasn&rsquo;t speaking to the internet for some unknown reason. A cold trip outside to flip a breaker sorted that out.</p>

<p>Another, more annoying issue was Airplay just refused to work. I listen to podcasts through an IKEA Sonos speaker in the kitchen when making dinner and whilst my iPhone could see the speaker, trying to play any stream through it just failed. It turns out Unifi turns multicast DNS off by default and I had to enable this AND restart my iPhone to make Airplay work - this took a while to debug.</p>

<h3 id="you-have-to-3d-print-something-though">You have to 3D print something though</h3>

<p>With the rack sorted and UDM in place my attention turned to the access points. I didn&rsquo;t mount these to the walls/ceilings and didn&rsquo;t with the TP-link ones either, instead I just designed and 3D printed these &lsquo;legs&rsquo;/stands for them to sit on and they rest on a shelf, one in my office and one on the other side of the house.</p>

<p>For the Unifi access points I just had to adjust the design for the stands as the hole placement is slightly shorter to fit the mounting bracket, but this did not take long and soon the APs were legless no more (after 8 hours of printing&hellip;)</p>

<figure>
<a href="/img/ap1.png"><img src="/img/ap1.png" class="center-img" title="Photo of Unifi U6+ Access point with custom 3d Printed legs" /></a>
<a href="/img/ap2.png"><img src="/img/ap2.png" class="center-img" title="Photo of Unifi U6+ Access point with custom 3d Printed legs" /></a>
</figure>

<p>You can find the design for these <a href="https://www.printables.com/model/1084081-ubiquiti-u6-wifi-ap-stands">here on Printables</a></p>

<h2 id="wrapping-up">Wrapping up</h2>

<p>Anyway, that&rsquo;s everything up to this point I think. It&rsquo;s been a journey. An expensive journey, but I&rsquo;m really pleased with the final result<sup>**</sup>.</p>

<p>One pleasant surprise about the Unifi ecosystem has just been how snappy the web interface is. This is probably a product of the hardware being much better than the TP Link OC200, but it&rsquo;s really good! The iOS apps are excellent as well and they have a really good WiFi debugging app called <a href="https://apps.apple.com/us/app/ubiquiti-wifiman/id1385561119">WifiMan</a> that&rsquo;s been invaluable.</p>

<p>As for IPv6? I might experiment once again soon, but I&rsquo;m gonna do some reading on how to configure the firewall on the Unifi system properly first.</p>

<p>Thanks for taking the time to read xxx</p>

<p><sup><i>** this is what I tell myself, but nothing is ever final</i></sup></p>

<p><br/>
<br/></p>

<h3 id="bonus-content-homelab-overview">Bonus content: homelab overview</h3>

<p>If you&rsquo;re interested in what I run on my homelab here&rsquo;s a quick overview. I might write a different blog post about my setup as I do some funky stuff with Tailscale and DNS and other things, but generally this is what I run:</p>

<h4 id="terramaster-nas">Terramaster NAS</h4>

<p><strong>Hardware</strong></p>

<ul>
<li>2x 4TB Seagate Ironwolf HDDs</li>
</ul>

<p><strong>Software</strong></p>

<ul>
<li>Unraid - I don&rsquo;t do anything other than run Unraid on the NAS, I don&rsquo;t run docker containers or VMs.</li>
</ul>

<h4 id="server">Server</h4>

<p><strong>Hardware</strong></p>

<ul>
<li>Intel i5-6500</li>
<li>24GiB RAM</li>
<li>500GiB NVMe SSD</li>
<li>240GB SSD</li>
</ul>

<p><strong>Software - VM 1</strong></p>

<p>This is the main VM that offers a bunch of network level things like DNS and HTTP proxy.</p>

<ul>
<li><a href="https://traefik.io/">Traefik</a> - reverse proxy.</li>
<li><a href="https://adguard.com/en/adguard-home/overview.html">Adguard Home</a> - DNS level adblocker</li>
<li><a href="https://tailscale.com/">Tailscaled</a> - Acts as a subnet router so I can access everything on my network via tailscale</li>
</ul>

<p><strong>Software - VM 2</strong></p>

<ul>
<li><a href="https://www.librechat.ai/">Librechat</a> - for accessing all of the commercial LLMs via APIs wrapped in a nice interface</li>
<li><a href="https://grafana.com/">Grafana</a> - displaying graphs for everything</li>
<li><a href="https://prometheus.io/">Prometheus</a> - metrics from everything inc. Home Assistant</li>
<li><a href="https://beancount.github.io/fava/">Fava</a> - finance UI for <a href="https://beancount.github.io">beancount</a></li>
<li><a href="https://docs.paperless-ngx.com/">Paperless-ngx</a> - document store for scanned documents/PDFs</li>
<li><a href="https://gitea.io/">Gitea</a> - self hosted git</li>
<li><a href="https://changedetection.io/">changedetection.io</a> - tells me when stuff changes on websites</li>
<li><a href="https://github.com/Manyfold3D/manyfold">Manyfold</a> - 3D print model store</li>
</ul>

<p><strong>Software - VM 3</strong></p>

<p>Home assistant basically. One day I might move this onto a separate machine.</p>

<ul>
<li><a href="https://www.home-assistant.io/">Home Assistant</a> - controls everything in my house&hellip;</li>
<li><a href="https://github.com/dresden-elektronik/deconz-rest-plugin">DeCONZ</a> - Zigbee gateway controller/interface for ConBee II USB stick</li>
</ul>

<p><strong>Software - VM 4</strong></p>

<p>Media stuff</p>

<ul>
<li><a href="https://www.plex.tv/">Plex</a> - watching movies/TV shows (streams off NAS)</li>
<li><a href="https://www.navidrome.org/">Navidrome</a> - for my music collection (streams off NAS)</li>
<li><a href="https://www.audiobookshelf.org/">Audiobookshelf</a> - for my audiobook collection (streams of NAS)</li>
<li>Software for finding and archiving linux ISOs</li>
</ul>

<p><sup><i>As a side note I wrote all these bullet points without links in them and asked an LLM to link to the relavent projects/pages and it did it!</i></sup></p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>No News is Good News: Using AI to auto skip the news on catch-up radio</title>
      <link>https://djharper.dev/post/2024/11/10/no-news-is-good-news-using-ai-to-auto-skip-the-news-on-catch-up-radio/</link>
      <pubDate>Sun, 10 Nov 2024 21:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2024/11/10/no-news-is-good-news-using-ai-to-auto-skip-the-news-on-catch-up-radio/</guid>
      <description>Recently, I&amp;rsquo;ve been expanding my music horizons by listening to radio show recordings rather than relying on algorithm-driven recommendations. While this has been great for discovering new music, there&amp;rsquo;s one persistent annoyance: outdated news bulletins from the time of broadcast are still present in the recordings. Hearing old news reports can be jarring, and manually skipping them is often imprecise.
What&amp;rsquo;s really needed is a podcast-style chapter system that would let listeners jump past these sections.</description>
      <content:encoded>
      <![CDATA[
        

<p>Recently, I&rsquo;ve been expanding my music horizons by listening to radio show recordings rather than relying on algorithm-driven recommendations. While this has been great for discovering new music, there&rsquo;s one persistent annoyance: outdated news bulletins from the time of broadcast are still present in the recordings. Hearing old news reports can be jarring, and manually skipping them is often imprecise.</p>

<p>What&rsquo;s really needed is a podcast-style chapter system that would let listeners jump past these sections. However, the current player from the broadcaster is just one continuous stream with no built-in navigation markers, making it impossible to efficiently bypass unwanted content.</p>

<p>So, with a weekend ahead of me free I went away and built something to auto-skip the news segments when listening. It&rsquo;s a browser extension that runs on the same page as the player, gets a handle on the <code>&lt;audio&gt;</code> element and skips the news segments when the time is reached, and also includes a bonus feature of scrolling through a transcript of the show.</p>

<figure>
<video width="640" height="480" autoplay loop muted controls>
    <source src="/img/browser-ext.mp4" type="video/mp4">
        Your browser does not support the video tag.
        <img src="/img/browser-ext.png" />
    </video>
    <figcaption>Skipping over the news</figcaption>
</figure>

<p>Now I can listen to the programmes without the scourge of old news ruining it.</p>

<h2 id="how-it-works">How it works</h2>

<p>You&rsquo;ll be disappointed to hear the solution isn&rsquo;t magic or doing anything fancy. The news segment detection happens in an out of band batch process and the browser extension just takes the output of that to control the player.</p>

<figure>
    <a href="/img/browser-ext.png"><img src="/img/browser-ext.png" /></a>
</figure>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#268bd2">player</span>.<span style="color:#268bd2">addEventListener</span>(<span style="color:#2aa198">&#39;timeupdate&#39;</span>, () =&gt; {
    <span style="color:#859900">this</span>.<span style="color:#268bd2">checkAndSkip</span>(<span style="color:#268bd2">player</span>);
});

<span style="color:#268bd2">seek</span>(<span style="color:#268bd2">player</span>, <span style="color:#268bd2">seconds</span>) {
    <span style="color:#268bd2">console</span>.<span style="color:#268bd2">log</span>(<span style="color:#2aa198">&#34;Moving to =&#34;</span>, <span style="color:#268bd2">seconds</span>);
    <span style="color:#268bd2">player</span>.<span style="color:#268bd2">currentTime</span> = <span style="color:#268bd2">seconds</span>;
    <span style="color:#268bd2">console</span>.<span style="color:#268bd2">log</span>(<span style="color:#2aa198">&#34;New currentTime =&#34;</span>, <span style="color:#268bd2">player</span>.<span style="color:#268bd2">currentTime</span>);
}

<span style="color:#268bd2">checkAndSkip</span>(<span style="color:#268bd2">player</span>) {
    <span style="color:#859900">if</span> (<span style="color:#859900">this</span>.<span style="color:#268bd2">skipPoints</span> == <span style="color:#859900;font-weight:bold">null</span>) {
        <span style="color:#859900">return</span>;
    }


    <span style="color:#93a1a1;font-style:italic">// checks if we need to skip the player forward
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#93a1a1;font-style:italic">// if the current time is within a skip point
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#859900">let</span> <span style="color:#268bd2">currentTime</span> = <span style="color:#268bd2">player</span>.<span style="color:#268bd2">currentTime</span>;
    <span style="color:#859900">for</span> (<span style="color:#859900">let</span> <span style="color:#268bd2">startTime</span> <span style="color:#859900">in</span> <span style="color:#859900">this</span>.<span style="color:#268bd2">skipPoints</span>[<span style="color:#2aa198">&#34;skips&#34;</span>]) {
        <span style="color:#268bd2">startTime</span> = <span style="color:#cb4b16">parseFloat</span>(<span style="color:#268bd2">startTime</span>);
        <span style="color:#859900">let</span> <span style="color:#268bd2">endTime</span> = <span style="color:#859900">this</span>.<span style="color:#268bd2">skipPoints</span>[<span style="color:#2aa198">&#34;skips&#34;</span>][<span style="color:#268bd2">startTime</span>];

        <span style="color:#93a1a1;font-style:italic">// If we&#39;re at a skip point (with small buffer for precision)
</span><span style="color:#93a1a1;font-style:italic"></span>        <span style="color:#859900">if</span> (<span style="color:#268bd2">currentTime</span> &gt;= <span style="color:#268bd2">startTime</span> &amp;&amp; <span style="color:#268bd2">currentTime</span> &lt; <span style="color:#268bd2">endTime</span>) {
            <span style="color:#268bd2">console</span>.<span style="color:#268bd2">log</span>(<span style="color:#2aa198">`Skip point detected, seeking to </span><span style="color:#2aa198">${</span><span style="color:#268bd2">endTime</span><span style="color:#2aa198">}</span><span style="color:#2aa198">`</span>);
           <span style="color:#859900">this</span>.<span style="color:#268bd2">seek</span>(<span style="color:#268bd2">player</span>, <span style="color:#268bd2">endTime</span>);
        }
    }
}
</code></pre></div>
</figure>

<h2 id="news-segment-detection">News Segment Detection</h2>

<p>Identifying where news segments begin and end presents a challenge. While others have explored similar problems, like automatically <a href="https://blog.rekawek.eu/2016/02/24/radio-adblock/">removing radio advertisements</a> through sophisticated <a href="https://en.wikipedia.org/wiki/Digital_signal_processing">digital signal processing</a> (DSP) techniques, these solutions typically rely on audio cues such as jingles or &ldquo;bookends&rdquo; to mark segment boundaries. Unfortunately, the shows I listen to don&rsquo;t use such clear audio markers to indicate news sections.</p>

<p>However, the news <em>does</em> tend to start/end with some words/phrases that could be considered bookends, although it&rsquo;s unclear how consistent these are across shows and newsreaders.</p>

<blockquote>
<p><em>BBC News at 7.30. This is Anthony Burchly.</em></p>

<p>&hellip;<em>That&rsquo;s 6 Music News, your next update is at 8.30</em></p>
</blockquote>

<p>So my initial idea was to find some way of getting a transcription of the show to look for these phrases and identify them as segments to skip, e.g. chances are if something opens with &ldquo;BBC News at&hellip;&rdquo; then within 3-4 minutes there should be a corresponding closing remark to indicate the end of the news segment.</p>

<p>Where does one turn when you have no idea what you are doing and will blindly trust anything? AI of course!</p>

<p>Getting the transcription seemed like a good fit for multi-modal LLMs, but I didn&rsquo;t feel comfortable uploading shows to the major players because getting the audio requires downloading them through means where it&rsquo;s probably legally questionable<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup>. I didn&rsquo;t want to get my AI accounts banned due to uploading copyrighted content<sup class="footnote-ref" id="fnref:2"><a href="#fn:2">2</a></sup>.</p>

<p>I decided instead to use local models, specifically OpenAI&rsquo;s <a href="https://github.com/openai/whisper">whisper</a> for speech recognition. After initial testing, I found that switching to <a href="https://github.com/ggerganov/whisper.cpp">whisper.cpp</a>, which offers GPU support, significantly improved performance. This setup allowed me to process audio files at reasonable speeds on my modest M1 Macbook Air.</p>

<p>Whisper.cpp supports outputting the transcript as JSON and they come out looking something like this.</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">...
{
 <span style="color:#268bd2;font-weight:bold">&#34;timestamps&#34;</span>: {
   <span style="color:#268bd2;font-weight:bold">&#34;from&#34;</span>: <span style="color:#2aa198">&#34;00:01:52,960&#34;</span>,
   <span style="color:#268bd2;font-weight:bold">&#34;to&#34;</span>: <span style="color:#2aa198">&#34;00:01:57,040&#34;</span>
  },
  <span style="color:#268bd2;font-weight:bold">&#34;offsets&#34;</span>: {
    <span style="color:#268bd2;font-weight:bold">&#34;from&#34;</span>: <span style="color:#2aa198;font-weight:bold">112960</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;to&#34;</span>: <span style="color:#2aa198;font-weight:bold">117040</span>
  },
  <span style="color:#268bd2;font-weight:bold">&#34;text&#34;</span>: <span style="color:#2aa198">&#34; Yee-haw, hey music fans, welcome to the 
</span><span style="color:#2aa198">new Music Fix Daily&#34;</span>
},
{
  <span style="color:#268bd2;font-weight:bold">&#34;timestamps&#34;</span>: {
    <span style="color:#268bd2;font-weight:bold">&#34;from&#34;</span>: <span style="color:#2aa198">&#34;00:01:57,040&#34;</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;to&#34;</span>: <span style="color:#2aa198">&#34;00:02:02,500&#34;</span>
  },
  <span style="color:#268bd2;font-weight:bold">&#34;offsets&#34;</span>: {
    <span style="color:#268bd2;font-weight:bold">&#34;from&#34;</span>: <span style="color:#2aa198;font-weight:bold">117040</span>,
    <span style="color:#268bd2;font-weight:bold">&#34;to&#34;</span>: <span style="color:#2aa198;font-weight:bold">122500</span>
  },
  <span style="color:#268bd2;font-weight:bold">&#34;text&#34;</span>: <span style="color:#2aa198">&#34;on BBC six music with Tom and Deb starting the show&#34;</span>
}
...</code></pre></div>
</figure>

<p>With this in place my first stab was to try and identify the bookends by looking for the phrases I described earlier. This kind of worked ok but wasn&rsquo;t consistent, e.g. you&rsquo;d get situations like this</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">...
{
  <span style="color:#268bd2;font-weight:bold">&#34;text&#34;</span>: <span style="color:#2aa198">&#34;BBC&#34;</span>
},
{
  <span style="color:#268bd2;font-weight:bold">&#34;text&#34;</span>: <span style="color:#2aa198">&#34;news at&#34;</span>
},
{
  <span style="color:#268bd2;font-weight:bold">&#34;text&#34;</span>: <span style="color:#2aa198">&#34;6:30, Sir Kier Starmer has&#34;</span>
}
...</code></pre></div>
</figure>

<p>Not optimal, and requires much more fooling around looking/forward backwards in the transcript to try to infer where the bookends sit. I was getting fed up around trying to come up with the solution that catches all things.</p>

<p>So where does one turn where they&rsquo;re fed up of their stupid python script and wants someone else to do something about it? AI of course!</p>

<p>Rather than continue struggling with manual pattern matching, I turned to AI for help. Following some prompt refinements<sup class="footnote-ref" id="fnref:3"><a href="#fn:3">3</a></sup> and an upgrade  from Gemini Flash to Pro, the model proved remarkably effective at identifying news segments.</p>

<p>Prompt:</p>

<div style="color:#b1b1b1; border-radius: 10px; background-color: #222; border:1px solid white; padding: 10px 10px 0px 10px; font-family: monospace; margin-bottom: 20px">
<blockquote>► This is a transcript from a radio show. Could you please identify the segments in the transcript that are BBC news reports so that I can trim them out 
</blockquote>

<blockquote>I'd like the output to be in JSON format with no surrounding markdown just pure JSON, here's an example</blockquote>

<blockquote>
<pre>
  [
    {
        "segment":{
            "start": "00:33:00",
            "end": "00:35:58"
        },
        "duration_seconds": 178
    },
    {
        "segment":{
            "start": "01:13:22",
            "end": "01:16:33"
        },
        "duration_secs": 123
    }
  ]</pre>
  </blockquote>
</div>

<p>The model produced something like this.</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-json" data-lang="json">[
  {
    <span style="color:#268bd2;font-weight:bold">&#34;segment&#34;</span>: {
      <span style="color:#268bd2;font-weight:bold">&#34;start&#34;</span>: <span style="color:#2aa198">&#34;00:29:36&#34;</span>,
      <span style="color:#268bd2;font-weight:bold">&#34;end&#34;</span>: <span style="color:#2aa198">&#34;00:32:41&#34;</span>
    },
    <span style="color:#268bd2;font-weight:bold">&#34;duration_seconds&#34;</span>: <span style="color:#2aa198;font-weight:bold">185</span>
  },
  {
    <span style="color:#268bd2;font-weight:bold">&#34;segment&#34;</span>: {
      <span style="color:#268bd2;font-weight:bold">&#34;start&#34;</span>: <span style="color:#2aa198">&#34;01:29:37&#34;</span>,
      <span style="color:#268bd2;font-weight:bold">&#34;end&#34;</span>: <span style="color:#2aa198">&#34;01:32:41&#34;</span>
    },
    <span style="color:#268bd2;font-weight:bold">&#34;duration_seconds&#34;</span>: <span style="color:#2aa198;font-weight:bold">184</span>
  }
]</code></pre></div>
</figure>

<p>Whether the model is consistent enough across lots of shows to produce decent results remains unclear, but from the handful I&rsquo;ve processed so far the results have looked pretty decent.</p>

<h2 id="putting-all-the-bits-together">Putting all the bits together</h2>

<p>Having solved the news detection challenge by trusting the output of whatever the AI gives, I created an automated pipeline that handles the entire process: downloading media files for the show by scraping the recordings page, processing them through whisper.cpp, and using Gemini Pro to identify the skip points.</p>

<p>The pipeline then saves both transcriptions and skip timestamps to disk, which are served via a simple server that resolves the programme ID to transcript.</p>

<figure>
<a href="/img/browser-ext-pipeline.png"><img src="/img/browser-ext-pipeline.png" /></a>
</figure>

<p>When on the playback page for a show, the browser extension requests the corresponding transcript from the server. It then configures automatic news segment skipping and renders the transcript on the page. The transcript display isn&rsquo;t essential to the functionality, I just thought it was neat.</p>

<p>I&rsquo;m not gonna open source this just yet, I wrote the code in a weekend, it&rsquo;s all a bit thrown together, untested garbage which I don&rsquo;t apologise for, I built it for a stupid personal grievance.</p>

<h2 id="on-reflection">On reflection</h2>

<p>This was a fun project but probably way over engineered for a small use case. Additionally during development it was funny, and in hindsight, unsurprising to find that the news segments are remarkably punctual (around 30 minutes and 90 minutes into the show, each running around 180-190 seconds) so just looking around timestamps with a simpler approach could achieve the same result - but where&rsquo;s the fun in that?</p>

<p>I also enjoyed writing the browser extension to augment some functionality on an existing website to suit my own needs. I even used LLMs to help me start writing it because I&rsquo;d never written one before and it always seemed like a faff, but these models got me started so I could have something up and running pretty quickly.</p>

<p>Future ideas if I can be bothered</p>

<ul>
<li>Try and find more &lsquo;segments&rsquo; in the show e.g. interviews</li>
<li>Try and identify song boundaries - this is probably harder with transcriptions but I wonder if you just threw the audio file into one of the larger multi-modal LLMs it might be able to do something</li>
</ul>

<p>Cheers xxx</p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1"><em>I&rsquo;m not a lawyer</em>
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
<li id="fn:2"><em>I&rsquo;m well aware that scrapers for training these models don&rsquo;t have the best reputation in regards to copyright&hellip;</em>
 <a class="footnote-return" href="#fnref:2"><sup>[return]</sup></a></li>
<li id="fn:3"><em>I&rsquo;m not a prompt engineer I&rsquo;m sure there are better ways of prompting these models</em>
 <a class="footnote-return" href="#fnref:3"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Installing Unraid on a Terramaster F4-424</title>
      <link>https://djharper.dev/post/2024/07/25/installing-unraid-on-a-terramaster-f4-424/</link>
      <pubDate>Thu, 25 Jul 2024 19:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2024/07/25/installing-unraid-on-a-terramaster-f4-424/</guid>
      <description>I recently bought a Terramaster F4-424 NAS for my home network. It&amp;rsquo;s quite a neat box and reasonably priced.
The reason why I chose this device over say, a Synology, is the fact that you can easily replace the operating system. I wasn&amp;rsquo;t keen on Terramaster&amp;rsquo;s provided software and wanted to try out Unraid instead.
Replacing the operating system is really just a matter of swapping a tiny bootable USB drive with another.</description>
      <content:encoded>
      <![CDATA[
        

<p>I recently bought a <a href="https://www.terra-master.com/global/products/homesoho-nas/f4-695.html">Terramaster F4-424 NAS</a> for my home network. It&rsquo;s quite a neat box and reasonably priced.</p>

<p>The reason why I chose this device over say, a Synology, is the fact that you can easily replace the operating system. I wasn&rsquo;t keen on Terramaster&rsquo;s provided software and wanted to try out <a href="https://unraid.net/">Unraid</a> instead.</p>

<p>Replacing the operating system is really just a matter of swapping a tiny bootable USB drive with another.</p>

<p>There are guides on this on YouTube that go pretty in-depth into what is a fairly simple process, but they&rsquo;re mostly for one of the previous models, not the F4-424.</p>

<figure>
<iframe width="560" height="315" src="https://www.youtube.com/embed/rs7jHH2XhKE?si=mAo1_TQYKh1CUmpE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></figure>

<p>I just want to document some of the differences I encountered in the F4-424 compared to the above video and other guides you might find online.</p>

<h2 id="replacing-the-usb-boot-drive">Replacing the USB boot drive</h2>

<p>The USB drive to replace is at the bottom, mounted at the edge of the motherboard.</p>

<p>There is not enough room between the top of the drive and the metal casing to be able to pull the drive out of the socket (contrary to the previous model)</p>

<figure>
<a href="/img/F4-424-1-sm.png"><img src="/img/F4-424-1-sm.png" class="center-img" title="" /></a>
</figure>

<p>So that means we need to lift the motherboard to get it out. Luckily it&rsquo;s just a simple case of removing 4 screws</p>

<figure>
<a href="/img/F4-424-2-sm.png"><img src="/img/F4-424-2-sm.png" class="center-img" title="" /></a>
</figure>

<p>Then you just need to gently lift the motherboard up, it might be a bit tight because the board is connected to a daughter board via a PCI-E slot (I presume) but some light wiggling should get it out.</p>

<figure>
<a href="/img/F4-424-3-sm.png"><img src="/img/F4-424-3-sm.png" class="center-img" title="" /></a>
</figure>

<p>With the motherboard lifted, you can swap the USB drives and then it&rsquo;s just a matter of reassembling everything back together.</p>

<h2 id="keyboard-and-mouse">Keyboard and mouse?</h2>

<p>Some of the guides talk about plugging in HDMI, keyboard and mouse etc to configure the BIOS to boot from the right drive. In my experience I plugged a HDMI cable in and turned the device on and it automatically booted into Unraid anyway, so no further configuration required.</p>

<p>Hope this guide helps!</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Prototyping Websites Using LLMs</title>
      <link>https://djharper.dev/post/2024/05/17/prototyping-websites-using-llms/</link>
      <pubDate>Fri, 17 May 2024 19:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2024/05/17/prototyping-websites-using-llms/</guid>
      <description>I recently had an idea for a website I wanted to build.
The website would help Electric Vehicle owners work out the best time to charge their car if they&amp;rsquo;re on a particular time-of-use tariff here in the UK.
However, I&amp;rsquo;m my own worst enemy when getting started on projects like this. I feel I spent a good chunk of time just working out how to center a &amp;lt;div&amp;gt; on the damn page which usually saps all the joy out of everything.</description>
      <content:encoded>
      <![CDATA[
        

<p><link rel="stylesheet" href="/css/2024-05-creating-websites.css"></p>

<p>I recently had an idea for a website I wanted to build.</p>

<p>The website would help Electric Vehicle owners work out the best time to charge their car if they&rsquo;re on a particular time-of-use tariff here in the UK.</p>

<p>However, I&rsquo;m my own worst enemy when getting started on projects like this. I feel I spent a good chunk of time just working out how to center a <code>&lt;div&gt;</code> on the damn page which usually saps all the joy out of everything. Even with just some hand rolled HTML it&rsquo;s easily a 15 minute job of bootstrapping.</p>

<p>So this time round I decided to skip the frustration and use Large Language Models to help me get started on my project! Heck, if it&rsquo;s <em>still</em> frustrating, at least you can tell the model off for getting it wrong!</p>

<h2 id="it-started-with-a-sketch">It started with a sketch</h2>

<p>So I whipped up a quick sketch of what I wanted my website to look like.</p>

<figure>
<a href="/img/website-sketch.webp"><img src="/img/website-sketch.webp" class="center-img" title="" /></a>
</figure>

<p>On the left would be the inputs e.g. how much battery is in your car, how powerful your charger is and how much you want to charge to.</p>

<p>On the right would be a summary of the cheapest time to charge, with some stats about prices in a table.</p>

<p>Below is a summary of how I got on. I might be misremembering some bits as it was a while ago.</p>

<h2 id="asking-my-mate-claude">Asking my mate Claude</h2>

<p>I asked Claude 3 Opus to write me the HTML for this structure.</p>

<figure>
<a href="/img/website-claude-1.png"><img src="/img/website-claude-1.png" class="center-img" title="" /></a>
</figure>

<p>Not the most detailed prompt, but lets see what we got.</p>

<p><details>
    <summary><span class="icon">►</span>&nbsp; Expand</summary>
    <a href="/img/website-claude-response-1.png"><img src="/img/website-claude-response-1-sm.png" class="center-img" title="" /></a>
</details></p>

<p>OK. It kind of got sidetracked about the cloudflare worker thing and the output HTML is basic. The model nonchalantly disregarded writing the CSS and it ignored the input names by just calling them <code>Input1</code> and <code>Input2</code> and the output side is empty.</p>

<figure>
<a href="/img/website-claude-2.png"><img src="/img/website-claude-2.png" class="center-img" title="" /></a>
</figure>

<p>Not the best start.</p>

<h2 id="get-back-to-work">Get back to work</h2>

<p>I asked lazy ass Claude to get off his backside and write me the damn CSS. Those <code>&lt;div&gt;s</code> are not going to center themselves.</p>

<figure>
<a href="/img/website-claude-request-css.png"><img src="/img/website-claude-request-css.png" class="center-img" title="" /></a>
</figure>

<p><details>
    <summary><span class="icon">►</span>&nbsp; Expand</summary>
    <a href="/img/website-claude-response-2.png"><img src="/img/website-claude-response-2-sm.png" class="center-img" title="" /></a>
</details></p>

<p>Which it did, and it worked!</p>

<p>It even decided to add fancy drop shadows which I didn&rsquo;t request but flair is flair I suppose.</p>

<figure>
<a href="/img/website-claude-3.png"><img src="/img/website-claude-3.png" class="center-img" title="" /></a>
</figure>

<h2 id="adding-some-logic">Adding some logic</h2>

<p>I&rsquo;m not really a web developer so I&rsquo;m not up to date on the latest trends but I&rsquo;ve dabbled with Vue.js in the past, but I can never remember the syntax and structures you need to get started. So I got Claude to figure that bit out.</p>

<figure>
<a href="/img/website-claude-request-7.png"><img src="/img/website-claude-request-7.png" class="center-img" title="" /></a>
</figure>

<p><details>
    <summary><span class="icon">►</span>&nbsp; Expand</summary>
    <a href="/img/website-claude-response-4.png"><img src="/img/website-claude-response-4-sm.png" class="center-img" title="" /></a>
</details></p>

<p>Great, that&rsquo;s saved 1-2 minutes of googling around, thanks Claude, have a cracker (<em>although interesting to see that it&rsquo;s reverted to not rendering the CSS again</em>)</p>

<figure>
<a href="/img/website-claude-js.png"><img src="/img/website-claude-js.png" class="center-img" title="" /></a>
</figure>

<h3 id="tweaking">Tweaking</h3>

<p>One thing I didn&rsquo;t like was how the &ldquo;Input&rdquo; labels were sitting on top of the input boxes instead of at the side. So I asked Claude to fix that</p>

<figure>
<a href="/img/website-claude-request-5.png"><img src="/img/website-claude-request-5.png" class="center-img" title="" /></a>
</figure>

<p><details>
    <summary><span class="icon">►</span>&nbsp; Expand</summary>
    <a href="/img/website-claude-response-3.png"><img src="/img/website-claude-response-3-sm.png" class="center-img" title="" /></a>
</details></p>

<figure>
<a href="/img/website-claude-4.png"><img src="/img/website-claude-4.png" class="center-img" title="" /></a>
</figure>

<h2 id="assistants">Assistants</h2>

<p>If you squint hard enough, the website looks almost how I&rsquo;d pictured it in the sketch at the top, at least in terms of layout.</p>

<p>Obviously all the  detail is missing, it ignored the inputs, outputs and text I&rsquo;d requested. Maybe I could have written better prompts to make it generate the whole thing. However, all the necessary pieces were in place for me to take over and start building to my liking, adding the controls and wiring up the logic.</p>

<p>As ever when doing web development, alignment issues creep in quickly, but Claude helped me out 😅</p>

<p><img src="/img/website-claude-prompt.png" /></p>

<p>After prototyping for a while I ended up not using the HTML the model had generated and switched my design to use <a href="https://getbootstrap.com">Bootstrap</a> instead, but I don&rsquo;t see that as a waste, it was a jumping off point and allowed me to prototype quicker. In hindsight I probably could have asked the model to generate me the HTML using Bootstrap.</p>

<p>Overall I was quite impressed with using these models as an assistant/tool for development, I regularly found myself asking the LLM for some advice on Vue.js concepts/syntax/functions I wasn&rsquo;t sure about and it often provided reasonable answers, although I did often resort to Google/StackOverflow and the Vue.js docs to make sure the model wasn&rsquo;t fooling around.</p>

<p>Oh and the website? Can be found at <a href="https://chargewiser.uk">https://chargewiser.uk</a></p>

<p>Cheers xxxx</p>

<p><a href="/img/chargewiser.png"><img src="/img/chargewiser-sm.png" /></a></p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Intercepting t.co links using DNS rewrites</title>
      <link>https://djharper.dev/post/2023/01/29/intercepting-t.co-links-using-dns-rewrites/</link>
      <pubDate>Sun, 29 Jan 2023 13:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2023/01/29/intercepting-t.co-links-using-dns-rewrites/</guid>
      <description>When someone links to something on twitter, either by embedding something or just pasting a URL, twitter will front it with its own t.co link. This means that you cannot verify what the URL is until you click it and your browser goes to the end result via t.co. I only really noticed this properly when my DNS sinkholing server (Adguard home) started blocking t.co links and I was getting an error when say, clicking a linked news article.</description>
      <content:encoded>
      <![CDATA[
        

<p>When someone links to something on twitter, either by embedding something or just pasting a URL, twitter will front it with its own <code>t.co</code> link. This means that you cannot verify what the URL is until you click it and your browser goes to the end result via <code>t.co</code>. I only really noticed this properly when my DNS sinkholing server (<a href="https://adguard.com/en/adguard-home/overview.html">Adguard home</a>) started blocking <code>t.co</code> links and I was getting an error when say, clicking a linked news article.</p>

<p>The obvious fix for this would be to just add <code>t.co</code> to my DNS allow list so these requests can go through. However, the fact that you cannot <em>see</em> the URL until you&rsquo;ve already navigated through to it irks me a lot, I&rsquo;d rather verify the link I&rsquo;m navigating to is something I want to visit.</p>

<p>There are browser extensions that solve this problem by modifying the DOM to uncloak the links (e.g. <a href="https://github.com/theAlinP/twitter-link-deobfuscator">twitter-link-deobfuscator</a>) which works pretty well, but this solution is limited to the browser and does not work on the Twitter app on Android. Other options are to copy the <code>t.co</code> link into a <a href="https://linkexpander.com/">link uncloaking website</a>, but this is fiddly and annoying, or install an <a href="https://github.com/Dakwamine/link-intercept">app on your phone</a></p>

<p>So I was looking for a more general solution that works across devices, what if there was a way of &ldquo;intercepting&rdquo; when you click on <code>t.co</code> links, unwrapping where it eventually leads to and presenting the user an interstitial page detailing this, with an option to continue forward.</p>

<p>Enter <a href="https://github.com/djhworld/theunwrapper"><em>the unwrapper</em></a>, a small service I wrote this weekend that does exactly this, but it abuses a lot of safeguards we have in place for the web so comes at a high price.</p>

<figure>
<a href="/img/theunwrapper.png"><img src="/img/theunwrapper.png" class="center-img" title="Screenshot displaying the unwrapper service, displaying its features like unwrapping a link from t.co into its underlying form" /></a>
</figure>

<h2 id="the-unwrapper">The Unwrapper</h2>

<p>The service is just a go server that, when receiving a request for <code>t.co</code>, makes a HEAD request to the shortened link and extracts the <code>Location</code> header <em>before</em> the redirect is followed. If this is found, the value in the header is returned and rendered on a simple interstitial page.</p>

<p>But how do you intercept the calls to <code>t.co</code>? The magic comes in by using (abusing?) the DNS rewriting feature on Adguard home to intercept DNS requests for <code>t.co</code> and return the IP of my reverse proxy, and then adding a proxy rule to forward all requests for the host <code>t.co</code> to the go service.</p>

<figure>
<a href="/img/arch.excalidraw.png"><img src="/img/arch.excalidraw.png" class="center-img" title="Screenshot displaying the architecture of how the components fit together. The client makes a DNS request to Adguard Home, which rewrites t.co to return the IP of the reverse proxy, which then forwards requests to 'the unwrapper' go service" /></a>
</figure>

<p>Surprisingly this worked pretty well, although with a huge caveat - this is your run of the mill Man in the Middle (MitM) attack. Browsers will, quite rightfully, complain that the certificate being presented from my reverse proxy is not valid for <code>t.co</code> so the user should not continue, or proceed with extreme caution.</p>

<p>To mitigate this I did something bad. Well, I&rsquo;m already doing something bad, I run my own self-signed Certificate Authority (CA) and my reverse proxy uses certs signed by this CA. This root CA is trusted on my devices so I can access various internal services that run on my network when I&rsquo;m connected to it, or via VPN when I&rsquo;m remote.</p>

<p>With the badness in place, I figured why not add to it by adding <code>t.co</code> to the <code>Subject Alternative Name</code> on the cert for my reverse proxy? Now the browser has no problem and doesn&rsquo;t complain anymore.</p>

<figure>
<a href="/img/secure.png"><img src="/img/secure.png" class="center-img" title="Screenshot displaying chrome trusting my self-signed certificate for t.co" /></a>
</figure>

<p>This is obviously a terrible solution and not recommended, but it works, and works on all my things, including the Twitter app on my phone. It&rsquo;s bad though, I&rsquo;m probably looking past a lot of other security issues that I&rsquo;ve just opened myself up to.</p>

<p>During my testing, another interesting issue quickly arose, a lot of the links cloaked under <code>t.co</code> tend to be links to <em>other</em> link shortening services like bit.ly, buff.ly, trib.al (as it turns out&hellip;.the list is endless) meaning the obfuscated link issue still remains.</p>

<h2 id="going-deeper">Going deeper</h2>

<p>So to get around this I extended the service to work with multiple &ldquo;known&rdquo; URL shortening services and &ldquo;follow&rdquo; the trail until you reach the end. Most of them work the same way, a simple redirect and <code>Location</code> header, so following the trail is really just finding where the chain stops, and throw an error if a cycle is detected.</p>

<p>It&rsquo;s actually quite funny to see how many hops some of these links can take you on, the deepest I&rsquo;ve ever seen is a link from NYTimes taking you on a 9 hop voyage around various link shortening services. I&rsquo;m guessing people are pasting short URLs to other short URLs into social media distribution platforms which just adds to the chain.</p>

<figure>
<a href="/img/nytime-unwrapped.png"><img src="/img/nytime-unwrapped.png" class="center-img" title="Screenshot displaying the unwrapper service for a link from the NY times, it shows one link jumping between 9 different short URLs to reach the actual underlying URL" /></a>
</figure>

<h2 id="bonus-feature">Bonus feature</h2>

<p>With the &ldquo;real&rdquo; link URL now avaialble it&rsquo;s often polluted with various query parameters used for tracking or affiliate descriptors, so the service also strips these out and presents a &ldquo;cleaned&rdquo; version of the link alongside the original which I can then choose to click on or copy to send to others.</p>

<h2 id="lessons-learned">Lessons learned</h2>

<p>I&rsquo;m not sure whether I&rsquo;m going to run this system full time as the obvious security safeguards put in place have just been overridden with hacky self signed certificates, DNS rewriting and other bad things, but it&rsquo;s been a useful exercise in exploring what&rsquo;s possible, even if it is absolutely awful. Probably should just unblock <code>t.co</code> on my adblocker and put the cowboy hacks aside for the time being 🤠</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>ChatGPT corrects itself when you tell it off</title>
      <link>https://djharper.dev/post/2022/12/02/chatgpt-corrects-itself-when-you-tell-it-off/</link>
      <pubDate>Fri, 02 Dec 2022 20:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2022/12/02/chatgpt-corrects-itself-when-you-tell-it-off/</guid>
      <description>The internet has been abuzz around playing with ChatGPT, a new chat AI thing. Putting biases and unfortunate responses aside, curiosity got the better of me and I started to fool around with it.
One of the surprising interactions was, I told it (assistant? eliza? alexa? mega-clippy?) off for getting something wrong, and it corrected itself.
 You are correct, the matrix in my previous response was incorrect.
 Like a teenager half arsing their homework, I managed to get them to actually make an effort.</description>
      <content:encoded>
      <![CDATA[
        

<p>The internet has been abuzz around playing with ChatGPT, a new <a href="https://chat.openai.com">chat AI thing</a>. Putting biases and unfortunate responses aside, curiosity got the better of me and I started to fool around with it.</p>

<p>One of the surprising interactions was, I told it (assistant? eliza? alexa? mega-clippy?) off for getting something wrong, and it corrected itself.</p>

<blockquote>
<p>You are correct, the matrix in my previous response was incorrect.</p>
</blockquote>

<p>Like a teenager half arsing their homework, I managed to get them to actually make an effort.</p>

<h2 id="the-conversation">The conversation</h2>

<p>I&rsquo;m doing <a href="https://adventofcode.com/2022">advent of code</a> again this year, a task I heartily start but eventually give up after day 15 or so, but the early days are always fun. Day 2 starts off talking about the game <a href="https://en.wikipedia.org/wiki/Rock_paper_scissors">Rock, Paper, Scissors</a> and computing some scoring logic based on some simple rules.</p>

<p>Being lazy and tired after work I was having trouble picturing all the outcomes of the game to represent them in my program, so I asked the tool to print a truth table. Not a very good question I&rsquo;ll admit, a truth table usually refers to boolean outcomes not the win/lose/draw states, but they ran with it anyway.</p>

<figure>
<a href="/img/chat-gpt1.png"><img src="/img/chat-gpt1.png" class="center-img" title="Screenshot displaying a question asked to the ChatGPT AI tool to print out a truth table for all outcomes of the game Rock, Paper, Scissors" /></a>
</figure>

<p>At first glance the output was pretty cool, but I found it hard to read. The labels for the rows were not printed so you couldn&rsquo;t determine who was player 1 and player 2.</p>

<p>So I asked them to print me a matrix.</p>

<figure>
<a href="/img/chat-gpt2.png"><img src="/img/chat-gpt2.png" class="center-img" title="Screenshot displaying telling the ChatGPT AI tool to print a matrix of outcomes for the game Rock, Paper, Scissors " /></a>
</figure>

<p>Nice.</p>

<p>But something was still off, the text talks about the rules of the game but the matrix looks completely wrong. You can&rsquo;t tie if both players present different objects.</p>

<p>So I gave them a sharp reply - you are wrong!</p>

<figure>
<a href="/img/chat-gpt3.png"><img src="/img/chat-gpt3.png" class="center-img" title="Screenshot displaying telling the ChatGPT AI tool it was wrong, and the tool corrected itself by printing a correct matrix" /></a>
</figure>

<p>To my surprise, they corrected themselves and printed the corrected matrix. Even admitting the previous answer was wrong!</p>

<p>In this case I knew the rules of the game, I just wanted something quick to reference while writing code, but it makes me wonder whether accepting the first response from these AI tools might not always be the best idea.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Warn me when processes on my Mac are running under Rosetta</title>
      <link>https://djharper.dev/post/2022/09/03/warn-me-when-processes-on-my-mac-are-running-under-rosetta/</link>
      <pubDate>Sat, 03 Sep 2022 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2022/09/03/warn-me-when-processes-on-my-mac-are-running-under-rosetta/</guid>
      <description>Rosetta is a great piece of technology, but can be a battery drain.
Most things on my Mac run natively but now and again I need to run apps under the x86 translation layer, and sometimes I forget these apps are running.
You can see what processes are running under Rosetta by looking in Activity Monitor under the &amp;ldquo;Kind&amp;rdquo; column, but this requires frequent checking.
   So I wrote this little xbar plugin to tell me when there are apps running under Rosetta.</description>
      <content:encoded>
      <![CDATA[
        <p><a href="https://en.wikipedia.org/wiki/Rosetta_(software)#Rosetta_2">Rosetta</a> is a great piece of technology, but can be a battery drain.</p>

<p>Most things on my Mac run natively but now and again I need to run apps under the x86 translation layer, and sometimes I forget these apps are running.</p>

<p>You can see what processes are running under Rosetta by looking in Activity Monitor under the &ldquo;Kind&rdquo; column, but this requires frequent checking.</p>

<figure>
<a href="/img/activity-monitor.png"><img src="/img/activity-monitor.png" class="center-img" title="Screenshot displaying the use of Activity Monitor on OSX to see what processor architecture is running. The top 3 processes are running on Intel architecture, and one on Apple Silicon" /></a>
</figure>

<p>So I wrote this little <a href="https://github.com/matryer/xbar">xbar</a> plugin to tell me when there are apps running under Rosetta. If there are, a warning icon appears on the status bar (refreshed every minute) and shows me what processes are running.</p>

<figure>
<a href="/img/rosetta-warn.png"><img src="/img/rosetta-warn.png" class="center-img" title="Screenshot displaying the use of the Rosetta Warn xbar plugin that displays the top 3 processes are running under Rosetta" /></a>
</figure>

<p>This way I get a visual aid and can close down any Intel apps that I don&rsquo;t need to run anymore. The script is very simple, I used this answer from the <a href="https://apple.stackexchange.com/a/431166">Apple stack exchange</a> to get all the processes running under Rosetta and wrote a script around it for xbar.</p>

<p>Source code and installation instructions can be found here: <a href="https://github.com/djhworld/rosetta-warn">https://github.com/djhworld/rosetta-warn</a></p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Executable PNGs</title>
      <link>https://djharper.dev/post/2020/12/26/executable-pngs/</link>
      <pubDate>Sat, 26 Dec 2020 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2020/12/26/executable-pngs/</guid>
      <description>It&#39;s an image and a program  A few weeks ago I was reading about PICO-8, a fantasy games console with limited constraints. What really piqued my interest about it was the novel way games are distributed, you encode them into a PNG image. This includes the game code, assets, everything. The image can be whatever you want, screenshots from the game, cool artwork or just text. To load them you pass the image as input to the PICO-8 program and start playing.</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<a href="/img/peek.webm"><video src="/img/peek.webm" loop="true" width="100%" title="The pixels have been adjusted in colour slightly." autoplay></video></a>
<figcaption><br/>It's an image <i>and</i> a program</figcaption>
</figure>

<p>A few weeks ago I was reading about <a href="https://www.lexaloffle.com/pico-8.php">PICO-8</a>, a fantasy games console with limited constraints. What really piqued my interest about it was the novel way games are distributed, you encode them into a PNG image. This includes the game code, assets, everything. The image can be whatever you want, screenshots from the game, cool artwork or just text. To load them you pass the image as input to the PICO-8 program and start playing.</p>

<p>This got me thinking, wouldn&rsquo;t it be cool if you could do that for programs on Linux? No! I hear you cry, that&rsquo;s a dumb idea, but whatever, herein lies an overview of possibly the dumbest things I&rsquo;ve worked on this year.</p>

<h2 id="encoding">Encoding</h2>

<p>I&rsquo;m not entirely sure what PICO-8 is actually doing, but at a guess it&rsquo;s probably use <a href="https://en.wikipedia.org/wiki/Steganography">Steganography</a> techniques to &lsquo;hide&rsquo; the data within the raw bytes of the image. There are a lot of resources out there that explain how Steganography works, but the crux of it is quite simple, your image your want to hide data into is made up of bytes, an image is made up of pixels. Pixels are made up of 3 Red Green and Blue (RGB) values, represented as 3 bytes. To hide your data (the &ldquo;payload&rdquo;) you essentially &ldquo;mix&rdquo; the bytes from your payload with the bytes from the image.</p>

<p>If you just replaced each byte in your cover image with the bytes from your payload, you would end up with sections of the image looking distorted as the colours probably wouldn&rsquo;t match with what your original image was. The trick is to be as subtle as possible, or <em>hide in plain sight</em>. This can be achieved by <em>spreading</em> your payload bytes over the bytes of the cover image by using the <em>least significant bits</em> to hide them in. In other words, make subtle adjustments to the byte values so the colour changes are not drastic enough to be perceptive by the human eye.</p>

<p>For example if your payload was the letter <code>H</code>, represented as <code>01001000</code> in binary (72), and your image contained a series of black pixels</p>

<figure>
<a href="/img/byte-replace1.png"><img src="/img/byte-replace1.png" class="center-img" title="The bits from the input bytes are spread across 8 output bytes by hiding them in the least significant bit" /></a>
<figcaption><br/>The bits from the input bytes are spread across 8 output bytes by hiding them in the least significant bit</figcaption>
</figure>

<p>The output is two-and-a-bit pixels that are slightly less black than before, but can you tell the difference?</p>

<figure>
<a href="/img/pixels1.png"><img src="/img/pixels1.png" class="center-img" title="The pixels have been adjusted in colour slightly." /></a>
<figcaption><br/>The pixels have been adjusted in colour slightly.</figcaption>
</figure>

<p>Well, an exceptionally trained colour connoisseur might be able to, but in reality these subtle shifts can really only be noticed by a machine. Retrieving your super secret <code>H</code> is just a matter of reading 8 bytes from the resulting image and re-assembling them back into 1 byte. Obviously hiding a single letter is lame, but this can scale to anything you want, a super secret sentence, a copy of <em>War and Peace</em>, a link to your soundcloud, the go compiler, the only limit is the amount of bytes available in your cover image as you&rsquo;ll require at least 8x whatever your input is.</p>

<h2 id="hiding-programs">Hiding programs</h2>

<p>So, back to the whole linux-executables-in-an-image thing, that old chestnut. Well, seeing as executables are just bytes, they can be hidden in images. Just like in the PICO-8 thing.</p>

<p>Before I could achieve this I decided to write my own <a href="https://github.com/djhworld/steg">Steganography library</a> and <a href="https://github.com/djhworld/stegtool">tool</a> to support encoding and decoding data into PNGs. Yes, there are lots of steganography libraries and tools out there but I learn better by building.</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ stegtool encode <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--cover-image htop-logo.png <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--input-data /usr/bin/htop <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--output-image htop.png
$
$ <span style="color:#cb4b16">echo</span> <span style="color:#2aa198">&#34;Super secret hidden message&#34;</span> | stegtool encode <span style="color:#2aa198">\ </span>
--cover-image image.png <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--output-image image-with-hidden-message.png
$ stegtool decode --image image-with-hidden-message.png
Super secret hidden message</code></pre></div>
</figure>

<p>As it&rsquo;s all written in <a href="https://www.rust-lang.org/">Rust</a> it wasn&rsquo;t that difficult to compile to WASM, so feel free to play with it here:</p>

<p><embed width="100%" height="825px" class="center-img" src="/demos/steg/"/></p>

<p>Anyway, now that can embed data, including executables into an image, how do we run them?</p>

<h2 id="get-it-running">Get it running</h2>

<p>The simple option would be to just run the tool above, <code>decode</code> the data into a new file, <code>chmod +x</code> it and then run it. It works but that&rsquo;s not fun enough. What I wanted was something similar to the PICO-8 experience, you pass something a PNG image and it takes care of the rest.</p>

<p>However, as it turns out, you can&rsquo;t just load some arbitrary set of bytes into memory and tell Linux to jump to it. Well, not in a direct way anyway, but you <em>can</em> use some cheap tricks to fudge it.</p>

<h2 id="memfd-create">memfd_create</h2>

<p>After reading <a href="https://magisterquis.github.io/2018/03/31/in-memory-only-elf-execution.html">this blogpost</a> it became apparent to me you can create an in-memory file and mark it as executable</p>

<blockquote>
<p>Wouldn’t it be cool to just grab a chunk of memory, put our binary in there, and run it without monkey-patching the kernel, rewriting execve(2) in userland, or loading a library into another process?</p>
</blockquote>

<p>This method uses the syscall <a href="https://man7.org/linux/man-pages/man2/memfd_create.2.html">memfd_create(2)</a> to create a file under the <code>/proc/self/fd</code> namespace of your process and load any data you want in it using <code>write</code>. I spent quite a while messing around with the <a href="https://crates.io/crates/libc">libc</a> bindings for Rust to get this to work, and had a lot of trouble understanding the data types you pass around, the documentation for these Rust bindings doesn&rsquo;t help much.</p>

<p>I got something working eventually though</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#859900">unsafe</span> {
    <span style="color:#859900">let</span> <span style="color:#268bd2">write_mode</span> = <span style="color:#2aa198;font-weight:bold">119</span>; <span style="color:#93a1a1;font-style:italic">// w
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#93a1a1;font-style:italic">// create executable in-memory file
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#859900">let</span> <span style="color:#268bd2">fd</span> = <span style="color:#268bd2">syscall</span>(<span style="color:#268bd2">libc</span>::<span style="color:#268bd2">SYS_memfd_create</span>, &amp;<span style="color:#268bd2">write_mode</span>, <span style="color:#2aa198;font-weight:bold">1</span>);
    <span style="color:#859900">if</span> <span style="color:#268bd2">fd</span> == -<span style="color:#2aa198;font-weight:bold">1</span> {
        <span style="color:#859900">return</span> <span style="color:#cb4b16">Err</span>(<span style="color:#cb4b16">String</span>::<span style="color:#268bd2">from</span>(<span style="color:#2aa198">&#34;memfd_create failed&#34;</span>));
    }

    <span style="color:#859900">let</span> <span style="color:#268bd2">file</span> = <span style="color:#268bd2">libc</span>::<span style="color:#268bd2">fdopen</span>(<span style="color:#268bd2">fd</span>, &amp;<span style="color:#268bd2">write_mode</span>); 

    <span style="color:#93a1a1;font-style:italic">// write contents of our binary
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#268bd2">libc</span>::<span style="color:#268bd2">fwrite</span>(
        <span style="color:#268bd2">data</span>.<span style="color:#268bd2">as_ptr</span>() <span style="color:#859900">as</span> *<span style="color:#859900">mut</span> <span style="color:#268bd2">libc</span>::<span style="color:#268bd2">c_void</span>, 
        <span style="color:#2aa198;font-weight:bold">8</span> <span style="color:#859900">as</span> <span style="color:#859900;font-weight:bold">usize</span>,
        <span style="color:#268bd2">data</span>.<span style="color:#268bd2">len</span>() <span style="color:#859900">as</span> <span style="color:#859900;font-weight:bold">usize</span>,
        <span style="color:#268bd2">file</span>,
    );
}
</code></pre></div>
</figure>

<p>Invoking <code>/proc/self/fd/&lt;fd&gt;</code> as a child process from the parent that created it is enough to run your binary.</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-rust" data-lang="rust"><span style="color:#859900">let</span> <span style="color:#268bd2">output</span> = <span style="color:#268bd2">Command</span>::<span style="color:#268bd2">new</span>(<span style="color:#268bd2">format</span>!(<span style="color:#2aa198">&#34;/proc/self/fd/{}&#34;</span>, <span style="color:#268bd2">fd</span>))
    .<span style="color:#268bd2">args</span>(<span style="color:#268bd2">args</span>)
    .<span style="color:#268bd2">stdin</span>(<span style="color:#268bd2">std</span>::<span style="color:#268bd2">process</span>::<span style="color:#268bd2">Stdio</span>::<span style="color:#268bd2">inherit</span>())
    .<span style="color:#268bd2">stdout</span>(<span style="color:#268bd2">std</span>::<span style="color:#268bd2">process</span>::<span style="color:#268bd2">Stdio</span>::<span style="color:#268bd2">inherit</span>())
    .<span style="color:#268bd2">stderr</span>(<span style="color:#268bd2">std</span>::<span style="color:#268bd2">process</span>::<span style="color:#268bd2">Stdio</span>::<span style="color:#268bd2">inherit</span>())
    .<span style="color:#268bd2">spawn</span>();
</code></pre></div>
</figure>

<p>Given these building blocks, I wrote <a href="https://github.com/djhworld/pngrun">pngrun</a> to run the images. It essentially&hellip;</p>

<ol>
<li>Accepts an image that has had our binary embedded in it from the steganography tool, and any arguments</li>
<li>Decodes it (i.e. extracts and re-assembles the bytes)</li>
<li>Creates an in-memory file using <code>memfd_create</code></li>
<li>Puts the bytes of the binary into the in-memory file</li>
<li>Invokes the file <code>/proc/self/fd/&lt;fd&gt;</code> as a child process, passing any arguments from the parent</li>
</ol>

<p>So you can run it like this</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ pngrun htop.png
&lt;htop output&gt;
$ pngrun go.png run main.go
Hello world!</code></pre></div>
</figure>

<p>Once <code>pngrun</code> exits the in-memory file is destroyed.</p>

<h2 id="binfmt-misc">binfmt_misc</h2>

<p>It&rsquo;s annoying having to type <code>pngrun</code> every time though, so my last cheap trick to this pointless gimmick was to use <a href="https://en.wikipedia.org/wiki/Binfmt_misc">binfmt_misc</a>, a system that allows you to &ldquo;execute&rdquo; files based on its file types. I think it was mainly designed for interpreters/virtual machines, like Java. So instead of typing <code>java -jar my-jar.jar</code> you can just type <code>./my-jar.jar</code> and it will invoke the <code>java</code> process to run your JAR. The caveat is your file <code>my-jar.jar</code> needs to be marked as executable first.</p>

<p>So adding an entry to binfmt_misc for <code>pngrun</code> to attempt to run any <code>png</code> files that have the <code>x</code> flag set was as simple as</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ cat /etc/binfmt.d/pngrun.conf
:ExecutablePNG:E::png::/home/me/bin/pngrun:
$ sudo systemctl restart binfmt.d
$ chmod +x htop.png
$ ./htop.png
&lt;output&gt;</code></pre></div>
</figure>

<h2 id="what-s-the-point">What&rsquo;s the point</h2>

<p>Well, there isn&rsquo;t one really. I was seduced by the idea of making PNG images run programs and got a bit carried away with it, but it was fun none the less. There&rsquo;s something amusing to me about distributing programs as an image, remember the ridiculous cardboard boxes PC software used to come in with artwork on the front, why not bring that back! (lets not)</p>

<p>It&rsquo;s really dumb though and comes with a lot of caveats that make it completely pointless and impractical, the main one being needing the stupid <code>pngrun</code> program on your machine. But I also noticed some weird stuff around programs like <code>clang</code>. I encoded it into this fun LLVM logo and while it runs OK, it fails when you try to compile something.</p>

<figure>
<a href="/img/DragonMedium.png"><img src="/img/DragonMedium.png" class="center-img" title="Clang/LLVM logo" /></a>
</figure>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ ./clang.png --version
clang version <span style="color:#2aa198;font-weight:bold">11</span>.0.0 (Fedora <span style="color:#2aa198;font-weight:bold">11</span>.0.0-2.fc33)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /proc/self/fd
$ ./clang.png main.c
error: unable to execute command: Executable <span style="color:#2aa198">&#34;&#34;</span> doesn&#39;t exist!</code></pre></div>
</figure>

<p>This is probably a product of the anonymous file thing, which can probably be overcome if I could be bothered to investigate.</p>

<h3 id="additional-reasons-why-this-is-dumb">Additional reasons why this is dumb</h3>

<p>A lot of binaries are quite large, and given the constraints of needing to fit them into an image, sometimes these need to be <em>big</em>, meaning you end up with comically large files.</p>

<p>Also most software isn&rsquo;t just one executable so the dream of just distributing a PNG kinda falls flat for more complex software like games.</p>

<h2 id="conclusion">Conclusion</h2>

<p>This is probably the dumbest project I&rsquo;ve worked on all year but it&rsquo;s been fun, I&rsquo;ve learned about Steganography, <code>memfd_create</code>, <code>binfmt_misc</code> and played a little more with Rust.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Socket activated services</title>
      <link>https://djharper.dev/post/2020/11/14/socket-activated-services/</link>
      <pubDate>Sat, 14 Nov 2020 17:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2020/11/14/socket-activated-services/</guid>
      <description>I&amp;rsquo;ve been running Linux on my personal computer for nearly six months now and just thought I&amp;rsquo;d write about a neat feature in systemd called systemd.socket
Personal finance on-demand tl;dr Created a socket activated service to spin up a local webapp I use sometimes when something connects to it, and then tear it down again after 5 minutes
I&amp;rsquo;m a big personal finance nerd and have spent the last 3 years cultivating a ledger file that contains pretty much every facet of my financial life.</description>
      <content:encoded>
      <![CDATA[
        

<p>I&rsquo;ve been running <a href="/post/2020/06/07/running-linux-on-my-macbook/">Linux</a> on my personal computer for nearly six months now and just thought I&rsquo;d write about a neat feature in systemd called <a href="https://www.freedesktop.org/software/systemd/man/systemd.socket.html">systemd.socket</a></p>

<h2 id="personal-finance-on-demand">Personal finance on-demand</h2>

<p><strong>tl;dr</strong> Created a socket activated service to spin up a local webapp I use sometimes when something connects to it, and then tear it down again after 5 minutes</p>

<p>I&rsquo;m a big personal finance nerd and have spent the last 3 years cultivating a ledger file that contains pretty much every facet of my financial life. This file is in a format understood by a suite of command line software called <a href="https://beancount.github.io/">Beancount</a>. It&rsquo;s is made even better used in conjunction with <a href="https://beancount.github.io/fava/">Fava</a>, a webapp to explore your beancount file.</p>

<p>Fava is a HTTP a service that reads your beancount file stored on disk and presents you with a series of reports and charts to help you make sense of it. I usually only use it when editing my file or occasionally when I need to check something.</p>

<figure>
<a href="/img/fava.png"><img src="/img/fava.png" title="Example screenshot taken from https://beancount.github.io/fava/" /></a>
<figcaption><br/>Example screenshot taken from https://beancount.github.io/fava/ </figcaption>
</figure>

<p>For quite some time I had this as a <code>--user</code> level systemd service, enabled to run on startup. This worked fine, but I noticed recently that the process seems to consume 2% CPU <em>at all times</em>. Not a big deal in the grand scheme of things but battery life on my laptop comes at a premium.</p>

<figure>
<a href="/img/fava-cpu.png"><img src="/img/fava-cpu.png" title="Fava CPU usage" /></a>
<figcaption><br/>Fava CPU usage </figcaption>
</figure>

<p>This made me think, <em>what if there is a way of starting up Fava only when I need it</em> 🤔, sort of like the serverless compute world where resources are spun up on-demand. It was at this point I remembered reading about <a href="https://www.freedesktop.org/software/systemd/man/systemd.socket.html">systemd.socket</a> where you can activate a service when something connects on a socket. At the time I&rsquo;d filed it away under the &ldquo;that sounds cool but I&rsquo;ll probably never use it&rdquo; place in my brain, but it&rsquo;s pleasing to now have an actual use case!</p>

<p>The setup is as follows</p>

<p>Create a <code>fava.socket</code> file under <code>~/.config/systemd/user</code> - this sets up the socket.</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">[Unit]
<span style="color:#268bd2">Description</span>=Fava Socket
<span style="color:#268bd2">PartOf</span>=fava.service

[Socket]
<span style="color:#268bd2">ListenStream</span>=<span style="color:#2aa198;font-weight:bold">127</span>.0.0.1:5000

[Install]
<span style="color:#268bd2">WantedBy</span>=sockets.target</code></pre></div>

<p>Create a <code>fava.service</code> file under <code>~/.config/systemd/user</code> - this defines the service</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">[Unit]
<span style="color:#268bd2">Description</span>=Fava
<span style="color:#268bd2">After</span>=network.target fava.socket

[Service]
<span style="color:#268bd2">Type</span>=simple
<span style="color:#268bd2">ExecStart</span>=fava -H <span style="color:#2aa198;font-weight:bold">127</span>.0.0.1 /home/djh/beancount/financials.beancount
<span style="color:#268bd2">RuntimeMaxSec</span>=<span style="color:#2aa198;font-weight:bold">300</span> <span style="color:#93a1a1;font-style:italic"># kill the service after 5 minutes.</span>

[Install]
<span style="color:#268bd2">WantedBy</span>=default.target</code></pre></div>

<p>Enable and start the socket + service</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">systemctl --user daemon-reload
systemctl --user <span style="color:#cb4b16">enable</span> fava.socket
systemctl --user start fava.socket</code></pre></div>

<p>Go to a web browser and visit <code>http://127.0.0.1:5000</code> and Fava should load.</p>

<p>After 5 minutes the service will be terminated, but you can start it again by refreshing the page.</p>

<p>Ideally I&rsquo;d want the socket to terminate the service if nothing connects to it for 5 minutes but I&rsquo;m not sure how to do that, hence the <code>RuntimeMaxSec</code> setting in the service file 😞</p>

<h2 id="better-ways">Better ways</h2>

<p>You might be wondering why not just host Fava on a Raspberry Pi, it&rsquo;s a web application afterall. In my case I tend to only use it when editing my beancount file which is stored locally on my Mac. Changes are committed to a git repository when I&rsquo;m happy with them. The hosted version could listen to changes to this git repository and keep its local copy up to date I suppose, but I&rsquo;d have to constantly create/push commits to see my changes. This approach also requires network access.</p>

<p>Another way would to just run <code>fava</code> directly on the command line when I need it, but I like the idea of requesting it on demand via a HTTP request and letting systemd handle the rest.</p>

<p>Anyways just thought I&rsquo;d share x.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Running Linux on my Macbook</title>
      <link>https://djharper.dev/post/2020/06/07/running-linux-on-my-macbook/</link>
      <pubDate>Sun, 07 Jun 2020 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2020/06/07/running-linux-on-my-macbook/</guid>
      <description>Obligatory desktop shot with nothing on it  This is another one of those posts, the one&amp;rsquo;s where Linux desktop apologists have the urge to justify to the world why they do things.
So here we go, a few weeks ago I installed Fedora 32 on my Macbook Pro (early 2015 model). In this post I hope to document the pitfalls, traps and joyous moments I found along the way, complete with the annoyances that I&amp;rsquo;ve come to tolerate.</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<a href="/img/desktop.jpg"><img src="/img/desktop.jpg" title="Obligatory desktop background shot. Not really material for /r/unixporn but it's mine." /></a>
<figcaption><br/>Obligatory desktop shot with nothing on it</figcaption>
</figure>

<p>This is another one of those posts, the one&rsquo;s where Linux desktop apologists have the urge to justify to the world why they do things.</p>

<p>So here we go, a few weeks ago I installed Fedora 32 on my Macbook Pro (early 2015 model). In this post I hope to document the pitfalls, traps and joyous moments I found along the way, complete with the annoyances that I&rsquo;ve come to tolerate.</p>

<p>I&rsquo;ll preface this post by saying most of the issues encountered are down to the minimalist nature of the setup I&rsquo;ve gone with. So don&rsquo;t take this as a reflection on Fedora/Linux, I suspect the defaults with GNOME come with a lot less footguns.</p>

<h2 id="rationale">Rationale</h2>

<p>The main reason for the switch was one killer app; the <a href="https://i3wm.org/">i3 window manager</a>. Keyboard shortcuts, tiled windows, lightning fast to use - it feels like a piece of software designed for people who tinker and use computers a lot.</p>

<p>Since installing I&rsquo;ve tweaked my configuration to</p>

<ul>
<li>Take screenshots with keyboard shortcuts similar to OSX using <a href="https://github.com/naelstrof/maim">maim</a></li>
<li>Always open my web browser on workspace 1</li>
<li>Remove title bars</li>
</ul>

<figure>
<a href="/img/scratchpad.png"><img src="/img/scratchpad.png" title="I wrote my own note taking software because I've never been happy with any of the solutions out there. It uses SQLite and renders markdown with LaTeX support and full text search. The scratchpad feature of i3wm makes note taking a breeze. I wrote my own FUSE backed filesystem that mounts the notes to my machine for editing and syncs them back to the server." /></a>
<figcaption><br/>The scratchpad window makes note taking a joy</figcaption>
</figure>

<p>..and it&rsquo;s generally been a joy to use. My favourite feature is the &ldquo;scratchpad&rdquo; where you can bring up and dismiss a window in the same workspace via a keyboard shortcut. This has been an absolute blast with some custom note taking software I wrote, which I mount via FUSE. Writing notes is a joy because the context switch is minimal.</p>

<h2 id="software">Software</h2>

<p>The meteoric rise of the web browser as a platform has made me realise that I don&rsquo;t really use that much native GUI software anymore. All I seemed to use on OSX was a web browser, Visual Studio Code and a terminal with lots of CLI/TUI software configured via a set of <a href="https://wiki.archlinux.org/index.php/Dotfiles">dotfiles</a>.</p>

<p>I&rsquo;m a huge gamer at heart but I&rsquo;m fortunate enough to own a beefy gaming PC and a set of consoles to meet that need, so I can&rsquo;t really comment on Linux support on that side of things.</p>

<p>So switching OS&rsquo;s isn&rsquo;t really that much of a barrier for most of my use cases.</p>

<h2 id="installation">Installation</h2>

<p>Installation was a bit of anxiety inducing to start with, for one thing I didn&rsquo;t want to screw up the boot partition of my Mac just in case things went a bit wrong and it took a while to search about ways to do this cleanly.</p>

<p>Thankfully <a href="https://alex.dzyoba.com/blog/macbook-air-linux/">Alex Dzyoba</a> wrote an excellent article on creating the appropriate partitions for dual booting.</p>

<p>Once that was done it mostly just seemed to work. Wi-Fi worked, sound worked, so I installed i3, applied my dotfiles and got going.</p>

<h2 id="keyboard-woes">Keyboard woes</h2>

<p>The first issue I encountered was trying to get the keyboard settings to work with the Macbook layout, especially on a GB localised keyboard and tuning it to recognise that I like Caps Lock and Ctrl to be switched.</p>

<p>This was solved with <code>setxkbmap</code> which you have to run on login:</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">setxkbmap -layout gb -model apple_laptop -variant mac -option <span style="color:#2aa198">&#34;ctrl:swapcaps&#34;</span></code></pre></div>

<p>Additionally, sometimes I throw my laptop onto a desk and plug it into an external keyboard (not Apple branded) and this also needs additional tuning when I plug it in as the alt/windows keys are swapped for some reason.</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">setxkbmap -layout gb -model apple_laptop -variant mac <span style="color:#2aa198">\ </span>
-option <span style="color:#2aa198">&#34;altwin:swap_lalt_lwin&#34;</span> -option <span style="color:#2aa198">&#34;ctrl:swapcaps&#34;</span></code></pre></div>

<p>This took a while to reach those settings but I&rsquo;m happy with them.</p>

<h3 id="the-copy-and-paste-problem">The copy and paste problem</h3>

<p>Moving from OSX to Linux means throwing away 10 years of muscle memory on keyboard shortcuts. Cmd+C, Cmd+V for clipboard just won&rsquo;t work without <a href="https://github.com/rbreaves/kinto">a lot of tinkering</a> that just didn&rsquo;t seem worth the effort to me. Pull off the band aid instead, it&rsquo;s got to happen some time.</p>

<p>So I&rsquo;ve had to train myself to go back to using Ctrl as the modifier key, which was tricky at first but it&rsquo;s amazing how quickly I&rsquo;ve adapted. The annoying part is having to remember to hit the Shift key when copying/pasting into terminals.</p>

<h2 id="displays">Displays</h2>

<p>As mentioned previously I often plug in my laptop to an external monitor and like the keyboard tweaking, this took a lot of effort. For one, I position my laptop <em>below</em> my external monitor so like the layout to be above/below. It took me ages to try and find the decent <code>xrandr</code> settings to support this. Every time I tried the monitor above bled into the laptop screen below.</p>

<p>Eventually I found the excellent tool <code>arandr</code> which presents a GUI interface to generate the appropriate settings.</p>

<figure>
<a href="/img/arandr.png"><img src="/img/arandr.png" title="arandr saves the day" /></a>
<figcaption><br/>arandr</figcaption>
</figure>

<p>Unfortunately I&rsquo;ve not found a way of automatically applying these settings when unplugging/plugging the monitor so I&rsquo;ve had to write a script to run when switching (which also includes the <code>setxkbmap</code> settings described above)</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">xrandr --output eDP1 --primary --mode 2560x1600 --pos 640x2400 <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--rotate normal --output DP1 --scale 2x2 --mode 1920x1200 --pos 0x0 <span style="color:#2aa198">\
</span><span style="color:#2aa198"></span>--rotate normal </code></pre></div>

<h3 id="retina">Retina</h3>

<p>To get everything scaling nicely on the laptop took a bit of effort but thankfully <a href="https://dougie.io/linux/hidpi-retina-i3wm/">Doug Beney</a> wrote a decent guide which was simple to implement.</p>

<h3 id="brightness-tuning">Brightness tuning</h3>

<p>It&rsquo;s easy to take for granted something as simple as changing the brightness on your screen, but it took me a while to figure this out. Thankfully using the guidance in this <a href="https://askubuntu.com/questions/715306/xbacklight-no-outputs-have-backlight-property-no-sys-class-backlight-folder">askubuntu post</a> and setting some i3wm config settings, I was able to make the brightness keys work roughly the way you would expect.</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#93a1a1;font-style:italic"># Screen brightness controls</span>
bindsym XF86MonBrightnessUp <span style="color:#cb4b16">exec</span> xbacklight -inc <span style="color:#2aa198;font-weight:bold">5</span>
bindsym XF86MonBrightnessDown <span style="color:#cb4b16">exec</span> xbacklight -dec <span style="color:#2aa198;font-weight:bold">5</span></code></pre></div>

<h2 id="please-just-go-to-sleep">Please just go to sleep</h2>

<p>Closing the lid on the laptop should suspend the OS but this just never seemed to work, I’d often find my machine in a state of hot panic the next morning with 30 minutes of battery left.</p>

<p>To solve this, thanks to an excellent post by <a href="https://joshtronic.com/2017/03/13/getting-suspend-in-linux-working-on-a-macbook-pro/">Josh Sherman</a>, you need to prevent the USB controller from waking up the system.</p>

<p>Unfortunately you need to apply this setting every time you boot so I wrote a <a href="https://gist.github.com/djhworld/734fd6fc36768ecff639cd2d6c656f5a">systemd service and script to enable this</a>.</p>

<h2 id="farewell-firefox">Farewell Firefox</h2>

<p>This is probably the most depressing part of switching. I&rsquo;ve been a long time Firefox fan, especially with extensions like <a href="https://addons.mozilla.org/en-GB/firefox/addon/tree-style-tab/">Tree Style Tabs</a> that acted as an enabler for my tab hoarding vices.</p>

<p>Unfortunately on Linux it&rsquo;s just been dogshit, absolute dogshit. Slow, takes ages to start, websites render in really slowly, switching between tabs feels so lethargic it&rsquo;s like the fire in the fox has gone out.</p>

<p>I&rsquo;ve tried everything to fix it, changing things in <code>about:config</code>, trying  Firefox Nightly and enabling WebRender but nothing seemed to be working.</p>

<p>In contrast Google Chrome is <em>lightning fast</em>, it really is night and day, so for my Linux forays sadly I&rsquo;ve had to go with the big G for now.</p>

<h2 id="other-tools-and-things-i-ve-setup">Other tools and things I&rsquo;ve setup</h2>

<ul>
<li><a href="https://github.com/alacritty/alacritty">alacritty</a> for my terminal, very slick, fast and has great font rendering.</li>
<li><a href="http://jonls.dk/redshift/">redshift</a> which acts like <a href="https://justgetflux.com/news/pages/macquickstart/">f.lux</a> for OSX, meaning my eyes don&rsquo;t get burnt at night.</li>
<li><a href="https://www.dropbox.com/en_GB/install-linux">Dropbox</a> was very easy to set up, especially when adding a systemd service (thanks to <a href="https://github.com/joeroback/dropbox">Joe Roback</a>)</li>
<li><a href="https://code.visualstudio.com/docs/setup/linux">Visual Studio Code</a> - unsurprisingly no problems there.</li>
<li><a href="https://www.gimp.org/">GIMP</a> - works fine for screenshot editing, if a little cumbersome</li>
</ul>

<h2 id="joys">Joys</h2>

<p>After getting everything working the way I like it, it all kinda just works?</p>

<h3 id="i3wm">i3wm</h3>

<p>i3wm is a blast to use, switching between workspaces, moving windows and getting used to the tiling has been a little bit of a learning curve but it&rsquo;s meant I&rsquo;m using my mouse much less.</p>

<p>The additional benefit is just how <em>fast</em> everything feels, it might just be a matter of perception but sometimes perceptions matter.</p>

<figure>
<a href="/img/editing-blog.png"><img src="/img/editing-blog.png" title="Site is rendered using Hugo and edited with vim" /></a>
<figcaption><br/>Me writing this blog post</figcaption>
</figure>

<p><strong>Note</strong>: I&rsquo;m aware of <a href="https://swaywm.org/">swaywm</a> that is config compatible with i3 and runs on Wayland. I&rsquo;ve tried this out and it seems neat, and it would probably solve the keyboard/monitor issues described above, but on my Retina display Chrome just looks really blurry. I&rsquo;m guessing because <a href="https://github.com/swaywm/sway/issues/1481">it&rsquo;s rendered via XWayland</a>, once that&rsquo;s resolved I&rsquo;ll look into making the switch.</p>

<h3 id="systemd">systemd</h3>

<p>Systemd gets a lot of flak in the community but I really, really like it. I&rsquo;ve already written a few services of my own that perform tasks or run software, installed under <code>.config/systemd/user</code> and they were trivial to write.</p>

<p>Getting used to the tooling has been a hill to climb but it feels so much better than the old days of <code>init.d</code> scripts.</p>

<h3 id="dnf">DNF</h3>

<p>I use Fedora on other machines in headless mode so I&rsquo;m fairly used to the tooling, but it&rsquo;s nice to have a decent package manager that generally keeps everything up to date. Homebrew on OSX is a heroic effort, but it&rsquo;s just not the same.</p>

<h2 id="annoyances">Annoyances</h2>

<h3 id="wifi-sometimes-drops">WiFi sometimes drops</h3>

<p>I&rsquo;ve not been able to figure this one out, but maybe once or twice a week the WiFi driver will just stop working. To fix it I have to issue a command to reload the kernel module.</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">sudo modprobe -r brcmfmac &amp;&amp; sudo modprobe brcmfmac</code></pre></div>

<h3 id="webcam">Webcam</h3>

<p>I do sometimes use Skype and Zoom to communicate with family members but the webcam doesn&rsquo;t work out of the box, it looks like there is a <a href="https://github.com/patjak/bcwc_pcie">reverse engineering effort going on</a> to remedy this, but I&rsquo;ve found my iPad works as a decent video calling device, so I&rsquo;ve not gotten around to fixing it.</p>

<h3 id="browser-hardware-video-acceleration">Browser hardware video acceleration</h3>

<p>Web browsers in Linux straight up do not support GPU video acceleration. This became apparent to me when investigating why my laptop was panting, expelling enough heat to cook an egg while watching a YouTube video about&hellip;cooking eggs.</p>

<p>Apparently there is a <a href="https://wiki.archlinux.org/index.php/Chromium#Hardware_video_acceleration">patched version of Chromium</a> out there that supposedly supports it but for the time being it doesn&rsquo;t look like the <a href="https://www.omgubuntu.co.uk/2018/10/hardware-acceleration-chrome-linux">browser vendors see this a top priority</a>. It&rsquo;s a shame but oh well.</p>

<p>Installing VLC and the intel driver works though, so that will have to do.</p>

<h3 id="osx-linux-differences">OSX/Linux differences</h3>

<p>There are a few things I immediately missed from the OSX world, but there mostly appear to be Linux equivalents or workarounds.</p>

<ul>
<li><strong>Screenshot editing:</strong> On OSX it&rsquo;s nice to take a screenshot then immediately jump into the in-built editor to add annotations and adjustments. This can be somewhat replicated with maim+GIMP.</li>
<li><strong>pbcopy/pbpaste:</strong> these are command-line tools to interact with the clipboard. The linux equivalent is <code>xclip</code></li>
<li><strong>Spotlight/Alfred:</strong> I only really used these as a quick calculator, and never made that much use of the file searching features. Firing up a terminal (Alt+Enter) and using <code>bc</code> seems to be a reasonable equivalent. I might see about binding this to a hotkey.</li>
<li><strong>Notifications:</strong> Setting up <a href="https://dunst-project.org/">dunst</a> offers good enough desktop notification support.</li>
<li><strong>Airdrop:</strong> Very infrequently I would use this to send things to my iPad - I&rsquo;ve not found a suitable solution for this yet.</li>
<li><strong>1Password:</strong> This really hasn&rsquo;t been an issue as <a href="https://1password.com/downloads/linux/">1PasswordX</a> works fine, if anything, I think it&rsquo;s better!</li>
</ul>

<h2 id="work">Work</h2>

<p>Switching to Linux in my home life suddenly presents a problem at work. The cognitive overhead of alternating between different keyboard shortcuts on different OS&rsquo;s didn&rsquo;t seem very appealing, along with the fact that I just knew I&rsquo;d miss i3.</p>

<p>So to workaround this I use a virtual machine via VMware Fusion which works surprisingly well. A little too well, it feels almost native! My work machine is a MBP 2019 with 6 cores and 32GB of RAM, so it&rsquo;s more than capable.</p>

<h2 id="overall">Overall</h2>

<p>There are always compromises to be had, whether it be Firefox performance, lack of HW video decoding in the browser and having to tweak a few things, but overall it&rsquo;s been a mostly positive experience to switch. I&rsquo;ll admit that a lot of the issues I came across were of my own making, but it&rsquo;s been worth it.</p>

<p>I&rsquo;ve really not missed OSX that much, in fact it&rsquo;s probably sealed my decision to not go with Apple next time round. The hardware is excellent, but getting Linux <a href="https://github.com/Dunedan/mbp-2016-linux">running on Macbook models &gt;= 2016</a> sounds like an exercise in sadness so it might be the end of the road for Apple laptops in my house.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>I don&#39;t know how CPUs work so I simulated one in code</title>
      <link>https://djharper.dev/post/2019/05/21/i-dont-know-how-cpus-work-so-i-simulated-one-in-code/</link>
      <pubDate>Tue, 21 May 2019 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2019/05/21/i-dont-know-how-cpus-work-so-i-simulated-one-in-code/</guid>
      <description>A few months ago it dawned on me that I didn&amp;rsquo;t really understand how computers work under the hood. I still don&amp;rsquo;t understand how modern computers work.
However, after making my way through But How Do It Know? by J. Clark Scott, a book which describes the bits of a simple 8-bit computer from the NAND gates, through to the registers, RAM, bits of the CPU, ALU and I/O, I got a hankering to implement it in code.</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<img src="/img/simple-computer/text-writer.gif" title="Hello World" style="margin-left:auto; margin-right:auto; display:block" />
</figure>

<p>A few months ago it dawned on me that I didn&rsquo;t really understand how computers work under the hood. I still don&rsquo;t understand how <em>modern</em> computers work.</p>

<p>However, after making my way through <a href="http://buthowdoitknow.com/"><em>But How Do It Know?</em></a> by J. Clark Scott, a book which describes the bits of a simple 8-bit computer from the NAND gates, through to the registers, RAM, bits of the CPU, ALU and I/O, I got a hankering to implement it in code.</p>

<p>While I&rsquo;m not <em>that</em> interested in the physics of the circuitry, the book just about skims the surface of those waters and gives a neat overview of the wiring and how bits move around the system without the requisite electrical engineering knowledge. For me though I can&rsquo;t get comfortable with book descriptions, I have to see things in action and learn from my inevitable mistakes, which led me to chart a course on the rough seas of writing a circuit in code and getting a bit weepy about it.</p>

<p>The fruits of my voyage can be seen in <a href="https://github.com/djhworld/simple-computer">simple-computer</a>; a simple computer that&rsquo;s simple and computes things.</p>

<figure>
<a href="/img/simple-computer/ascii.png"><img src="/img/simple-computer/ascii1.png" title="ASCII" /></a>
<a href="/img/simple-computer/brush.png"><img src="/img/simple-computer/brush1.png" title="brush" /></a>
<a href="/img/simple-computer/text-writer.png"><img src="/img/simple-computer/text-writer1.png" title="doing the typesin'" /></a>
<figcaption>
<br/>
Example programs 
</figcaption>
</figure>

<p>It is quite a neat little thing, the CPU code is implemented <a href="https://github.com/djhworld/simple-computer/blob/master/cpu/cpu.go#L763">as a horrific splurge of gates turning on and off</a> but it works, I&rsquo;ve <a href="https://github.com/djhworld/simple-computer/blob/master/cpu/cpu_test.go">unit tested it</a>, and we all know unit tests are irrefutable proof that something works.</p>

<p>It handles <a href="https://github.com/djhworld/simple-computer/blob/master/io/keyboard.go#L20">keyboard inputs</a>, and renders text <a href="https://github.com/djhworld/simple-computer/blob/master/io/display.go#L13">to a display</a> using a painstakingly crafted set of glyphs for a professional font I&rsquo;ve named &ldquo;Daniel Code Pro&rdquo;. The only cheat bit is to get the keyboard input and display output working I had to hook up go channels to speak to the outside world via <a href="https://github.com/djhworld/simple-computer/blob/master/cmd/simulator/glfw_io.go">GLFW</a>, but the rest of it is a simulated circuit.</p>

<p>I even wrote a <a href="https://github.com/djhworld/simple-computer/blob/master/asm/assembler.go">crude assembler</a> which was eye opening to say the least. It&rsquo;s not perfect. Actually it&rsquo;s a bit crap, but it highlighted to me the problems that other people have already solved many, many years ago and I think I&rsquo;m a better person for it. Or worse, depending who you ask.</p>

<h1 id="but-why-you-do-that">But why you do that?</h1>

<blockquote>
<p><center><em>&ldquo;I&rsquo;ve seen thirteen year old children do this in Minecraft, come back to me when you&rsquo;ve built a <u>REAL</u> CPU out of telegraph relays&rdquo;</em></center></p>
</blockquote>

<p>My mental model of computing is stuck in beginner computer science textbooks, and the CPU that powers the  <a href="https://github.com/djhworld/gomeboycolor">gameboy emulator I wrote back in 2013</a> is really nothing like the CPUs that are running today. Even saying that, the emulator is just a state machine, it doesn&rsquo;t describe the stuff at the logic gate level. You can implement most of it using just a <code>switch</code> statement and storing the state of the registers.</p>

<p>So I&rsquo;m trying to get a better understanding of this stuff because I don&rsquo;t know what L1/L2 caches are, I don&rsquo;t know what pipelining means, I&rsquo;m not entirely sure I understand the Meltdown and Spectre vulnerability papers. Someone told me they were optimising their code to make use of CPU caches, I don&rsquo;t know how to verify that other than taking their word for it. I&rsquo;m not really sure what all the x86 instructions mean. I don&rsquo;t understand how people off-load work to a GPU or TPU. I don&rsquo;t know what a TPU is. I don&rsquo;t know how to make use of SIMD instructions.</p>

<p>But all that is built on a foundation of knowledge you need to earn your stripes for, so I ain&rsquo;t gonna get there without reading the map first. Which means getting back to basics and getting my hands dirty with something simple. The &ldquo;Scott Computer&rdquo; described in the book is simple. That&rsquo;s the reason.</p>

<h1 id="great-scott-it-s-alive">Great Scott! It&rsquo;s alive!</h1>

<p>The Scott computer is an 8-bit processor attached to 256 bytes of RAM, all connected via an 8-bit system bus. It has 4 general purpose registers and can execute <a href="https://github.com/djhworld/simple-computer#instructions">17 machine instructions</a>. Someone built a visual simulator <a href="http://www.buthowdoitknow.com/but_how_do_it_know_cpu_model.html">for the web here</a>, which is really cool, I dread to think how long it took to track all the wiring states!</p>

<figure>
<a href="/img/simple-computer/scott-cpu.png"><img src="/img/simple-computer/scott-cpu.png" title="The Scott CPU" /></a>
<figcaption>
<br/>
A diagram outlining all the components that make up the Scott CPU
<br/>
Copyright © 2009 - 2016 by Siegbert Filbinger and John Clark Scott.
</figcaption>
</figure>

<p>The book takes you on a journey from the humble NAND gate, onto a Bit of memory, onto a register and then keeps layering on components until you end up with something resembling the above. I really recommend reading it, even if you are already familiar with the concepts because it&rsquo;s quite a good overview. I don&rsquo;t recommend the Kindle version though because the diagrams are sometimes hard to zoom in and decipher on a screen. A perennial problem for the Kindle in my experience.</p>

<p>The only thing that&rsquo;s different about my computer is I upgraded it to 16-bit to have more memory to play with, as storing even just the glyphs for the <a href="https://github.com/djhworld/simple-computer/blob/master/_programs/ascii.asm#L27">ASCII table</a> would have dwarfed most of the 8-bit machine described in the book, with not much room left for useful code.</p>

<h1 id="my-development-journey">My development journey</h1>

<p>During development it really was just a case of reading the text, scouring the diagrams and then attempting to translate that using a general purpose programming language code and definitely <em>not</em> using something that&rsquo;s designed for integrated circuit development. The reason why I wrote it in Go, is well, I know a bit of Go. Naysayers might chime in and say, you blithering idiot! I can&rsquo;t believe you didn&rsquo;t spend all your time learning <a href="https://en.wikipedia.org/wiki/VHDL">VHDL</a> or <a href="https://en.wikipedia.org/wiki/Verilog">Verilog</a> or <a href="http://www.cburch.com/logisim/">LogSim</a> or whatever but I&rsquo;d already written my bits and bytes and NANDs by that point, I was in too deep. Maybe I&rsquo;ll learn them next and weep about my time wasted, but that&rsquo;s my cross to bear.</p>

<p>In the grand scheme of things most of the computer is just passing around a bunch of booleans, so any boolean friendly language will do the job.</p>

<p>Applying a schema to those booleans is what helps you (the programmer) derive its meaning, and the biggest decision anyone needs to make is decide what <a href="https://en.wikipedia.org/wiki/Endianness">endianness</a> your system is going to use and make sure all the components transfer things to and from the bus in the right order.</p>

<p>This was an absolute pain in the backside to implement. From the offset I opted for little endian but when testing the ALU my hair took a beating trying to work out why the numbers were coming out wrong. Many, <em>many</em> print statements took place on this one.</p>

<p>Development did take a while, maybe about a month or two during some of my free time, but once the CPU was done and successfully able to execute 2 + 2 = 5, I was happy.</p>

<p>Well, until the book discussed the I/O features, with designs for a simple keyboard and display interface so you can get things in and out of the machine. <em>Well I&rsquo;ve already gotten this far</em>, no point in leaving it in a half finished state. I set myself a goal of being able to type something on a keyboard and render the letters on a display.</p>

<h1 id="peripherals">Peripherals</h1>

<p>The peripherals use the <a href="https://en.wikipedia.org/wiki/Adapter_pattern">adapter pattern</a> to act as a hardware interface between the CPU and the outside world. It&rsquo;s probably not a huge leap to guess this was what the software design pattern took inspiration from.</p>

<figure>
<img src="/img/simple-computer/io.png" title="i couldn't be bothered to do the corners around the CPU for the system bus" style="margin-left:auto; margin-right:auto; display:block" />
<figcaption>
<br/>
How the I/O adapters connect to a GLFW window
</figcaption>
</figure>

<p>With this separation of concerns it was actually pretty simple to hook the other end of the keyboard and display to a window managed by GLFW. In fact I just pulled most of the code from my <a href="https://github.com/djhworld/gomeboycolor-glfw">emulator</a> and reshaped it a bit, using go channels to act as the signals in and out of the machine.</p>

<h1 id="bringing-it-to-life">Bringing it to life</h1>

<figure>
<img src="/img/simple-computer/brush.gif" title="brush.bin" style="margin-left:auto; margin-right:auto; display:block" />
</figure>

<p>This was probably the most tricky part, or at least the most cumbersome. Writing assembly with such a limited instruction set sucks. Writing assembly using a crude assembler I wrote sucks even more because you can&rsquo;t shake your fist at someone other than yourself.</p>

<p>The biggest problem was juggling the 4 registers and keeping track of them, pulling and putting stuff in memory as a temporary store. Whilst doing this I remembered the Gameboy CPU having a stack pointer register so you could push and pop state. Unfortunately this computer doesn&rsquo;t have such a luxury, so I was mostly moving stuff in and out of memory on a bespoke basis.</p>

<p>The only pseudo instruction I took the time to implement was <code>CALL</code> to help calling functions, this allows you to run a function and then return to the point after the function was called. Without that stack though you can only call one level deep.</p>

<p>Also as the machine does not support interrupts, you have to implement awful polling code for functions like getting keyboard state. The book does discuss the steps needed to implement interrupts, but it would involve a lot more wiring.</p>

<p>But anyway enough of the moaning, I ended up writing <a href="https://github.com/djhworld/simple-computer/blob/master/_programs/README.md">four programs</a> and most of them make use of some shared code for drawing fonts, getting keyboard input etc. Not exactly operating system material but it did make me appreciate some of the services a simple operating system might provide.</p>

<p>It wasn&rsquo;t easy though, the trickiest part of the text-writer program was getting the maths right to work out when to go to a newline, or what happens when you hit the enter key.</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-asm" data-lang="asm"><span style="color:#268bd2">main-getInput:</span>
	<span style="color:#268bd2">CALL</span> <span style="color:#268bd2">ROUTINE-io-pollKeyboard</span>
	<span style="color:#268bd2">CALL</span> <span style="color:#268bd2">ROUTINE-io-drawFontCharacter</span>
	<span style="color:#268bd2">JMP</span> <span style="color:#268bd2">main-getInput</span></code></pre></div>
<figcaption>The main loop for the text-writer program</figcaption>
</figure>

<p>I didn&rsquo;t get round to implementing the backspace key either, or any of the modifier keys. Made me appreciate how much work must go in to making text editors and how tedious that probably is.</p>

<h1 id="on-reflection">On reflection</h1>

<p>This was a fun and very rewarding project for me. In the midst of programming in the assembly language I&rsquo;d largely forgotten about the NAND, AND and OR gates firing underneath. I&rsquo;d ascended into the layers of abstraction above.</p>

<p>While the CPU in the is very simple and a long way from what&rsquo;s sitting in my laptop, I think this project has taught me a lot, namely:</p>

<ul>
<li>How bits move around  between all components using a bus</li>
<li>How a <em>simple</em> ALU works</li>
<li>What a <em>simple</em> Fetch-Decode-Execute cycle looks like</li>
<li>That a machine without a stack pointer register + concept of a stack sucks</li>
<li>That a machine without interrupts sucks</li>
<li>What an assembler is and does</li>
<li>How a peripherals communicate with a simple CPU</li>
<li>How <em>simple</em> fonts work and an approach to rendering them on a display</li>
<li>What a <em>simple</em> operating system might start to look like</li>
</ul>

<p>So what&rsquo;s next? The book said that no-one has built a computer like this since 1952, meaning I&rsquo;ve got 67 years of material to brush up on, so that should keep me occupied for a while. I see the <a href="https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf">x86 manual is 4800 pages long</a>, enough for some fun, light reading at bedtime.</p>

<p>Maybe I&rsquo;ll have a brief dalliance with operating system stuff, a flirtation with the C language, a regrettable evening attempting to <a href="https://obsolescence.wixsite.com/obsolescence/pidp-11">solder up a PiDP-11 kit</a> then probably call it quits. I dunno, we&rsquo;ll see.</p>

<p>With all seriousness though I think I&rsquo;m going to start looking into RISC based stuff next, maybe RISC-V, but probably start with early RISC processors to get an understanding of the lineage. Modern CPUs have a lot more features like caches and stuff so I want to understand them as well. A lot of stuff out there to learn.</p>

<p>Do I need to know any of this stuff in my day job? Probably helps, but not really, but I&rsquo;m enjoying it, so whatever, thanks for reading xxxx</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Reed Solomon codes are cool</title>
      <link>https://djharper.dev/post/2019/02/24/reed-solomon-codes-are-cool/</link>
      <pubDate>Sun, 24 Feb 2019 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2019/02/24/reed-solomon-codes-are-cool/</guid>
      <description>Imagine wending your way through a great book on your e-reader, the world melting away, and suddenly everything comes crashing back to reality with an apologetic Sorry! Chapter 20 corrupted! message.
A few tired cells of the flash storage gave up the ghost overnight and corrupted your book.
Wouldn&amp;rsquo;t it be great if your device didn&amp;rsquo;t complain about its innards, and recovered from the problem itself?</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<img src="/img/kindle.jpg" title="my bits aren't what they used to be" style="margin-left:auto; margin-right:auto; display:block" />
<br/>
</figure>

<p>Imagine wending your way through a great book on your e-reader, the world melting away, and suddenly everything comes crashing <span title="back to life">back to reality</span> with an apologetic <em>Sorry! Chapter 20 corrupted!</em> message.</p>

<p>A few tired cells of the flash storage gave up the ghost overnight and corrupted your book.</p>

<p>Wouldn&rsquo;t it be great if your device didn&rsquo;t complain about its innards, and recovered from the problem itself?</p>

<p>As with all blog articles with long winded opening sections, there&rsquo;s a big reveal coming. Which gives me great pleasure to welcome <a href="https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction">Reed Solomon</a> to the stage! Come on out Reed, Solomon - you too!</p>

<h2 id="recovering-from-corruption">Recovering from corruption</h2>

<p><em>sigh. I know&hellip;</em></p>

<p>The central idea of Reed Solomon codes, is you <strong>can recover data in the event of corruption or data loss up to a tolerated level of failure</strong>. This is commonly found in data storage or signal processing systems as an extra layer of protection in the event of problems arising.</p>

<p>It&rsquo;s actually used a lot in your day to day life, <a href="https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction">according to Wikipedia</a>:</p>

<blockquote>
<p><em>They have many applications, the most prominent of which include consumer technologies such as CDs, DVDs, Blu-ray Discs, QR Codes, data transmission technologies such as DSL and WiMAX, broadcast systems such as satellite communications, DVB and ATSC, and storage systems such as RAID 6.</em></p>
</blockquote>

<p>Even if a QR code gets a bit smudged in the rain, or your CD-ROM copy of Encarta &lsquo;98 is suffering from a bout of bit rot, you can take solace in the fact that this error correction algorithm keeps the good times rolling.</p>

<p>Which is kind of reassuring in a way, especially with the write-once-read-many situations where getting a backup copy might involve a lot of time and money.</p>

<p>The Wikipedia page doesn&rsquo;t mention cloud storage, I&rsquo;m pretty sure Google use it as part of their file storage systems<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup>, so you have Reed Solomon to thank for at least one of the lines of defense that keep your cloud data durable.</p>

<p>It&rsquo;s even being used as a <a href="https://onlinelibrary.wiley.com/doi/abs/10.1002/anie.201411378">safety net for storing data in DNA</a>, which is mind blowing. If anything, it shows that if you can store something in bits, this algorithm is generic enough to provide an error correction mechanism.</p>

<h2 id="what-s-going-on">What&rsquo;s going on</h2>

<p>In the storage case, you prepare your data by splitting it into fixed evenly sized <em>data shards</em>, then feed them to the algorithm. After some number crunching, it will return some additional <em>parity shards</em>. This bag of shards make up the set of data you store on disk(s).</p>

<figure>
<img src="/img/reed-solomon1.png" title="serving up some good parity" style="margin-left:auto; margin-right:auto; display:block" />
<br/>
</figure>

<p>Those parity shards, are the magic sauce that lets you recover <strong>any combination</strong> of corrupted or missing shard(s).</p>

<figure>
<img src="/img/reed-solomon2.png" title="hey listen, we all lose things some time" style="margin-left:auto; margin-right:auto; display:block" />
<br/>
</figure>

<p>It just amazes me that this works! It feels like some sort of magic, but it&rsquo;s actually grounded in some clever mathematics, that I won&rsquo;t attempt to explain or provide a strained analogy for, <a href="https://www.backblaze.com/blog/reed-solomon/">other people have done it better</a>.</p>

<p>It&rsquo;s not limitless though, the amount of parity shards dictates the maximum number of failed shards you can recover. So you&rsquo;d base this on expected level of failure.</p>

<p>You could say you want one parity shard for each data shard, the downside to that approach is the storage required would be double, as well as increasing the computation time needed to recover from failure. Probably overkill for not much gain.</p>

<p>According to <a href="https://www.backblaze.com/blog/reed-solomon/">this post</a>, Backblaze uses a parity of 3 for their Vault product, which from the sounds of it is the sweet spot between balancing the risk of failure vs. processing speed.</p>

<blockquote>
<p><em>A Vault splits data into 17 shards, and has to calculate 3 parity shards from that, so that’s the configuration we use for performance measurements. Running in a single thread on Storage Pod hardware, our library can process incoming data at 149 megabytes per second.</em></p>
</blockquote>

<h2 id="yeah-seen-it-all-before">Yeah, seen it all before</h2>

<p>This might be common knowledge to a lot of people but I found it an interesting detour down the Wikipedia rabbit hole. I&rsquo;m not sure if I&rsquo;ll ever find myself implementing something that uses this, but it&rsquo;s nice to have it in the toolbox.</p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1">The Data Centre as a Computer, 1.6.2 <a href="https://www.morganclaypool.com/doi/pdf/10.2200/S00874ED3V01Y201809CAC046">https://www.morganclaypool.com/doi/pdf/10.2200/S00874ED3V01Y201809CAC046</a>
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>I ported my Gameboy Color emulator to WebAssembly</title>
      <link>https://djharper.dev/post/2018/09/21/i-ported-my-gameboy-color-emulator-to-webassembly/</link>
      <pubDate>Fri, 21 Sep 2018 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2018/09/21/i-ported-my-gameboy-color-emulator-to-webassembly/</guid>
      <description>Around five years ago I wrote a Gameboy Color emulator in Go. It was a very frustrating, but rewarding experience that I&amp;rsquo;ve been dining out on in job interviews ever since.
However, as the passage of time progressed, it landed on the pile of mostly-done-but-not-finished projects and left largely abandoned. One might generously say, on hiatus. Well, until very recently that is.
  That 5 year gap   You see, a few weeks ago Go 1.</description>
      <content:encoded>
      <![CDATA[
        

<figure>
<img src="/img/gomeboy/tetris.gif" title="Notice how I've obnoxiously coloured each letter of COLOR" width="400px" style="margin-left:auto; margin-right:auto; display:block" />
<br/>
</figure>

<p>Around five years ago I wrote a <a href="https://djharper.dev/gomeboycolor/">Gameboy Color emulator</a> in Go. It was a very frustrating, but rewarding experience that I&rsquo;ve been dining out on in job interviews ever since.</p>

<p>However, as the passage of time progressed, it landed on the pile of mostly-done-but-not-finished projects and left largely abandoned. One might generously say, on <em>hiatus</em>. Well, until very recently that is.</p>

<figure>
<img src="/img/gomeboy/gomeboycolor-commits.png" title="That 5 year void was filled with other stuff. I went on a cruise ship once." style="" />
<figcaption>
That 5 year gap
</figcaption>
</figure>

<p>You see, a few weeks ago <a href="https://blog.golang.org/go1.11">Go 1.11 came out</a>, and with it came the promise of <em>experimental</em> support for compiling Go code to <a href="https://webassembly.org/">WebAssembly</a>. There&rsquo;s nothing one likes more than experimental APIs so this got me thinking, what could I do to test out this new WASM target?</p>

<p><em>If only I had a decently sized project written in Go that wasn&rsquo;t some trivial TODO list manager</em> 🤔</p>

<h2 id="hello-old-friend">Hello, old friend</h2>

<p>Going back to old code is like looking at old photos of yourself. So young, so naive, questionable style. Much to my surprise though, compiling the project using the new WASM target <strong>actually worked.</strong></p>

<p>As in, within 5 minutes of commenting out code related to GLFW/GL calls, there was something running in the browser. Obviously, not rendering anything to the page, but there was stuff printing to the developer tools console at least to indicate the emulator was running.</p>

<p>This absolutely blew my mind, here was some old code, written for a non browser environment in a language not supported by browsers, running in the browser. It was exciting enough for me to blast out furious torrent of commits.</p>

<p>The end result being <a href="/demos/gomeboycolor/">gomeboycolor WASM edition</a> (warning: ~5mb or so download), go on, try it out, load some ROMs on it that you&rsquo;ve illegally downloaded<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup>.</p>

<p>Alternatively try out the demo below, click &ldquo;start&rdquo; to run the emulator and &ldquo;stop&rdquo; to stop it. The preinstalled ROM is a <a href="http://gbdev.gg8.se/wiki/articles/Test_ROMs">test suite</a> used to test the CPU:</p>

<p><embed width="100%" height="250px" class="center-img" src="/demos/gomeboycolor-demo/"/></p>

<h3 id="reinventing-the-wheel">Reinventing the wheel</h3>

<p>Running emulators in the <a href="https://github.com/fcambus/jsemu#nintendo">browser isn&rsquo;t new</a> and I&rsquo;d imagine some people have had some fun porting other emulators using <a href="https://github.com/kripken/emscripten">emscripten</a>. In fact someone is doing a <a href="https://github.com/torch2424/wasmBoy">WASM Gameboy emulator in AssemblyScript</a>, so I&rsquo;m definitely not the first.</p>

<p>There are some caveats and performance issues of this WASM implementation that I will explain below if you&rsquo;re still interested in my long ramblings, but it mostly works.</p>

<p>Except in Google Chrome, that is. Oh boy there&rsquo;s trouble there, we&rsquo;ll get onto that. Firefox and Safari seem to perform reasonably well though. I&rsquo;ve not tried Edge.</p>

<figure>
<a href="/img/gomeboy/zelda.png"><img src="/img/gomeboy/zelda1.png" title="Zelda Links Awakening" /></a>
<a href="/img/gomeboy/mariotennis.png"><img src="/img/gomeboy/mariotennis1.png" title="Mario Tennis" /></a>
<a href="/img/gomeboy/tetrisdx.png"><img src="/img/gomeboy/tetrisdx1.png" title="Tetris DX" /></a>
<a href="/img/gomeboy/pokemon.png"><img src="/img/gomeboy/pokemon1.png" title="I played this in school back in the year 2000" /></a>
<a href="/img/gomeboy/mario_bros.png"><img class="" src="/img/gomeboy/mario_bros1.png" title="" /></a>
<a href="/img/gomeboy/rtype.png"><img class="" src="/img/gomeboy/rtype1.png" title="" /></a>
<a href="/img/gomeboy/wario.png"><img class="" src="/img/gomeboy/wario1.png" title="" /></a>
<a href="/img/gomeboy/lionking.png"><img class="" src="/img/gomeboy/lionking1.png" title="" /></a>
<figcaption>
<br/>
Admit it, you're just here for the screenshots
</figcaption>
</figure>

<h2 id="draw-an-owl-2">Draw an owl <sup class="footnote-ref" id="fnref:2"><a href="#fn:2">2</a></sup></h2>

<p>No one ever talks about the journey, just the destination. But this time I&rsquo;ll indulge myself a little and document a few challenges I found along the way. Maybe we can all learn something. Learn something about porting Gameboy Color emulators to WASM at least.</p>

<blockquote>
<p><em>By the way, as a side note, if you want to know what programming your own Gameboy emulator is like, <a href="https://">@Tomek1024</a> paints <a href="https://blog.rekawek.eu/2017/02/09/coffee-gb/">a pretty accurate portrait</a>, I&rsquo;m impressed he managed to do it in less than two months. Embarrassingly it took me about six. Okay maybe seven. It was 2013.</em></p>
</blockquote>

<p>Back to WebAssembly&hellip;</p>

<p>The first real issue was, while my previous self had endeavored to make the code very modular, with individual packages for each hardware component (e.g. CPU, GPU, memory management unit etc) there were some bits of the code base that made a lot of assumptions about its environment. These issues only presented themselves at runtime with the WASM virtual machine throwing up.</p>

<p>The problems were namely anything that was doing stuff around the <a href="https://golang.org/pkg/os/"><code>os</code></a> package, so like opening files, querying filesystems and user info. My emulator expected to read ROM files, and save battery state<sup class="footnote-ref" id="fnref:3"><a href="#fn:3">3</a></sup> to disk. The browser WASM environment is in a sandbox and won&rsquo;t let you play outside it.</p>

<p>So the first round of refactoring was to move the <a href="https://github.com/djhworld/gomeboycolor/blob/master/cartridge/cartridge.go#L58">ROM-loading</a> and <a href="https://github.com/djhworld/gomeboycolor/blob/master/saves/store.go">save-saving layers</a> to the outer edges and use interfaces like <code>io.Reader</code> further in. As a professional Java developer I should have known better, even 5 years ago, but still.</p>

<p>So that&rsquo;s lesson 1. Limit the scope of your environment to the bits of code that need to know. Abstract elsewhere</p>

<h3 id="graphics">Graphics</h3>

<p>That was the easy bit. But no one likes playing a Gameboy with no screen, so this is where the hacks started to creep in.</p>

<p>Every so often the emulator emits a 2D array (<em>frame</em>) of pixels. The real hardware does this at a rate of 59.7 frames per second.<sup class="footnote-ref" id="fnref:4"><a href="#fn:4">4</a></sup></p>

<p>Drawing an array of pixels seemed like a prime candidate for the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API">HTML5 canvas API</a>, constructing an <a href="https://developer.mozilla.org/en-US/docs/Web/API/ImageData"><code>ImageData</code></a> object and repainting on every frame update. When trying this though, using the go <a href="https://tip.golang.org/pkg/syscall/js/"><code>syscall/js</code></a> package, the performance was abysmal, eventually causing the browser tab to freeze. It appeared as though the having the emulator and UI on the same thread was causing a lot of contention.</p>

<p>Sounded like a job for threading to me, but WebAssembly doesn&rsquo;t support threads (<a href="https://github.com/WebAssembly/threads">yet</a>) so I needed to figure out something else.</p>

<h3 id="have-faith-in-the-workers">Have faith in the workers</h3>

<p>Which led me towards <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">Web Workers</a>.</p>

<blockquote>
<p><em>Web Workers is a simple means for web content to run scripts in background threads. The worker thread can perform tasks without interfering with the user interface.</em></p>
</blockquote>

<p>Perfect!</p>

<p>Initialising the emulator <a href="https://github.com/djhworld/gomeboycolor-wasm/blob/master/static/gomeboycolor/js/worker.js#L21">inside a worker</a> and then using the <code>postMessage</code> API to post the frames to the user interface on every tick seemed a more promising approach.</p>

<p>There were still performance issues though, as I was sending an array of bytes out of WASM land into JS land on every frame call. While the <code>postMessage</code> API supports sending an extra parameter to &lsquo;transfer&rsquo; <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Passing_data_by_transferring_ownership_(transferable_objects)">ownership of large data sets</a>&hellip;</p>

<blockquote>
<p><em>Transferable objects are transferred from one context to another with a zero-copy operation, which results in a vast performance improvement when sending large data sets.</em></p>
</blockquote>

<p>&hellip;it seemed tricky to do in Go code because the transferable parameter needs to be an <code>ArrayBuffer</code> <del>which doesn&rsquo;t seem possible</del><sup><a href="#edit-2018-09-02">*see edit</a></sup> with the API that <code>syscall/js</code> provides.</p>

<p>Instead, to workaround this, the first hack was born. Within my Go code, the array of pixels gets converted to a base64 string, which is sent to a global javascript function that issues the <code>postMessage</code> call. This yielded a much smoother experience in the user interface!</p>

<figure>
<img src="/img/gomeboy/graphics.png" class="center-img" title="" />
<figcaption>
<br/>
The hack in diagram form.
</figcaption>
</figure>

<p>Go code:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#859900">func</span> (<span style="color:#268bd2">s</span> *<span style="color:#268bd2">html5CanvasDisplay</span>) <span style="color:#268bd2">DrawFrame</span>(<span style="color:#268bd2">screenData</span> *<span style="color:#268bd2">types</span>.<span style="color:#268bd2">Screen</span>) {
    <span style="color:#859900">for</span> <span style="color:#268bd2">y</span> := <span style="color:#2aa198;font-weight:bold">0</span>; <span style="color:#268bd2">y</span> &lt; <span style="color:#2aa198;font-weight:bold">144</span>; <span style="color:#268bd2">y</span>++ {
        <span style="color:#859900">for</span> <span style="color:#268bd2">x</span> := <span style="color:#2aa198;font-weight:bold">0</span>; <span style="color:#268bd2">x</span> &lt; <span style="color:#2aa198;font-weight:bold">160</span>; <span style="color:#268bd2">x</span>++ {
            <span style="color:#859900">var</span> <span style="color:#268bd2">pixel</span> <span style="color:#268bd2">types</span>.<span style="color:#268bd2">RGB</span> = <span style="color:#268bd2">screenData</span>[<span style="color:#268bd2">y</span>][<span style="color:#268bd2">x</span>]
            <span style="color:#268bd2">s</span>.<span style="color:#268bd2">imageData</span>[<span style="color:#268bd2">i</span>] = <span style="color:#268bd2">pixel</span>.<span style="color:#268bd2">Red</span>
            <span style="color:#268bd2">s</span>.<span style="color:#268bd2">imageData</span>[<span style="color:#268bd2">i</span>+<span style="color:#2aa198;font-weight:bold">1</span>] = <span style="color:#268bd2">pixel</span>.<span style="color:#268bd2">Green</span>
            <span style="color:#268bd2">s</span>.<span style="color:#268bd2">imageData</span>[<span style="color:#268bd2">i</span>+<span style="color:#2aa198;font-weight:bold">2</span>] = <span style="color:#268bd2">pixel</span>.<span style="color:#268bd2">Blue</span>
            <span style="color:#268bd2">s</span>.<span style="color:#268bd2">imageData</span>[<span style="color:#268bd2">i</span>+<span style="color:#2aa198;font-weight:bold">3</span>] = <span style="color:#2aa198;font-weight:bold">255</span>
            <span style="color:#268bd2">i</span> += <span style="color:#2aa198;font-weight:bold">4</span>
        }
   }

   <span style="color:#93a1a1;font-style:italic">// hack
</span><span style="color:#93a1a1;font-style:italic"></span>   <span style="color:#268bd2">screenData</span> := <span style="color:#268bd2">base64</span>.<span style="color:#268bd2">StdEncoding</span>.<span style="color:#268bd2">EncodeToString</span>(<span style="color:#268bd2">s</span>.<span style="color:#268bd2">imageData</span>)

   <span style="color:#93a1a1;font-style:italic">// call global function
</span><span style="color:#93a1a1;font-style:italic"></span>   <span style="color:#268bd2">js</span>.<span style="color:#268bd2">Global</span>().<span style="color:#268bd2">Call</span>(<span style="color:#2aa198">&#34;sendScreenUpdate&#34;</span>, <span style="color:#268bd2">screenData</span>)
}</code></pre></div>
<figcaption>Note the image data is a flattened array that represents the pixel grid</figcaption>
</figure>

<p>Javascript in the worker:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#859900">function</span> <span style="color:#268bd2">sendScreenUpdate</span>(<span style="color:#268bd2">bs64</span>) {
    <span style="color:#93a1a1;font-style:italic">// decode base 64 back to byte array
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#859900">var</span> <span style="color:#268bd2">bytes</span> = <span style="color:#268bd2">base64js</span>.<span style="color:#268bd2">toByteArray</span>(<span style="color:#268bd2">bs64</span>)
    <span style="color:#859900">var</span> <span style="color:#268bd2">buf</span> =  <span style="color:#859900">new</span> <span style="color:#268bd2">Uint8ClampedArray</span>(<span style="color:#268bd2">bytes</span>).<span style="color:#268bd2">buffer</span>;

    <span style="color:#93a1a1;font-style:italic">// uses transferable on post message
</span><span style="color:#93a1a1;font-style:italic"></span>    <span style="color:#268bd2">postMessage</span>([<span style="color:#2aa198">&#34;screen-update&#34;</span>, <span style="color:#268bd2">buf</span>], [<span style="color:#268bd2">buf</span>]);
}
</code></pre></div>
<figcaption>Decode the base64 and then send the data to the user interface</figcaption>
</figure>

<p>The update canvas function in the user interface:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#268bd2">worker</span>.<span style="color:#268bd2">onmessage</span> = <span style="color:#859900">function</span>(<span style="color:#268bd2">e</span>) {
    <span style="color:#859900">if</span>(<span style="color:#268bd2">e</span>.<span style="color:#268bd2">data</span>[<span style="color:#2aa198;font-weight:bold">0</span>] == <span style="color:#2aa198">&#34;screen-update&#34;</span>) {
       <span style="color:#268bd2">updateCanvas</span>(<span style="color:#268bd2">e</span>.<span style="color:#268bd2">data</span>[<span style="color:#2aa198;font-weight:bold">1</span>]);
    }
}
<span style="color:#859900">function</span> <span style="color:#268bd2">updateCanvas</span>(<span style="color:#268bd2">screenData</span>) {
    <span style="color:#859900">var</span> <span style="color:#268bd2">decodedData</span> = <span style="color:#859900">new</span> <span style="color:#268bd2">Uint8ClampedArray</span>(<span style="color:#268bd2">screenData</span>);
    <span style="color:#859900">var</span> <span style="color:#268bd2">imageData</span> = <span style="color:#859900">new</span> <span style="color:#268bd2">ImageData</span>(<span style="color:#268bd2">decodedData</span>, <span style="color:#2aa198;font-weight:bold">160</span>, <span style="color:#2aa198;font-weight:bold">144</span>);
    <span style="color:#268bd2">canvasContext</span>.<span style="color:#268bd2">putImageData</span>(<span style="color:#268bd2">imageData</span>, <span style="color:#268bd2">canvas</span>.<span style="color:#268bd2">width</span> / <span style="color:#2aa198;font-weight:bold">4</span>, <span style="color:#2aa198;font-weight:bold">4</span>);
}
</code></pre></div>
<figcaption>Repaint the canvas context with the new frame</figcaption>
</figure>

<h3 id="the-curse-of-the-workers">The curse of the workers</h3>

<figure>
<img src="/img/gomeboy/zelda-storm.png" title="I like the starting sequence in Zelda" width="200px" style="margin-left:auto; margin-right:auto; display:block" />
<br/>
<figcaption>Moody</figcaption>
</figure>

<p>Unfortunately the web worker approach presented a whole new set of challenges. It was beginning to feel I was making a rod for my own back.</p>

<p>Gamers usually like to play games by interacting with them via key or button presses. As Web Workers are <strong>isolated</strong>, they don&rsquo;t have access to all the good stuff you get in the browser like setting up handlers for keyboard events and so on. The only way you can communicate with them is by sending them letters via the <code>postMessage</code> call and hoping they read them.</p>

<p>So the next challenge was to start posting the keyboard updates back to the emulator.</p>

<figure>
<img src="/img/gomeboy/keyboard.png" class="center-img" title="" />
<figcaption>
<br/>
The Circle 
</figcaption>
</figure>

<p>Go code:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#859900">var</span> <span style="color:#268bd2">messageCB</span> <span style="color:#268bd2">js</span>.<span style="color:#268bd2">Callback</span>
<span style="color:#268bd2">messageCB</span> = <span style="color:#268bd2">js</span>.<span style="color:#268bd2">NewCallback</span>(<span style="color:#859900">func</span>(<span style="color:#268bd2">args</span> []<span style="color:#268bd2">js</span>.<span style="color:#268bd2">Value</span>) {
    <span style="color:#268bd2">input</span> := <span style="color:#268bd2">args</span>[<span style="color:#2aa198;font-weight:bold">0</span>].<span style="color:#268bd2">Get</span>(<span style="color:#2aa198">&#34;data&#34;</span>)
    <span style="color:#859900">switch</span> <span style="color:#268bd2">input</span>.<span style="color:#268bd2">Index</span>(<span style="color:#2aa198;font-weight:bold">0</span>).<span style="color:#268bd2">String</span>() {
        <span style="color:#859900">case</span> <span style="color:#2aa198">&#34;keyup&#34;</span>:
            <span style="color:#93a1a1;font-style:italic">// tell emulator what key has been released 
</span><span style="color:#93a1a1;font-style:italic"></span>            <span style="color:#268bd2">i</span>.<span style="color:#268bd2">KeyHandler</span>.<span style="color:#268bd2">KeyUp</span>(<span style="color:#268bd2">input</span>.<span style="color:#268bd2">Index</span>(<span style="color:#2aa198;font-weight:bold">1</span>).<span style="color:#268bd2">Int</span>())
        <span style="color:#859900">case</span> <span style="color:#2aa198">&#34;keydown&#34;</span>:
            <span style="color:#93a1a1;font-style:italic">// tell emulator what key has been pressed
</span><span style="color:#93a1a1;font-style:italic"></span>            <span style="color:#268bd2">i</span>.<span style="color:#268bd2">KeyHandler</span>.<span style="color:#268bd2">KeyDown</span>(<span style="color:#268bd2">input</span>.<span style="color:#268bd2">Index</span>(<span style="color:#2aa198;font-weight:bold">1</span>).<span style="color:#268bd2">Int</span>())
    }
})

<span style="color:#93a1a1;font-style:italic">// receive messages from outside
</span><span style="color:#93a1a1;font-style:italic"></span><span style="color:#268bd2">self</span> := <span style="color:#268bd2">js</span>.<span style="color:#268bd2">Global</span>().<span style="color:#268bd2">Get</span>(<span style="color:#2aa198">&#34;self&#34;</span>)
<span style="color:#268bd2">self</span>.<span style="color:#268bd2">Call</span>(<span style="color:#2aa198">&#34;addEventListener&#34;</span>, <span style="color:#2aa198">&#34;message&#34;</span>, <span style="color:#268bd2">messageCB</span>, <span style="color:#859900;font-weight:bold">false</span>)</code></pre></div>
<figcaption>Handling the messages received in the worker</figcaption>
</figure>

<p>Javascript code in the user interface:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#859900">let</span> <span style="color:#268bd2">keydownhandler</span> = <span style="color:#859900">function</span> (<span style="color:#268bd2">e</span>) {
    <span style="color:#268bd2">worker</span>.<span style="color:#268bd2">postMessage</span>([<span style="color:#2aa198">&#34;keydown&#34;</span>, <span style="color:#268bd2">e</span>.<span style="color:#268bd2">which</span>])
}
<span style="color:#859900">let</span> <span style="color:#268bd2">keyuphandler</span> = <span style="color:#859900">function</span> (<span style="color:#268bd2">e</span>) {
    <span style="color:#268bd2">worker</span>.<span style="color:#268bd2">postMessage</span>([<span style="color:#2aa198">&#34;keyup&#34;</span>, <span style="color:#268bd2">e</span>.<span style="color:#268bd2">which</span>])
}
<span style="color:#cb4b16">document</span>.<span style="color:#268bd2">addEventListener</span>(<span style="color:#2aa198">&#34;keydown&#34;</span>, <span style="color:#268bd2">keydownhandler</span>);
<span style="color:#cb4b16">document</span>.<span style="color:#268bd2">addEventListener</span>(<span style="color:#2aa198">&#34;keyup&#34;</span>, <span style="color:#268bd2">keyuphandler</span>);
</code></pre></div>
<figcaption>Sending keyboard change events to the worker</figcaption>
</figure>

<p>This back and forth <code>postMessage</code> approach was working and soon ballooned into a some sort of faux protocol that</p>

<ul>
<li>Allows users to load ROMs into the emulator by passing the byte array to the worker</li>
<li>Allows users to configure the emulator settings by passing configuration data to the worker</li>
<li>Allows the emulator to send battery save data to the UI thread, which in turn puts it in LocalStorage

<ul>
<li>On emulator start, the save data is loaded from LocalStorage and sent to the emulator</li>
</ul></li>
<li>Allows the emulator to send a diagnostic frame rate counter back to the user interface</li>
</ul>

<p>I&rsquo;ll be honest, it was a royal pain in the backside to write and is probably very fragile and prone to breakages. There would be more arrows on this diagram to express the <span title="R.I.P. Barry" style="cursor:pointer"><u>to-me, to-you</u></span> handshaking but frankly I&rsquo;ve had enough of diagrams.</p>

<figure>
<img src="/img/gomeboy/final.png" class="center-img" title="" />
<figcaption>
<br/>
Imagine more arrows
</figcaption>
</figure>

<h3 id="performance">Performance</h3>

<p>That was the implementation side of things, there was a lot of deck chair rearranging elsewhere, but most of the effort was in getting stuff in and out of the emulator.</p>

<p>Performance was still a problem though, the framerate was choppy and the keyboard input mechanism was unreliable, with some presses being skipped, making the games feel spongy and not fun to play.</p>

<p>To try and isolate whether the problem was due to WebAssembly performance, I introduced a &ldquo;headless&rdquo; mode that stops sending the screen data on every draw frame call. This was to try and remove the whole web worker &rarr; UI dance from the equation and just see how well the emulator can run.</p>

<p>The following tests were performed using these browsers on OSX 10.13.6:</p>

<ul>
<li>Chrome <code>69.0.3497.100</code></li>
<li>Firefox <code>63.0b4</code></li>
<li>Safari <code>12.0 (13606.2.11)</code></li>
</ul>

<p>Using the Game <em>Tetris DX</em>, running for 60 seconds, these were the results:</p>

<figure>
<img src="/img/gomeboy/tetrisdx-headless-graph.png" class="center-img" title="" />
<figcaption>
<br/>
</figcaption>
</figure>

<p>As you can see, Safari runs a pretty smooth shop. Firefox was more erratic but the emulator kept on pausing every so often so that would account for the drops. Chrome, while a fairly straight line, didn&rsquo;t even make it past 20fps.</p>

<p>So WASM performance, at least on Firefox and Safari seemed pretty reasonable.</p>

<p>Repeating the same test with headless mode turned off, there was a definite 10 frame or so performance penalty, probably owing to the base64 conversions and passing messages bit. Oddly Firefox didn&rsquo;t seem to pause at all during this test.</p>

<figure>
<img src="/img/gomeboy/tetrisdx-graph.png" class="center-img" title="" />
<figcaption>
<br/>
</figcaption>
</figure>

<p>The problem with the unreliable keypresses was still there though, sometimes the emulator just wasn&rsquo;t responding at all to anything. My hunch at the time was, as there is no threading in WASM yet, there&rsquo;s probably a lot of spinning plates going on around handling <code>postMessage</code> callbacks. I don&rsquo;t know how the browser internals work on this one, but I&rsquo;d imagine they have timers and stuff going on that poll for updates.</p>

<p>So the next logical step was to see about slowing the emulator down by locking the frame rate to a maximum fixed value. The reasoning being, a slower emulator might give the browser more headroom to do its thing. I chose a 30fps lock for this test</p>

<figure>
<img src="/img/gomeboy/tetrisdx-lock-30fps-graph.png" class="center-img" title="" />
<figcaption>
<br/>
</figcaption>
</figure>

<p>A marked improvement in stability! Plus, my hunch was right, the keyboard was <em>much</em> much more reliable. Chrome continued to be in the doldrums though and still wasn&rsquo;t acknowledging input. Redoing the test with a frame rate lock of 25fps just about made the keyboard work for Chrome, but it made for pretty choppy visuals.</p>

<h3 id="conclusion">Conclusion</h3>

<p>Good god, that was a long post. Sorry.</p>

<p>This little experiment made for a fun ride. I somewhat suspect the use of web workers in this manner is definitely not what that feature was designed for. It&rsquo;s doubtful that many people would build video games in the browser in this way. A better approach might be to use WebGL, but my mental, physical and emotional strength is not there right now to open that can of worms.</p>

<p>The coolest thing about this, and maybe the promise of WASM in general, is I can send my emulator to my friends without having to worry about whether they have shared libraries on their system. I don&rsquo;t have to spin up a bunch of infrastructure to build versions for different operating systems<sup class="footnote-ref" id="fnref:5"><a href="#fn:5">5</a></sup>. It just works, and I&rsquo;m sure it&rsquo;s going to get better and better as WASM develops.</p>

<p>On the Chrome front? I don&rsquo;t know why the performance just isn&rsquo;t there for this use case. The biggest surprise to me was actually Safari, I&rsquo;m a born and bred Firefox boy and Safari never really made it into my esteemed browser list. Good job Apple 👍</p>

<p>If I were to do this again I&rsquo;d probably take a second look at <a href="https://github.com/torch2424/wasmBoy">wasmBoy</a> to see how they&rsquo;re doing it, it doesn&rsquo;t look like they are using web workers, and are using HTML5 canvas to render the output so I&rsquo;ve probably made a few missteps somewhere.</p>

<p>If you want to see the code, I have a few repos. A benefit of doing refactoring work was it allowed me to decouple the &lsquo;frontend&rsquo; and &lsquo;backend&rsquo; bits of the codebase. So anyone can write a frontend that handles the screen display and keyboard controls which hooks into the backend <a href="https://github.com/djhworld/gomeboycolor">where the emulator logic is</a>. The frontends I&rsquo;ve written so far can be found here:</p>

<ul>
<li><a href="https://github.com/djhworld/gomeboycolor-wasm">gomeboycolor-wasm</a> - The WASM version described in this blog post</li>
<li><a href="https://github.com/djhworld/gomeboycolor-glfw">gomeboycolor-glfw</a> - This is what my emulator was originally written for, and uses GLFW to render the screen to a window</li>
<li><a href="https://github.com/djhworld/gomeboycolor/tree/master/_examples">gomeboycolor/_examples</a> - For fun I wrote an example frontend that renders in the terminal. It&rsquo;s playable to a degree!<br /></li>
</ul>

<p>Thank you for reading, happy WASM&rsquo;ing.</p>

<p><a name="edit-2018-09-02"></a>
<small class="edits"><strong>UPDATE - 2018-09-22:</strong> <em>Since this post was written, <a href="https://twitter.com/JohanBrandhorst">Johan Brandhorst</a> on the gophers slack #webassembly channel gave me some tips on how to avoid the base64 hack with some tweaks to the Go code, turns out it is possible to use transferable on the <code>postMessage</code> call after all - see <a href="https://github.com/djhworld/gomeboycolor-wasm/commit/4f9933fd0fab6d9f310776d89f8c296e72af1c9a">this commit</a> for the changes!</em></a></p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1">Nintendo have been very active recently shutting down ROM sites, and threatening legal action. So I&rsquo;m not going to risk hosting the ROMs myself.
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
<li id="fn:2"><a href="http://sethgodin.typepad.com/.a/6a00d83451b31569e2019aff29b7cd970c-800wi">http://sethgodin.typepad.com/.a/6a00d83451b31569e2019aff29b7cd970c-800wi</a>
 <a class="footnote-return" href="#fnref:2"><sup>[return]</sup></a></li>
<li id="fn:3">Save data was written to memory and kept active via a battery. Which was cool <a href="https://medium.com/@paul.klingelhuber/reviving-gameboy-games-save-feature-4151c9b7273b">until the battery ran out of power</a>&hellip;
 <a class="footnote-return" href="#fnref:3"><sup>[return]</sup></a></li>
<li id="fn:4"><a href="https://en.wikipedia.org/wiki/Game_Boy#Technical_specifications">https://en.wikipedia.org/wiki/Game_Boy#Technical_specifications</a>
 <a class="footnote-return" href="#fnref:4"><sup>[return]</sup></a></li>
<li id="fn:5">While Go supports cross compiling, it gets tricky if you have CGO bindings to shared libraries like <code>libSDL</code> or <code>libGLFW</code>
 <a class="footnote-return" href="#fnref:5"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>I thought I found a browser security bug</title>
      <link>https://djharper.dev/post/2018/08/12/i-thought-i-found-a-browser-security-bug/</link>
      <pubDate>Sun, 12 Aug 2018 09:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2018/08/12/i-thought-i-found-a-browser-security-bug/</guid>
      <description>A few weeks ago I thought I&amp;rsquo;d stumbled across something really bad when just casually browsing the web. It all started on a financial information website, upon clicking a link, the page partially loaded some of its content, then, without warning, redirected the browser to a completely different domain with some weird spam/search engine content on it, from a known domain squatter.
Strange&amp;hellip;
After refreshing a few times, it was still doing it.</description>
      <content:encoded>
      <![CDATA[
        

<p>A few weeks ago I thought I&rsquo;d stumbled across something really bad when just casually browsing the web. It all started on a financial information website, upon clicking a link, the page partially loaded some of its content, then, without warning, redirected the browser to a completely different domain with some weird spam/search engine content on it, <a href="https://dyn.com/blog/ne-body-out-there/">from a known domain squatter</a>.</p>

<p><em>Strange&hellip;</em></p>

<p>After refreshing a few times, it was still doing it. Oddly, this behaviour seemed to only appear in Firefox; Chrome and Safari did not exhibit the same. This was a Firefox thing, I was sure of it!</p>

<p>After digging into the source of the affected website, it became apparent that something seemed off with this <code>&lt;embed&gt;</code> tag for flash content:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-html" data-lang="html">&lt;<span style="color:#268bd2;font-weight:bold">embed</span> 
  <span style="color:#268bd2">id</span>=<span style="color:#2aa198">&#34;button&#34;</span> 
  <span style="color:#268bd2">src</span>=<span style="color:#2aa198">&#34;http://&lt;squatters-domain&gt;/zclip/js/ZeroClipboard.swf&#34;</span> 
  <span style="color:#268bd2">type</span>=<span style="color:#2aa198">&#34;application/x-shockwave-flash&#34;</span>&gt;
&lt;/<span style="color:#268bd2;font-weight:bold">embed</span>&gt;</code></pre></div>
<figcaption>Always host your own</figcaption>
</figure>

<p>They had hardcoded a URL to a SWF file on a domain since taken over by a squatter. A quick look on <a href="https://archive.org">waybackmachine</a> suggested the previous owner was a developer hosting some code they had written, but had since let the domain expire.</p>

<p>So instead, the browser gets whatever is now being served. This is bad in itself because they could craft a malicious SWF file to do nefarious things if they were really inclined.</p>

<p>However, even if they did return a SWF file, it didn&rsquo;t explain why the redirect  was occurring, because I don&rsquo;t have Flash installed on my computer.</p>

<h3 id="flash-or-no-flash">Flash or no flash</h3>

<p>This is where I begun to think this was a security issue, because the response for the &lsquo;swf&rsquo; request was HTML with some javascript in it, with the smoking gun being right here:</p>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-javascript" data-lang="javascript"><span style="color:#859900">if</span> (<span style="color:#268bd2">top</span>.<span style="color:#268bd2">location</span> != <span style="color:#268bd2">location</span>){
  <span style="color:#268bd2">top</span>.<span style="color:#268bd2">location</span>.<span style="color:#268bd2">href</span> = <span style="color:#268bd2">location</span>.<span style="color:#268bd2">protocol</span> + <span style="color:#2aa198">&#34;//&#34;</span> 
                        + <span style="color:#268bd2">location</span>.<span style="color:#268bd2">host</span> 
                        + <span style="color:#268bd2">location</span>.<span style="color:#268bd2">pathname</span>;
}
</code></pre></div>
<figcaption>Get the browser to it!</figcaption>
</figure>

<p>Setting <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/top">top.location.href</a> was causing the redirect to happen.</p>

<p><em>So wait a minute, the affected website had an <code>&lt;embed&gt;</code> tag in it, expecting some flash content, but got served some HTML+Javascript instead, which it embedded and executed? That seemed weird fallback behaviour to me.</em></p>

<p>I raised this on <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1473833">bugzilla</a> because at the time my initial thought was this was only affecting Firefox.</p>

<p>After writing a small application to mimic the issue, the browsers behaved differently when it came to setting the <code>type</code> to <code>application/x-shockwave-flash</code><sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup></p>

<ul>
<li>Chrome displayed a &ldquo;click here to run flash&rdquo; placeholder

<ul>
<li>After enabling flash, it did not embed the response (i.e. it did nothing)</li>
</ul></li>
<li>Firefox embedded the response so executed the javascript</li>
<li>Safari displayed a &ldquo;Missing Plug-in&rdquo; placeholder</li>
</ul>

<figure>
    <img style="margin-left:auto; margin-right:auto;display:block" src="/img/safari-missing.png" title="Safari" />
    <img style="width:250px; margin-left:auto; margin-right:auto;display:block" src="/img/chrome-placeholder.png" title="Chrome" />
<figcaption style="margin-top:1.0rem">Examples of the placeholders in Safari/Chrome</figcaption>
</figure>

<p>After further investigation it turns out the &ldquo;problem&rdquo; can be replicated in Chrome too, using other MIME types like <code>type=&quot;video/webm&quot;</code>. In this particular case the behaviour changed slightly</p>

<ul>
<li>Chrome embedded the response so executed the javascript</li>
<li>Firefox embedded the response so executed the javascript</li>
<li>Safari displayed a &ldquo;Missing Plug-in&rdquo; placeholder</li>
</ul>

<p>I tried this with a few different MIME types and got differing results. You can see this in action on this <a href="https://staging.public.djharper.co.uk/demos/embed-issue/demo">demo page I built</a>. Try it out on different browsers to see how they behave. Note if you have Flash or Quicktime installed, then you will see different results.</p>

<h3 id="embed-ain-t-so-simple">Embed ain&rsquo;t so simple</h3>

<p>One of the purposes of the <code>&lt;embed&gt;</code> tag is to allow third party plug-ins such as Flash or Quicktime to be embedded into a page, but as these have fallen out of favour over the years it appears the general advice is <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed">&ldquo;don&rsquo;t use it&rdquo;</a></p>

<blockquote>
<p><em>Keep in mind that most modern browsers have deprecated and removed support for browser plug-ins, so relying upon <embed> is generally not wise if you want your site to be operable on the average user&rsquo;s browser</em><sup class="footnote-ref" id="fnref:2"><a href="#fn:2">2</a></sup></p>
</blockquote>

<p>However, clearly the browsers seem to behave differently with the <code>type</code> attribute, and how they handle the response. While Safari seems to be staunchly against doing anything without a plugin installed for the <code>type</code>, Firefox and Chrome are a bit more of a mixed bag.</p>

<h3 id="results">Results</h3>

<p>The <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1473833">firefox bug</a> engendered some really interesting discussion, and their engineers <a href="https://phabricator.services.mozilla.com/D2542">implemented a patch</a> to change this behaviour in Firefox, <em>possibly</em> scheduled for version 63! They also <a href="https://github.com/whatwg/html/issues/3876">raised a spec bug on the HTML standards github</a> around this to get some clarity on this issue. I thought that was really cool!</p>

<p>On the Chrome side of things, I <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=860782#c2">filed a similar report</a>, but they closed it, saying it was a larger problem with the web in general. Very polite and curt response, but fair enough.</p>

<blockquote>
<p><em>You are correct that there can be a vector for crypto-miners, or possibly deceptive messaging to a user, but that is a larger problem on the web. When content is loaded there are signals as to what type of content it is, but we don&rsquo;t constrain it based the the extension of the resource file (i.e. &lsquo;.webm&rsquo;).</em></p>
</blockquote>

<p>As for the financial website? I eventually got a contact in their information security department and filed the issue with them. They&rsquo;ve since fixed it by removing the hardcoded dependency on that domain and are hosting the SWF file themselves.</p>

<h3 id="is-it-a-security-bug">Is it a security bug?</h3>

<p>I started out this journey just bumbling around on the web, and ended up encountering some behaviour that didn&rsquo;t seem right.</p>

<p>But after thinking about it, I&rsquo;m in two minds.</p>

<p>On the one hand, the <code>&lt;embed&gt;</code> tag is doing its job, embedding stuff. The question is, if you specify the <code>type</code> attribute with a valid MIME type, <a href="https://github.com/whatwg/html/issues/3876">what should the browser do</a>, and what should the fallback case be if the requested plugin cannot be handled or is missing?</p>

<p>You could argue that this is a non-issue that just stems from questionable coding practises, especially around hardcoding dependencies to third party sites that you cannot trust.<sup class="footnote-ref" id="fnref:3"><a href="#fn:3">3</a></sup> However there is a bit of me that wonders if there are dark corners of websites out there that are embedding video<sup class="footnote-ref" id="fnref:4"><a href="#fn:4">4</a></sup> or flash content from domains that have since changed owners, or started returning dodgy responses.</p>

<p>Clearly the domain squatter is setting <code>top.location.href</code> for a reason, which makes me think they already know about this behaviour and are using it as a way to drive the browser into redirecting to their content. Other nefarious actors could use this &lsquo;feature&rsquo; to silently embed cryptominers, or craft redirects to phishing websites, but it&rsquo;s quite an involved process that relies on the &lsquo;victims&rsquo; website embedding content from elsewhere, so probably not a high risk.</p>

<p>It was an interesting ride anyway.</p>

<p><small class="edits"><strong>UPDATE - 2018-08-13:</strong> <em>The Chromium team kindly relaxed the viewing permissions on the <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=860782#c2">bug I had filed</a> (it was in their security space which is understandably restricted), so I&rsquo;ve updated the post to include the link</em></small></p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1">I didn&rsquo;t have time to test Microsoft Edge, or Opera. It&rsquo;s safe to assume Internet Explorer has weird behaviour though.
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
<li id="fn:2">Quoted from <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/embed">MDN web docs - <embed>: The Embed External Content element</a>
 <a class="footnote-return" href="#fnref:2"><sup>[return]</sup></a></li>
<li id="fn:3"><a href="https://www.troyhunt.com/the-javascript-supply-chain-paradox-sri-csp-and-trust-in-third-party-libraries">The JavaScript Supply Chain Paradox: SRI, CSP and Trust in Third Party Libraries</a> -  troyhunt.com
 <a class="footnote-return" href="#fnref:3"><sup>[return]</sup></a></li>
<li id="fn:4">Given the general advice for audio/video is <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Supported_media_formats">to use the HTML5 media elements</a>, this is probably a legacy issue.
 <a class="footnote-return" href="#fnref:4"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Using HyperLogLog in production, a retrospective</title>
      <link>https://djharper.dev/post/2018/03/29/using-hyperloglog-in-production-a-retrospective/</link>
      <pubDate>Thu, 29 Mar 2018 12:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2018/03/29/using-hyperloglog-in-production-a-retrospective/</guid>
      <description>A few years ago I was involved in a project that required us to provide a time series metric on how many concurrent users were using our products, and what quality of service they were receiving.
On embarking on this journey, it quickly became apparent that the tricky part would be doing the count of unique concurrent users, over a set of dimensions in one minute windows. We&amp;rsquo;d run into the classic count-distinct problem.</description>
      <content:encoded>
      <![CDATA[
        <p>A few years ago I was involved in a project that required us to provide a time series metric on how many concurrent users were using our products, and what quality of service they were receiving.</p>

<p>On embarking on this journey, it quickly became apparent that the tricky part would be doing the count of unique concurrent users, over a set of dimensions in one minute windows. We&rsquo;d run into the classic <a href="https://en.wikipedia.org/wiki/Count-distinct_problem">count-distinct problem</a>.</p>

<p>Our dataset was stored in Amazon S3, with <a href="https://prestodb.io/">PrestoDB</a> as our query backend, and the query we were running was slow and difficult to scale. As you can imagine, running <code>count(distinct)</code> on thousands of anonymous session IDs across millions of records isn&rsquo;t an easy problem to solve. Well, until someone pointed us in the direction of the <a href="https://prestodb.io/docs/current/functions/aggregate.html#approx_distinct"><code>approx_distinct</code></a> function.</p>

<p><em><strong>tl;dr:</strong> We tried using <a href="https://en.wikipedia.org/wiki/HyperLogLog">HyperLogLog</a> over fine grained time series data in production, but at the time it was not working as well as we&rsquo;d hoped. I wanted to explore HyperLogLog further though as I thought it was cool, so spent some time building <a href="https://djharper.dev/demos/hyperloglog/">HyperLogLog Playground</a>, a website to help visualise what this cool algorithm is doing under the hood.</em></p>

<p>By trading away some degree of accuracy, <code>approx_distinct</code> gave us a result in a much quicker time, with what looked like a reasonable trade off for production use. However, after a few weeks, it was becoming obvious that, while the speed of the query was satisfactory, sometimes the result added noise. You would see that over one minute windows, the line on our graphs would deviate in a sporadic fashion.</p>

<p>This meant users were questioning the validity of the data, was it the error in the approximation, or were there user impacting problems? In this case, <em>at the time</em> it seemed the approximate count was the wrong choice for fine grained time series analysis, and meant we had to pivot towards a streaming data pipeline that would give a fully accurate picture, at the cost of latency, added infrastructure and complexity.</p>

<p>Since then, it looks like the Presto contributors <a href="https://github.com/prestodb/presto/pull/9397">implemented a change</a> to increase the precision options of <code>approx_distinct</code>, so this might have alleviated our noise problem, but we had moved on by that point. Our users liked having a near real time, accurate figure from the new pipeline.</p>

<p>So while, at the time, <code>approx_distinct</code> did not work for us, I was still intrigued by what <code>approx_distinct</code> was actually doing. What made it so quick? How could it get so close to the actual figure? This line of questioning inspired me to create a small website called <a href="https://djharper.dev/demos/hyperloglog/">HyperLogLog Playground</a> where I could somewhat consolidate my understanding, while learning a bit of Javascript on the way. It was a fun exercise and I hope people like it.</p>

<p><img src="/img/hyperloglog-playground.png" title="hyperloglog playground" style="max-width:40%;" />
<img src="/img/hyperloglog-playground-add.png" title="hyperloglog playground" style=" max-width:40%;" /></p>

<p>Whenever you are presented with a count distinct problem, it&rsquo;s always worth keeping HyperLogLog in mind as a tool to use, but just be 100% sure that the small loss of accuracy is tolerable, and for time series data make sure you test it against a variety of scenarios.</p>

      ]]>
      </content:encoded>
    </item>
    
    <item>
      <title>Running Go AWS Lambda functions locally</title>
      <link>https://djharper.dev/post/2018/01/27/running-go-aws-lambda-functions-locally/</link>
      <pubDate>Sat, 27 Jan 2018 12:00:00 +0000</pubDate>
      
      <guid>https://djharper.dev/post/2018/01/27/running-go-aws-lambda-functions-locally/</guid>
      <description>AWS recently announced Go support for Lambda, giving developers more choice over how their functions are written.
In an attempt to kick the tires of the new runtime, I found myself rummaging around the open source library required when writing Lambda functions in Go, and was delighted to find a glimpse into what happens when your function is invoked. This post is a brief tour of what I’ve gathered, and describes a simple way of invoking your function in a local environment.</description>
      <content:encoded>
      <![CDATA[
        

<p>AWS <a href="https://aws.amazon.com/about-aws/whats-new/2018/01/aws-lambda-supports-go/">recently announced</a> Go support for Lambda, giving developers more choice over how their functions are written.</p>

<p>In an attempt to kick the tires of the new runtime, I found myself rummaging around <a href="https://github.com/aws/aws-lambda-go">the open source library</a> required when writing Lambda functions in Go, and was delighted to find a glimpse into what happens when your function is invoked. </p>

<p>This post is a brief tour of what I’ve gathered, and describes a simple way of invoking your function in a local environment.</p>

<p><em><strong>tl;dr:</strong> AWS Go Lambdas are invoked using net/rpc over TCP and make use of the Go standard library. You can &ldquo;simulate&rdquo; a lambda being invoked, which could be useful for integration tests or sanity checking, <a href="#testing-locally">see below</a> for an example</em></p>

<h2 id="background">Background</h2>

<p>A Go lambda needs two things to run</p>

<ol>
<li>A handler to handle requests</li>
<li>A main function that calls <code>lambda.Start(...)</code> with your handler as an argument</li>
</ol>

<figure>
<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#dc322f;font-weight:bold">package</span> <span style="color:#268bd2">main</span>

<span style="color:#dc322f;font-weight:bold">import</span> (
    <span style="color:#2aa198">&#34;strings&#34;</span>
    <span style="color:#2aa198">&#34;github.com/aws/aws-lambda-go/lambda&#34;</span>
)
 
<span style="color:#859900">func</span> <span style="color:#268bd2">ToUpperHandler</span>(<span style="color:#268bd2">input</span> <span style="color:#859900;font-weight:bold">string</span>) (<span style="color:#859900;font-weight:bold">string</span>, <span style="color:#859900;font-weight:bold">error</span>) {
    <span style="color:#859900">return</span> <span style="color:#268bd2">strings</span>.<span style="color:#268bd2">ToUpper</span>(<span style="color:#268bd2">input</span>), <span style="color:#859900;font-weight:bold">nil</span>
}
 
<span style="color:#859900">func</span> <span style="color:#268bd2">main</span>() {
    <span style="color:#268bd2">lambda</span>.<span style="color:#268bd2">Start</span>(<span style="color:#268bd2">ToUpperHandler</span>)
}</code></pre></div>
<figcaption>The simplest of functions that converts a string to uppercase</figcaption>
</figure>

<p><code>lambda.Start(ToUpperHandler)</code> is where the magic happens, it is a blocking call that will block until the process is killed or an exception is propagated that cannot be handled.</p>

<p>This gives us our first clue that AWS isn’t simply just running your go binary every time a function is invoked, it’s actively listening for requests and passing them to your handler. </p>

<p>Doing it this way has a few performance benefits, it allows you to set up expensive, thread safe dependencies up front so they are warm if your function is called more than once<sup class="footnote-ref" id="fnref:1"><a href="#fn:1">1</a></sup>.</p>

<h2 id="looking-underneath">Looking underneath</h2>

<p>Taking a look at the <a href="https://github.com/aws/aws-lambda-go/">code under the hood</a>, we can see that it:</p>

<ul>
<li><a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/entry.go#L39">Creates a TCP server</a> listening on a port defined by the environment variable <code>_LAMBDA_SERVER_PORT</code></li>
<li>Uses the <a href="https://golang.org/pkg/net/rpc/">net/rpc</a> package to handle <code>Ping</code> and <code>Invoke</code> requests from remote clients</li>
<li>Uses the <a href="https://golang.org/pkg/context/">context</a> package to store and manage state </li>
<li>Uses the <a href="https://golang.org/pkg/encoding/json/">encoding/json</a> package to perform SerDe for objects you pass to and from your function</li>
</ul>

<p>The use of net/rpc is really neat in a way, just plain old Go standard library code.</p>

<h2 id="remote-calls">Remote calls</h2>

<p>The two methods you can call via RPC are in <a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/function.go#L24">function.go</a> and they allow remote clients to:</p>

<ul>
<li>Perform <a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/function.go#L19"><code>Ping</code></a> requests by sending a <a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/messages/messages.go#L5"><code>*messages.PingRequest</code></a> object, and unsurprisingly this does exactly what it says on the tin. I&rsquo;m assuming AWS use this to check liveliness of your function and whether it is still reachable wherever they are hosting it.</li>
<li>Perform <a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/function.go#L24"><code>Invoke</code></a> requests by sending a <a href="https://github.com/aws/aws-lambda-go/blob/d050bf32adc65f57141c11e49a34b67610a7c45d/lambda/messages/messages.go#L16"><code>*messages.InvokeRequest</code></a> object,

<ul>
<li>I&rsquo;m wondering what the <code>Deadline</code> attribute is for. At one point in the execution path, they use whatever this is set to with the <code>context.WithDeadline</code> function, which leads me to believe this might be the timeout you have configured against your lambda.</li>
</ul></li>
</ul>

<p>These functions will be called by something in AWS that is managing the lifetime of an invocation.</p>

<h2 id="diagram">Diagram</h2>

<p>To help visualise this, I drew this crude, overly simplified diagram that describes the above interactions. Obviously the AWS Lambda service has a lot of components and infrastructure that we are not privy to, but I think conceptually it&rsquo;s <em>mostly</em> right.</p>

<p><img src="/img/go-execution.png" title="one day I'll draw this on a whiteboard" style="max-width:120%; border-radius:3%" /></p>

<h2 id="testing-locally">Testing locally</h2>

<p>As the lambda is just listening on a port over TCP, it&rsquo;s pretty simple to test the above behaviour locally.</p>

<p>By forcing the lambda to run on a known port<sup class="footnote-ref" id="fnref:2"><a href="#fn:2">2</a></sup></p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#268bd2">_LAMBDA_SERVER_PORT</span>=<span style="color:#2aa198;font-weight:bold">8001</span> go run lambda.go</code></pre></div>

<p>And then writing a client to submit a <code>InvokeRequest</code> to it, you can successfully execute the function end-to-end, which might be useful for integration testing or whatever.</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">$ go run client.go <span style="color:#2aa198;font-weight:bold">8001</span> <span style="color:#2aa198">&#34;\&#34;daniel\&#34;&#34;</span>
<span style="color:#2aa198">&#34;DANIEL&#34;</span></code></pre></div>

<p>I&rsquo;ve created a small library imaginatively called <a href="https://github.com/djhworld/go-lambda-invoke">go-lambda-invoke</a> that wraps up this logic, meaning you can just make the following call in your code</p>

<div class="highlight"><pre style="color:#586e75;background-color:#eee8d5;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-go" data-lang="go"><span style="color:#268bd2">response</span>, <span style="color:#268bd2">err</span> := <span style="color:#268bd2">golambdainvoke</span>.<span style="color:#268bd2">Run</span>(<span style="color:#2aa198;font-weight:bold">8001</span>, <span style="color:#2aa198">&#34;daniel&#34;</span>)</code></pre></div>

<p>It probably has limited uses, in most cases just writing plain old unit tests for your logic should be sufficient, rather than testing the scaffolding AWS erects around it. However I could see it being useful if you want to test that you&rsquo;ve built a valid <code>linux</code> binary and perform some pre-deploy sanity tests or something.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Speculating over what AWS are doing under the hood with Lambda is a pastime that has circled the internet ever since it launched, and this post doesn&rsquo;t really reveal much to answer that question. Is it containers? Who knows. Probably.</p>

<p>But I think looking at how the Go programming model works, a plain old TCP server that allows RPC clients to connect to it, gives us at least a small glimpse into the interactions AWS are performing with your application.</p>
<div class="footnotes">

<hr />

<ol>
<li id="fn:1">See <a href="https://docs.aws.amazon.com/lambda/latest/dg/go-programming-model-handler-types.html#go-programming-model-handler-execution-environment-reuse">Using Global State to Maximize Performance</a>
 <a class="footnote-return" href="#fnref:1"><sup>[return]</sup></a></li>
<li id="fn:2">Note the environment variable <code>_LAMBDA_SERVER_PORT</code> might change between library versions
 <a class="footnote-return" href="#fnref:2"><sup>[return]</sup></a></li>
</ol>
</div>

      ]]>
      </content:encoded>
    </item>
    
  </channel>
</rss>
