<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Imran&#x27;s Blog</title>
    <subtitle>Stuff I feel like blogging about.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blog.imraniqbal.org/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-01-27T00:00:00+00:00</updated>
    <id>https://blog.imraniqbal.org/atom.xml</id>
    <entry xml:lang="en">
        <title>Reworking dad&#x27;s site (again)</title>
        <published>2024-01-27T00:00:00+00:00</published>
        <updated>2024-01-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/reworking-dads-site/"/>
        <id>https://blog.imraniqbal.org/reworking-dads-site/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/reworking-dads-site/">&lt;p&gt;I decided to rework my dad&#x27;s site again. But first some history:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;site-version-1&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#site-version-1&quot; aria-label=&quot;Anchor link for: site-version-1&quot;&gt;
    #
&lt;&#x2F;a&gt;
Site version 1&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Dad uploads documents via ftp to hosting company.&lt;&#x2F;li&gt;
&lt;li&gt;Files are now public and viewable.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This was his workflow for years.
The problems started when he relocated to our home country and had to pay for renewal of service.
The hosting company does not accept payment from our home country, and the conversion makes the cost too unreasonable.
This is when my dad asked me to pay for him since I am still in North America.
Since this is (and most probably will stay) static files, I thought why not take advantage of free hosting provided by forges for static pages.
This led to site version 2.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;site-version-2&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#site-version-2&quot; aria-label=&quot;Anchor link for: site-version-2&quot;&gt;
    #
&lt;&#x2F;a&gt;
Site version 2&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Dad uploads documents to my local server via sftp.&lt;&#x2F;li&gt;
&lt;li&gt;Something listens for file changes.&lt;&#x2F;li&gt;
&lt;li&gt;That something then makes a git commit and pushes it.&lt;&#x2F;li&gt;
&lt;li&gt;The forge of choice then publishes this to their static hosting site.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For the forge, I ended up going with GitLab.
I already had a GitLab account I was using some other stuff.
GitLab has a nice feature where you can give another account a &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;user&#x2F;permissions.html&quot;&gt;developer role&lt;&#x2F;a&gt;.
This ensures the following about that account:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Can not perform destructive actions on the repository.&lt;&#x2F;li&gt;
&lt;li&gt;Can not change any GitLab Pages settings.&lt;&#x2F;li&gt;
&lt;li&gt;Can push code.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This let me create a bot account that can whose credentials would come in handy for step 3 and GitLab CI would take care of 4.
Also, in some crazy scenario, if someone was able to access that account&#x27;s ssh keys or account, generating garbage commits is all they would be able to do.&lt;&#x2F;p&gt;
&lt;p&gt;My dad is unable to use (or understand) ssh keys, so his account would need to be able to login via password.
I also did not want someone who got access to his account to be able to mess with the users home directory, where the ssh keys and git repository exists.
The solution was a sftp chroot and bind mount.
With step 2 remaining, I decided to create my own program for it.
I covered this in a previous &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;sftp&#x2F;&quot;&gt;post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This ended up with its &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;sshguard&#x2F;&quot;&gt;own issues&lt;&#x2F;a&gt; which led to site 2.1.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;site-version-2-1&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#site-version-2-1&quot; aria-label=&quot;Anchor link for: site-version-2-1&quot;&gt;
    #
&lt;&#x2F;a&gt;
Site version 2.1&lt;&#x2F;h3&gt;
&lt;p&gt;This is like version 2 except instead of going over the public network step 1 is now done via tailscale (plain wireguard out of the question for similar reasons to why ssh keys aren&#x27;t viable).&lt;&#x2F;p&gt;
&lt;p&gt;And voilà!
I now have my dads work backed up in git while being invisible to him and it&#x27;s cheaper than paying the hosting company from version 1.
All is well except one big issue.
I have broken my dads workflow and subsequently reduced his enjoyment (and frequency) of working on the site.&lt;&#x2F;p&gt;
&lt;p&gt;The biggest difference between version 1 and 2, is the feedback after uploading files.
My dad works on his site by editing the html and then viewing the results AFTER uploading the files.
This works well enough for him and he was happy with it.
I ruined it with version 2.
In version 2 there is a couple minutes delay as the changes make their way through git and GitLabs CI to for publishing.
I am unsure if caching is in play here, but it would also add to the feedback time.&lt;&#x2F;p&gt;
&lt;p&gt;I should be able to do better (and I did! hence this post), which led to site version 3.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;site-version-3-now-with-100-more-nix&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#site-version-3-now-with-100-more-nix&quot; aria-label=&quot;Anchor link for: site-version-3-now-with-100-more-nix&quot;&gt;
    #
&lt;&#x2F;a&gt;
Site version 3 (now with 100% more nix)&lt;&#x2F;h3&gt;
&lt;p&gt;My requirements for site version 3 were:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Immediate feedback from version 1&lt;&#x2F;li&gt;
&lt;li&gt;All the good stuff from version 2&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This led me to the following solution:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Cheap VPS&lt;&#x2F;li&gt;
&lt;li&gt;Caddy for serving files and SSL certificates&lt;&#x2F;li&gt;
&lt;li&gt;sftp&#x2F;bind mount&#x2F;etc from version 2&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The reasons I do not want to host this on my local server is that I have a residential connection and that connection&#x27;s public IP changes from time to time.
I didn&#x27;t want to complicate this by throwing in dynamic DNS as well.&lt;&#x2F;p&gt;
&lt;p&gt;For the cheap VPS I went with Hetzner which offers a 2 core ARM virtual server with 4GB of ram, 40GB of disk space and 20TB of egress for € 3.3 a month.&lt;&#x2F;p&gt;
&lt;p&gt;I composed site version 2 out of a bunch of custom systemd jobs, a custom binary, a custom user account and some custom ssh configuration.
The remaining thing that needs to change is to include caddy for hosting the files out of the sftp chroot.
I did not want to manage a bunch of bespoke configuration files and settings, which led me to Nix and Guix.&lt;&#x2F;p&gt;
&lt;p&gt;I chose Nix for a couple reasons.
Hetzner already provides Nix ISO&#x27;s in their web UI.
Nix builds upon systemd (which also allows it to infect other host distributions).
I am a fan that Guix uses guile as opposed to its own bespoke language, but I am already spending enough innovation tokens on Nix to give up systemd too.&lt;&#x2F;p&gt;
&lt;p&gt;I won&#x27;t bore you with what Nix&#x2F;Guix is, but the nice thing is that I now have a file that can recreate the site version 3 from scratch on a new system.
I am quite suprised by just how easy the process was.
I hit one snag with my custom ssh configuration.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;openssh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;enable &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;settings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;PasswordAuthentication &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;settings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;KbdInteractiveAuthentication &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;settings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;PermitRootLogin &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;no&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;extraConfig &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;  Match User &amp;lt;dads account&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      ChrootDirectory &amp;lt;dads sftp jail directory&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      ForceCommand internal-sftp
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      PasswordAuthentication yes
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;      MaxAuthTries 6
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;  Match all
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By setting &lt;code&gt;PasswordAuthentication&lt;&#x2F;code&gt; to &lt;code&gt;false&lt;&#x2F;code&gt;, Nix &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;592047fc9e4f7b74a4dc85d1b9f5243dfe4899e3&#x2F;nixos&#x2F;modules&#x2F;services&#x2F;networking&#x2F;ssh&#x2F;sshd.nix#L621-L625&quot;&gt;disables unix authentication for PAM&lt;&#x2F;a&gt;.
This means that &lt;code&gt;&amp;lt;dads account&amp;gt;&lt;&#x2F;code&gt; will never be able to login with the password I set.
I rectified it by adding this line to my configuration.nix:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;nix&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-nix &quot;&gt;&lt;code class=&quot;language-nix&quot; data-lang=&quot;nix&quot;&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# Allows the sftp stuff about to work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;security&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;pam&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;sshd&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;unixAuth &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;mkForce true&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;thoughts-on-nix-guix&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#thoughts-on-nix-guix&quot; aria-label=&quot;Anchor link for: thoughts-on-nix-guix&quot;&gt;
    #
&lt;&#x2F;a&gt;
Thoughts on Nix&#x2F;Guix&lt;&#x2F;h3&gt;
&lt;p&gt;I like them.&lt;&#x2F;p&gt;
&lt;p&gt;Some time during setup, I managed to get to a wonky state where all environment variables were unset.
This caused every nix command to fail saying something about an undefined nix path environment variable.
I started panicking but then some googling said to set variable to the nix store, and the rollback command worked and I was back to a golden state.&lt;&#x2F;p&gt;
&lt;p&gt;The like hit when I wanted to view metrics on the VPS using prometheus&#x2F;grafana.
The diff to add that to my configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;diff&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-diff &quot;&gt;&lt;code class=&quot;language-diff&quot; data-lang=&quot;diff&quot;&gt;&lt;span&gt;diff --git a&#x2F;configuration.nix b&#x2F;configuration.nix
&lt;&#x2F;span&gt;&lt;span&gt;index 4dc89e6..7c33b0c 100644
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;--- a&#x2F;configuration.nix
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;+++ b&#x2F;configuration.nix
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;@@ -93,6 +93,7 @@
&lt;&#x2F;span&gt;&lt;span&gt;   systemd.services.tailscaled.serviceConfig = {
&lt;&#x2F;span&gt;&lt;span&gt;       Environment = [
&lt;&#x2F;span&gt;&lt;span&gt;          &amp;quot;TS_NO_LOGS_NO_SUPPORT=true&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+         &amp;quot;TS_PERMIT_CERT_UID=caddy&amp;quot; # let caddy get https certs
&lt;&#x2F;span&gt;&lt;span&gt;       ];
&lt;&#x2F;span&gt;&lt;span&gt;       LogNamespace = &amp;quot;shadow-realm&amp;quot;;
&lt;&#x2F;span&gt;&lt;span&gt;   };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;@@ -123,6 +124,17 @@
&lt;&#x2F;span&gt;&lt;span&gt;     &amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;   };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+  services.prometheus = {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+    exporters = {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+      node = {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+        enable = true;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+        enabledCollectors = [ &amp;quot;systemd&amp;quot; ];
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+        port = 9100;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+      };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+    };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+  };
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+
&lt;&#x2F;span&gt;&lt;span&gt;   # Allows the sftp stuff about to work
&lt;&#x2F;span&gt;&lt;span&gt;   security.pam.services.sshd.unixAuth = lib.mkForce true;
&lt;&#x2F;span&gt;&lt;span&gt;   security.doas.enable = true;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c594c5;&quot;&gt;@@ -227,6 +239,14 @@
&lt;&#x2F;span&gt;&lt;span&gt;       file_server
&lt;&#x2F;span&gt;&lt;span&gt;       root * &amp;lt;sftp jail&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;     &amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+    virtualHosts.&amp;quot;&amp;lt;tailscale host name&amp;gt;&amp;quot;.extraConfig = &amp;#39;&amp;#39;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+      handle &#x2F;metrics&#x2F;node {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+        rewrite * &#x2F;metrics
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+        reverse_proxy localhost:${toString config.services.prometheus.exporters.node.port}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+      }
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;+    &amp;#39;&amp;#39;;
&lt;&#x2F;span&gt;&lt;span&gt;   };
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;   # I don&amp;#39;t want to install home manager for this
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This small diff ensures the following:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Node exported up and running&lt;&#x2F;li&gt;
&lt;li&gt;Caddy and tailscale work together to ensure HTTPS on the private tailscale url&lt;&#x2F;li&gt;
&lt;li&gt;Caddy makes node exporter metrics available under &#x2F;metrics&#x2F;node&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I then had to go to my local server (running arch btw) and install prometheus and grafana.
I had two options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use containers (which I am starting to hate and deserve their own rant blog post)&lt;&#x2F;li&gt;
&lt;li&gt;Use system packages&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I opted for option 2, and made sure to copy the prometheus configuration to my git repository of rag tag files needed to recreate this server.
Compared to using Nix it feels so archaic.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not all sunshines and rainbows though, there are things I am not a fan of:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;(Nix) Most tutorials are out of date&lt;&#x2F;li&gt;
&lt;li&gt;(Nix) NixLang is ¯\_(ツ)_&#x2F;¯ (could lua have worked?)&lt;&#x2F;li&gt;
&lt;li&gt;(Guix) Harder stance on Free packages (but it&#x27;s super easy to add your own channel&#x2F;packages)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Those are incredibly minor compared to the value these projects provide.
In the future I am hoping to get rid of containers&#x2F;docker on my local server and replace it entirely with Guix, and moving my desktop to one of them.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>WTF Ubiquiti</title>
        <published>2024-01-23T00:00:00+00:00</published>
        <updated>2024-01-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/wtf-ubiquiti/"/>
        <id>https://blog.imraniqbal.org/wtf-ubiquiti/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/wtf-ubiquiti/">&lt;p&gt;This is a rant.&lt;&#x2F;p&gt;
&lt;p&gt;My previous AP was the Unifi AP AC-lite.
I bought it close to 10 years ago now.
I set it up once and never touched it again, until I had to &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;unifi-software&#x2F;&quot;&gt;change my SSID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to buy the UC6+ as the price is right, I already have POE available, it &lt;em&gt;should&lt;&#x2F;em&gt; just be a straight upgrade.
The setup went smoother than last time.
I now have 2 access points so using the mobile app was actually viable.
I put in the SSID&#x2F;password and a new username&#x2F;password combination for login (after dodging the create cloud account prompt).
The new AP rebooted, I unplugged the old AP, everything was fine and dandy.&lt;&#x2F;p&gt;
&lt;p&gt;SIKE!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;uc6_shit.png&quot; alt=&quot;picture of the stupid AP spamming DNS requests&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For some stupid ass reason (adoption?) the AP (at IP 10.0.0.3) spams for the host &lt;code&gt;unifi&lt;&#x2F;code&gt; 85 THOUSAND TIMES a day.
That is almost one look up every second.
For reference the blurred out entry below the AP is my main computer at a modest 3 thousand requests.
At least program in some exponential back off.&lt;&#x2F;p&gt;
&lt;p&gt;According to the ubiquiti subreddit this is no big issue and it&#x27;s okay for the AP make these requests.
For reference the previous AP never did this.
This is newer behaviour on newer firmware.
To make this device shut up, a controller must pair and adopt it.
As pointed out in the linked blog post at the start, I do not have a unifi controller.&lt;&#x2F;p&gt;
&lt;p&gt;The docker image for the all in one controller is no longer maintained, but it still works.
I had to jump through the same hoops making it clear that I do not want a cloud account.
The AP shut up after adoption, but then started trying to phone home to the mothership at &lt;code&gt;trace.svc.ui.com&lt;&#x2F;code&gt;.
It continues to try and do that every 5 minutes.&lt;&#x2F;p&gt;
&lt;p&gt;The real kicker comes when you shut down the controller.
The AP which is no longer able to communicate with the controller (even though it&#x27;s in dumb standalone mode) will continue to spam requests to &lt;code&gt;unifi&lt;&#x2F;code&gt; (every 5 seconds) and &lt;code&gt;trace.svc.ui.com&lt;&#x2F;code&gt; (every 5 minutes).&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t want to fight with my network hardware.
I don&#x27;t it to phone &amp;quot;home&amp;quot;.
I don&#x27;t want it part of a cloud offering.
I just need it to take the public connection from my ISP and make it available to my devices.&lt;&#x2F;p&gt;
&lt;p&gt;I guess this is my fault for buying ubiquiti, but they used to be so good and there is no real pro-sumer alternative.
The silver lining to all this is that I can &lt;a href=&quot;https:&#x2F;&#x2F;forum.openwrt.org&#x2F;t&#x2F;adding-support-for-ubiquiti-unifi-6-plus-u6&#x2F;170072&quot;&gt;flash the AP with OpenWrt&lt;&#x2F;a&gt; but I&#x27;ll need to get a USB&#x2F;UART adapter.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Year in review</title>
        <published>2023-12-31T00:00:00+00:00</published>
        <updated>2023-12-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/2023-review/"/>
        <id>https://blog.imraniqbal.org/2023-review/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/2023-review/">&lt;p&gt;I thought I should do a recap for this year.
Though also for some typing practice.&lt;&#x2F;p&gt;
&lt;p&gt;I started this year unemployed, and still burned out from the last job.
I do not remember the states of my finances, but I do get a new job later on&lt;&#x2F;p&gt;
&lt;p&gt;In no particular order:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;momo&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#momo&quot; aria-label=&quot;Anchor link for: momo&quot;&gt;
    #
&lt;&#x2F;a&gt;
Momo&lt;&#x2F;h3&gt;
&lt;p&gt;I got my cat Momo.
She is on the opposite end of the personality spectrum compared to Yar.
She is confident and unfazed by new people.
She even purrs at the vet.&lt;&#x2F;p&gt;
&lt;p&gt;She is also overweight and wants to eat everything.
I had to buy some fancy food bowls that specifically open for registered chips.
It has been an ordeal trying to keep the cats from each others food.
Momo is not allowed to eat Yar&#x27;s food as she is fat and needs to lose weight.
Yar is not allowed to eat Momo&#x27;s food as he has allergies and requires medication.&lt;&#x2F;p&gt;
&lt;p&gt;She loves to play with toys.
Though so did Yar when he was her age (2).
He no longer cares much about them at 4.
We will see if she follows a similar route.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hogwarts-legacy&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#hogwarts-legacy&quot; aria-label=&quot;Anchor link for: hogwarts-legacy&quot;&gt;
    #
&lt;&#x2F;a&gt;
Hogwarts Legacy&lt;&#x2F;h3&gt;
&lt;p&gt;This game sucked ass.
Complete waste of $102 CAD.
I wish I never bought and played it.
It shouldn&#x27;t even be in this post but I want to vent about just how much of a let down that game is.
Incredible IP wasted on a subpar game.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-partner&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#my-partner&quot; aria-label=&quot;Anchor link for: my-partner&quot;&gt;
    #
&lt;&#x2F;a&gt;
My Partner&lt;&#x2F;h3&gt;
&lt;p&gt;She left Canada to pursue her PhD in the states.
I am hoping to move there too to support her.
She has been having a rough time, but I think she will pull through.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;side-stuff&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#side-stuff&quot; aria-label=&quot;Anchor link for: side-stuff&quot;&gt;
    #
&lt;&#x2F;a&gt;
Side stuff&lt;&#x2F;h3&gt;
&lt;p&gt;Activity on my usent downloader has stalled since starting a new job.
It&#x27;s hard to find time in the day to both gym and have time left over for programming after work.&lt;&#x2F;p&gt;
&lt;p&gt;I have managed to rework my dad&#x27;s site stuff for a third time, this time with NixOS.
There should be an upcoming blog post about it.&lt;&#x2F;p&gt;
&lt;p&gt;The hard part, as mentioned before, is finding the time now that I employed again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-old-team&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#my-old-team&quot; aria-label=&quot;Anchor link for: my-old-team&quot;&gt;
    #
&lt;&#x2F;a&gt;
My old team&lt;&#x2F;h3&gt;
&lt;p&gt;I got to meet my old team in person.
I quit right before there the company get together post COVID.
They are a nice and fun bunch but &amp;quot;stuck&amp;quot; at a crap company that pays too much 😉.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-new-team&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#my-new-team&quot; aria-label=&quot;Anchor link for: my-new-team&quot;&gt;
    #
&lt;&#x2F;a&gt;
My new team&lt;&#x2F;h3&gt;
&lt;p&gt;I started a new job and met my new team in person too!
The name of the place is Psiphon and they work in internet cencorship circumnavigation.
Smart folks that work primarily in go.
If there is one thing I am grateful for it&#x27;s that I wont have to touch ruby again for a long time.&lt;&#x2F;p&gt;
&lt;p&gt;Work culture at the company is unique, hands off and low meetings.
The company places a lot of trust in the employees to get the job done and I hope I don&#x27;t betray that trust.
Not fired yet.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Written in Dvorak</title>
        <published>2023-10-26T00:00:00+00:00</published>
        <updated>2023-10-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/dvorak/"/>
        <id>https://blog.imraniqbal.org/dvorak/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/dvorak/">&lt;p&gt;These past weeks I have been learning to touch type Dvorak.
I have been using the lovely tool &lt;a href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;gtypist&#x2F;&quot;&gt;&lt;code&gt;gtypist&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote this entire post using the layout.
My typing speed took a considerable hit.
I average about 80 words per minute with QWERTY, but now I am down to 15.
Here&#x27;s to hoping that it gets better with practice.&lt;&#x2F;p&gt;
&lt;p&gt;Also this is messing with my vim muscle memory.
This took a considerable time to type out.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>ZFS, video files and compression</title>
        <published>2023-09-07T00:00:00+00:00</published>
        <updated>2023-09-07T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/zfs-compression-and-video-files/"/>
        <id>https://blog.imraniqbal.org/zfs-compression-and-video-files/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/zfs-compression-and-video-files/">&lt;p&gt;Did this earlier this week and thought I would share.
My server stores video files for TV shows and movies I have watched.
Other types of files exist but it&#x27;s almost entirely video files.
These take up quite a bit of space.&lt;&#x2F;p&gt;
&lt;p&gt;The server consists of a single zpool, aptly named &amp;quot;mediafiles&amp;quot;.
ZFS supports compression, but the default is to have it off.
Enabling compression is a single command away (&lt;code&gt;man zfs-set&lt;&#x2F;code&gt; and &lt;code&gt;man zfsprops&lt;&#x2F;code&gt;).
One thing to note, compression works going forward.
This means that existing files are not compressed, but any new files written to the pool will be.
If you do want to retroactively compress files, you will need to rewrite your files to disk (by moving and unmoving).&lt;&#x2F;p&gt;
&lt;p&gt;This is what I did for my 12 TB of data.
If you are smarter than, or me from the future you would know this is an astoundingly stupid idea.
This is why:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;If you read the man page properly, a file has to be compressible by 7&#x2F;8 of its original size for ZFS to consider it for compression&lt;&#x2F;li&gt;
&lt;li&gt;Video files are already compressed, the codec determines the compression.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This means, that after I had put 12 TB of write load on my zpool, my compression remained at 1.00x.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;❯ doas zfs get all | rg compress
&lt;&#x2F;span&gt;&lt;span&gt;mediafiles                  compressratio         1.00x                  -
&lt;&#x2F;span&gt;&lt;span&gt;mediafiles                  compression           zstd                   local
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Yay 🤦.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lazy days are the best</title>
        <published>2023-07-29T00:00:00+00:00</published>
        <updated>2023-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/lazy-days/"/>
        <id>https://blog.imraniqbal.org/lazy-days/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/lazy-days/">&lt;p&gt;Today had nice weather.
Instead of doing anything I spent the day sunbathing with my cats.&lt;&#x2F;p&gt;
&lt;p&gt;Lazy days are the best.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Advertising Works</title>
        <published>2023-03-02T00:00:00+00:00</published>
        <updated>2023-03-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/advertising-works/"/>
        <id>https://blog.imraniqbal.org/advertising-works/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/advertising-works/">&lt;p&gt;It&#x27;s no secret that I &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;ads-fire-tv&#x2F;&quot;&gt;despise ads&lt;&#x2F;a&gt;.
I go out of my way to block them where and when I can.
I run a Pi-hole on my network, run uBlock Origin (and some other) extensions in my browser.
I avoid any product mentioned in a YouTube sponsored segment (y&#x27;all already know which ones I&#x27;m talking about).&lt;&#x2F;p&gt;
&lt;p&gt;One ad (that I&#x27;ve noticed so far) has managed to affect me and caused me to buy it&#x27;s product.
I was in the grocery store the other day and found myself in the ice cream aisle.
I saw a box of Klondike Reese&#x27;s ice cream bars.
I have never had a Klondike before this.
Instantly I thought of the tagline &amp;quot;What would you do for a Klondike bar?&amp;quot;.
I have never seen a Klondike ad on TV&#x2F;radio&#x2F;movie, but I had seen tonnes of memes with the tagline in it.
It has its own page on &lt;a href=&quot;https:&#x2F;&#x2F;knowyourmeme.com&#x2F;memes&#x2F;what-would-you-do-for-a-klondike-bar&quot;&gt;Know Your Meme&lt;&#x2F;a&gt;.
It&#x27;s also referred to in &lt;a href=&quot;https:&#x2F;&#x2F;genius.com&#x2F;18272&quot;&gt;some music&lt;&#x2F;a&gt;.
I thought to myself &amp;quot;why not? Let&#x27;s see if it lives up to the hype&amp;quot; and grabbed a box.&lt;&#x2F;p&gt;
&lt;p&gt;It wasn&#x27;t until I got home that I realized I had fallen for an ad.
I had fallen for an ad through memes, of all things!
For the record, it did not live up to the hype.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Not enough ram</title>
        <published>2023-01-18T00:00:00+00:00</published>
        <updated>2023-01-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/ram/"/>
        <id>https://blog.imraniqbal.org/ram/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/ram/">&lt;p&gt;I use a thinkpad flex 14 AMD laptop hooked up to my TV so that I can work in my living room.
This is to keep my cat happy, who for whatever reason, hates my office room.&lt;&#x2F;p&gt;
&lt;p&gt;This thing has about six gigabytes of ram in it and I am constantly running into linux&#x27;s oom (out of memory) killer.
I am not doing anything crazy either, in fact at the time of writing this blog post this is the output of &lt;code&gt;free -h&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;               total        used        free      shared  buff&#x2F;cache   available
&lt;&#x2F;span&gt;&lt;span&gt;Mem:           5.7Gi       3.6Gi       581Mi       120Mi       1.6Gi       1.7Gi
&lt;&#x2F;span&gt;&lt;span&gt;Swap:             0B          0B          0B
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The processes I am running:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;KDE&lt;&#x2F;li&gt;
&lt;li&gt;firefox&lt;&#x2F;li&gt;
&lt;li&gt;kitty&lt;&#x2F;li&gt;
&lt;li&gt;tmux&lt;&#x2F;li&gt;
&lt;li&gt;neovim&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Of these the biggest offender is firefox (4 tabs open by the way), the reason being web developers have lost the sauce.
Tabs are taking up more than 100mb a pop.&lt;&#x2F;p&gt;
&lt;p&gt;WTF?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Put some effort in for goodness sake</title>
        <published>2022-12-06T00:00:00+00:00</published>
        <updated>2022-12-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/shit-programmers/"/>
        <id>https://blog.imraniqbal.org/shit-programmers/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/shit-programmers/">&lt;p&gt;Incredibly simple trick to figure out how lazy the programmers behind a company are, just view the transactional emails sent by said company as text instead of html.&lt;&#x2F;p&gt;
&lt;p&gt;I made the switch over to &lt;a href=&quot;https:&#x2F;&#x2F;useplaintext.email&quot;&gt;plaintext email&lt;&#x2F;a&gt; a little over a year ago and it&#x27;s just sad seeing how programmers at billion dollar companies drop the ball on this.
There will be stray html tags like &lt;code&gt;&amp;lt;ul&amp;gt;&lt;&#x2F;code&gt; or &lt;code&gt;&amp;lt;li&amp;gt;&lt;&#x2F;code&gt; inside the email, the formatting is just horrendous. Or my personal favourite, from Charles Schwab, a nicely formatted message saying that the content belongs solely to the html message.&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;schwab_sux.ef2375eaf5c438b2.png&quot;  alt=&quot;Schwab programmers sucking ass&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;If you view the raw message, you will see the access code nested inside a HUGE chunk of useless html.&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;schwab_sux2.26227824f7b747c2.png&quot;  alt=&quot;Schwab programmers sucking ass still&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;This 6 digit code needed for login is somehow magically only for html emails 🙄.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-is-this-lazy-bad&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#how-is-this-lazy-bad&quot; aria-label=&quot;Anchor link for: how-is-this-lazy-bad&quot;&gt;
    #
&lt;&#x2F;a&gt;
How is this lazy&#x2F;bad?&lt;&#x2F;h3&gt;
&lt;p&gt;Sending multipart emails is stupid simple, every web framework has an easy way to send email that has an argument for plaintext and html message bodies.
For example here is &lt;a href=&quot;https:&#x2F;&#x2F;docs.djangoproject.com&#x2F;en&#x2F;4.1&#x2F;topics&#x2F;email&#x2F;&quot;&gt;django&lt;&#x2F;a&gt;&#x27;s documentation, and here is &lt;a href=&quot;https:&#x2F;&#x2F;guides.rubyonrails.org&#x2F;action_mailer_basics.html&quot;&gt;rails&lt;&#x2F;a&gt;&#x27;.
The effort to support plaintext is negligible.
Yet somehow nearly every company and their development team manages to fuck it up.&lt;&#x2F;p&gt;
&lt;p&gt;Y&#x27;all get paid too much money to be half-assing things like this.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Thankful for offline documentation</title>
        <published>2022-12-02T00:00:00+00:00</published>
        <updated>2022-12-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/offline-docs/"/>
        <id>https://blog.imraniqbal.org/offline-docs/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/offline-docs/">&lt;p&gt;I am on a transatlantic flight from Europe to Canada.
Internet is not available.
Well, technically it&#x27;s available, but it it&#x27;s rated at 120mb for 60 Swiss Francs which is  outside of my price range.&lt;&#x2F;p&gt;
&lt;p&gt;Before the flight I downloaded the first day&#x27;s first part problem from &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;offline-docs&#x2F;adventofcode.com&quot;&gt;Advent of Code&lt;&#x2F;a&gt; (AoC).
I wanted to practice both go and rust (and ideally zig as well).
I am not well versed in any of these languages, and when working on side projects I need to google. A lot.
As mentioned in the opening paragraph that is impossible for me.
This is when I discovered that both of these wonderful languages (skipped zig for now) have their documentation available offline.&lt;&#x2F;p&gt;
&lt;p&gt;For go, you can use &lt;code&gt;go doc&lt;&#x2F;code&gt; and there is tab completion (at least on arch with fish) that lets you explore the entirety of the standard library through your console.
For rust,  there is &lt;code&gt;rustup doc&lt;&#x2F;code&gt; which opens the standard library in your browser via local files.
This also includes a couple handy books, like the cargo book and rustc book as well as rust by example (though some of the links still link to the web).
It also has a functioning search which came in handy.
I could not find a zig counterpart (at least from &lt;code&gt;man zig&lt;&#x2F;code&gt; and &lt;code&gt;zig --help&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;After creating a solution in both go and rust, I ended up committing the target directory.
I &lt;code&gt;git rm&lt;&#x2F;code&gt;&#x27;d it and wanted to add a top level rule to prevent myself from repeating this mistake for future problems for AoC.
The problem was I forgot what the glob syntax was, which I could not google, but there exists &lt;code&gt;man 5 gitignore&lt;&#x2F;code&gt; which had everything I needed in it.&lt;&#x2F;p&gt;
&lt;p&gt;The other thing I realized is my phone is pretty useless without data.
I have a single game on it that works offline and some songs downloaded on Spotify.
The game is pretty boring and can in no way fill up 8 hours of flight time.
The songs I have already heard a hundred times over.
For everything else it&#x27;s dead weight in my pocket.&lt;&#x2F;p&gt;
&lt;p&gt;A future action item for myself might be to have more offline activities to do as I forgot to pack my books.
This experience makes me thankful for useful man pages and offline docs.
It&#x27;s near impossible to do anything offline nowadays thanks to everything being a SaaS.
It also makes me a bit sad that my computer and phone are just expensive web portals.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Italian coffee kind of sucks</title>
        <published>2022-11-26T00:00:00+00:00</published>
        <updated>2022-11-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/italian-coffee/"/>
        <id>https://blog.imraniqbal.org/italian-coffee/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/italian-coffee/">&lt;p&gt;I have always wanted to go to Italy, namely for the food and the &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;i-love-coffee&#x2F;&quot;&gt;coffee&lt;&#x2F;a&gt;.
Imagine my surprise when I went to Italy (Rome + Naples) and found the coffee to not live up to its hype.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-does-it-suck&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#why-does-it-suck&quot; aria-label=&quot;Anchor link for: why-does-it-suck&quot;&gt;
    #
&lt;&#x2F;a&gt;
Why does it suck?&lt;&#x2F;h3&gt;
&lt;p&gt;Saying it sucks is a bit harsh but here are the main reasons why I did not enjoy it:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;They use robusta beans&lt;&#x2F;li&gt;
&lt;li&gt;Italians like their dark roast (even at specialty shops)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;what-s-wrong-with-robusta&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#what-s-wrong-with-robusta&quot; aria-label=&quot;Anchor link for: what-s-wrong-with-robusta&quot;&gt;
    #
&lt;&#x2F;a&gt;
What&#x27;s wrong with robusta?&lt;&#x2F;h3&gt;
&lt;p&gt;Robusta generally tastes reminds of Tim Hortons&#x2F;Starbucks coffee.
It&#x27;s woody, burnt, just dark and unpleasant.&lt;&#x2F;p&gt;
&lt;p&gt;I prefer lighter roast arabica coffee.
These tend to have that fruity fermented flavour.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-about-specialty-shops&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#what-about-specialty-shops&quot; aria-label=&quot;Anchor link for: what-about-specialty-shops&quot;&gt;
    #
&lt;&#x2F;a&gt;
What about specialty shops?&lt;&#x2F;h3&gt;
&lt;p&gt;Italy is traditional country and has been slow to adopt to the third wave coffee movement.
That said, there are still some specialty shops that you can find but as mentioned above the beans are darkly roasted, to appeal to the classic flavour Italians like.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-do-you-like-about-italian-coffee&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#what-do-you-like-about-italian-coffee&quot; aria-label=&quot;Anchor link for: what-do-you-like-about-italian-coffee&quot;&gt;
    #
&lt;&#x2F;a&gt;
What do you like about Italian coffee?&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;The culture&lt;&#x2F;li&gt;
&lt;li&gt;The price&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The biggest culture shock is that you pay after you have consumed your coffee.
You line up at the counter and place your order then go pay at the register when you finish.
Though there are some stores where you first need to buy ticket which you hand to the barista.
I have not found an easy way to discern between these two systems.&lt;&#x2F;p&gt;
&lt;p&gt;Cappuccino is for the morning, eaten with a pastry.
Then espresso (a café) for the rest of the day.
You generally have an espresso after dinner as dessert.&lt;&#x2F;p&gt;
&lt;p&gt;Back home I make myself a latte in the morning and have a decaf espresso in the evening.
I am going to start incorporating some form of sugar in the morning (maybe on the weekends) assuming I can fit it into my calorie budget.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Status update, August 2022</title>
        <published>2022-08-29T00:00:00+00:00</published>
        <updated>2022-08-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/status-update-august-2022/"/>
        <id>https://blog.imraniqbal.org/status-update-august-2022/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/status-update-august-2022/">&lt;p&gt;Damn it has been a long time since I have posted anything on my blog.
The biggest difference between then and now is that I have quit my job.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#why&quot; aria-label=&quot;Anchor link for: why&quot;&gt;
    #
&lt;&#x2F;a&gt;
Why?&lt;&#x2F;h3&gt;
&lt;p&gt;I was not happy.
For those who know me, it&#x27;s no secret that I don&#x27;t enjoy web development.
Doubly so, if its with rails.
I feel like I have not learnt anything new despite being at the job for two years.
Web development isn&#x27;t a technically deep field, once you know how to throw objects in a database and how to display them there&#x27;s not much left to do.&lt;&#x2F;p&gt;
&lt;p&gt;I am in a position where not having employment for a while is not a big deal.
This means that I can spend my time doing what I want to do, instead of using it primarily for work.
Coupled with the fact that I have my citizenship and new passport, I want to enjoy as much time off work as possible.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-comes-next&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#what-comes-next&quot; aria-label=&quot;Anchor link for: what-comes-next&quot;&gt;
    #
&lt;&#x2F;a&gt;
What comes next?&lt;&#x2F;h3&gt;
&lt;p&gt;For now, a lot of rest and relaxation.
It&#x27;s possible to enjoy the sun instead of wasting it indoors working.
I do have some side projects that I want to work on, focused on learning things I did not get the oppurtunity to work on at my previous employment.&lt;&#x2F;p&gt;
&lt;p&gt;I will be trying to write more, as now I have enough time to do so.
Plus it gives me motivation to actually complete the projects that I write about.
The act of writing things down supposedly increases the chances that those things will actually get done.&lt;&#x2F;p&gt;
&lt;p&gt;My funds will deplete, and I will need to seek employment again, but until then I want to complete as much of my projects done as possible, as well as some well deserved travelling done.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Status Update</title>
        <published>2022-05-02T00:00:00+00:00</published>
        <updated>2022-05-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/status-update/"/>
        <id>https://blog.imraniqbal.org/status-update/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/status-update/">&lt;p&gt;Well it&#x27;s been a sold three months since my last blog entry.
Not a lot has happened, but at the same time some stuff has.&lt;&#x2F;p&gt;
&lt;p&gt;Most importantly, I, at long last, have gotten my Canadian citizenship.
This has been 14 years in the making.
I am now awaiting for the processing of my passport application so that I can have that sweet Canadian traveling privilege.&lt;&#x2F;p&gt;
&lt;p&gt;With relation to my blog, there is nothing that I have felt like blogging about.
I sold my condo and moved into a rental.
I wanted to write up a post about all the lessons learnt from that ordeal, but I could not bring myself to do so.
I do not like writing, and there is nothing interesting that I have done.
This is why this post is just a status update.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;real-world-stuff&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#real-world-stuff&quot; aria-label=&quot;Anchor link for: real-world-stuff&quot;&gt;
    #
&lt;&#x2F;a&gt;
Real world stuff&lt;&#x2F;h2&gt;
&lt;ol&gt;
&lt;li&gt;I own a couch now (technically a love seat, since a couch would not fit through my door).&lt;&#x2F;li&gt;
&lt;li&gt;I own a coffee table, as well as a cabinet for the bathroom.&lt;&#x2F;li&gt;
&lt;li&gt;The gym is going well, muscle is starting to come back
&lt;ol&gt;
&lt;li&gt;bench is at a plate 25&lt;&#x2F;li&gt;
&lt;li&gt;squat is at 2 plates&lt;&#x2F;li&gt;
&lt;li&gt;deadlift is at 3 plates&lt;&#x2F;li&gt;
&lt;li&gt;overhead shoulder press is at 35&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;My weight is gradually coming down.
My goal is to have all my covid weight gone by next year.
My right arm is pretty weak compared to my left, this makes sense given my injuries.
My grip strength is preventing me from attempting higher weights (at least for deadlift).&lt;&#x2F;p&gt;
&lt;p&gt;I want to get a second cat.
I worry that Yar gets bored during the day.
I can&#x27;t play with him during work, and I am too exhausted after the gym.
His previous owners donated him to the humane society because he didn&#x27;t place nice with other pets, so getting another cat is probably not the best idea.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;digital-stuff&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#digital-stuff&quot; aria-label=&quot;Anchor link for: digital-stuff&quot;&gt;
    #
&lt;&#x2F;a&gt;
Digital stuff&lt;&#x2F;h2&gt;
&lt;p&gt;I am constantly fidgeting with my lua scripts for neovim, it&#x27;s quite fun.
I want to use fennel instead but have been procrastinating.
There&#x27;s the overhead of running a VM inside of neovim, but I can also use the AOT compilation to just output lua files.&lt;&#x2F;p&gt;
&lt;p&gt;I have had the pleasure of using Go at my day job.
After years of python and ruby, it&#x27;s a breath of fresh air.
Plus it feels like a modern day C, which earns it a lot of brownie points in my book.&lt;&#x2F;p&gt;
&lt;p&gt;Rust seems cool, but is just so darn overcomplicated.
I want to like it, but there are a couple things that just rub me the wrong way:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It feels more like C++ than C&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cargo&lt;&#x2F;code&gt; feels like &lt;code&gt;npm&lt;&#x2F;code&gt;.
To do anything useful you&#x27;ll need to &lt;code&gt;cargo add&lt;&#x2F;code&gt; your way through a lot of things.
It does not help that every guide for rust encourages this behaviour.&lt;&#x2F;li&gt;
&lt;li&gt;Code in the wild is unreadable.&lt;&#x2F;li&gt;
&lt;li&gt;The rust fan&#x27;s rub me the wrong way and treat the language the &amp;quot;end all be all&amp;quot; of programming languages.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The whole &lt;code&gt;cargo&lt;&#x2F;code&gt; aspect of rust is also what makes it hard to include in existing projects (e.g. linux).
Offline and reproducible builds are important and modern languages just poop all over it.&lt;&#x2F;p&gt;
&lt;p&gt;I have been trying to contribute packages to Guix, as well as use it more.
It&#x27;s a neat piece of tech, but not as popular as it&#x27;s counterpart, nix.
Part of my dislike of rust also comes from trying to package binaries for it, see the above point about reproducibility.&lt;&#x2F;p&gt;
&lt;p&gt;A language that excites me is Zig.
It seems to embody everything I love in a programming language, fingers crossed for the 1.0 launch.
I have an idea or two of what I would like to build with it, but between work and gym, I don&#x27;t have much time or energy to do anything in my free time.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lessons learnt from exposing my server to the internet part 2</title>
        <published>2022-02-26T00:00:00+00:00</published>
        <updated>2022-02-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/tailscale/"/>
        <id>https://blog.imraniqbal.org/tailscale/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/tailscale/">&lt;p&gt;Not too long ago, I had written about how I &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;sftp&#x2F;&quot;&gt;exposed my ssh port onto the internet&lt;&#x2F;a&gt; (for use by my dad).
I had also written about how I started experiencing &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;sshguard&#x2F;&quot;&gt;attacks on said port and what I did to harden against it&lt;&#x2F;a&gt;.
This is the next (fingers crossed, last) step in my hardening of my server from the outside world.&lt;&#x2F;p&gt;
&lt;p&gt;What had happened was a determined script kiddie wanted in to my server.
This person was trying to brute force, from seemingly infinite IP addresses.
Using an IP lookup, they were coming from cloud providers such as digital ocean and alibaba cloud.
The script kiddie was smart enough to figure out that I banned on every third failure, so they changed their attack to try twice before switching ips.
This brute force attempt went for days.
As far as I can tell they were not able to gain access to my system, after all I had disabled login passwords so without a trusted ssh key nothing was to be had.
My ssshguard blacklist grew from &amp;lt;10 to almost 1000 ips.
Since this was taking up precious CPU cycles and disk space (from logging all these attempts) I decided to move to more extreme measures.&lt;&#x2F;p&gt;
&lt;p&gt;I changed sshguard to blacklist after a single failed attempt and to remember blocked ips for 100x longer.
This solution worked for a while, but then it ended up banning my own dad.
My dad (as mentioned before) is not tech savvy.
This means that he will (and did) mess up his password, or switch around his username.
From a security POV it&#x27;s damn near impossible to tell the difference between an unlucky user trying to login versus an attacker trying to gain access to your system.
I resorted to whitelisting his IP for his specific user, and removed his IP from the sshguard blacklist (and iptables rules).
This would work until a) his IP changed or b) he inevitably messes up his password again and ends up banned again.
Neither is a perfect solution.&lt;&#x2F;p&gt;
&lt;p&gt;I had given some thought to setting up wireguard or similar VPN solution and having my dad use that, but remember a ssh key was too much for him.
Then I came across &lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;&quot;&gt;tailscale&lt;&#x2F;a&gt;.
The beauty about tailscale is that they have a generous free tier (which also allows sharing machines to other free accounts).
They also have incredibly simple to setup clients for every os.
Within a 10 minute call I was able to get my dad setup and have him access my server.
This means that I no longer need to have an ssh port exposed nor have sshguard running.
Using tailscale also means that I can ease up on my draconian sshd configurations and allow my dad more login attempts and longer login grace period, all without having to worry about attackers taking advantage of it.&lt;&#x2F;p&gt;
&lt;p&gt;Another killer feature of tailscale is that you can &lt;a href=&quot;https:&#x2F;&#x2F;tailscale.com&#x2F;kb&#x2F;1018&#x2F;acls&#x2F;?q=access#tests&quot;&gt;write tests&lt;&#x2F;a&gt; inside your access control configuration to ensure that no changes break any assumptions the tests test for.
In my case I use them to ensure that my dad can access port 22 and nothing else on my server (I have a couple other ports that I use that I do not want him to have access to).&lt;&#x2F;p&gt;
&lt;p&gt;All in all, I am kind of sad that I had to end relying on such a solution, but malicious actors are why we can&#x27;t have nice things.
Also major props to tailscale for having an install process and a client so simple that my father can install and use it without constant help.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>The pain of updating my SSID with Unifi (AP AC-lite)</title>
        <published>2022-01-14T00:00:00+00:00</published>
        <updated>2022-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/unifi-software/"/>
        <id>https://blog.imraniqbal.org/unifi-software/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/unifi-software/">&lt;p&gt;As the title suggests, I have a Unifi AP AC-lite that I use to serve wireless in my home, paired with an EdgeRouter PoE.
This is my story of trying to change my wireless SSID.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-1-the-mobile-app&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#step-1-the-mobile-app&quot; aria-label=&quot;Anchor link for: step-1-the-mobile-app&quot;&gt;
    #
&lt;&#x2F;a&gt;
Step 1. The mobile app&lt;&#x2F;h2&gt;
&lt;p&gt;Unifi provides a mobile app that should, supposedly, make managing your ubiquiti network easier.
I installed it with hopes of this being a quick and simple process (spoiler alert: it wasn&#x27;t).&lt;&#x2F;p&gt;
&lt;p&gt;The first thing the mobile app asks for is to connect to your ubiquiti controller (and ubiquiti account).
I do not have a ubiquiti controller (or account).
The app does present another option of connecting to a standalone AP (which I do have). &lt;&#x2F;p&gt;
&lt;p&gt;Upon selecting this option it presents you with a screen saying the AP needs to be in factory setup mode (glowing white ring) for the app to connect to it.
The catch-22 here is that if I reset my AP, how is my phone (and thus the app) going to connect to my network as the AP?
The AP is my sole method of providing wifi in my household.&lt;&#x2F;p&gt;
&lt;p&gt;When you factory reset the AP, it has a default network it broadcasts that requires it MAC address to connect to.
You can scan QR code on the back of the AP to speed up this process.
After resetting my AP, the app refused to connect to it even with the code scan process.
As of now I no longer have a useable wireless network in my home.&lt;&#x2F;p&gt;
&lt;p&gt;I google&#x27;d around for folks who were experiencing a similar issue and found &lt;a href=&quot;https:&#x2F;&#x2F;community.ui.com&#x2F;questions&#x2F;Cant-see-new-AP-with-app&#x2F;9c1d79f2-fbdb-4d39-aef7-499d3f546ceb&quot;&gt;this&lt;&#x2F;a&gt; post.
Turns out my AP was running firmware version 3.something.
To use the app I would need to update to the latest firmware version.
To do this I had to ssh into my AP (default username&#x2F;password is &lt;code&gt;ubnt&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;ubnt&lt;&#x2F;code&gt;) and run the commands listed in the link.
After upgrading my firmware, the app still failed to connect&#x2F;find the AP.
Yippee.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-unifi-controller&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#step-2-unifi-controller&quot; aria-label=&quot;Anchor link for: step-2-unifi-controller&quot;&gt;
    #
&lt;&#x2F;a&gt;
Step 2. Unifi controller&lt;&#x2F;h2&gt;
&lt;p&gt;At this point, my remaining option was to get the unifi controller&#x2F;application running.
The unifi controller is a chunky boy.
An AUR package is available as &lt;a href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;unifi&#x2F;&quot;&gt;unifi&lt;&#x2F;a&gt; and look at those dependencies.
You need mongodb and java to run it.
The build of mongodb fails with the &lt;a href=&quot;https:&#x2F;&#x2F;aur.archlinux.org&#x2F;packages&#x2F;mongodb&#x2F;#comment-835956&quot;&gt;following error&lt;&#x2F;a&gt;.
At this point it&#x27;s around 1 a.m. and my patience is running thin.
I found a &lt;a href=&quot;https:&#x2F;&#x2F;docs.linuxserver.io&#x2F;images&#x2F;docker-unifi-controller&quot;&gt;docker image&lt;&#x2F;a&gt; provided by the lovely folks at &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;unifi-software&#x2F;linuxserver.io&quot;&gt;LinuxServer.io&lt;&#x2F;a&gt;.
This came with some special instructions around setting the inform url (since docker internal IPs are not the same your network IPs).&lt;&#x2F;p&gt;
&lt;p&gt;And boom, at long last I updated my SSID 🥲.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;
    #
&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;This whole processed sucked ass.
No other way to put it.
Ubiquiti want you to use their app, they want you to use their controller application, they want you to have an account.
All I want, is just to access my router&#x27;s or AP&#x27;s interface and change my wireless network name. &lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Converting my init.vim to lua</title>
        <published>2021-12-14T00:00:00+00:00</published>
        <updated>2021-12-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/neovim-lua/"/>
        <id>https://blog.imraniqbal.org/neovim-lua/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/neovim-lua/">&lt;p&gt;I use neovim as my main driver for all my coding and text editing needs.
They released version 0.5 (up to 0.6 now) &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;neovim&#x2F;neovim&#x2F;pull&#x2F;12235&quot;&gt;which supports having your vim configuration in lua (5.1 via LuaJIT) instead of in vimscript&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve never used lua before but it&#x27;s a nice small language. LuaJIT is also crazy fast.
You can find most of what you need to know with either &lt;code&gt;:h lua&lt;&#x2F;code&gt; and some &lt;a href=&quot;https:&#x2F;&#x2F;learnxinyminutes.com&#x2F;docs&#x2F;lua&#x2F;&quot;&gt;syntax reference&lt;&#x2F;a&gt;.
One of the nicest changes is the ability to use maps for storing configuration for plugins. For example the following &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dense-analysis&#x2F;ale&quot;&gt;ale&lt;&#x2F;a&gt; configuration:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;vim&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-vim &quot;&gt;&lt;code class=&quot;language-vim&quot; data-lang=&quot;vim&quot;&gt;&lt;span style=&quot;color:#f28779;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span&gt;g:ale_fixers = {
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;*&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;remove_trailing_lines&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;trim_whitespace&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;python&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;autopep8&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;black&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;isort&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;javascript&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;eslint&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;vue&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;eslint&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;dhall&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;dhall-format&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;go&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;gofmt&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;],
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\   &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;rust&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;rustfmt&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;\&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Becomes this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;lua&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-lua &quot;&gt;&lt;code class=&quot;language-lua&quot; data-lang=&quot;lua&quot;&gt;&lt;span&gt;vim&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;g&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ale_fixers &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;*&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;remove_trailing_lines&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;trim_whitespace&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;python &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;autopep8&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;black&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;isort&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;javascript &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;eslint&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;vue &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;eslint&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;dhall &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;dhall-format&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;gofmt&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;rust &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;{ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;rustfmt&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Some rough spots still exist (e.g. auto commands) but there is a nice &lt;code&gt;vim.cmd&lt;&#x2F;code&gt; escape hatch that can still take regular vimscript. Super excited to see what else is in store for this project!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lessons learnt from exposing my server to the internet</title>
        <published>2021-11-04T00:00:00+00:00</published>
        <updated>2021-11-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/sshguard/"/>
        <id>https://blog.imraniqbal.org/sshguard/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/sshguard/">&lt;p&gt;My previous &lt;a href=&quot;https:&#x2F;&#x2F;blog.imraniqbal.org&#x2F;sftp&#x2F;&quot;&gt;post&lt;&#x2F;a&gt; was about how I setup a SFTP server for my dad.
This post is going to be about all the things I learned about exposing your server to the internet.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unable-to-login&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#unable-to-login&quot; aria-label=&quot;Anchor link for: unable-to-login&quot;&gt;
    #
&lt;&#x2F;a&gt;
Unable to login&lt;&#x2F;h2&gt;
&lt;p&gt;About a week after I exposed my server via SFTP to the world, I wanted to log in to my server to do some maintenance.
Much to my shock, my server was unresponsive to my regular ssh commands.&lt;&#x2F;p&gt;
&lt;p&gt;This is when the panic started setting in.
My server lives in my home close to my desktop computer.
The server appeared to be still on, the lights were still on and the fans were still spinning.
I resisted the urge to reboot it and try to ssh again, as I wanted to know &lt;strong&gt;why&lt;&#x2F;strong&gt; I could not ssh in.&lt;&#x2F;p&gt;
&lt;p&gt;I temporarily repurposed my second monitor on my desktop and plugged in an extra keyboard that I had.
The standard login prompt greeted me, which meant the server was not dead and I was able to login.&lt;&#x2F;p&gt;
&lt;p&gt;I checked the status of the ssh process with a &lt;code&gt;systemctl status sshd&lt;&#x2F;code&gt; and (un)surprisingly it&#x27;s dead.
The next step was to figure out why it&#x27;s dead.
This started with looking at the logs via &lt;code&gt;journalctl -b -t sshd&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;Loads of similar lines
&lt;&#x2F;span&gt;&lt;span&gt;---
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245607]: Unable to negotiate with 86.111.187.162 port 36589: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245610]: Unable to negotiate with 86.111.187.162 port 36596: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245618]: Unable to negotiate with 86.111.187.162 port 36605: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:33 server sshd[245622]: Unable to negotiate with 86.111.187.162 port 36616: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245628]: Unable to negotiate with 86.111.187.162 port 36623: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245632]: Unable to negotiate with 86.111.187.162 port 36634: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245636]: Unable to negotiate with 86.111.187.162 port 36647: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Oct 08 11:04:34 server sshd[245640]: Unable to negotiate with 86.111.187.162 port 36652: no matching key exchange method found. Their offer: diffie-hellman-group14-sha1,diffie&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Oh.&lt;&#x2F;p&gt;
&lt;p&gt;Looked like folks over the internet were having a blast trying to break into the server.
The first step I needed to take was to free up my monitor and keyboard and go back to the comfort of working from my desktop.
A simple &lt;code&gt;systemctl start sshd&lt;&#x2F;code&gt; was all that was needed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;remediation&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#remediation&quot; aria-label=&quot;Anchor link for: remediation&quot;&gt;
    #
&lt;&#x2F;a&gt;
Remediation&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s clear that I need to harden my ssh setup.
I already the following setup:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;root login disabled&lt;&#x2F;li&gt;
&lt;li&gt;password logins disabled (with the exception being the SFTP user)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;From the logs, looks like no one has even figured out username for the ftp user and the password for it&#x27;s a long generated one via Bitwarden.
The first thing I did was reduce &lt;code&gt;MaxAuthTries&lt;&#x2F;code&gt; to 1.
The reasoning was pretty simple, either you know how to get in or you don&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;The next steps were to consult &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;title&#x2F;OpenSSH#Protecting_against_brute_force_attacks&quot;&gt;the arch wiki&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Turned out I had already taken most of the necessary steps.
I was just missing a rate limiting&#x2F;blacklisting solution.
I settled on using &lt;code&gt;sshguard&lt;&#x2F;code&gt; combined with &lt;code&gt;ufw&lt;&#x2F;code&gt;.
I verified with my dad that he could still login and do his work.&lt;&#x2F;p&gt;
&lt;p&gt;Fingers crossed, this would be the last time I would have to deal with something like this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#results&quot; aria-label=&quot;Anchor link for: results&quot;&gt;
    #
&lt;&#x2F;a&gt;
Results&lt;&#x2F;h2&gt;
&lt;p&gt;My server has been up and running ever since with no issue to ssh.
I have also installed &lt;a href=&quot;https:&#x2F;&#x2F;www.netdata.cloud&#x2F;&quot;&gt;&lt;code&gt;netdata&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and made it accessible solely over the local network.
This is just to give me a birds eye view of what is going on with my server without having to ssh in.&lt;&#x2F;p&gt;
&lt;p&gt;Taking a quick look at logs shows me that sshguard is working as intended&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;Nov 03 14:27:28 server sshguard[1006]: Attack from &amp;quot;176.111.173.238&amp;quot; on service SSH with danger 10.
&lt;&#x2F;span&gt;&lt;span&gt;Nov 03 14:27:28 server sshguard[1006]: Blocking &amp;quot;176.111.173.238&#x2F;32&amp;quot; forever (REDACTED)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>SFTP is super awesome</title>
        <published>2021-09-27T00:00:00+00:00</published>
        <updated>2021-09-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/sftp/"/>
        <id>https://blog.imraniqbal.org/sftp/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/sftp/">&lt;p&gt;My dad has a site that he has been working on since the windows 3.1 days.
It&#x27;s static site and up until now he had been paying a little over a hundred dollars a year to have it hosted.&lt;&#x2F;p&gt;
&lt;p&gt;This came up during the winter of last year, where he had to pay for a renewal for his domain as well as his hosting.
The hosting company did not accept payment from where he&#x27;s located, so he asked me to handle the payments on his behalf.
This is when I realized that he has been overpaying for a service that you can get without paying (e.g GitLab pages).
There was no getting away from the cost of the domain name, that&#x27;s just the nature of having your own domain, but hosting (for static sites) can be costless.&lt;&#x2F;p&gt;
&lt;p&gt;His current workflow was to make changes on his computer then upload the files via ftp to the hosting platform.
These files are served out of the ftp directory to the web.&lt;&#x2F;p&gt;
&lt;p&gt;Getting my dad to use git was a no go from the start.
As much as I like it and use it everyday for personal and professional use, it isn&#x27;t beginner friendly.
In fact there&#x27;s a good chunk of professional programmers that cannot use it properly either.
On top of learning the git commands and workflow he would also need to understand ssh keys which are not that easy to explain either (or why they are important).
Especially coming from the old ftp workflow.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-solution&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#the-solution&quot; aria-label=&quot;Anchor link for: the-solution&quot;&gt;
    #
&lt;&#x2F;a&gt;
The solution&lt;&#x2F;h1&gt;
&lt;p&gt;I still wanted to his site backed by git.
I think it&#x27;s a great idea as it gives us source control instead of just files that live on his hard drive.
Having a git repository also lets me take advantage of the free hosting that GitLab pages provides.&lt;&#x2F;p&gt;
&lt;p&gt;This means I would need to be able to provide a ftp like interface that ties into a git repository.
My first thought was that I need something that I can watch a directory for file system events, and after a set delay execute some commands.
&lt;a href=&quot;https:&#x2F;&#x2F;facebook.github.io&#x2F;watchman&#x2F;&quot;&gt;Watchman&lt;&#x2F;a&gt; exists but its a little heavy handed for what I needed.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to write my own tool &lt;a href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;imran_iqbal&#x2F;dcmd&quot;&gt;&lt;code&gt;dcmd&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
This also gave me a convenient excuse to dabble in rust some more.&lt;&#x2F;p&gt;
&lt;p&gt;This would cover the committing + pushing part of our solution.
All that I had to do was to provide ftp interface that pointed to the relevant part of the git project.&lt;&#x2F;p&gt;
&lt;p&gt;I originally wanted to use &lt;code&gt;vsftp&lt;&#x2F;code&gt; as it&#x27;s included in arch&#x27;s community repository.
At moment it seems to not be working (core dumps on access) as well as out of date.
The other thing I did not like about &lt;code&gt;vsftp&lt;&#x2F;code&gt; is that it&#x27;s underlying protocol is ftp and ftp is not secure (sends password over plain text).&lt;&#x2F;p&gt;
&lt;p&gt;This is where SFTP comes in.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s provided by default by OpenSSH and as the &lt;code&gt;S&lt;&#x2F;code&gt; indicates it&#x27;s more secure.
I would need to do the following to get it setup&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a SFTP user&lt;&#x2F;li&gt;
&lt;li&gt;Create a chroot jail for the user&lt;&#x2F;li&gt;
&lt;li&gt;Create a symlink in the jail that maps to the static files in the git repository&lt;&#x2F;li&gt;
&lt;li&gt;Edit my &lt;code&gt;sshd_config&lt;&#x2F;code&gt; file to use the jail&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Now I mentioned before that my dad is not familiar with ssh (and ssh keys) so I would need a password for him.
On my server I disable password logins for security.
I thought this would be a problem but turns out we can have both out of the box.
All I needed to add to my configuration was this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;lt;Rest of config&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Match User &amp;lt;my sftp user&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        ChrootDirectory &amp;lt;path to my jail&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        ForceCommand internal-sftp
&lt;&#x2F;span&gt;&lt;span&gt;        PasswordAuthentication yes
&lt;&#x2F;span&gt;&lt;span&gt;Match all
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This forces the SFTP user to the jail and allows it to login using a password.
I had concerns that this would also grant ssh access to my machine but turns out it does not:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;❯ ssh &amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;user&amp;gt;@&amp;lt;hosts&amp;gt;&amp;#39;s password:
&lt;&#x2F;span&gt;&lt;span&gt;This service allows sftp connections only.
&lt;&#x2F;span&gt;&lt;span&gt;Connection to &amp;lt;host&amp;gt; closed.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Even if the password gets leaked I can be sure that my server remains secured (and why the title of this post is about SFTP being awesome).&lt;&#x2F;p&gt;
&lt;p&gt;The last thing I wanted to do is limit the blast radius if somehow the git credentials were compromised.
Basically I do not want to lose any commits or have the repository deleted.
For this I ended up creating a &amp;quot;bot&amp;quot; account on GitLab (just a fresh account) and gave it its own ssh keys.
This bot would then have developer (not maintainer) access to the git project.
This means the bot can push directly to the repository and have CI run and deploy to pages, but it can&#x27;t change repository settings or force push.&lt;&#x2F;p&gt;
&lt;p&gt;The one other thing I was missing a systemd service file for &lt;code&gt;dcmd&lt;&#x2F;code&gt; that would ensure it keeps running.
This was simple enough (I&#x27;ve done it before for other things) and there&#x27;s a crap ton of resources on the internet for it (including the systemd man pages) so I won&#x27;t be pasting the service file here.&lt;&#x2F;p&gt;
&lt;p&gt;The last thing to do before handing the credentials over to my dad was to make sure that this whole setup actually worked.&lt;&#x2F;p&gt;
&lt;p&gt;I SFTP&#x27;d in with the user I had set up and this is when I found out that you can&#x27;t symlink to directories out of the chroot jail.
This kind of makes sense and it would not be a good jail if the user could access files outside of it.&lt;&#x2F;p&gt;
&lt;p&gt;A bind mount remedied this problem.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;mount --bind src_dir target_dir
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Bind mounts basically copies a directory over to another place, but they point to the same files in the file system.
This means that if the files are added&#x2F;removed in the SFTP directory they are also modified in the git repository.
This lets &lt;code&gt;dcmd&lt;&#x2F;code&gt; do its job and create a commit and push it.&lt;&#x2F;p&gt;
&lt;p&gt;My dad confirmed that the credentials worked for him and he can go back to his old workflow.
The difference this time is his work is now preserved somewhere else other than his computer and he gets to save some money! :D&lt;&#x2F;p&gt;
&lt;p&gt;This was a super fun side project and I can already think of some improvements to &lt;code&gt;dcmd&lt;&#x2F;code&gt; if I ever want dabble in rust some more.
It&#x27;s a nice feeling when I can use my expertise&#x2F;skills to help out my dad.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Going back to the gym</title>
        <published>2021-09-14T00:00:00+00:00</published>
        <updated>2021-09-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/gym/"/>
        <id>https://blog.imraniqbal.org/gym/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/gym/">&lt;p&gt;The province that I am in lifted covid restrictions for the gym.
I used to work out a lot before covid, during the pandemic I have co-opted more of a sedentary lifestyle.&lt;&#x2F;p&gt;
&lt;p&gt;I signed up for a new membership as soon as possible to the closest gym.
The first workout (per body part) always sucks.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The weights that I am able to lift is far below what I used to be able to do&lt;&#x2F;li&gt;
&lt;li&gt;I am going to be sore for days after the workout and the worked out body parts will be quite useless&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Despite the pain, it&#x27;s a pleasant experience.
It has a huge improvement on my mood, though going outside might also contribute to that.
I am super hoping that the government does not lock things down now that vaccine administration is nearing 80ish percent.&lt;&#x2F;p&gt;
&lt;p&gt;My old gym goal used to be a 600 lb deadlift before I was 30.
I think it might be doable to achieve by 35 (sitting pretty at 135 lbs at the moment).&lt;&#x2F;p&gt;
&lt;p&gt;Oh and in other happier news, the vertigo has gone (more like I just got used to it)!
At the least it has not affected any of my workouts and I hope it remains that way.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Vertigo</title>
        <published>2021-06-28T00:00:00+00:00</published>
        <updated>2021-06-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/vertigo/"/>
        <id>https://blog.imraniqbal.org/vertigo/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/vertigo/">&lt;p&gt;For the first time in my life I have gotten vertigo.
It&#x27;s not clear from where I got it, or how.
All I know is that it sucks quite a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s to hoping it passes soon and my body adjusts to my new sense of balance.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Django Rest Framework</title>
        <published>2021-05-12T00:00:00+00:00</published>
        <updated>2021-05-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/drf/"/>
        <id>https://blog.imraniqbal.org/drf/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/drf/">&lt;p&gt;In one of my previous roles I had to use &lt;a href=&quot;https:&#x2F;&#x2F;www.django-rest-framework.org&#x2F;&quot;&gt;Django Rest Framework&lt;&#x2F;a&gt; (drf)
to make APIs and I enjoyed it. It fit my mental model for building robust APIs.&lt;&#x2F;p&gt;
&lt;p&gt;My mental model is as follows:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A user sends a request&lt;&#x2F;li&gt;
&lt;li&gt;Check auth&lt;&#x2F;li&gt;
&lt;li&gt;Check request body (do we have the right fields&#x2F;structure)&lt;&#x2F;li&gt;
&lt;li&gt;Perform basic validation (types)&lt;&#x2F;li&gt;
&lt;li&gt;Perform business logic validation&lt;&#x2F;li&gt;
&lt;li&gt;Persist to data store&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;drf makes this super easy with its serializers. Views + permissions take care of auth in most cases and serializers do the rest.&lt;&#x2F;p&gt;
&lt;p&gt;A small amount noob traps do exist.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-modelserializer&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#using-modelserializer&quot; aria-label=&quot;Anchor link for: using-modelserializer&quot;&gt;
    #
&lt;&#x2F;a&gt;
Using ModelSerializer&lt;&#x2F;h3&gt;
&lt;p&gt;This is the first trap most people fall into.
ModelSerializers are convenient when prototyping but most of the time the input that you want from a user does not map nicely onto an existing model.
This is true the larger&#x2F;more complex your web API gets.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;trying-to-use-the-same-serializer-for-update-and-create&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#trying-to-use-the-same-serializer-for-update-and-create&quot; aria-label=&quot;Anchor link for: trying-to-use-the-same-serializer-for-update-and-create&quot;&gt;
    #
&lt;&#x2F;a&gt;
Trying to use the same serializer for update and create&lt;&#x2F;h3&gt;
&lt;p&gt;This is another noob trap that ends up resulting in some crazy complex and hard to follow code.
Most of the time it turns out the logic you want for create operations ends up being quite different from the logic you want when it comes to update operations.
This results in a lot of validation code that depends on the existence of &lt;code&gt;self.instance&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s trivially easy to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;encode&#x2F;django-rest-framework&#x2F;blob&#x2F;71e6c30034a1dd35a39ca74f86c371713e762c79&#x2F;rest_framework&#x2F;generics.py#L112-L128&quot;&gt;switch out&lt;&#x2F;a&gt; which serializer gets used based on action&lt;&#x2F;p&gt;
&lt;p&gt;Using a serializer per action lets you have nicely segregated logic per verb per endpoint.
This makes it super clear what code gets run for &lt;code&gt;POST &#x2F;something&lt;&#x2F;code&gt; vs &lt;code&gt;PUT &#x2F;something&lt;&#x2F;code&gt; vs &lt;code&gt;GET &#x2F;something&lt;&#x2F;code&gt;.
This in turn leads pretty easy maintainability when needing to make a change as you can be sure a change to the behaviour of one action does not impact the others.
Which in turn leads to pretty easy testing, you just need to test each specific serializer plus its validation logic.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-using-a-separate-serializer-for-retrieve-vs-list&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#not-using-a-separate-serializer-for-retrieve-vs-list&quot; aria-label=&quot;Anchor link for: not-using-a-separate-serializer-for-retrieve-vs-list&quot;&gt;
    #
&lt;&#x2F;a&gt;
Not using a separate serializer for retrieve vs list&lt;&#x2F;h3&gt;
&lt;p&gt;For reference a retrieve is asking for a single record i.e. &lt;code&gt;GET &#x2F;things&#x2F;1&lt;&#x2F;code&gt; and list is asking for more than one record i.e. &lt;code&gt;GET &#x2F;things&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Generally the data you want in a detail view (retrieve) is different from the data you want in a list view and your serializers should reflect this.
Detail serializers might need to do a lot of extra look up to get related records to present a nice detailed record.&lt;&#x2F;p&gt;
&lt;p&gt;A mistake I&#x27;ve seen made is folks end up using this same detail serializer for their list operations.
This results in an unholy amount of N+1 queries and abysmal performance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-using-a-separate-serializer-for-read-vs-write-operations&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#not-using-a-separate-serializer-for-read-vs-write-operations&quot; aria-label=&quot;Anchor link for: not-using-a-separate-serializer-for-read-vs-write-operations&quot;&gt;
    #
&lt;&#x2F;a&gt;
Not using a separate serializer for read vs write operations&lt;&#x2F;h3&gt;
&lt;p&gt;This is not quite a noob trap but it does go a long way in the simplification of code.
I find that more often than not you would like to receive data in a field as one type, but when returning the data it needs to be another type. e.g. Take in a foreign key but return a string from the related object.
drf does not make it easy to re-use a field if its a different type on the way out.
The maintainable&#x2F;easy way out of this predicament is to just use a different serializer for incoming data vs outgoing.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;
    #
&lt;&#x2F;a&gt;
Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;The general takeaway from this is that you probably want to avoid using ModelSerializers and don&#x27;t try to make a single serializer a one size fits all solution.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Covid-19, condos and dogs</title>
        <published>2021-04-13T00:00:00+00:00</published>
        <updated>2021-04-13T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/covid-condos-and-dogs/"/>
        <id>https://blog.imraniqbal.org/covid-condos-and-dogs/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/covid-condos-and-dogs/">&lt;p&gt;It has been an interesting year (or two).
Covid has caused a lot of people to adopt new pets, myself included.
Why not? We&#x27;re all home all the time anyway right?&lt;&#x2F;p&gt;
&lt;p&gt;The issue with this is that I live in a condo (I&#x27;ll get around to writing about all the joys of owning a condo).
This means if you, or your new dog, ends up making a lot noise (read: barking) all your neighbours suffer.
You can complain to the property manager and they can do something about it, but because of covid, your property manager probably doesn&#x27;t care.
They didn&#x27;t care before covid either, but they sure as hell don&#x27;t now.&lt;&#x2F;p&gt;
&lt;p&gt;These horrible dog owners then cause other issues with their pets.
Like pissing in the elevators.
Pissing on their balconies which then drip down to other units.
Taking a crap in the lobby.
Leaving their dog tied to a pole outside of the condo building while they are off doing w&#x2F;e.&lt;&#x2F;p&gt;
&lt;p&gt;If you are going to live in a condo, do yourself a favour and find one that has a strict no dog policy.&lt;&#x2F;p&gt;
&lt;p&gt;Imran didn&#x27;t you say you get a new pet as well?
Yeah, I got a cat.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Upgrading my home server</title>
        <published>2021-03-28T00:00:00+00:00</published>
        <updated>2021-03-28T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/upgrading-my-server/"/>
        <id>https://blog.imraniqbal.org/upgrading-my-server/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/upgrading-my-server/">&lt;p&gt;Like most nerds I have a home server that I use to serve media (via Plex) and maybe host some personal projects.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;history&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#history&quot; aria-label=&quot;Anchor link for: history&quot;&gt;
    #
&lt;&#x2F;a&gt;
History&lt;&#x2F;h1&gt;
&lt;p&gt;I built the first version of my server (I think) about 8ish years ago.
It&#x27;s a simple and cheap setup.
The OS ended up being ubuntu server since it had been quite a while since I dabbled in setting up a distro from scratch.
This was the fastest way to have something up and running.
For storage the OS was on a small SSD plus a 3 TB hard drive for the data.
It had some ASRock motherboard that had a processor built into it and some negligible amount of ram.
It wasn&#x27;t powerful but it got the job done.
It also started to struggle when streaming shows in 1080p.&lt;&#x2F;p&gt;
&lt;p&gt;The first upgrade was changing the OS.
Though this isn&#x27;t an &amp;quot;upgrade&amp;quot; I guess.
I switched to using arch instead of ubuntu since a rolling release distro is less hassle to keep updated.&lt;&#x2F;p&gt;
&lt;p&gt;The next &amp;quot;upgrade&amp;quot; was switching from manually installed services to docker.
There was a two reasons for this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I wanted to learn about docker&lt;&#x2F;li&gt;
&lt;li&gt;I had two python applications that started having clashing versions of dependencies&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This was my first foray into using docker and was quite pleasant.
No more clashing dependencies and became a lot easier to bring up new services.
Not to mention it helped increased the reproducibility of the server.&lt;&#x2F;p&gt;
&lt;p&gt;A couple years after that I got a i7 4790K + motherboard combo for a decent price.
This was my first major upgrade to it, no longer did it struggle to stream 1080p videos.&lt;&#x2F;p&gt;
&lt;p&gt;A little while later I upgraded the ram to 16 GB because why not?
And soon thereafter I had to upgrade the storage drive since 3 TB was not cutting it anymore.&lt;&#x2F;p&gt;
&lt;p&gt;I managed to grab five 3 TB Western Digital NAS red drives on sale during black Friday.
I decided to go with use a software raid, a coworker recommended ZFS so I went with that.
Creating a zpool with raidz1 was straight forward.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-present-day&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#the-present-day&quot; aria-label=&quot;Anchor link for: the-present-day&quot;&gt;
    #
&lt;&#x2F;a&gt;
The present day&lt;&#x2F;h1&gt;
&lt;p&gt;I have started running out of space on the zpool.
For this upgrade I decided on shucking some hard drives since it&#x27;s hard to get regular hard drives at a competitive price.
For those that do not know what &amp;quot;shucking a hard drive&amp;quot; is, it&#x27;s when you buy an external hard drive and rip it out of its enclosure.
The drives in these are typically &amp;quot;white label&amp;quot; drives but are still regular drives, just cheaper.&lt;&#x2F;p&gt;
&lt;p&gt;I ended up getting five 14 TB WD easystore drives.
All ended up being white label EDFZ drives.
I ran &lt;a href=&quot;&#x2F;badblocks-14tb&quot;&gt;badblocks&lt;&#x2F;a&gt; on them and verified there were no bad sectors.&lt;&#x2F;p&gt;
&lt;p&gt;The next step was figuring out how I was going to transfer all my data off my old zpool onto this new one.
For reference my motherboard has 6 SATA ports, which are all in use.
One for the SSD and the other five for the 5×3 TB zpool.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to keep all the USB connectors on the drives attached as the motherboard had plenty of free USB ports.
This simplified the transfer process quite a bit.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zpool&lt;&#x2F;span&gt;&lt;span&gt; create new_pool raidz &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;new drives&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zfs&lt;&#x2F;span&gt;&lt;span&gt; snapshot old_pool@now
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zfs&lt;&#x2F;span&gt;&lt;span&gt; send old_pool@now &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;| &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zfs&lt;&#x2F;span&gt;&lt;span&gt; recv&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -F&lt;&#x2F;span&gt;&lt;span&gt; new_pool
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zpool&lt;&#x2F;span&gt;&lt;span&gt; export old_pool new_pool
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;poweroff
&lt;&#x2F;span&gt;&lt;span style=&quot;font-style:italic;color:#5c6773;&quot;&gt;# After powering back up
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;zfs&lt;&#x2F;span&gt;&lt;span&gt; set mountpoint=&#x2F;old_mountpoint new_pool
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In order these commands do the following:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a new zpool out of the new drives&lt;&#x2F;li&gt;
&lt;li&gt;Create a snapshot of the data on the old zpool&lt;&#x2F;li&gt;
&lt;li&gt;Send that snapshot over to the new zpool&lt;&#x2F;li&gt;
&lt;li&gt;Let ZFS know I intend to physically disconnect both pools&lt;&#x2F;li&gt;
&lt;li&gt;Turn off the machine
&lt;ol&gt;
&lt;li&gt;This lets me remove the USB adapters for the new drives and plug them in via SATA after disconnecting the old drives.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Change the mountpoint of the new zpool to be the same as the old zpool so applications can&#x27;t tell the difference&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I lucked out and singular drive out of the five needed to have the 3.3v pin taped over.
For why that was necessary I recommend watching this video: &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=1YqMn1pCRd8&quot;&gt;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=1YqMn1pCRd8&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I super hope I do not have to upgrade again anytime soon!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Running badblocks on a 14tb hard drive</title>
        <published>2021-03-15T00:00:00+00:00</published>
        <updated>2021-03-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/badblocks-14tb/"/>
        <id>https://blog.imraniqbal.org/badblocks-14tb/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/badblocks-14tb/">&lt;p&gt;This is going to be my first time shucking hard drives.
I managed to get a couple 14tb WD easystores for about 20$&#x2F;TB.
According to &#x2F;r&#x2F;datahoarders it&#x27;s always a good idea to run badblocks on the
hard drives to make sure there are no faults.&lt;&#x2F;p&gt;
&lt;p&gt;Since this is my first time it&#x27;s better to err on the side of caution&lt;&#x2F;p&gt;
&lt;p&gt;Well here is how long the command has been running for so far:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;badblocks.png&quot; alt=&quot;badblocks 110 hours and counting&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;EDIT&lt;&#x2F;p&gt;
&lt;p&gt;The full time was almost a week.
Started at Wednesday 23:00 and finished at Wednesday 22:00&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Trying out James Hoffman&#x27;s donut espresso</title>
        <published>2021-03-15T00:00:00+00:00</published>
        <updated>2021-03-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/donut-espresso/"/>
        <id>https://blog.imraniqbal.org/donut-espresso/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/donut-espresso/">&lt;p&gt;Last week James Hoffman came out with a &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=j-_E7R-Eu_w&quot;&gt;video&lt;&#x2F;a&gt; about how to make a tasty donut flavoured espresso drink.
The full recipe is &lt;a href=&quot;https:&#x2F;&#x2F;www.jameshoffmann.co.uk&#x2F;weird-coffee-science&#x2F;coffee-and-a-donut&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to try it but I had hard time getting donuts.
I ended up getting some generic donuts from my local grocery store (I forget to take a picture before making the recipe).&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;donuts_package.b7a44bae10d577b9.jpg&quot;  alt=&quot;Empty donut package&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;Here are the donuts all chopped up and added to the pot, I ended up using oat milk:&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;starting_off.1427e81979a7e5a3.jpg&quot;  alt=&quot;Chopped up donuts in a pot with oat milk&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;And here there are after 30 minutes of soaking and simmering:&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;mush.3d372ad52b25e241.jpg&quot;  alt=&quot;Donut and oat milk soup&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;After chilling in the fridge over night here is the final filter.
I decided to use a reusable coffee filter.
In the future I would rather use a paper filter as the reusable filter did not survive this experiment. Ironic.&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;second_filter.0f809fc47ae93c83.jpg&quot;  alt=&quot;Donut milk strained through a reuseable coffee filter&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;And here is the finished product:&lt;&#x2F;p&gt;

&lt;div&gt;
    &lt;img src=&quot;https:&amp;#x2F;&amp;#x2F;blog.imraniqbal.org&amp;#x2F;processed_images&amp;#x2F;final_product.a0be8b3e157f01de.jpg&quot;  alt=&quot;Donut espresso in a Kruve glass&quot;  &#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;thoughts&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#thoughts&quot; aria-label=&quot;Anchor link for: thoughts&quot;&gt;
    #
&lt;&#x2F;a&gt;
Thoughts&lt;&#x2F;h3&gt;
&lt;p&gt;Over all pretty fun to make.
The end result was much too rich.
This is perhaps because I got the ratio of donut to milk wrong.
Since the donuts in the package were pretty small I thought I would use 8 donuts.
This was a miscalculation on my part as a Krispy Kreme donut weights in at 49 g (&lt;a href=&quot;https:&#x2F;&#x2F;www.bd.com&#x2F;resource.aspx?IDX=23622&quot;&gt;source&lt;&#x2F;a&gt;) and these donuts would be 550&#x2F;12 (= ~46 g).
My resultant donut infused milk would be twice the concentration that it&#x27;s supposed to be.&lt;&#x2F;p&gt;
&lt;p&gt;I would not mind doing this again in the future (maybe make a recurring ritual out of it), but next time I should weigh my donuts before infusion.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Python and multiline lambdas</title>
        <published>2021-02-04T00:00:00+00:00</published>
        <updated>2021-02-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/python-lambda/"/>
        <id>https://blog.imraniqbal.org/python-lambda/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/python-lambda/">&lt;p&gt;The title of this post &lt;em&gt;should&lt;&#x2F;em&gt; be &amp;quot;Shut up about python and multi-line lambdas&amp;quot;.
I browse HackerNews a bit more than I should, if I&#x27;m being honest.
Every now and again there will be a post involving python on the front page.
Predictably there will always be (at least) one person in the comments moaning about the lack of multi-line lambdas and how it&#x27;s holding python back from being a &amp;quot;real&amp;quot; language.&lt;&#x2F;p&gt;
&lt;p&gt;In python the lambda keyword is syntactic sugar.
There&#x27;s no difference between a one line function and a lambda.
Here is the docs on lambdas: &lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;reference&#x2F;expressions.html#lambda&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;reference&#x2F;expressions.html#lambda&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a look at this using the python interpreter:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ff3333;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;func1&lt;&#x2F;span&gt;&lt;span&gt;(a)&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;...     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;func2 &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;lambda &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;a &lt;&#x2F;span&gt;&lt;span&gt;: a &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;* &lt;&#x2F;span&gt;&lt;span&gt;a
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;from &lt;&#x2F;span&gt;&lt;span&gt;dis &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;import &lt;&#x2F;span&gt;&lt;span&gt;dis
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;dis&lt;&#x2F;span&gt;&lt;span&gt;(func1)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;2           0 &lt;&#x2F;span&gt;&lt;span&gt;LOAD_FAST                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;(a)
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;LOAD_FAST                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;(a)
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;4 &lt;&#x2F;span&gt;&lt;span&gt;BINARY_MULTIPLY
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;6 &lt;&#x2F;span&gt;&lt;span&gt;RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;dis&lt;&#x2F;span&gt;&lt;span&gt;(func2)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;1           0 &lt;&#x2F;span&gt;&lt;span&gt;LOAD_FAST                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;(a)
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;2 &lt;&#x2F;span&gt;&lt;span&gt;LOAD_FAST                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;0 &lt;&#x2F;span&gt;&lt;span&gt;(a)
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;4 &lt;&#x2F;span&gt;&lt;span&gt;BINARY_MULTIPLY
&lt;&#x2F;span&gt;&lt;span&gt;              &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;6 &lt;&#x2F;span&gt;&lt;span&gt;RETURN_VALUE
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&amp;gt;&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Look at that, they are the same.
They even have the same bytecode.&lt;&#x2F;p&gt;
&lt;p&gt;Keeping in mind that functions are first class objects in python, it should be pretty easy to have a multi-line &amp;quot;lambda&amp;quot;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;some_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;some_list&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;multiline_lambda&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;x&lt;&#x2F;span&gt;&lt;span&gt;):
&lt;&#x2F;span&gt;&lt;span&gt;        temp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;x &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;        other_var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;other_func&lt;&#x2F;span&gt;&lt;span&gt;(x)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;temp &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;+ &lt;&#x2F;span&gt;&lt;span&gt;other_var
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;    r &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f28779;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(multiline_lambda&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span&gt;some_list)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then why, pray tell, does python have all these functional words and concepts without being &amp;quot;properly&amp;quot; functional?
Just to appeal to the masses, which is something &lt;a href=&quot;https:&#x2F;&#x2F;developers.slashdot.org&#x2F;story&#x2F;13&#x2F;08&#x2F;25&#x2F;2115204&#x2F;interviews-guido-van-rossum-answers-your-questions&quot;&gt;Guido van Rossum regrets&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I love coffee</title>
        <published>2021-01-31T00:00:00+00:00</published>
        <updated>2021-01-31T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/i-love-coffee/"/>
        <id>https://blog.imraniqbal.org/i-love-coffee/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/i-love-coffee/">&lt;p&gt;I used to hate coffee.
I never understood why people enjoyed drinking coffee so much and so often.
Among my coworkers and friends it wasn&#x27;t unheard of someone drinking 8 cups of coffee a day.
Though, to be fair, programmers generally seem to consume an unholy amount of coffee.&lt;&#x2F;p&gt;
&lt;p&gt;To me coffee always tasted bad.
Everyone told me it&#x27;s an acquired taste and that I would come around, but it just tasted so bad.
Coffee was harsh, bitter and tasted like something burnt so I just avoided it.
This worked well enough in the start of my career but soon I found myself relying on the occasional energy drink for a boost.
Now energy drinks aren&#x27;t the healthiest thing for you, so one day I started to have coffee instead.
As noted before I &lt;strong&gt;REALLY&lt;&#x2F;strong&gt; did not like the taste of coffee.
To make it drinkable for myself I had to basically &amp;quot;drown&amp;quot; the coffee in milk to mask any of the &amp;quot;flavour&amp;quot; and chug it as fast as possible.
The whole process was rather unenjoyable, but hey, at least it&#x27;s healthier than all the chemicals from energy drinks.&lt;&#x2F;p&gt;
&lt;p&gt;A little while later I found myself working at a company that had a fully automatic coffee machine.
This thing was capable of making anything from regular drip to a sugary mocha.
I &lt;em&gt;think&lt;&#x2F;em&gt; &lt;a href=&quot;https:&#x2F;&#x2F;espresso-etc.com&#x2F;office-coffee&#x2F;machines-Eccellenza.html&quot;&gt;this&lt;&#x2F;a&gt; was the machine, or at least something that had a similar appearance.
I started having the super sugary mochas since they managed to mask the taste the coffee reasonably well and gave me that nice caffeine kick.&lt;&#x2F;p&gt;
&lt;p&gt;Soon after I started working from home on Fridays since I signed up for a recreational sport league and would not be able to make the meet up time if I had to drive from the office.
The problem was when I was working from home I no longer had access to coffee.
I resorted to ordering my coffee from either McDonalds or Starbucks since I had a location for each within walking distance from my home.
This is when I started to notice the costs racking up.
Five to ten dollars for cup of coffee was a bit much.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s at this point that I started researching how to make coffee at home.
I was keen to continue making milk based drinks as I still didn&#x27;t like the taste of coffee.
I would need an espresso machine to make lattes at home (switched up from mochas because of the sugar content).
I was not too keen on Nespresso as I tried a sample at one of their stores and damn it tasted bad.
After a lot of research, I concluded that I would need a grinder, a semi automatic machine and fresh roasted beans.&lt;&#x2F;p&gt;
&lt;p&gt;I settled on a &lt;a href=&quot;https:&#x2F;&#x2F;baratza.com&#x2F;grinder&#x2F;encore&#x2F;&quot;&gt;Barazta Encore&lt;&#x2F;a&gt;, a &lt;a href=&quot;https:&#x2F;&#x2F;www.gaggia.com&#x2F;manual-machines&#x2F;new-classic&#x2F;&quot;&gt;Gaggia Classic&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;49thcoffee.com&#x2F;collections&#x2F;single-origin&#x2F;products&#x2F;epic-espresso-soe&quot;&gt;beans&lt;&#x2F;a&gt; from 49th parallel (so much for saving money 🤦).
I dialed in my shot and had a taste.
My goodness it&#x27;s a night and day difference.
What I thought was a drink that&#x27;s supposed to be harsh, dark and bitter actually tasted clean, bright and florally.
It was delicious and I got hooked.&lt;&#x2F;p&gt;
&lt;p&gt;Now I don&#x27;t claim that I pulled a god shot from the classic.
Light roasts are quite hard to get right with the hardware I had at the time.
I had even done an &lt;a href=&quot;https:&#x2F;&#x2F;coffeeforums.co.uk&#x2F;topic&#x2F;3412-adjusting-the-opv-over-pressure-valve-gaggia-classic&#x2F;&quot;&gt;OPV mod&lt;&#x2F;a&gt; to bring the pressure to 9 bars.
The stark difference between quality fresh roasted beans and what was (and is) readily available was staggering.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out nearly every place has over roasted the life out of their beans.
You can see this when you walk into a Starbucks or second cup.
The hopper is full with incredibly oily beans (a sign of being over roasted).
All the coffee found on grocery store shelves (even Costco) is over roasted.
The reason is that over roasted beans tend have a longer shelf life than lighter (or properly) roasted beans and keep their &amp;quot;flavour&amp;quot; longer.
This is also the reason why all &amp;quot;mainstream&amp;quot; (I know hipster much) coffee tastes so bad.
It&#x27;s because all the flavour has been roasted out of the beans.
If the coffee you are buying does not have a roast date on it, it most probably falls into this category.&lt;&#x2F;p&gt;
&lt;p&gt;Turns out I didn&#x27;t hate coffee per se, I just hated improperly made coffee.
Coffee, when done properly, is delicious and makes for quite the fun hobby.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Replacing my fire TV with a pi + Kodi</title>
        <published>2021-01-24T00:00:00+00:00</published>
        <updated>2021-01-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/replacing-fire-tv/"/>
        <id>https://blog.imraniqbal.org/replacing-fire-tv/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/replacing-fire-tv/">&lt;p&gt;A while ago I &lt;a href=&quot;&#x2F;ads-fire-tv&quot;&gt;wrote&lt;&#x2F;a&gt; about ads showing up on my TV player, which annoyed me.
To solve this annoyance I repurposed a &lt;a href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;&quot;&gt;raspberry pi&lt;&#x2F;a&gt; I had lying around into TV player over the winter holidays.&lt;&#x2F;p&gt;
&lt;p&gt;All I needed to do was be able to access my Plex server and twitch.
One option was to install &lt;a href=&quot;https:&#x2F;&#x2F;www.android.com&#x2F;intl&#x2F;en_ca&#x2F;tv&#x2F;&quot;&gt;android&lt;&#x2F;a&gt; on the &lt;a href=&quot;https:&#x2F;&#x2F;forum.xda-developers.com&#x2F;t&#x2F;dev-rom-unofficial-lineageos-17-1-android-10-for-raspberry-pi-4-b.4139059&#x2F;&quot;&gt;pi&lt;&#x2F;a&gt; and just use the official apps.
It looks like not even android TV is free from ads anymore (&lt;a href=&quot;https:&#x2F;&#x2F;old.reddit.com&#x2F;r&#x2F;AndroidTV&#x2F;comments&#x2F;ic380w&#x2F;android_tvs_homescreen_ads_are_rolling_out_heres&#x2F;&quot;&gt;source&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;In my previous post I had mentioned wanting to going for an &lt;a href=&quot;https:&#x2F;&#x2F;osmc.tv&#x2F;&quot;&gt;osmc device&lt;&#x2F;a&gt;.
Upon further research I found out osmc actually runs on top of &lt;a href=&quot;https:&#x2F;&#x2F;kodi.tv&#x2F;&quot;&gt;Kodi&lt;&#x2F;a&gt;.
Kodi is an open source (and ad free) piece of software that would be cable of doing what I wanted.
It comes with &lt;a href=&quot;https:&#x2F;&#x2F;kodi.tv&#x2F;addons&quot;&gt;add-ons&lt;&#x2F;a&gt; for Plex and twitch (and more).
The installation and setup was incredibly straight forward thanks to &lt;a href=&quot;https:&#x2F;&#x2F;libreelec.tv&#x2F;&quot;&gt;LibreELEC&lt;&#x2F;a&gt;.
I highly recommend donating to either project if you use them:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;LibreELEC: &lt;a href=&quot;https:&#x2F;&#x2F;libreelec.tv&#x2F;contribute&#x2F;&quot;&gt;https:&#x2F;&#x2F;libreelec.tv&#x2F;contribute&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Kodi: &lt;a href=&quot;https:&#x2F;&#x2F;kodi.tv&#x2F;contribute&#x2F;donate&quot;&gt;https:&#x2F;&#x2F;kodi.tv&#x2F;contribute&#x2F;donate&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All I had to do was replace my fire TV with the pi.&lt;&#x2F;p&gt;
&lt;p&gt;I am using a wireless keyboard + mouse combo to interact with the pi.
It&#x27;s possible to use the fire TV remote with the pi (it&#x27;s just a Bluetooth keyboard).
I live in a condo that has quite a fair amount of Bluetooth devices advertising themselves for pairing so its difficult to find the remote in the pairing list.
I may try again in the future as it&#x27;s possible due to it being the holidays loads of folks were setting up new devices as well.&lt;&#x2F;p&gt;
&lt;p&gt;For now though good riddance fire TV and good riddance to ads.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I love linux</title>
        <published>2021-01-05T00:00:00+00:00</published>
        <updated>2021-01-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/i-love-linux/"/>
        <id>https://blog.imraniqbal.org/i-love-linux/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/i-love-linux/">&lt;p&gt;I upgraded to linux 5.10 and noticed that &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;canonical&#x2F;lightdm&quot;&gt;LightDM&lt;&#x2F;a&gt; would fail to start consistently.
I was still able to switch to a different tty and &lt;code&gt;startx&lt;&#x2F;code&gt; manually, after which everything would work fine.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at my &lt;code&gt;journalctl -b&lt;&#x2F;code&gt; was interesting&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: [drm] fb0: amdgpudrmfb frame buffer device
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop systemd[1]: lightdm.service: Scheduled restart job, restart counter is at 5.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop systemd[1]: Stopped Light Display Manager.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop audit[1]: SERVICE_START pid=1 uid=0 auid=4294967295 ses=4294967295 msg=&amp;#39;unit=lightdm comm=&amp;quot;systemd&amp;quot; exe=&amp;quot;&#x2F;usr&#x2F;lib&#x2F;systemd&#x2F;system&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop audit[1]: SERVICE_STOP pid=1 uid=0 auid=4294967295 ses=4294967295 msg=&amp;#39;unit=lightdm comm=&amp;quot;systemd&amp;quot; exe=&amp;quot;&#x2F;usr&#x2F;lib&#x2F;systemd&#x2F;systemd&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop systemd[1]: lightdm.service: Start request repeated too quickly.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop systemd[1]: lightdm.service: Failed with result &amp;#39;exit-code&amp;#39;.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop systemd[1]: Failed to start Light Display Manager.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring gfx_0.0.0 uses VM inv eng 0 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.0.0 uses VM inv eng 1 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.1.0 uses VM inv eng 4 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.2.0 uses VM inv eng 5 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.3.0 uses VM inv eng 6 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.0.1 uses VM inv eng 7 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.1.1 uses VM inv eng 8 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.2.1 uses VM inv eng 9 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring comp_1.3.1 uses VM inv eng 10 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring kiq_2.1.0 uses VM inv eng 11 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring sdma0 uses VM inv eng 12 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring sdma1 uses VM inv eng 13 on hub 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring vcn_dec uses VM inv eng 0 on hub 1
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring vcn_enc0 uses VM inv eng 1 on hub 1
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring vcn_enc1 uses VM inv eng 4 on hub 1
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: amdgpu 0000:28:00.0: amdgpu: ring jpeg_dec uses VM inv eng 5 on hub 1
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: [drm] Initialized amdgpu 3.40.0 20150101 for 0000:28:00.0 on minor 0
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: EDAC amd64: F17h_M70h detected (node 0).
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: EDAC amd64: Node 0: DRAM ECC disabled.
&lt;&#x2F;span&gt;&lt;span&gt;Jan 05 21:20:04 linux-desktop kernel: EDAC amd64: F17h_M70h detected (node 0).
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It looks like LightDM failed to start 5 times before the &lt;code&gt;amdgpu&lt;&#x2F;code&gt; module initialized.
Looking at my &lt;code&gt;Xorg.0.log&lt;&#x2F;code&gt; file confirmed this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;[     5.874] (EE) open &#x2F;dev&#x2F;dri&#x2F;card0: No such file or directory
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (WW) Falling back to old probe method for modesetting
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) open &#x2F;dev&#x2F;dri&#x2F;card0: No such file or directory
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) Screen 0 deleted because of no matching config section.
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (II) UnloadModule: &amp;quot;modesetting&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) Device(s) detected, but none match those in the config file.
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE)
&lt;&#x2F;span&gt;&lt;span&gt;Fatal server error:
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) no screens found(EE)
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE)
&lt;&#x2F;span&gt;&lt;span&gt;Please consult the The X.Org Foundation support
&lt;&#x2F;span&gt;&lt;span&gt;	at http:&#x2F;&#x2F;wiki.x.org
&lt;&#x2F;span&gt;&lt;span&gt; for help.
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) Please also check the log file at &amp;quot;&#x2F;var&#x2F;log&#x2F;Xorg.0.log&amp;quot; for additional information.
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE)
&lt;&#x2F;span&gt;&lt;span&gt;[     5.874] (EE) Server terminated with error (1). Closing log file.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The interesting part being &lt;code&gt;open &#x2F;dev&#x2F;dri&#x2F;card0: No such file or directory&lt;&#x2F;code&gt;.
The fix was actually pretty straightforward as this is a known issue on the &lt;a href=&quot;https:&#x2F;&#x2F;wiki.archlinux.org&#x2F;index.php&#x2F;LightDM#LightDM_does_not_appear_or_monitor_only_displays_TTY_output&quot;&gt;ArchWiki&lt;&#x2F;a&gt;.
It&#x27;s as simple as adding the following to your &lt;code&gt;lightdm.conf&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;   [LightDM]
&lt;&#x2F;span&gt;&lt;span&gt;   logind-check-graphical=true
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;After a reboot, LightDM is back to working!&lt;&#x2F;p&gt;
&lt;p&gt;I enjoy the fact that:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;My operating system lets me debug it&lt;&#x2F;li&gt;
&lt;li&gt;It has ample documentation and an active community around it.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using Dhall to manage my docker-compose.yml</title>
        <published>2020-12-27T00:00:00+00:00</published>
        <updated>2020-12-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/first-time-with-dhall/"/>
        <id>https://blog.imraniqbal.org/first-time-with-dhall/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/first-time-with-dhall/">&lt;p&gt;I saw &lt;a href=&quot;https:&#x2F;&#x2F;dhall-lang.org&#x2F;&quot;&gt;Dhall&lt;&#x2F;a&gt; mentioned on a job posting for NoRedInk.
I had never heard of it before but NoRedInk did create &lt;a href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; and are fond of functional tooling so it piqued my interest.&lt;&#x2F;p&gt;
&lt;p&gt;For reference, Dhall is a simple language.
It&#x27;s not Turing complete, but it&#x27;s strongly typed.
It&#x27;s primarily used for managing configuration files.
It has some helpers to output JSON&#x2F;YML&#x2F;bash, but you can also output to plain text.&lt;&#x2F;p&gt;
&lt;p&gt;The website does a good job of showing what it can do and its use cases so I wont dive into those.
Converting my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;compose-spec&#x2F;compose-spec&#x2F;blob&#x2F;master&#x2F;spec.md#compose-file&quot;&gt;docker-compose.yaml&lt;&#x2F;a&gt; that I use to manage my media server was the first usecase I could think of.&lt;&#x2F;p&gt;
&lt;p&gt;I wont be posting the full source but I hope I convey how much fun I had working with Dhall.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;experiment&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#experiment&quot; aria-label=&quot;Anchor link for: experiment&quot;&gt;
    #
&lt;&#x2F;a&gt;
Experiment&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ll just be focusing on a small part of the docker-compose file, namely the ports.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s say this is my compose file&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-yml &quot;&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;plex&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;linuxserver&#x2F;plex:latest
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;ports&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;32400:32400
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;32400:32400&#x2F;udp
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;environment&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;VERSION&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;latest&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A quick translate over would get us&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let plex =
&lt;&#x2F;span&gt;&lt;span&gt;      { image = &amp;quot;linuxserver&#x2F;plex:latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      , ports = [ &amp;quot;32400:32400&amp;quot;, &amp;quot;32400:32400&#x2F;udp&amp;quot; ]
&lt;&#x2F;span&gt;&lt;span&gt;      , environment.VERSION = &amp;quot;latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;in  { services.plex = plex }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When passed into &lt;code&gt;dhall&lt;&#x2F;code&gt; gets us this:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; dhall --file test.dhall
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;{ services.plex
&lt;&#x2F;span&gt;&lt;span&gt;  =
&lt;&#x2F;span&gt;&lt;span&gt;  { environment.VERSION = &amp;quot;latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  , image = &amp;quot;linuxserver&#x2F;plex:latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  , ports = [ &amp;quot;32400:32400&amp;quot;, &amp;quot;32400:32400&#x2F;udp&amp;quot; ]
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;dhall-to-yaml&lt;&#x2F;code&gt; produces the following:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; dhall-to-yaml --file test.dhall
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;services:
&lt;&#x2F;span&gt;&lt;span&gt;  plex:
&lt;&#x2F;span&gt;&lt;span&gt;    environment:
&lt;&#x2F;span&gt;&lt;span&gt;      VERSION: latest
&lt;&#x2F;span&gt;&lt;span&gt;    image: linuxserver&#x2F;plex:latest
&lt;&#x2F;span&gt;&lt;span&gt;    ports:
&lt;&#x2F;span&gt;&lt;span&gt;      - &amp;quot;32400:32400&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      - &amp;quot;32400:32400&#x2F;udp&amp;quot;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Which is the exact same file (keys are sorted so the output is deterministic).&lt;&#x2F;p&gt;
&lt;p&gt;I always forget what order the ports should go (host:container) so let&#x27;s take advantage of Dhall to make it easier on me.
Before I make any changes to the file I&#x27;m going to be grabbing the Dhall SHA so I can compare it to my refactored code&#x27;s SHA, if they are equal then the output has not changed.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; dhall hash --file test.dhall
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First lets move &lt;code&gt;ports&lt;&#x2F;code&gt; into it&#x27;s own variable and verify the hash&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let ports
&lt;&#x2F;span&gt;&lt;span&gt;    : List Text
&lt;&#x2F;span&gt;&lt;span&gt;    = [ &amp;quot;32400:32400&amp;quot;, &amp;quot;32400:32400&#x2F;udp&amp;quot; ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let plex =
&lt;&#x2F;span&gt;&lt;span&gt;      { image = &amp;quot;linuxserver&#x2F;plex:latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      , ports
&lt;&#x2F;span&gt;&lt;span&gt;      , environment.VERSION = &amp;quot;latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;in  { services.plex = plex }
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; dhall hash --file test.dhall
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;OK we can be sure we didn&#x27;t change our output at all. Don&#x27;t worry about &lt;code&gt;: List Text&lt;&#x2F;code&gt; that is just a type annotation.&lt;&#x2F;p&gt;
&lt;p&gt;Lets start by making a type that we can store the port mapping in and a function to convert it to text.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;    : Type
&lt;&#x2F;span&gt;&lt;span&gt;    = { host : Natural, container : Natural }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let PortMapping&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;    : PortMapping -&amp;gt; Text
&lt;&#x2F;span&gt;&lt;span&gt;    = \(pm : PortMapping) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;${Natural&#x2F;show pm.host}:${Natural&#x2F;show pm.container}&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can supply the ports we want in any order and be confident the output order is correct.
Let&#x27;s update &lt;code&gt;ports&lt;&#x2F;code&gt; to use this new functionality.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let ports
&lt;&#x2F;span&gt;&lt;span&gt;    : List Text
&lt;&#x2F;span&gt;&lt;span&gt;    = [ PortMapping&#x2F;show { host = 32400, container = 32400 }
&lt;&#x2F;span&gt;&lt;span&gt;      , &amp;quot;${PortMapping&#x2F;show { host = 32400, container = 32400 }}&#x2F;udp&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our hash is still &lt;code&gt;sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd&lt;&#x2F;code&gt; so we haven&#x27;t broken anything yet.&lt;&#x2F;p&gt;
&lt;p&gt;This is better but we still need to do some string interpolation if we want to create a UDP port mapping.
The &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;config&#x2F;containers&#x2F;container-networking&#x2F;&quot;&gt;docker documentation&lt;&#x2F;a&gt; says the notation without &lt;code&gt;&#x2F;udp&lt;&#x2F;code&gt; or &lt;code&gt;&#x2F;tcp&lt;&#x2F;code&gt; will default to tcp.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s create an enum that will represent these two and update our code to use it.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let Port
&lt;&#x2F;span&gt;&lt;span&gt;    : Type
&lt;&#x2F;span&gt;&lt;span&gt;    = &amp;lt; tcp : PortMapping | udp : PortMapping &amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let Port&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;    : Port -&amp;gt; Text
&lt;&#x2F;span&gt;&lt;span&gt;    = \(p : Port) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        merge
&lt;&#x2F;span&gt;&lt;span&gt;          { tcp = PortMapping&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;          , udp = \(pm : PortMapping) -&amp;gt; &amp;quot;${PortMapping&#x2F;show pm}&#x2F;udp&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;          }
&lt;&#x2F;span&gt;&lt;span&gt;          p
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let ports
&lt;&#x2F;span&gt;&lt;span&gt;    : List Text
&lt;&#x2F;span&gt;&lt;span&gt;    = [ Port&#x2F;show (Port.tcp { host = 32400, container = 32400 })
&lt;&#x2F;span&gt;&lt;span&gt;      , Port&#x2F;show (Port.udp { container = 32400, host = 32400 })
&lt;&#x2F;span&gt;&lt;span&gt;      ]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Much nicer! And our hash is still producing &lt;code&gt;sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now what would be even nicer is we did not have to specify the port twice if they are the same value.
Let&#x27;s do that by expanding our &lt;code&gt;Port&lt;&#x2F;code&gt; enum to supply a &lt;code&gt;tcp_mirror&lt;&#x2F;code&gt; and a &lt;code&gt;udp_mirror&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let Port
&lt;&#x2F;span&gt;&lt;span&gt;    : Type
&lt;&#x2F;span&gt;&lt;span&gt;    = &amp;lt; tcp : PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;      | udp : PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;      | tcp_mirror : Natural
&lt;&#x2F;span&gt;&lt;span&gt;      | udp_mirror : Natural
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let Port&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;    : Port -&amp;gt; Text
&lt;&#x2F;span&gt;&lt;span&gt;    = \(p : Port) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        let udpPortMapping = \(pm : PortMapping) -&amp;gt; &amp;quot;${PortMapping&#x2F;show pm}&#x2F;udp&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        in  merge
&lt;&#x2F;span&gt;&lt;span&gt;              { tcp = PortMapping&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;              , udp = udpPortMapping
&lt;&#x2F;span&gt;&lt;span&gt;              , tcp_mirror =
&lt;&#x2F;span&gt;&lt;span&gt;                  \(port : Natural) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    PortMapping&#x2F;show { host = port, container = port }
&lt;&#x2F;span&gt;&lt;span&gt;              , udp_mirror =
&lt;&#x2F;span&gt;&lt;span&gt;                  \(port : Natural) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    udpPortMapping { host = port, container = port }
&lt;&#x2F;span&gt;&lt;span&gt;              }
&lt;&#x2F;span&gt;&lt;span&gt;              p
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let ports
&lt;&#x2F;span&gt;&lt;span&gt;    : List Text
&lt;&#x2F;span&gt;&lt;span&gt;    = [ Port&#x2F;show (Port.tcp_mirror 32400), Port&#x2F;show (Port.udp_mirror 32400) ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;-- sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can include the &lt;code&gt;List&#x2F;map&lt;&#x2F;code&gt; function from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dhall-lang&#x2F;dhall-lang&#x2F;tree&#x2F;master&#x2F;Prelude&quot;&gt;&lt;code&gt;Prelude&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; so we do not have manually transform our port list.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;final-test-dhall&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#final-test-dhall&quot; aria-label=&quot;Anchor link for: final-test-dhall&quot;&gt;
    #
&lt;&#x2F;a&gt;
Final &lt;code&gt;test.dhall&lt;&#x2F;code&gt;&lt;&#x2F;h4&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;let List&#x2F;map = https:&#x2F;&#x2F;prelude.dhall-lang.org&#x2F;List&#x2F;map
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;    : Type
&lt;&#x2F;span&gt;&lt;span&gt;    = { host : Natural, container : Natural }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let PortMapping&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;    : PortMapping -&amp;gt; Text
&lt;&#x2F;span&gt;&lt;span&gt;    = \(pm : PortMapping) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        &amp;quot;${Natural&#x2F;show pm.host}:${Natural&#x2F;show pm.container}&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let Port
&lt;&#x2F;span&gt;&lt;span&gt;    : Type
&lt;&#x2F;span&gt;&lt;span&gt;    = &amp;lt; tcp : PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;      | udp : PortMapping
&lt;&#x2F;span&gt;&lt;span&gt;      | tcp_mirror : Natural
&lt;&#x2F;span&gt;&lt;span&gt;      | udp_mirror : Natural
&lt;&#x2F;span&gt;&lt;span&gt;      &amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let Port&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;    : Port -&amp;gt; Text
&lt;&#x2F;span&gt;&lt;span&gt;    = \(p : Port) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;        let udpPortMapping = \(pm : PortMapping) -&amp;gt; &amp;quot;${PortMapping&#x2F;show pm}&#x2F;udp&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;        in  merge
&lt;&#x2F;span&gt;&lt;span&gt;              { tcp = PortMapping&#x2F;show
&lt;&#x2F;span&gt;&lt;span&gt;              , udp = udpPortMapping
&lt;&#x2F;span&gt;&lt;span&gt;              , tcp_mirror =
&lt;&#x2F;span&gt;&lt;span&gt;                  \(port : Natural) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    PortMapping&#x2F;show { host = port, container = port }
&lt;&#x2F;span&gt;&lt;span&gt;              , udp_mirror =
&lt;&#x2F;span&gt;&lt;span&gt;                  \(port : Natural) -&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;                    udpPortMapping { host = port, container = port }
&lt;&#x2F;span&gt;&lt;span&gt;              }
&lt;&#x2F;span&gt;&lt;span&gt;              p
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let portsList
&lt;&#x2F;span&gt;&lt;span&gt;    : List Port
&lt;&#x2F;span&gt;&lt;span&gt;    = [ Port.tcp_mirror 32400, Port.udp_mirror 32400 ]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;let plex =
&lt;&#x2F;span&gt;&lt;span&gt;      { image = &amp;quot;linuxserver&#x2F;plex:latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      , ports = List&#x2F;map Port Text Port&#x2F;show portsList
&lt;&#x2F;span&gt;&lt;span&gt;      , environment.VERSION = &amp;quot;latest&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;      }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;in  { services.plex = plex }
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;-- sha256:db23b00a8e79e084306b72ecc788f0b95579080c883cb72045a3ae6ea43999fd
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;my-conclusion&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#my-conclusion&quot; aria-label=&quot;Anchor link for: my-conclusion&quot;&gt;
    #
&lt;&#x2F;a&gt;
My Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Using Dhall is super fun.
I can totally see the usefulness in a shared team environment for managing configurations.
I think I went a bit overboard as my ~120 line compose file is now ~400 lines of Dhall code 😅.
Except now it&#x27;s statically typed and it will be super easy to add new services to my server (or in context of the experiment, new ports to the Plex service).&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Stop showing me ads on my TV player</title>
        <published>2020-12-21T00:00:00+00:00</published>
        <updated>2020-12-21T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/ads-fire-tv/"/>
        <id>https://blog.imraniqbal.org/ads-fire-tv/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/ads-fire-tv/">&lt;p&gt;I am a huge fan of dumb TV&#x27;s made smart via a TV player, something along the likes of an apple TV or a Roku.&lt;&#x2F;p&gt;
&lt;p&gt;The first device that I owned was the first generation of a google chromecast.
It worked decently enough, but it would struggle when trying to play back some of the more higher definition media that I had.&lt;&#x2F;p&gt;
&lt;p&gt;I upgraded from the chromecast to a Roku 3.
It worked splendidly for a while, up until they decided to take up half the home screen with a giant ad.
This pissed me off. I already paid around $100-ish dollars for the damn thing.
And now they want to shove ads down my throat.&lt;&#x2F;p&gt;
&lt;p&gt;I then upgraded to an amazon fire TV. These things were not available in Canada so I had to order it from the states.
It came out to about $150 and also worked fine for a short period of time.
That is until, you guessed it, they started showing &lt;a href=&quot;https:&#x2F;&#x2F;advertising.amazon.com&#x2F;en-ca&#x2F;resources&#x2F;ad-specs&#x2F;fire-tv&quot;&gt;ads on the home screen&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am not against advertising and I understand why it exists and its use in free products.
The issue is that these things are not free (or cheap).
It sucks to pay for something and to have ads shown on top of that.
The shittier part is that ads are sometimes for amazon&#x27;s own products like prime video or prime music.
I already have amazon prime. Why are you advertising these things to me?
I paid for the hardware and have a subscription on top of that. Why am I getting ads?
How much money is enough money?&lt;&#x2F;p&gt;
&lt;p&gt;This pissed me off enough that I spent my evening trying to get rid of them.
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;codefaktor&#x2F;FTVLaunchX&quot;&gt;FTVLaunchX&lt;&#x2F;a&gt; is an project that will let you set your launcher to anything else.
There&#x27;s also &lt;a href=&quot;https:&#x2F;&#x2F;tv.aptoide.com&#x2F;&quot;&gt;aptoide&lt;&#x2F;a&gt; that&#x27;s worth mentioning (install untrusted apps at your own risk).&lt;&#x2F;p&gt;
&lt;p&gt;When I feel like spending some more money (yet again) for an ad free experience I will go for a vera from &lt;a href=&quot;https:&#x2F;&#x2F;osmc.tv&#x2F;&quot;&gt;https:&#x2F;&#x2F;osmc.tv&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Please for goodness sake, stop showing me ads on my TV streamer!
All I need you to do is stream content from my local network to my TV!
If you want to show me ads, at least let me get the hardware without paying, otherwise bugger off.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Goodbye Google Play Music</title>
        <published>2020-11-30T00:00:00+00:00</published>
        <updated>2020-11-30T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/goodbye-google-play-music/"/>
        <id>https://blog.imraniqbal.org/goodbye-google-play-music/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/goodbye-google-play-music/">&lt;p&gt;This weekend I spent some time migrating my music off Google Play Music to Spotify.&lt;&#x2F;p&gt;
&lt;p&gt;I am going to miss its auto generated radio stations which helped introduce me to a lot of new music.&lt;&#x2F;p&gt;
&lt;p&gt;The last Google product that I actually enjoyed was Google Inbox, which was subsequently also killed.
With this I am probably not going to be buying into any new Google products.&lt;&#x2F;p&gt;
&lt;p&gt;The lesson learnt is that do not pay for a service that is not the primary income
for that company. Otherwise you do not have any garauntees that they wont kill
the service off once they&#x27;re bored with it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s to hoping Spotify won&#x27;t shut down in the immediate future.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Moving my blog to zola</title>
        <published>2020-11-29T00:00:00+00:00</published>
        <updated>2020-11-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/moving-to-zola/"/>
        <id>https://blog.imraniqbal.org/moving-to-zola/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/moving-to-zola/">&lt;p&gt;I decided to move my blog to &lt;a href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; instead of Docusaurus.
They are both static site generators, but Zola is much faster.&lt;&#x2F;p&gt;
&lt;p&gt;Heres a Docusaurus build:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;yarn_build.png&quot; alt=&quot;yarn build&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And heres a Zola build:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;zola_build.png&quot; alt=&quot;Zola build&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>My first time using rust</title>
        <published>2020-11-01T00:00:00+00:00</published>
        <updated>2020-11-01T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/first-time-with-rust/"/>
        <id>https://blog.imraniqbal.org/first-time-with-rust/</id>
        
        <summary type="html">&lt;p&gt;Replacing one of my shell scripts with a compiled binary using rust.&lt;&#x2F;p&gt;
</summary>
        
    </entry>
    <entry xml:lang="en">
        <title>The wonders of a proxy auto-config (PAC) file</title>
        <published>2020-10-12T00:00:00+00:00</published>
        <updated>2020-10-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/pac-file/"/>
        <id>https://blog.imraniqbal.org/pac-file/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/pac-file/">&lt;p&gt;How I improved my work from home setup using a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Proxy_auto-config&quot;&gt;proxy auto-config&lt;&#x2F;a&gt; (PAC) file.&lt;&#x2F;p&gt;
&lt;!--more--&gt;
&lt;p&gt;My last couple of roles have had me using a MacBook Pro to develop on. I don&#x27;t
any qualms against mac os and much rather prefer it to a windows , though
ideally I would like to use linux as it&#x27;s what I use on my desktop computer.&lt;&#x2F;p&gt;
&lt;p&gt;As far as development goes I use the command line whenever I can, so my weapons
of choice end up being (neo)vim and tmux. These work quite well on
mac os (and are installable via homebrew). I have not tried to use these on
windows but I do know its possible via the linux subsystem (at that point I&#x27;d
rather be using linux).&lt;&#x2F;p&gt;
&lt;p&gt;When I work from home I attach the mac to a dongle that supplies it
power and an Ethernet connection. I have the power settings set to never let the mac sleep,
even with its lid closed. I can then connect to the mac with the following
command from my desktop.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ssh&lt;&#x2F;span&gt;&lt;span&gt;  user@laptops-ip&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span&gt; tmux&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -2&lt;&#x2F;span&gt;&lt;span&gt; attach
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives me my tmux session from the work mac on my regular desktop. This is
convenient as I do not have a lot of space in my home for a separate work
desk&#x2F;setup.&lt;&#x2F;p&gt;
&lt;p&gt;I can do almost everything I need to via this setup (slack and related
tools are all accessible via their web ui&#x27;s) and over all it&#x27;s been working
pretty well. That is until my latest role.&lt;&#x2F;p&gt;
&lt;p&gt;This new role requires me to have a VPN connection active to access
some internal sites (some of which are sorely needed during development). Up until
now I had been developing as much as can using the ssh method mentioned
above and then switching over to my work laptop to access the restricted internal
sites. After a couple of weeks this grew pretty tiring. I would like a
solution that would:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Let me continue using my existing setup&lt;&#x2F;li&gt;
&lt;li&gt;Not require me to install any work related software on my personal computer&lt;&#x2F;li&gt;
&lt;li&gt;Proxy work related connections through my work computer and nothing else&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;socks5-proxy-over-ssh&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#socks5-proxy-over-ssh&quot; aria-label=&quot;Anchor link for: socks5-proxy-over-ssh&quot;&gt;
    #
&lt;&#x2F;a&gt;
SOCKS5 Proxy Over SSH&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;ssh&lt;&#x2F;code&gt; is quite a wonderful tool and comes with a solution built in for proxy-ing
requests. Lets take a look at &lt;code&gt;man ssh&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;man&lt;&#x2F;span&gt;&lt;span&gt; ssh
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;-D &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span&gt;bind_address:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;port
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;Specifies&lt;&#x2F;span&gt;&lt;span&gt; a local “dynamic” application-level port forwarding.  This works by allocating a socket to listen to port on the local
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;side,&lt;&#x2F;span&gt;&lt;span&gt; optionally bound to the specified bind_address.  Whenever a connection is made to this port, the connection is forwarded
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;over&lt;&#x2F;span&gt;&lt;span&gt; the secure channel, and the application protocol is then used to determine where to connect to from the remote machine.
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;Currently&lt;&#x2F;span&gt;&lt;span&gt; the SOCKS4 and SOCKS5 protocols are supported, and ssh will act as a SOCKS server.  Only root can forward privileged
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;ports.&lt;&#x2F;span&gt;&lt;span&gt;  Dynamic port forwardings can also be specified in the configuration file.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;IPv6&lt;&#x2F;span&gt;&lt;span&gt; addresses can be specified by enclosing the address in square brackets.  Only the superuser can forward privileged ports.
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;By&lt;&#x2F;span&gt;&lt;span&gt; default, the local port is bound in accordance with the GatewayPorts setting.  However, an explicit bind_address may be used
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt; bind the connection to a specific address.  The bind_address of “localhost” indicates that the listening port be bound for
&lt;&#x2F;span&gt;&lt;span&gt;     &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;local &lt;&#x2F;span&gt;&lt;span&gt;use only, while an empty address or ‘*’ indicates that the port should be available from all interfaces.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;---
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This would cover my first two requirements pretty well. All I have to run is&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#212733;color:#ccc9c2;&quot;&gt;&lt;code&gt;&lt;span&gt;ssh -D 1234 user@laptops-ip -C -N
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and this opens a SOCKS5 proxy available at &lt;code&gt;localhost:1234&lt;&#x2F;code&gt; on my local machine.
If I update my Firefox settings to use it I can now access my work internal
sites without having to install the VPN locally. The downside is now that &lt;strong&gt;ALL&lt;&#x2F;strong&gt;
my Firefox traffic is passing through my work computer. This is where the PAC
file comes into play.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-pac-file&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#the-pac-file&quot; aria-label=&quot;Anchor link for: the-pac-file&quot;&gt;
    #
&lt;&#x2F;a&gt;
The PAC File&lt;&#x2F;h2&gt;
&lt;p&gt;The PAC file is pretty simple. It&#x27;s a single JavaScript file that exposes a
function &lt;code&gt;FindProxyForURL(url, host)&lt;&#x2F;code&gt; that lets Firefox know whether to
proxy a request for a url. More information is available via &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTTP&#x2F;Proxy_servers_and_tunneling&#x2F;Proxy_Auto-Configuration_(PAC)_file&quot;&gt;MDN&lt;&#x2F;a&gt;.
All that&#x27;s left to do is to update firefox&#x27;s settings to use it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;ff_pac_setting.png&quot; alt=&quot;Firefox network settings&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here is my PAC file&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;DIRECT &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;DIRECT&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;SOCKS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;SOCKS5 127.0.0.1:1234&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;const &lt;&#x2F;span&gt;&lt;span&gt;WORK_URLS &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;*.internal.site&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;FindProxyForURL&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;host&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(WORK_URLS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;some&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;shExpMatch&lt;&#x2F;span&gt;&lt;span&gt;(host&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span&gt;h))) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;SOCKS&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffa759;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span&gt;DIRECT&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Setting up my blog</title>
        <published>2020-10-11T00:00:00+00:00</published>
        <updated>2020-10-11T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.imraniqbal.org/blog-setup/"/>
        <id>https://blog.imraniqbal.org/blog-setup/</id>
        
        <content type="html" xml:base="https://blog.imraniqbal.org/blog-setup/">&lt;p&gt;I wanted to use &lt;a href=&quot;https:&#x2F;&#x2F;v2.docusaurus.io&#x2F;&quot;&gt;Docusaurus&lt;&#x2F;a&gt; to setup my blog because I might be using it for work and wanted to test it out.
I am also a fan of the fact that it&#x27;s a static site builder, which means I can use it with a free static site hosting solution.
I decided to go with &lt;a href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;stages-devops-lifecycle&#x2F;pages&#x2F;&quot;&gt;GitLab Pages&lt;&#x2F;a&gt; because I am already using GitLab and it lets me use a custom domain.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-the-gitlab-project&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#setting-up-the-gitlab-project&quot; aria-label=&quot;Anchor link for: setting-up-the-gitlab-project&quot;&gt;
    #
&lt;&#x2F;a&gt;
Setting up the GitLab project&lt;&#x2F;h2&gt;
&lt;p&gt;This was pretty straight forward. I followed this &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;user&#x2F;project&#x2F;pages&#x2F;getting_started&#x2F;pages_from_scratch.html&quot;&gt;guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First I created a simple enough html page.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#f28779;&quot;&gt;echo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;This is me testing stuff.&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; index.html
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Followed by a &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-yaml &quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;alpine:3.12.0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;workflow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;$CI_COMMIT_BRANCH&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;mkdir public
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;cp index.html public&#x2F;index.html
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;quot;master&amp;quot;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then another quick &lt;a href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;user&#x2F;project&#x2F;pages&#x2F;custom_domains_ssl_tls_certification&#x2F;&quot;&gt;guide&lt;&#x2F;a&gt; for setting up a custom domain + SSL certificate.&lt;&#x2F;p&gt;
&lt;p&gt;And voilà!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;img&#x2F;initial_blog.png&quot; alt=&quot;custom domain setup&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;using-docusaurus&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#using-docusaurus&quot; aria-label=&quot;Anchor link for: using-docusaurus&quot;&gt;
    #
&lt;&#x2F;a&gt;
Using Docusaurus&lt;&#x2F;h2&gt;
&lt;p&gt;Like most modern JavaScript projects, to actually use Docusaurus I needed to first install an initializer that would setup a project for me.
The &lt;a href=&quot;https:&#x2F;&#x2F;v2.docusaurus.io&#x2F;docs&#x2F;installation&quot;&gt;install docs&lt;&#x2F;a&gt; mentioned using &lt;code&gt;npx&lt;&#x2F;code&gt; to install and use the initializer.&lt;&#x2F;p&gt;
&lt;p&gt;This was my first time hearing about &lt;code&gt;npx&lt;&#x2F;code&gt; and I wasn&#x27;t too keen on installing yet another JavaScript tool on my system.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;yarn&lt;&#x2F;span&gt;&lt;span&gt; add @docusaurus&#x2F;init@next
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;node&lt;&#x2F;span&gt;&lt;span&gt; node_modules&#x2F;@docusaurus&#x2F;init&#x2F;bin&#x2F;index.js init my-blog classic
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This seemed to work well enough, the sole issue was the Docusaurus files were now in a dir called &lt;code&gt;my-blog&lt;&#x2F;code&gt; rather than my project root.&lt;&#x2F;p&gt;
&lt;p&gt;First I needed to cleanup the install files then move everything in &lt;code&gt;my-blog&lt;&#x2F;code&gt; a directory up.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -rf&lt;&#x2F;span&gt;&lt;span&gt; node_modules package.json yarn.lock
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;mv&lt;&#x2F;span&gt;&lt;span&gt; my-blog&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#f29e74;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt; .
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffd580;&quot;&gt;rm&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ffcc66;&quot;&gt; -rf&lt;&#x2F;span&gt;&lt;span&gt; my-blog
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;yarn start&lt;&#x2F;code&gt; now showed me the default Docusaurus project page. At this point all that I had to do was to follow the &lt;a href=&quot;https:&#x2F;&#x2F;v2.docusaurus.io&#x2F;docs&#x2F;blog#blog-only-mode&quot;&gt;Blog-only mode&lt;&#x2F;a&gt; guide and customize to my needs.&lt;&#x2F;p&gt;
&lt;p&gt;One annoying thing is that the &lt;code&gt;doc&lt;&#x2F;code&gt; directory has to exist with a single &lt;code&gt;.md&lt;&#x2F;code&gt; file in it to prevent Docusaurus from failing to start.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;updating-gitlab-ci-yml&quot;&gt;&lt;a class=&quot;anchor&quot; href=&quot;#updating-gitlab-ci-yml&quot; aria-label=&quot;Anchor link for: updating-gitlab-ci-yml&quot;&gt;
    #
&lt;&#x2F;a&gt;
Updating &lt;code&gt;.gitlab-ci.yml&lt;&#x2F;code&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Updated my CI configuration to use a node image and to cache &lt;code&gt;node_modules&lt;&#x2F;code&gt;&#x2F;yarn cache&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yml&quot; style=&quot;background-color:#212733;color:#ccc9c2;&quot; class=&quot;language-yml &quot;&gt;&lt;code class=&quot;language-yml&quot; data-lang=&quot;yml&quot;&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;image&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;node:14.13.1-alpine3.12
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;workflow&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;$CI_COMMIT_BRANCH&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;cache&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;key&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;quot;$CI_PROJECT_ID&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;node_modules&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;.yarn&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;script&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;yarn config set cache-folder .yarn
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;yarn install
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;yarn build --out-dir public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;artifacts&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;paths&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;      - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;public
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;rules&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;    - &lt;&#x2F;span&gt;&lt;span style=&quot;color:#73d0ff;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color:#ccc9c2cc;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bae67e;&quot;&gt;&amp;#39;$CI_COMMIT_BRANCH == &amp;quot;master&amp;quot;&amp;#39;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
</feed>
