<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>kistu</title>
  <link rel="alternate" href="/" />
  <link rel="self" href="/index.xml" />
  <subtitle>Recent content on kistu</subtitle>
  <id>/</id>
  <generator uri="http://gohugo.io" version="0.124.0">Hugo</generator>
  <language>en</language>
  <updated>2026-07-06T15:03:37+02:00</updated>
  <author>
    
    
  </author>
  
      <entry>
        <title>Automatically Generated Images</title>
        <link rel="alternate" href="/posts/011-automatically-generated-images/" />
        <id>/posts/011-automatically-generated-images/</id>
        <published>2026-07-06T15:03:37+02:00</published>
        <updated>2026-07-06T15:03:37+02:00</updated>
        <summary type="html">Images can broadly be classified by their source:
Camera Software The aspect that makes an image taken from a camera valued is that it is a represention of the real world.
The aspect that makes digitally created images valued is that is is a deliberate representation of an image in the artist&amp;rsquo;s mind.
Both of these are therefore valued because there is an element that makes it - in some sense - real.</summary>
          <content type="html"><![CDATA[<p>Images can broadly be classified by their source:</p>
<ul>
<li>Camera</li>
<li>Software</li>
</ul>
<p>



    
    <input type="checkbox" id="zoomCheck-9f62e" hidden>
    <label for="zoomCheck-9f62e">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="image-sources.svg" alt="image sources" 
             />
    </label>

</p>
<p>The aspect that makes an image taken from a camera valued is that it is a represention of the real world.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-2180c" hidden>
    <label for="zoomCheck-2180c">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="camera.svg" alt="camera" 
             />
    </label>

</p>
<p>The aspect that makes digitally created images valued is that is is a deliberate representation of an image in the artist&rsquo;s mind.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-76617" hidden>
    <label for="zoomCheck-76617">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="software.svg" alt="software" 
             />
    </label>

</p>
<p>Both of these are therefore valued because there is an element that makes it - in some sense - <strong>real</strong>.</p>
<p>Automatically generated images do not share this element. These images are not representations of the real world, nor are they a deliberate representation of an image in the artist&rsquo;s mind. Generated images are loosely based on patterns from such representations - <strong>this is not sufficient</strong>.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-9a4c6" hidden>
    <label for="zoomCheck-9a4c6">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="auto-generation.svg" alt="auto generation" 
             />
    </label>

</p>
<p>Of course, these are not clear boundaries. And the appeal of automatically generated images cannot be ignored. It is an accelerant - imitating at a moments notice that which previously took much time and resources. It provides to the masses an ability they previously couldn&rsquo;t achieve. However, these imitations mock that which we value in an image. If there are no clear boundaries, we must at least define some scale or varying degree.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-a8fca" hidden>
    <label for="zoomCheck-a8fca">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="scale.svg" alt="scale" 
             />
    </label>

</p>
<p>Images taken from a camera require some signal processing and filtering to convert readings from a sensor to a digital image. This can be considered a minimum necessity. However, further modifications are done using software to change how the image appears - this is not particularly desirable (unless it is deliberate and explicit). Worse, some modern cameras use automatic generation to enchance the image!</p>
<p>



    
    <input type="checkbox" id="zoomCheck-e199a" hidden>
    <label for="zoomCheck-e199a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="camera-scale.svg" alt="camera scale" 
             />
    </label>

</p>
<p>Digital images are created using software. The artist has an image in mind, and the software is simply a medium through which it is expressed. Parts of this process can be delegated to automatic generation and classified by the control exerted:</p>
<ul>
<li>Auxillary (how to do X in Y)</li>
<li>Partial (do X in Y)</li>
<li>Unconstrained (create Z)</li>
</ul>
<p>How different is the result from the image that the artist had in mind?</p>
<ol>
<li>Auxillary control is very beneficial - learning &ldquo;how to do X in Y&rdquo; (typically through text generation) doesn&rsquo;t change the end result and can assist in reaching it more quickly.</li>
<li>Partial control is more complex . To &ldquo;do X in Y&rdquo;, the classification depends on what X is. If X is precise and restrictive, it may be considered fair use.</li>
<li>To &ldquo;create Z&rdquo; is generally a massive deviation simply because there is no way of precisely capturing the artists intent.</li>
</ol>
<p>



    
    <input type="checkbox" id="zoomCheck-3af62" hidden>
    <label for="zoomCheck-3af62">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="software-scale.svg" alt="software scale" 
             />
    </label>

</p>
<p>The problem is that all these delegation types may eventually produce a comparable result. It is the artists responsibility to be deliberate and transparent, and it is the patrons responsibility to demand it.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-30767" hidden>
    <label for="zoomCheck-30767">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="the-result.svg" alt="the result" 
             />
    </label>

</p>
<p>What if the artist does not have a particular image in mind? It may be vague; it is not inconceivable that the process of creation influences and directs the final form. For such creations, the influence may be subject to the same scrutiny as the creation. Is the influence a true representation of the real world, or a deliberate representation of an artist?</p>
<p>



    
    <input type="checkbox" id="zoomCheck-cd84a" hidden>
    <label for="zoomCheck-cd84a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="influences.svg" alt="influences" 
             />
    </label>

</p>
<p>Ultimately, this is a preference. It is possible to define the boundary concretely, to draw a line seperating that which is acceptable and that which is not - but this is subject to change and scrutiny. The artist must decide.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-66bca" hidden>
    <label for="zoomCheck-66bca">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="boundary.svg" alt="boundary" 
             />
    </label>

</p>
<p>Summary:</p>
<ul>
<li>The value of an image from a camera is it&rsquo;s accurate representation of the real world.</li>
<li>The value of an image created or modified using software is the deliberate representation of an image in the artist&rsquo;s mind.</li>
<li>Automatically generated images are based on patterns in these representations - this is insufficient to serve as an accurate or deliberate representation.</li>
<li>This is a distinction of degree - there are various scales of influence.</li>
<li>It is the artists repsonsibility to be deliberate and transparent.</li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>Moderation is Necessary</title>
        <link rel="alternate" href="/posts/010-moderation-is-necessary/" />
        <id>/posts/010-moderation-is-necessary/</id>
        <published>2026-05-12T13:20:31+02:00</published>
        <updated>2026-05-12T13:20:31+02:00</updated>
        <summary type="html">Moderation is necessary.
Too little is not good.
Too much is not good.
These extremes are not great.
You must first realize this balance. If you listen carefully, too much and too little with guide you there.
And then you will realize your potential.</summary>
          <content type="html"><![CDATA[<p>Moderation is necessary.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-ff862" hidden>
    <label for="zoomCheck-ff862">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="moderation.svg" alt="moderation" 
             />
    </label>

</p>
<p>Too little is not good.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-e626d" hidden>
    <label for="zoomCheck-e626d">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="too-little.svg" alt="too little" 
             />
    </label>

</p>
<p>Too much is not good.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-4d8a7" hidden>
    <label for="zoomCheck-4d8a7">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="too-much.svg" alt="too much" 
             />
    </label>

</p>
<p>These extremes are not great.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-d268a" hidden>
    <label for="zoomCheck-d268a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="extremes.svg" alt="extremes" 
             />
    </label>

</p>
<p>You must first realize this balance. If you listen carefully, too much and too little with guide you there.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-e6008" hidden>
    <label for="zoomCheck-e6008">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="balance.svg" alt="balance" 
             />
    </label>

</p>
<p>And then you will realize your potential.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-cf19e" hidden>
    <label for="zoomCheck-cf19e">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="potential.svg" alt="potential" 
             />
    </label>

</p>
]]></content>
      </entry>
      <entry>
        <title>Reset Windows 10/11 Password</title>
        <link rel="alternate" href="/posts/006-reset-windows-1011-password/" />
        <id>/posts/006-reset-windows-1011-password/</id>
        <published>2026-05-10T10:55:13+02:00</published>
        <updated>2026-05-10T10:55:13+02:00</updated>
        <summary type="html">To reset a Windows 10/11 password (if you cannot login to your local user account), the Accessibility utility can be exploited.
This method does not require an external installation media, but only works for local accounts. If the drive is encrypted with BitLocker, it&amp;rsquo;s key will be required (this method does not reset the key).
Source: https://dev.to/jackscott7/reset-windows-1011-password-without-usb-or-reinstall-43m6
Online Microsoft accounts require internet access to login; passwords are tied to this account and cannot be reset locally.</summary>
          <content type="html"><![CDATA[<p>To reset a Windows 10/11 password (if you cannot login to your local user account), the Accessibility utility can be exploited.</p>
<p>This method does not require an external installation media, but only works for local accounts. If the drive is encrypted with BitLocker, it&rsquo;s key will be required (this method does not reset the key).</p>
<p>Source: <a href="https://dev.to/jackscott7/reset-windows-1011-password-without-usb-or-reinstall-43m6">https://dev.to/jackscott7/reset-windows-1011-password-without-usb-or-reinstall-43m6</a></p>
<p>Online Microsoft accounts require internet access to login; passwords are tied to this account and cannot be reset locally.</p>
<h1 id="boot-into-windows-recovery">Boot into Windows Recovery</h1>
<ul>
<li>Press the power button once the Windows/Computer logo appears on startup</li>
<li>Repeat 3 times; the next boot should show a &ldquo;Recovery&rdquo; screen</li>
<li>Click on &ldquo;See advanced repair options&rdquo;</li>
</ul>
<p>Some sources recommend pressing F11 on startup to enter &ldquo;Recovery&rdquo;, but this is not reliable; some machines may use F10 or F12 instead. Use the force shutdown method above instead.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-8ae5a" hidden>
    <label for="zoomCheck-8ae5a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="01-windows-recovery.png" alt="windows recovery" 
             />
    </label>

</p>
<h1 id="open-cmd-session">Open cmd session</h1>
<p>Select &ldquo;Troubleshoot&rdquo;




    
    <input type="checkbox" id="zoomCheck-e5a52" hidden>
    <label for="zoomCheck-e5a52">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="02-choose-an-option.png" alt="choose an option" 
             />
    </label>

</p>
<p>Select &ldquo;Advanced options&rdquo;




    
    <input type="checkbox" id="zoomCheck-1e488" hidden>
    <label for="zoomCheck-1e488">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="03-troubleshoot.png" alt="troubleshoot" 
             />
    </label>

</p>
<p>Select &ldquo;Command Prompt&rdquo;




    
    <input type="checkbox" id="zoomCheck-3fbec" hidden>
    <label for="zoomCheck-3fbec">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="04-advanced-options.png" alt="advanced options" 
             />
    </label>

</p>
<p>This will open a command prompt window.




    
    <input type="checkbox" id="zoomCheck-d643f" hidden>
    <label for="zoomCheck-d643f">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="05-cmd.png" alt="cmd" 
             />
    </label>

</p>
<h1 id="overwrite-utilman-with-cmd">Overwrite utilman with cmd</h1>
<p>Identify the operating system partition (value for &ldquo;osdevice&rdquo;). This is typically &ldquo;C:\&rdquo;, but it may be different.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bcdedit
</span></span></code></pre></div><p>



    
    <input type="checkbox" id="zoomCheck-e9a72" hidden>
    <label for="zoomCheck-e9a72">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="06-bcdedit.png" alt="bcdedit" 
             />
    </label>

</p>
<p>Create a backup of &ldquo;Utilman.exe&rdquo;, and then replace it with &ldquo;cmd.exe&rdquo;:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">copy</span> C:\Windows\System32\Utilman.exe C:\Windows\System32\Utilman.bak
</span></span><span class="line"><span class="cl"><span class="k">copy</span> C:\Windows\System32\cmd.exe     C:\Windows\System32\Utilman.exe
</span></span></code></pre></div><p>If prompted to overwrite, accept by typing &ldquo;Yes&rdquo;.</p>
<blockquote>
<p>Windows is not case sensitive. If you experience difficulty due to keyboard layout issues, special symbols (like &ldquo;\&rdquo;) can be copied from the terminal prompt.</p>
</blockquote>
<p>



    
    <input type="checkbox" id="zoomCheck-7c8f3" hidden>
    <label for="zoomCheck-7c8f3">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="07-copy.png" alt="copy" 
             />
    </label>

</p>
<p>The command prompt window can be closed and the system can now be rebooted.</p>
<h1 id="reset-password">Reset Password</h1>
<p>After startup, select the &ldquo;Accessibility&rdquo; icon at the bottom right of the screen. This will open a command prompt window.




    
    <input type="checkbox" id="zoomCheck-a25ab" hidden>
    <label for="zoomCheck-a25ab">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="08-accessibility.png" alt="accessibility" 
             />
    </label>

</p>
<p>List the users (to verify username) and change the password:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">net user
</span></span><span class="line"><span class="cl">net user <span class="s2">&#34;UserNameHere&#34;</span> <span class="s2">&#34;NewPasswordHere&#34;</span>
</span></span></code></pre></div><p>



    
    <input type="checkbox" id="zoomCheck-e50ae" hidden>
    <label for="zoomCheck-e50ae">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="09-password-reset.png" alt="password reset" 
             />
    </label>

</p>
<p>Although the next step is not necessary, it it recommended for security. Once logged in, open a command prompt and restore the Accessibility utility:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl"><span class="k">copy</span> C:\Windows\System32\Utilman.bak C:\Windows\System32\Utilman.exe
</span></span></code></pre></div><p>



    
    <input type="checkbox" id="zoomCheck-c7846" hidden>
    <label for="zoomCheck-c7846">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="10-restore.png" alt="restore" 
             />
    </label>

</p>
<p>If this returns access denied errors like above (even with admin privileges), boot back into recovery mode to restore.</p>
]]></content>
      </entry>
      <entry>
        <title>The Black Box</title>
        <link rel="alternate" href="/posts/008-the-blackbox/" />
        <id>/posts/008-the-blackbox/</id>
        <published>2026-04-26T15:01:49+02:00</published>
        <updated>2026-04-26T15:01:49+02:00</updated>
        <summary type="html">The black box is a representation of a system.
No knowledge about the internal workings of the black box is expected. It may not be known, or you may not know it. The system is only known in terms of inputs and associated outputs.
All inputs are not the same. Some inputs control what the system does.
Other inputs control what the system operates on.
The output can be used to drive inputs - this is feedback.</summary>
          <content type="html"><![CDATA[<p>The black box is a representation of a system.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b3269" hidden>
    <label for="zoomCheck-b3269">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="blackbox.svg" alt="blackbox" 
             />
    </label>

</p>
<p>No knowledge about the internal workings of the black box is expected. It may not be known, or you may not know it. The system is only known in terms of inputs and associated outputs.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-f9775" hidden>
    <label for="zoomCheck-f9775">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="inputs-and-outputs.svg" alt="inputs and outputs" 
             />
    </label>

</p>
<p>All inputs are not the same. Some inputs control what the system does.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-9c54b" hidden>
    <label for="zoomCheck-9c54b">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="control-inputs.svg" alt="control inputs" 
             />
    </label>

</p>
<p>Other inputs control what the system operates on.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-9971c" hidden>
    <label for="zoomCheck-9971c">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="data-inputs.svg" alt="data inputs" 
             />
    </label>

</p>
<p>The output can be used to drive inputs - this is feedback.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-561d2" hidden>
    <label for="zoomCheck-561d2">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="feedback.svg" alt="feedback" 
             />
    </label>

</p>
<p>To know the black box is to know the relation between it&rsquo;s inputs and outputs; to contruct a model of how it might work.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b5e2f" hidden>
    <label for="zoomCheck-b5e2f">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="model.svg" alt="model" 
             />
    </label>

</p>
<p>A model may reveal the internal workings of the black box.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-cc008" hidden>
    <label for="zoomCheck-cc008">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="reveal.svg" alt="reveal" 
             />
    </label>

</p>
<p>Does the black box make you uncomfortable? Does it leave you with the feeling that your knowledge is superficial or incomplete? Do you perhaps seek to know everything? If so, there is one more thing you should know.</p>
<p>All systems have black boxes. Complete knowledge of any system is not possible. Uncovering how a system works - and constructing more models - reveals more black boxes.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-a5072" hidden>
    <label for="zoomCheck-a5072">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="everything.svg" alt="everything" 
             />
    </label>

</p>
<hr>
<p>Some elements were inspired by <a href="https://en.wikipedia.org/wiki/Black_box">https://en.wikipedia.org/wiki/Black_box</a>.</p>
]]></content>
      </entry>
      <entry>
        <title>The Imbalance</title>
        <link rel="alternate" href="/posts/007-the-imbalance/" />
        <id>/posts/007-the-imbalance/</id>
        <published>2026-03-28T23:37:05+02:00</published>
        <updated>2026-03-29T18:32:27+02:00</updated>
        <summary type="html">First, there is simplicity.
Over billions of years, that simplicity becomes slightly more complex.
Complexity is accelerated by dependency.
That complexity can only persist because it gives enough time for the environment to support it - for it&amp;rsquo;s dependencies to persist. This is a delicate balance.
All complexities are not the same. The Earth has evolved to deal with much of it. However, some complexities - the ones we create - are very difficult to undo.</summary>
          <content type="html"><![CDATA[<p>First, there is simplicity.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-5a435" hidden>
    <label for="zoomCheck-5a435">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="simplicity.svg" alt="simplicity" 
             />
    </label>

</p>
<p>Over billions of years, that simplicity becomes slightly more complex.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-21adb" hidden>
    <label for="zoomCheck-21adb">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="complexity.svg" alt="complexity" 
             />
    </label>

</p>
<p>Complexity is accelerated by dependency.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-3f08a" hidden>
    <label for="zoomCheck-3f08a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="dependency.svg" alt="dependency" 
             />
    </label>

</p>
<p>That complexity can only persist because it gives enough time for the environment to support it - for it&rsquo;s dependencies to persist. This is a delicate balance.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-a8fca" hidden>
    <label for="zoomCheck-a8fca">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="scale.svg" alt="scale" 
             />
    </label>

</p>
<p>All complexities are not the same. The Earth has evolved to deal with much of it. However, some complexities - the ones we create - are very difficult to undo.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-fc1eb" hidden>
    <label for="zoomCheck-fc1eb">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="different-complexity.svg" alt="different complexity" 
             />
    </label>

</p>
<p>When that complexity far exceeds what the environment is capable of handling, it makes me sad.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-f6802" hidden>
    <label for="zoomCheck-f6802">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="plastic.svg" alt="plastic" 
             />
    </label>

</p>
<p>Because that means one of two things:</p>
<ul>
<li>we must find some way of balancing this complexity</li>
<li>the scale must tip, the environment must adapt</li>
</ul>
<p>



    
    <input type="checkbox" id="zoomCheck-fee98" hidden>
    <label for="zoomCheck-fee98">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="imbalance.svg" alt="imbalance" 
             />
    </label>

</p>
<p>This may not be inherently bad, but the future of life then becomes an uncertainty.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-11dff" hidden>
    <label for="zoomCheck-11dff">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="earth.svg" alt="earth" 
             />
    </label>

</p>
]]></content>
      </entry>
      <entry>
        <title>Servers on Android</title>
        <link rel="alternate" href="/posts/009-servers-on-android/" />
        <id>/posts/009-servers-on-android/</id>
        <published>2026-03-28T08:53:29+02:00</published>
        <updated>2026-04-17T11:49:46+02:00</updated>
        <summary type="html">Android is a powerful operating system. To fully utilize it&amp;rsquo;s capabilities, a specific set of tools are required. Termux is a terminal emulator application enhanced with a large set of command line utilities ported to Android 1. It is used to install a server for remote access (sshd) and for serving a static website (httpd). These are exposed to the internet using Tor.
The Terminal A terminal emulator provides an interface to the system using text commands.</summary>
          <content type="html"><![CDATA[<p>Android is a powerful operating system. To fully utilize it&rsquo;s capabilities, a specific set of tools are required. Termux is a terminal emulator application enhanced with a large set of command line utilities ported to Android <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It is used to install a server for remote access (sshd) and for serving a static website (httpd). These are exposed to the internet using Tor.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-d6d88" hidden>
    <label for="zoomCheck-d6d88">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="servers-on-android.svg" alt="servers on android" 
             />
    </label>

</p>
<h1 id="the-terminal">The Terminal</h1>
<p>A terminal emulator provides an interface to the system using text commands. A core set of utilites are provided by default, and more can be installed and upgraded with pkg - the Termux package manager <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-99e2b" hidden>
    <label for="zoomCheck-99e2b">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="termux-terminal.jpg" alt="termux terminal|10x30" 
             />
    </label>

</p>
<p>Refer to the official documentation to learn more about Termux: <a href="https://wiki.termux.dev/wiki/Main_Page">https://wiki.termux.dev/wiki/Main_Page</a>.</p>
<h2 id="installation">Installation</h2>
<p>The official source for installation is F-Droid, although builds are published on Github as well <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. Google PlayStore is not recommended. Do not mix applications between sources. Applications are installed by downloading and opening the Android Package Kit (APK) file.</p>
<blockquote>
<p>To install applications from APK files, &ldquo;Install from Unknown Sources&rdquo; must be temporarily enabled in Settings for the file manager. For security, disable it once installation is completed.</p>
</blockquote>
<p>The following applications need to be installed:</p>
<ul>
<li><a href="https://f-droid.org/en/packages/com.termux/">termux</a></li>
<li><a href="https://f-droid.org/en/packages/com.termux.api/">termux-api</a></li>
<li><a href="https://f-droid.org/en/packages/com.termux.boot/">termux-boot</a></li>
</ul>
<blockquote>
<p>F-Droid does not need to be downloded; search for the &ldquo;Download APK&rdquo; link under the latest version.</p>
</blockquote>
<p><code>termux</code> is the main application. <code>termux-api</code> exposes device functionality like vibrate, clipboard, torch and fingerprint <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. <code>termux-boot</code> allows Termux to automatically start after device is restarted <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>.</p>
<h1 id="servers">Servers</h1>
<h2 id="sshd">sshd</h2>
<blockquote>
<p>Remote acceses via Secure SHell (ssh).</p>
</blockquote>
<p>Install and start <code>sshd</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pkg install openssh
</span></span><span class="line"><span class="cl">sshd
</span></span></code></pre></div><p>To use ssh, the hostname of the server and a username/password combination is required. Run <code>whoami</code> to determine the username (typically in the format u0_aXXX), and set the password with <code>passwd</code>.</p>
<p>The local IP address of the phone can be used to test the connection. The local IP address can be found under WiFi settings by expanding the currently connected network. Run <code>ssh</code> from a different machine on the same network:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh -p <span class="m">8022</span> u0_aXXX@192.168.0.1
</span></span></code></pre></div><blockquote>
<p>What follows is configuring key based authentication. While not strictly necessary, it is recommended for security. It can be configured later if required.</p>
</blockquote>
<p>The password will be prompted when connecting. This is not very secure since attackers could try and guess the username/password. To increase security, switch to key-based authentication by generating a key with <code>ssh-keygen</code> on the machine you will be connecting to the phone from:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">ssh-keygen -t rsa
</span></span><span class="line"><span class="cl">Enter file in which to save the key (/home/username/.ssh/id_rsa): /home/username/.ssh/keyname
</span></span><span class="line"><span class="cl">Enter passphrase for &#34;key-filename&#34; (empty for no passphrase): 
</span></span></code></pre></div><p>This will create two files under <code>/home/username/.ssh</code>:</p>
<ul>
<li><code>keyname</code>: a passphrase protected private key</li>
<li><code>keyname.pub</code>: a public key</li>
</ul>
<p>The public key&rsquo;s content must be copied to the file <code>/data/data/com.termux/files/home/.ssh/known_hosts</code>. This can be done using USB, by copying the text to clipboard and pasting it over <code>ssh</code>, or by using <code>ssh-copy-id</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh-copy-id -p <span class="m">8022</span> -i /home/username/.ssh/keyname.pub u0_aXXX@192.168.0.1
</span></span></code></pre></div><p>The private key can now be used to connect to the phone without a password:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh -p <span class="m">8022</span> -i /home/username/.ssh/keyname u0_aXXX@192.168.0.1
</span></span></code></pre></div><p>Once the key-based authentication has been tested, disable password authentication by editing <code>/data/data/com.termux/files/usr/etc/ssh/sshd_config</code> and setting <code>PasswordAuthentication no</code>. Restart <code>sshd</code> for the changes to take effect:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pkill sshd
</span></span><span class="line"><span class="cl">sshd
</span></span></code></pre></div><p>Try connecting from a different machine without the key; this should now fail:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">ssh -o <span class="nv">PasswordAuthentication</span><span class="o">=</span>yes <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -o <span class="nv">PreferredAuthentications</span><span class="o">=</span>keyboard-interactive,password <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -o <span class="nv">PubkeyAuthentication</span><span class="o">=</span>no <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    u0_aXXX@192.168.0.1
</span></span></code></pre></div><p>Keep the private key at <code>/home/username/.ssh/keyname</code> secure - you cannot login remotely without it if &ldquo;PasswordAuthentication&rdquo; is disabled.</p>
<h2 id="httpd">httpd</h2>
<blockquote>
<p>Web server to host a website.</p>
</blockquote>
<p>Install <code>httpd</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pkg install apache2
</span></span></code></pre></div><p>Edit <code>/data/data/com.termux/files/usr/etc/apache2/httpd.conf</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-apache" data-lang="apache"><span class="line"><span class="cl"><span class="c"># Set server hostname and path to executables</span>
</span></span><span class="line"><span class="cl"><span class="nb">ServerName</span> <span class="m">127.0.0.1</span>
</span></span><span class="line"><span class="cl"><span class="nb">ServerRoot</span> <span class="s2">&#34;/data/data/com.termux/files/usr&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Set port which server accepts connections over</span>
</span></span><span class="line"><span class="cl"><span class="nb">Listen</span> <span class="m">8080</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Set directory from which Apache will serve files</span>
</span></span><span class="line"><span class="cl"><span class="nb">DocumentRoot</span> <span class="s2">&#34;/data/data/com.termux/files/home/folderToServe&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Set the file to save errors to</span>
</span></span><span class="line"><span class="cl"><span class="nb">ErrorLog</span> <span class="s2">&#34;/data/data/com.termux/files/home/httpd.log&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Handle concurrent connections with Worker Multi-Processing Module</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> mpm_worker_module libexec/apache2/mod_mpm_worker.so
</span></span><span class="line"><span class="cl"><span class="c"># Maps file extensions to content types</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> mime_module libexec/apache2/mod_mime.so
</span></span><span class="line"><span class="cl"><span class="c"># Ensures proper privilege handling on Unix systems</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> unixd_module libexec/apache2/mod_unixd.so
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Set default webpage to return when client connects to the server</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> dir_module libexec/apache2/mod_dir.so
</span></span><span class="line"><span class="cl"><span class="nb">DirectoryIndex</span> index.html
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Prevent arbitrary file requests (lock down filesystem)</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> authz_core_module libexec/apache2/mod_authz_core.so
</span></span><span class="line"><span class="cl"><span class="nt">&lt;Directory</span> <span class="s">&#34;/&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Require</span> <span class="k">all</span> denied
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/Directory&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c"># Allow access only from local connections (127.0.0.1)</span>
</span></span><span class="line"><span class="cl"><span class="nb">LoadModule</span> authz_host_module libexec/apache2/mod_authz_host.so
</span></span><span class="line"><span class="cl"><span class="nt">&lt;Directory</span> <span class="s">&#34;/data/data/com.termux/files/home/folderToServe&#34;</span><span class="nt">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="nb">Require</span> local
</span></span><span class="line"><span class="cl"><span class="nt">&lt;/Directory&gt;</span>
</span></span></code></pre></div><p>Replace <code>folderToServer</code> with the path your website is saved at. Modules are available for more functionality - refer to the Apache http server documentation: <a href="https://httpd.apache.org/docs/current/mod/">https://httpd.apache.org/docs/current/mod/</a>.</p>
<p>A folder from a different machine can be copied to the phone over <code>ssh</code> using <code>rsync</code> (without the <code>--dry-run</code> flag):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">rsync -havP --checksum --delete --dry-run <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -e <span class="s1">&#39;ssh -i /home/username/.ssh/keyname&#39;</span>
</span></span><span class="line"><span class="cl">    /local/path/to/folder/in/machine <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    u0_aXXX@192.168.0.1:/data/data/com.termux/files/home/folderToServe
</span></span></code></pre></div><p>Test to ensure the server starts without errors by running <code>httpd</code>. If the command doesn&rsquo;t fail due to invalid configuration, errors will be logged to the file specified above by <code>ErrorLog</code>. The files under <code>DocumentRoot</code> should now be served - visit <code>127.0.0.1:8080</code> on the phone&rsquo;s browser.</p>
<h1 id="tor">Tor</h1>
<blockquote>
<p>The proxy to the Internet.</p>
</blockquote>
<p>To access a server over the internet, the server needs a public IP address. A Virtual Private Server (VPS) is a publicly accessible machine hosted by a cloud provider. Since the phone itself does not have a public IP address, it must use a reverse proxy like <a href="https://github.com/fatedier/frp">frp</a> or <a href="https://nginx.org/">nginx</a> running on a VPS to be accessible on the internet. An alternative is to use the Tor network.</p>
<p>The Tor network is a group of volunteer-operated servers <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>. It uses a special protocol for privacy and anonymity. A Tor service can be set up to make the local services accessible to the internet.</p>
<p>A Tor service address is different from public domains; this is the link for the Tor Project homepage:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">http://2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion/
</span></span></code></pre></div><p>It can be accessed using the <a href="https://www.torproject.org/download/">Tor Browser</a>, and will not work with browsers that do not route traffic through the Tor network.</p>
<!-- raw HTML omitted -->
<p>To setup the Tor service, edit <strong>tor</strong> <strong>r</strong>un <strong>c</strong>onfig <code>/data/data/com.termux/files/usr/etc/tor/torrc</code> and append:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="c1"># Service for sshd</span>
</span></span><span class="line"><span class="cl">HiddenServiceDir /data/data/com.termux/files/home/.tor/ssh/
</span></span><span class="line"><span class="cl">HiddenServicePort <span class="m">22</span> 127.0.0.1:8022
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Service for httpd</span>
</span></span><span class="line"><span class="cl">HiddenServiceDir /data/data/com.termux/files/home/.tor/webserver/
</span></span><span class="line"><span class="cl">HiddenServicePort <span class="m">80</span> 127.0.0.1:8080
</span></span></code></pre></div><p><code>HiddenServiceDir</code> defines where the identity files for the service must be stored. <code>HiddenServicePort</code> maps a virtual port to a local port. Clients will target the virtual port (22 or 80) and <code>tor</code> will forward this traffic to the specified local port (8022 or 8080).</p>
<p>Execute tor and get the hostame of the service:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">tor
</span></span><span class="line"><span class="cl">cat /data/data/com.termux/files/home/.tor/ssh/hostname
</span></span></code></pre></div><p>Use <a href="https://gitlab.torproject.org/tpo/core/torsocks/">torsocks</a> to proxy ssh connections:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">torsocks ssh -p <span class="m">22</span> -i /home/username/.ssh/keyname.pub <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    u0_aXXX@abcde23456abcde23456abcde23456abcde23456abcde23456abcdef.onion
</span></span></code></pre></div><blockquote>
<p>The Termux documentation recommends <code>proxychains-ng</code> over <code>torsocks</code> as <code>torsocks</code> may not work since Android 8 <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.</p>
</blockquote>
<p>Use the Tor Browser to visit the website. The services hosted on Android with Termux can now be accessed from anywhere in the world with an internet connection and Tor.</p>
<h1 id="autorun-on-boot">Autorun on boot</h1>
<blockquote>
<p>Keeping the services alive.</p>
</blockquote>
<p>When the phone is rebooted, the services will stop. To automatically start up, the service must be enabled and Termux must be started on boot.</p>
<h2 id="enable-services">Enable services</h2>
<p>Services run in the background and restart when certain conditions are triggered.</p>
<p>Install the service manager:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pkg install termux-services
</span></span></code></pre></div><p>Start the services and enable it to autostart when Termux starts.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sv up sshd
</span></span><span class="line"><span class="cl">sv up httpd
</span></span><span class="line"><span class="cl">sv up tor
</span></span><span class="line"><span class="cl">sv-enable sshd
</span></span><span class="line"><span class="cl">sv-enable httpd
</span></span><span class="line"><span class="cl">sv-enable tor
</span></span></code></pre></div><p>The number of services supported is limited; refer to the documentation to learn more: <a href="https://wiki.termux.dev/wiki/Termux-services">https://wiki.termux.dev/wiki/Termux-services</a>.</p>
<p>The logs for running services are stored at <code>/data/data/com.termux/files/usr/var/log/sv/</code>.</p>
<p>To ensure that the serivce works, restart the service after killing all currently running processes:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sv down httpd
</span></span><span class="line"><span class="cl">pgrep httpd
</span></span><span class="line"><span class="cl">pkill -f httpd
</span></span><span class="line"><span class="cl">sv up httpd
</span></span><span class="line"><span class="cl">cat /data/data/com.termux/files/usr/var/log/sv/httpd/current
</span></span></code></pre></div><h2 id="start-termux-on-boot">Start Termux on boot</h2>
<p>Create the directory for boot scripts:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mkdir -p /data/data/com.termux/files/home/.termux/boot
</span></span></code></pre></div><p>Create a file (<code>nvim start-services</code>) under the boot directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/data/data/com.termux/files/usr/bin/sh
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>termux-wake-lock
</span></span><span class="line"><span class="cl">. <span class="nv">$PREFIX</span>/etc/profile
</span></span></code></pre></div><p><code>termux-wake-lock</code> stops Android from going into deep sleep - this prevents Android&rsquo;s aggressive power management from suspending background processes. The second line sources <code>/data/data/com.termux/files/usr/etc/profile</code> - this configures the environment.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://wiki.termux.dev/wiki/Getting_started">https://wiki.termux.dev/wiki/Getting_started</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://wiki.termux.dev/wiki/Package_Management">https://wiki.termux.dev/wiki/Package_Management</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://wiki.termux.dev/wiki/Main_Page#Installation">https://wiki.termux.dev/wiki/Main_Page#Installation</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://wiki.termux.dev/wiki/Termux:API">https://wiki.termux.dev/wiki/Termux:API</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://wiki.termux.dev/wiki/Termux:Boot">https://wiki.termux.dev/wiki/Termux:Boot</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://support.torproject.org/about-tor/how-tor-works/overview">https://support.torproject.org/about-tor/how-tor-works/overview</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7">
<p><a href="https://wiki.termux.dev/wiki/Bypassing_NAT">https://wiki.termux.dev/wiki/Bypassing_NAT</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>Is happiness a state?</title>
        <link rel="alternate" href="/posts/005-is-happiness-a-state/" />
        <id>/posts/005-is-happiness-a-state/</id>
        <published>2026-03-09T19:55:32+02:00</published>
        <updated>2026-03-09T19:55:32+02:00</updated>
        <summary type="html">Is happiness a state?
It matters because we wish to be happy. To be happy as long as we can. To never be unhappy - if at all possible. Is it possible?
That largely depends on our the measure of happiness.
Happiness could be:
a particular position a particular direction a state; some combination of position and direction What is happiness for you?
How would you measure it?</summary>
          <content type="html"><![CDATA[<p>Is happiness a state?</p>
<p>It matters because we wish to be happy. To be happy as long as we can. To never be unhappy - if at all possible. Is it possible?</p>
<p>That largely depends on our the measure of happiness.</p>
<p>Happiness could be:</p>
<ul>
<li>a particular position</li>
<li>a particular direction</li>
<li>a state; some combination of position and direction</li>
</ul>
<p>



    
    <input type="checkbox" id="zoomCheck-da512" hidden>
    <label for="zoomCheck-da512">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="curve-of-happiness.svg" alt="curve of happiness" 
             />
    </label>

</p>
<p>What is happiness for you?<br>
How would you measure it?</p>
<hr>
]]></content>
      </entry>
      <entry>
        <title>Audio Engines in C&#43;&#43;</title>
        <link rel="alternate" href="/posts/004-audio-engines-in-c&#43;&#43;/" />
        <id>/posts/004-audio-engines-in-c&#43;&#43;/</id>
        <published>2026-02-25T22:24:26+02:00</published>
        <updated>2026-03-26T21:18:27+02:00</updated>
        <summary type="html">Context Rhythmbox-Reloaded is a C++ application that needs to play audio files. There are libraries that handle this - they simply need to be included, initialized and used. miniaudio is easy to integrate, but has file format limitations. libvlc can support almost any format, but bundling it with the application impacts size and performance. Dynamic linking addresses this issue, but creates a requirement for external dependencies. As a compromise, the application may try to use libvlc, but fall back to miniaudio.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p><a href="https://git.kistu.dev/kistu/rhythmbox-reloaded-desktop">Rhythmbox-Reloaded</a> is a C++ application that needs to play audio files. There are libraries that handle this - they simply need to be included, initialized and used. <code>miniaudio</code> is easy to integrate, but has file format limitations. <code>libvlc</code> can support almost any format, but bundling it with the application impacts size and performance. Dynamic linking addresses this issue, but creates a requirement for external dependencies. As a compromise, the application may try to use <code>libvlc</code>, but fall back to <code>miniaudio</code>.</p>
<h1 id="miniaudio">Miniaudio</h1>
<p><a href="https://miniaud.io/">Miniaudio</a> is an audio playback library for C and C++; it is made up of a single source file without external dependencies.</p>
<p>To use the library, download the header file <a href="https://raw.githubusercontent.com/mackron/miniaudio/master/miniaudio.h">miniaudio.h</a>.</p>
<p>To test the library by playing a file, create <code>ma-test.cpp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="cp">#define MINIAUDIO_IMPLEMENTATION
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;miniaudio.h&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">	<span class="c1">// Miniaudio variables
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="n">ma_engine</span> <span class="n">engine</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">ma_result</span> <span class="n">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="n">ma_sound</span> <span class="n">sound</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// Initialize engine
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="n">result</span> <span class="o">=</span> <span class="n">ma_engine_init</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">engine</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">==</span> <span class="n">MA_SUCCESS</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// Load file
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>		<span class="n">result</span> <span class="o">=</span> <span class="n">ma_sound_init_from_file</span><span class="p">(</span><span class="o">&amp;</span><span class="n">engine</span><span class="p">,</span> <span class="s">&#34;file.mp3&#34;</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">sound</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">		<span class="k">if</span> <span class="p">(</span><span class="n">result</span> <span class="o">==</span> <span class="n">MA_SUCCESS</span><span class="p">){</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// Start playback
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="n">ma_sound_start</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sound</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Playing file.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">			<span class="c1">// Wait until completion
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>			<span class="k">while</span><span class="p">(</span><span class="o">!</span><span class="n">ma_sound_at_end</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sound</span><span class="p">)){</span>
</span></span><span class="line"><span class="cl">				<span class="n">ma_sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="p">}</span>
</span></span><span class="line"><span class="cl">			<span class="n">ma_sound_uninit</span><span class="p">(</span><span class="o">&amp;</span><span class="n">sound</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">			<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Playback completed.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">			<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Failed to load file.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">		<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&#34;Failed to initialize file.</span><span class="se">\n</span><span class="s">&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Compile and run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">g++ ma-test.cpp -o ma-test.o
</span></span><span class="line"><span class="cl">./ma-test.o
</span></span></code></pre></div><blockquote>
<p>Ensure the header file <code>miniaudio.h</code> and audio file <code>file.mp3</code> are in the same folder as the above source code file.</p>
</blockquote>
<p>The high level API has built in support for WAV, MP3 and FLAC - other formats require more complex configuration with the low level API. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> If support for an extensive list of audio formats is required, <code>libvlc</code> may be better suited.</p>
<h1 id="libvlc">libvlc</h1>
<p><a href="https://code.videolan.org/videolan/libvlc">libvlc</a> is a C library that is the core engine of the VLC media player. VideoLAN provides official C++ bindings <a href="https://code.videolan.org/videolan/libvlcpp">(libvlcpp)</a> that acts as a wrapper for libvlc. The bindings consist of header files; only the <code>vlcpp</code> folder - containing several .hpp files - is required from the repository: <a href="https://code.videolan.org/videolan/libvlcpp">https://code.videolan.org/videolan/libvlcpp</a>.</p>
<blockquote>
<p>VLC and libvlc are required dependencies. Some sources of VLC may not expose libvlc as a library. Refer to the VLC installation guides for your particular system.</p>
</blockquote>
<p>Once libvlc is accessible and the <code>vlcpp</code> folder has been copied to the current folder, the engine can be tested; create <code>vlc-test.cpp</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c++" data-lang="c++"><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&#34;vlcpp/vlc.hpp&#34;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp">#include</span> <span class="cpf">&lt;unistd.h&gt;</span><span class="cp">
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl"><span class="kt">int</span> <span class="nf">main</span><span class="p">(){</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="c1">// VLC variables
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="n">VLC</span><span class="o">::</span><span class="n">Instance</span> <span class="n">m_instance</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">VLC</span><span class="o">::</span><span class="n">MediaPlayer</span> <span class="n">m_player</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="n">VLC</span><span class="o">::</span><span class="n">Media</span> <span class="n">m_media</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Initialize with video disabled
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">static</span> <span class="k">const</span> <span class="kt">char</span><span class="o">*</span> <span class="k">const</span> <span class="n">vlc_args</span><span class="p">[]</span> <span class="p">{</span><span class="s">&#34;--no-video&#34;</span><span class="p">,</span> <span class="s">&#34;--vout=dummy&#34;</span><span class="p">};</span>
</span></span><span class="line"><span class="cl">    <span class="n">m_instance</span> <span class="o">=</span> <span class="n">VLC</span><span class="o">::</span><span class="n">Instance</span><span class="p">(</span><span class="kt">int</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">vlc_args</span><span class="p">)</span><span class="o">/</span><span class="k">sizeof</span><span class="p">(</span><span class="n">vlc_args</span><span class="p">[</span><span class="mi">0</span><span class="p">])),</span> <span class="n">vlc_args</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="n">m_player</span> <span class="o">=</span> <span class="n">VLC</span><span class="o">::</span><span class="n">MediaPlayer</span><span class="p">(</span><span class="n">m_instance</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">m_media</span> <span class="o">=</span> <span class="n">VLC</span><span class="o">::</span><span class="n">Media</span><span class="p">(</span><span class="n">m_instance</span><span class="p">,</span> <span class="s">&#34;file.mp3&#34;</span><span class="p">,</span> <span class="n">VLC</span><span class="o">::</span><span class="n">Media</span><span class="o">::</span><span class="n">FromPath</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="n">m_player</span><span class="p">.</span><span class="n">setMedia</span><span class="p">(</span><span class="n">m_media</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Start playback
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">if</span> <span class="p">(</span><span class="n">m_player</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">m_player</span><span class="p">.</span><span class="n">play</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Wait for playback to start, then finish
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>        <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">m_player</span><span class="p">.</span><span class="n">isPlaying</span><span class="p">()){</span> <span class="n">usleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="k">while</span> <span class="p">(</span><span class="n">m_player</span><span class="p">.</span><span class="n">isPlaying</span><span class="p">())</span> <span class="p">{</span> <span class="n">usleep</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>libvlc must be linked when compiling:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">g++ -lvlc vlc-test.cpp -o vlc-test.o
</span></span><span class="line"><span class="cl">./vlc-test.o
</span></span></code></pre></div><blockquote>
<p>If you need to package the application as an AppImage, you must exclude libvlc when building. VLC initialization unexpectedly fails with partial vlc installations (missing some plugins) and including the full installation would be quite inefficient - it is better to dynamically link.</p>
</blockquote>
<h1 id="fallback">Fallback</h1>
<p>It would be beneficial if the application could retain some functionality even if <code>libvlc</code> is unavailable. When using <code>libvlc</code> by <code>#include &lt;vlcpp/vlc.hpp&gt;</code>, the application does not start without libvlc.</p>
<p>Using preprocessor directives only works for compilation. The developer machine can be required to have vlc installed, but this doesn&rsquo;t solve the runtime issue.</p>
<p>Another option is to load the library at runtime using <code>dlopen</code> or <code>LoadLibrary</code>, but since <code>libvlcpp</code> is being used as a binding, this doesn&rsquo;t seem easily possible.</p>
<p>Currently, fallback is not supported. Two build version may be offered - one using <code>miniaudio</code> and the other <code>libvlc</code>. An integrated version would be prefered.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://miniaud.io/docs/manual/index.html#Decoding">https://miniaud.io/docs/manual/index.html#Decoding</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>PostgreSQL with a REST API</title>
        <link rel="alternate" href="/posts/003-postgresql-with-a-rest-api/" />
        <id>/posts/003-postgresql-with-a-rest-api/</id>
        <published>2026-02-16T19:41:13+02:00</published>
        <updated>2026-04-21T12:16:04+02:00</updated>
        <summary type="html">Context The requirement is to implement a new database design for managing a variety of digital assets. Each asset has an associated identifier (hash) and metadata. The hash is calculated using a tool like sha512sum or phash. Metadata is extracted in JSON format using tools like ffprobe or exiftool. Ideally, only specific required metadata fields will be stored in the database, but that makes future changes more complex and requires making certain assumptions about the type of digital asset.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p>The requirement is to implement a new database design for managing a variety of digital assets. Each asset has an associated identifier (hash) and metadata. The hash is calculated using a tool like <code>sha512sum</code> or <code>phash</code>. Metadata is extracted in JSON format using tools like <code>ffprobe</code> or <code>exiftool</code>. Ideally, only specific required metadata fields will be stored in the database, but that makes future changes more complex and requires making certain assumptions about the type of digital asset. To make the database as general purpose as possible, the database management system (DBMS) needs to support JSON documents.</p>
<h1 id="postgresql">PostgreSQL</h1>
<p><a href="https://www.postgresql.org">PostgreSQL</a> is an open source relational database that supports JSON types. To better understand the configuration of this database, it is beneficial to briefly cover some terminology and hierarchies.</p>
<p>PostgreSQL uses a client-server model. The server accepts client connections at a specified port (5432 by default) and stores data at a specified location (typically <code>/usr/local/pgsql/data</code> or <code>/var/lib/postgresql/data</code>). The server can manage any number of databases stored in this data directory; this forms a single database cluster. The database itself consists of schemas; schemas act as a namespace for organizing database objects like tables and functions (SQL queries).</p>
<p>



    
    <input type="checkbox" id="zoomCheck-bb973" hidden>
    <label for="zoomCheck-bb973">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="postgresql-organization-hierarchy.svg" alt="postgresql organization hierarchy" 
             />
    </label>

</p>
<p>PostgreSQL uses roles and privileges for access control. Roles are created and assigned permissions to perform specific actions:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">create role role_name password <span class="s1">&#39;role_password&#39;</span><span class="p">;</span> 
</span></span><span class="line"><span class="cl">grant usage on schema schema_name to role_name<span class="p">;</span>  <span class="c1"># allows accessing objects under specified schema</span>
</span></span><span class="line"><span class="cl">grant INSERT on schema.table to role_name<span class="p">;</span>       <span class="c1"># allows action on specified object</span>
</span></span></code></pre></div><p>The specified action can be ALL, or INSERT, SELECT, UPDATE, DELETE and so on.</p>
<h1 id="implementation">Implementation</h1>
<p>Docker is used to deploy the services. Three services are used:</p>
<ul>
<li><code>db</code>: the PostgreSQL server and database cluster</li>
<li><a href="https://www.adminer.org/en/"><code>adminer</code></a>: webpage that allows running SQL commands</li>
<li><code>api</code>: <a href="https://docs.postgrest.org">Postg<strong>REST</strong></a> web server that exposes a REST API</li>
</ul>
<p>



    
    <input type="checkbox" id="zoomCheck-779ce" hidden>
    <label for="zoomCheck-779ce">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="services-connection.svg" alt="services connection" 
             />
    </label>

</p>
<p><code>psql</code> is a terminal based client interface for the PostgreSQL server. <code>pgAdmin</code> and <code>phpMyAdmin</code> are graphical interfaces similar to <code>adminer</code>; GUIs make running SQL commands easier by eliminating the need for terminal access. Adminer can be used to create schemas, perform CRUD operations on tables, create roles, assign permissions and execute other SQL commands.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b31bc" hidden>
    <label for="zoomCheck-b31bc">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="adminer-interface.png" alt="adminer interface" 
             />
    </label>

</p>
<p>The <code>api</code> is used to enable clients to perform actions without specialized tools for connecting to the database; only the ability to send HTTP requests is required.</p>
<h2 id="docker">Docker</h2>
<p>Create <code>docker-compose.yml</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">services</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">db</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">postgres:18.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_USER</span><span class="p">:</span><span class="w"> </span><span class="l">${ADMIN_USERNAME}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_PASSWORD</span><span class="p">:</span><span class="w"> </span><span class="l">${ADMIN_PASSWORD}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">POSTGRES_DB</span><span class="p">:</span><span class="w"> </span><span class="l">${DATABASE_NAME}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">pgdata:/var/lib/postgresql</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">adminer</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">adminer:5.4.2</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">restart</span><span class="p">:</span><span class="w"> </span><span class="l">always</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">8080</span><span class="p">:</span><span class="m">8080</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">db</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">api</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">postgrest/postgrest:v14.5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">ports</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="m">3000</span><span class="p">:</span><span class="m">3000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PGRST_SERVER_HOST</span><span class="p">:</span><span class="w"> </span><span class="m">0.0.0.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PGRST_DB_URI</span><span class="p">:</span><span class="w"> </span><span class="l">postgres://${AUTH_ROLE_USERNAME}:${AUTH_ROLE_PASSWORD}@db:5432/${DATABASE_NAME}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PGRST_OPENAPI_SERVER_PROXY_URI</span><span class="p">:</span><span class="w"> </span><span class="l">http://127.0.0.1:3000</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">PGRST_JWT_SECRET</span><span class="p">:</span><span class="w"> </span><span class="l">${JWT_SECRET}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">depends_on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span>- <span class="l">db</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">volumes</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pgdata</span><span class="p">:</span><span class="w">
</span></span></span></code></pre></div><p>Define the variables in <code>.env</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-env" data-lang="env"><span class="line"><span class="cl"><span class="nv">ADMIN_USERNAME</span><span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="nv">ADMIN_PASSWORD</span><span class="o">=</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">DATABASE_NAME</span><span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="nv">DATABASE_SCHEMAS</span><span class="o">=</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">AUTH_ROLE_USERNAME</span><span class="o">=</span>
</span></span><span class="line"><span class="cl"><span class="nv">AUTH_ROLE_PASSWORD</span><span class="o">=</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">JWT_SECRET</span><span class="o">=</span>
</span></span></code></pre></div><blockquote>
<p>Use <code>echo $(&lt; /dev/urandom tr -dc A-Za-z0-9 | head -c32)</code> to generate secure alphanumeric passwords; change length (to <code>-c64</code>, <code>c128</code>, &hellip;) as required. The JWT secret must be 32 characters.</p>
</blockquote>
<p>Start the services with <code>docker compose up -d</code>.</p>
<h2 id="database-roles">Database roles</h2>
<p>Two roles are used to separate authentication (verifying identity) from authorization (verifying permissions). Clients connect to the <code>authenticator</code> role by providing the correct password, but this role does not have any permissions. The client can then switch to another role (<code>SET ROLE role_name</code>) which has the required permissions.</p>
<p>The following can be executed using a client interface (like <code>psql</code> or <code>Adminer</code>):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">create role role_name nologin<span class="p">;</span> 
</span></span><span class="line"><span class="cl">create role authenticator noinherit login password <span class="s1">&#39;example_authenticator_password&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">grant role_name to authenticator<span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">create schema schema_name<span class="p">;</span>
</span></span><span class="line"><span class="cl">grant usage on schema schema_name to role_name<span class="p">;</span>
</span></span><span class="line"><span class="cl">grant all on schema_name.table_name to role_name<span class="p">;</span>
</span></span></code></pre></div><blockquote>
<p>The nologin rolename and authenticator rolename and password should match that defined in <code>.env</code>.</p>
</blockquote>
<ul>
<li><code>nologin</code> disables connecting directly as the specified role</li>
<li><code>noinherit</code> prevents default permissions from being automatically assigned</li>
</ul>
<p>The containers need to be restarted for <code>postgrest</code> to start without errors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">docker compose down
</span></span><span class="line"><span class="cl">docker compose up -d
</span></span></code></pre></div><p>Check for keywords <code>error</code> or <code>fatal</code> in the logs (<code>docker compose logs</code>).</p>
<h2 id="api-token-generation">API token generation</h2>
<p>The following steps are from the PostgREST documentation <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>Use the JWT secret defined in <code>.env</code>. Create a signed token using bash and openssl, replacing role_name with the nologin role:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span><span class="nb">set</span> -e
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">JWT_SECRET</span><span class="o">=</span><span class="s1">&#39;test_secret_that_is_at_least_32_characters_long&#39;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">_base64 <span class="o">()</span> <span class="o">{</span> openssl base64 -e -A <span class="p">|</span> tr <span class="s1">&#39;+/&#39;</span> <span class="s1">&#39;-_&#39;</span> <span class="p">|</span> tr -d <span class="s1">&#39;=&#39;</span><span class="p">;</span> <span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">header</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> -n <span class="s1">&#39;{&#34;alg&#34;:&#34;HS256&#34;,&#34;typ&#34;:&#34;JWT&#34;}&#39;</span> <span class="p">|</span> _base64<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">payload</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> -n <span class="s2">&#34;{\&#34;role\&#34;:\&#34;role_name\&#34;}&#34;</span> <span class="p">|</span> _base64<span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="nv">signature</span><span class="o">=</span><span class="k">$(</span><span class="nb">echo</span> -n <span class="s2">&#34;</span><span class="nv">$header</span><span class="s2">.</span><span class="nv">$payload</span><span class="s2">&#34;</span> <span class="p">|</span> openssl dgst -sha256 -hmac <span class="s2">&#34;</span><span class="nv">$JWT_SECRET</span><span class="s2">&#34;</span> -binary <span class="p">|</span> _base64<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> -n <span class="s2">&#34;</span><span class="nv">$header</span><span class="s2">.</span><span class="nv">$payload</span><span class="s2">.</span><span class="nv">$signature</span><span class="s2">&#34;</span>
</span></span></code></pre></div><p>Run this script (<code>./script_name.sh</code>) to create the token.</p>
<h2 id="api-usage">API usage</h2>
<p>Set your token value (this needs to be repeated when a new shell is opened):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">read</span> TOKEN
</span></span></code></pre></div><p>To retrieve records:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl https://domain_name.org/table_name -X GET <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -H <span class="s2">&#34;Accept-Profile: schema_name&#34;</span>
</span></span></code></pre></div><blockquote>
<p>Note, the schema must be created manually (using Adminer) and defined in the environment (<code>DATABASE_SCHEMAS</code>) with the containers restarted after updates for the schema to be accessible. <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
</blockquote>
<p>To insert new data:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">curl https://domain_name.org/table_name -X POST <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -H <span class="s2">&#34;Authorization: Bearer </span><span class="nv">$TOKEN</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -H <span class="s2">&#34;Content-Type: application/json&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -d <span class="s2">&#34;{&#34;</span>column1<span class="s2">&#34;: &#34;</span>value<span class="s2">&#34;, &#34;</span>column2<span class="s2">&#34;: {key1: value1, key2: value2}, &#34;</span>column3<span class="s2">&#34;: 3 }&#34;</span>
</span></span></code></pre></div><h2 id="troubleshooting">Troubleshooting</h2>
<p>There are two different ways of approaching troubleshooting - by starting with something that doesn&rsquo;t work and removing/modifying components until does, or by starting with something that does work and adding components until it doesn&rsquo;t. The second approach is easier when there are many things that could go wrong.</p>
<p>A common error that may be faced is that the binding port is already used. The syntax for port binding is <code>- host_port:container_port</code>. The host post may need to be changed if there is a conflict.</p>
<p>When modifying the <code>docker-compose.yml</code>, if you mount the <code>pgdata</code> volume to <code>/var/lib/postgresql/data</code> and try to access the API, it will state that the <code>JWT secret is not set</code>. If you have a look at logs of the PostgreSQL service, you will notice that it is actually not running - the error references a pull request which requires that the volume be mounted at <code>/var/lib/postgresql</code> instead for PostgreSQL v18+ <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>. Determining this true cause by troubleshooting the API would have been tedious and time consuming.</p>
<p>The PostgREST REST API requires cache invalidation; if faced with errors of the format <code>Could not find xxx in the schema cache</code> and after verification that xxx indeed exists in the specified schema, simply stop and start the service to update cache.</p>
<p>Note, one PostgREST instance can only connect to one database instance. If API access is required for multiple databases in the same database cluster, each database requires a PostgREST instance <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>. Depending on your requirements, using multiple schemas may be a better option.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://docs.postgrest.org/en/v14/tutorials/tut1.html#step-3-sign-a-token">https://docs.postgrest.org/en/v14/tutorials/tut1.html#step-3-sign-a-token</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://docs.postgrest.org/en/v14/references/api/schemas.html">https://docs.postgrest.org/en/v14/references/api/schemas.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://github.com/docker-library/postgres/pull/1259">https://github.com/docker-library/postgres/pull/1259</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://github.com/PostgREST/postgrest/issues/1090">https://github.com/PostgREST/postgrest/issues/1090</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>Packaging Qt Applications for Linux</title>
        <link rel="alternate" href="/posts/002-packaging-qt-applications/" />
        <id>/posts/002-packaging-qt-applications/</id>
        <published>2025-11-28T00:15:11+02:00</published>
        <updated>2026-03-26T20:51:04+02:00</updated>
        <summary type="html">Rhythmbox-Reloaded is a C++ application that uses Qt for the user interface. An executable that anyone can use to run the application needs to be created. For Windows and Apple systems, there is little variation in operating system structure so simply building the application on that platform is sufficient. This is not the case for Linux with its many distributions and variations.
Single Distribution The first step is to get an executable for the developer machine.</summary>
          <content type="html"><![CDATA[<p><a href="https://git.kistu.dev/kistu/rhythmbox-reloaded-desktop">Rhythmbox-Reloaded</a> is a C++ application that uses Qt for the user interface. An executable that anyone can use to run the application needs to be created. For Windows and Apple systems, there is little variation in operating system structure so simply building the application on that platform is sufficient. This is not the case for Linux with its many distributions and variations.</p>
<h1 id="single-distribution">Single Distribution</h1>
<p>The first step is to get an executable for the developer machine. This is quite simple - it can be done in the Qt Creator IDE or using the terminal with <code>qmake</code> and <code>make</code>. <code>qmake</code> generates a Makefile based on the contents of a project file.</p>
<p>The specifics of the project file aren&rsquo;t important; this file is created automatically by QtCreator and defines a few configurations (compiler versions and required Qt modules) as well as files to include and compile. Here is a minimal example of a project file <code>Application.pro</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">QT       += core gui
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">CONFIG += c++17
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">SOURCES += \
</span></span><span class="line"><span class="cl">    application-src/main.cpp \
</span></span><span class="line"><span class="cl">    application-src/mainwindow.cpp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">HEADERS += \
</span></span><span class="line"><span class="cl">    application-headers/mainwindow.h
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">FORMS += \
</span></span><span class="line"><span class="cl">    application-forms/mainwindow.ui
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">INCLUDEPATH += application-headers/
</span></span><span class="line"><span class="cl">INCLUDEPATH += application-src/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">RESOURCES += \
</span></span><span class="line"><span class="cl">    application-styling/application-styling.qrc
</span></span></code></pre></div><p><code>qmake</code> reads this file and generates a Makefile:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-makefile" data-lang="makefile"><span class="line"><span class="cl"><span class="c">#############################################################################
</span></span></span><span class="line"><span class="cl"><span class="c"># Generated by qmake (3.1) (Qt 6.2.4)
</span></span></span><span class="line"><span class="cl"><span class="c">#############################################################################
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="c">####### Compiler, tools and options
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nv">CC</span>            <span class="o">=</span> gcc
</span></span><span class="line"><span class="cl"><span class="nv">CXX</span>           <span class="o">=</span> g++
</span></span><span class="line"><span class="cl"><span class="nv">MKDIR</span>         <span class="o">=</span> mkdir -p
</span></span><span class="line"><span class="cl"><span class="nv">COPY</span>          <span class="o">=</span> cp -f
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Output directory
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nv">OBJECTS_DIR</span>   <span class="o">=</span> ./
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Files
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nv">SOURCES</span>       <span class="o">=</span> ../application-src/main.cpp <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		../application-src/mainwindow.cpp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">OBJECTS</span>       <span class="o">=</span> main.o <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		mainwindow.o 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">DIST</span>          <span class="o">=</span> /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/spec_pre.prf <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/unix.conf <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		/usr/lib/x86_64-linux-gnu/qt6/mkspecs/common/linux.conf <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		../application-src/main.cpp <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>		../application-src/mainwindow.cpp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Build rules
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nf">Application</span><span class="o">:</span> <span class="n">ui_mainwindow</span>.<span class="n">h</span> <span class="k">$(</span><span class="nv">OBJECTS</span><span class="k">)</span>  
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>LINK<span class="k">)</span> <span class="k">$(</span>LFLAGS<span class="k">)</span> -o <span class="k">$(</span>TARGET<span class="k">)</span> <span class="k">$(</span>OBJECTS<span class="k">)</span> <span class="k">$(</span>OBJCOMP<span class="k">)</span> <span class="k">$(</span>LIBS<span class="k">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Sub-libraries
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nf">mocclean</span><span class="o">:</span> <span class="n">compiler_moc_header_clean</span> <span class="n">compiler_moc_objc_header_clean</span> <span class="n">compiler_moc_source_clean</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Compile
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nf">main.o</span><span class="o">:</span> ../<span class="n">application</span>-<span class="n">src</span>/<span class="n">main</span>.<span class="n">cpp</span> ../<span class="n">application</span>-<span class="n">headers</span>/<span class="n">mainwindow</span>.<span class="n">h</span>
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>CXX<span class="k">)</span> -c <span class="k">$(</span>CXXFLAGS<span class="k">)</span> <span class="k">$(</span>INCPATH<span class="k">)</span> -o main.o ../application-src/main.cpp
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c">####### Install
</span></span></span><span class="line"><span class="cl"><span class="c"></span>
</span></span><span class="line"><span class="cl"><span class="nf">install_target</span><span class="o">:</span> <span class="n">first</span> <span class="n">FORCE</span>
</span></span><span class="line"><span class="cl">	@test -d <span class="k">$(</span>INSTALL_ROOT<span class="k">)</span>/opt/Application/bin <span class="o">||</span> mkdir -p <span class="k">$(</span>INSTALL_ROOT<span class="k">)</span>/opt/Application/bin
</span></span><span class="line"><span class="cl">	<span class="k">$(</span>QINSTALL_PROGRAM<span class="k">)</span> <span class="k">$(</span>QMAKE_TARGET<span class="k">)</span> <span class="k">$(</span>INSTALL_ROOT<span class="k">)</span>/opt/Application/bin/<span class="k">$(</span>QMAKE_TARGET<span class="k">)</span>
</span></span><span class="line"><span class="cl">	-<span class="k">$(</span>STRIP<span class="k">)</span> <span class="k">$(</span>INSTALL_ROOT<span class="k">)</span>/opt/Application/bin/<span class="k">$(</span>QMAKE_TARGET<span class="k">)</span>
</span></span></code></pre></div><p>Running <code>make</code> uses the GNU Compiler Collection to generate an <code>elf</code> binary that can be executed.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-0aea2" hidden>
    <label for="zoomCheck-0aea2">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="compilation-chain.svg" alt="compilation-chain" 
             />
    </label>

</p>
<h1 id="multiple-distributions">Multiple Distributions</h1>
<p>A critical limitation of the executable generated above is that it depends on certain libraries in a way that makes it distribution dependent.</p>
<h2 id="discovering-the-issue">Discovering the Issue</h2>
<p>The most likely way this issue would be discovered is by running the application on different distributions - typically a given with even with a small user base. However, for this particular project, the issue was intrestingly identified in the CI/CD pipeline.</p>
<p>You may wish to automate the building and publishing of the application when the source code is pushed to remote git repository. Git hosting services often include an &ldquo;Actions&rdquo; feature that can use docker to achieve this. A minimal image that is often chosen is Alpine Linux. Required packages are installed with the Alpine package manager <code>apk</code> and the process for building the application outlined above is followed.</p>
<p>To test the generated binary, run it on the development machine. In this case, it happens to be Arch Linux - an operating system that is organised differently to Alpine Linux. Running the binary results in an error: <code>Error: file not found</code>.</p>
<p>To understand the cause of this error, a better understanding is required of how Make and the GNU compiler works.</p>
<h2 id="compilation-and-linking-process">Compilation and Linking Process</h2>
<p>The compiler&rsquo;s actions can be divided into 4 stages handled by different components.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-179d2" hidden>
    <label for="zoomCheck-179d2">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="compiler-components.svg" alt="compiler components" 
             />
    </label>

</p>
<table>
<thead>
<tr>
<th>Component</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>Preprocessor</td>
<td>expand or resolve lines starting with the directive (&rsquo;#')</td>
</tr>
<tr>
<td>Compiler</td>
<td>translates source code to assembly code</td>
</tr>
<tr>
<td>Assembler</td>
<td>translates assembly code to object code</td>
</tr>
<tr>
<td>Linker</td>
<td>combines multiple object files and links external libraries</td>
</tr>
</tbody>
</table>
<p>Examining the output of each stage gives a better insight into the process.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-ec13b" hidden>
    <label for="zoomCheck-ec13b">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="compiler-stages-with-output.svg" alt="compiler stages with output" 
             />
    </label>

</p>
<p>The component of importance here is the linker.</p>
<h3 id="dynamic-linking">Dynamic Linking</h3>
<p>Dynamic link object files are identified by the <code>.so</code> for &ldquo;shared object&rdquo; extension on Linux and <code>.dll</code> for &ldquo;dynamically linked library&rdquo; on Windows.</p>
<p>When linking, the symbols of the object file is added instead of the entire file itself.</p>
<p>The following command lists all the symbols in the libcurl library:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">objdump -T /usr/lib/libcurl.so
</span></span></code></pre></div><p>During runtime, missing symbols are resolved by the system. This makes the application more resource efficient and means changes to shared objects can be easily propagated. The comes at the cost of comptatibility since more assumptions are made about the system - such as having the shared object in an accessible path.</p>
<h3 id="static-linking">Static Linking</h3>
<p>All object files are stored directly in the resulting executable. Statically linked libraries are identified by an <code>.a</code> for &ldquo;archive&rdquo; extension.</p>
<p>Static linking results in better compatibility since it minimizes external dependencies. However, is increases resource consumption and makes it more difficult to update.</p>
<p>Static linking Qt is not trivial. The official installation only includes dynamic libraries and static linking may potentially face licensing restrictions. Static linking requires building Qt from source and additional configuration.</p>
<h2 id="packaging-with-appimage">Packaging with AppImage</h2>
<p>AppImages takes a different approach. The executable binary is generated using the same process outlined before, but this is then packaged with most required files to run with minimal external dependencies.</p>
<p>It should be noted that AppImages do still have some dependencies, but these are likely fulfilled by default by your distribution. There are some exceptions; Filesystem in Userspace (FUSE), which allows non-root users to mount filesystems and is required for the AppImage to work, is not bundled with older AppImages and may need to be installed explicitly.</p>
<p>If this is your first time packaging an application with AppImage, getting it to work may take time. After much trial and error, it seems that packaging with AppImage has two requirements: a compliant <code>AppDir</code> and a specific version of <code>glibc</code>.</p>
<h3 id="appdir-structure">AppDir Structure</h3>
<p>Creating the <code>AppDir</code> is quite simple and can be done manually; this is how it is organized:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">└── AppDir
</span></span><span class="line"><span class="cl">    └── usr
</span></span><span class="line"><span class="cl">        ├── bin
</span></span><span class="line"><span class="cl">        │   └── app-name.elf
</span></span><span class="line"><span class="cl">        └── share
</span></span><span class="line"><span class="cl">            ├── applications
</span></span><span class="line"><span class="cl">            │   └── app-name.desktop
</span></span><span class="line"><span class="cl">            └── icons
</span></span><span class="line"><span class="cl">                └── hicolor
</span></span><span class="line"><span class="cl">                    └── 256x256
</span></span><span class="line"><span class="cl">                        └── apps
</span></span><span class="line"><span class="cl">                            └── app-name.svg 
</span></span></code></pre></div><p><code>app-name.svg</code> is an image file that will be displayed as the application&rsquo;s icon. <code>app-name.elf</code> is the binary that was generated.</p>
<p><code>app-name.desktop</code> is a desktop entry file:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">Type=Application
</span></span><span class="line"><span class="cl">Exec=app-name
</span></span><span class="line"><span class="cl">Name=app-name
</span></span><span class="line"><span class="cl">Icon=app-name
</span></span><span class="line"><span class="cl">Categories=Utility;
</span></span></code></pre></div><blockquote>
<p>Note that the file extension is excluded in the entries for <code>Exec</code> and <code>Icon</code>.</p>
</blockquote>
<h3 id="glibc">glibc</h3>
<p><code>glibc</code> is a standard C library that provides many common and important functions. The <code>glibc</code> contraint is a little more nuanced.</p>
<blockquote>
<p>To produce binaries that are compatible with many target systems, build on the oldest still-supported build system.</p>
</blockquote>
<p><a href="https://github.com/probonopd/linuxdeployqt">linuxdeployqt</a> - the chosen packaging tool - recommends Ubuntu and enforces this system constraint quite strictly to encourage developers to support the most platforms.</p>
<p>The initial attempt on Alpine Linux failed because the glibc version was too new. From the documentation, it wasn&rsquo;t entirely clear if &ldquo;still-supported&rdquo; referred to the &ldquo;end of standard support&rdquo; or &ldquo;end of life&rdquo;. Ubuntu 14.04 was the oldest system that had not reached end of life. Not only did the system have network issues (there was a bug with libcurl3 that made TLS connections unusable), but the application didn&rsquo;t even compile. Sections of the source code needed to be modified because the <code>glibc</code> version was too old and didn&rsquo;t recognise some of the newer syntax.</p>
<p>At this stage, the packaging tool was abandoned and alternatives were tested - this time on Arch Linux, a rolling distribution with the latest versions of all packages. After much frustration, the issue was narrowed down to the <code>glibc</code> version again (this was not as obvious as it is in hindsight). Arch Linux does not support rolling back to a specific version, so the correct version was manually downloaded from <code>https://archive.archlinux.org/packages/g/glibc/</code> and forcefully installed. This promptly broke the system, causing a blue screen of death on shutdown (a first time on Linux). Luckily this was inside a virtual machine. Before shutdown, new terminals could not be opened, but some commands in an already open terminal worked and was used to copy some important data to a thumbdrive. The thumbdrive later failed due to an unrelated issue.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-c17e7" hidden>
    <label for="zoomCheck-c17e7">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="linux-bsod.png" alt="Linux&amp;rsquo;s blue screen of death" 
             />
    </label>

</p>
<p>Ubuntu 22.04 - the oldest system that still had standard support - was used next. Everything went smoothly once the format for the AppDir was established and application was successfully packaged and run on different distributions.</p>
<p>A example of a command that might be used to create an AppImage on Ubuntu 22.04:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">./linuxdeploy.AppImage -appimage <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    AppDir/usr/share/applications/app-name.desktop <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -qmake<span class="o">=</span>/usr/bin/qmake6 <span class="nv">qmldir</span><span class="o">=</span>application-styling/ <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    -qmldir-importscanner<span class="o">=</span>/usr/lib/qt6/libexec/qmlimportscanner
</span></span></code></pre></div><blockquote>
<p>The <code>qmldir</code> and <code>importscanner</code> are required to include styling and image assets defined as Qt Resources.</p>
</blockquote>
<p>It is intresting to see the lifespan of the software we build, and the chain of dependencies it requires.</p>
]]></content>
      </entry>
      <entry>
        <title>Jupyter Notebook on Docker</title>
        <link rel="alternate" href="/posts/001-jupyter-on-docker/" />
        <id>/posts/001-jupyter-on-docker/</id>
        <published>2025-11-11T09:10:59+02:00</published>
        <updated>2026-04-17T09:37:59+02:00</updated>
        <summary type="html">The design described here is for a very specific set of requirements; to create a reproducable and easy to configure environment to use Jupyter notebooks - one that is suitable for quickly experimenting and sharing ideas. Multiple virtual environments will be required for use across different projects.
Install Jupyter in a virtual environment Create and setup the virtual environment:
python3 -m virtualenv env-name source env-name/bin/activate pip3 install jupyter Run Jupyter Notebook:</summary>
          <content type="html"><![CDATA[<p>The design described here is for a very specific set of requirements; to create a reproducable and easy to configure environment to use Jupyter notebooks - one that is suitable for quickly experimenting and sharing ideas. Multiple virtual environments will be required for use across different projects.</p>
<h2 id="install-jupyter-in-a-virtual-environment">Install Jupyter in a virtual environment</h2>
<p>



    
    <input type="checkbox" id="zoomCheck-8a28a" hidden>
    <label for="zoomCheck-8a28a">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-in-virtualenv.svg" alt="install jupyter in virtualenv" 
             />
    </label>

</p>
<p>Create and setup the virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">python3 -m virtualenv env-name
</span></span><span class="line"><span class="cl"><span class="nb">source</span> env-name/bin/activate
</span></span><span class="line"><span class="cl">pip3 install jupyter
</span></span></code></pre></div><p>Run Jupyter Notebook:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">jupyter notebook
</span></span></code></pre></div><p>While it is very easy to install modules by sourcing the correct virtualenv and using <code>pip</code>, there are a few drawbacks to this approach. Jupyter Notebook needs to be installed for each new environment created. If used as a server, this would become quite tedious and difficult to maintain - particularly if there is a need to use multiple environemnts at once with different users.</p>
<h2 id="install-jupyter-as-global-system-package">Install Jupyter as global system package</h2>
<p>



    
    <input type="checkbox" id="zoomCheck-f536f" hidden>
    <label for="zoomCheck-f536f">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-as-system-package.svg" alt="install jupyter as system package" 
             />
    </label>

</p>
<p>The Jupyter Notebook installation can be centralized using the system package manager. Virtual environments are created separately.</p>
<p>To install Jupyter Notebook on Debian based systems, use <code>apt</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo apt install python3 python3-virtualenv jupyter
</span></span><span class="line"><span class="cl">jupyter notebook
</span></span></code></pre></div><p>This is convenient because there is only one Jupyter Notebook installation - it can be accessed on localhost:8888. Next, virtual environments must created, and modules need to be installed in different virtual environments.</p>
<h3 id="commands">Commands</h3>
<p>Commands can be run from within a notebook. A better idea of how Jupyter Notebook works is needed to understand this. Internally, it has a component called the IPython Kernel. This is responsible for communicating with the Python interpreter on the system. The <code>ipykernel</code> is included by default when Jupyter Notebook is installed, but it can also be installed independently.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-28a4e" hidden>
    <label for="zoomCheck-28a4e">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="how-ipython-kernel-integrates.svg" alt="how ipython kernel integrates" 
             />
    </label>

</p>
<p>Shell commands are provided by the operating system. They can be run by prefixing a line with an exclaimation mark:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="err">!</span><span class="n">ls</span> <span class="p">;</span> <span class="n">cd</span> <span class="n">example_dir</span>
</span></span></code></pre></div><p>This spawns a subprocess that exits upon completion; it is equivalent (on Linux) to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">subprocess</span>
</span></span><span class="line"><span class="cl"><span class="n">subprocess</span><span class="o">.</span><span class="n">run</span><span class="p">([</span><span class="s2">&#34;bash&#34;</span><span class="p">,</span> <span class="s2">&#34;-c&#34;</span><span class="p">,</span> <span class="s2">&#34;ls ; cd example_dir&#34;</span><span class="p">])</span>
</span></span></code></pre></div><p>In this case, the <code>ls</code> will list the files in the current directory and <code>cd</code> will change the directory. The change in directory will not persist since the process exists after completion.</p>
<p>Magic commands (prefixed with a percentage sign) are provided by the IPython kernel. State changes persist because these commands are run inside the kernel process itself (and apply to the virtual environment in which the kernel was installed):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">cd</span> <span class="n">example_dir</span>
</span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">page</span> <span class="n">my_file</span><span class="o">.</span><span class="n">txt</span>
</span></span><span class="line"><span class="cl"><span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">bs4</span>
</span></span></code></pre></div><p>This prints <code>example_dir/my_file.txt</code> and installs BeautifulSoup (a module for HTML parsing) in the current virtual environment.</p>
<p>For all available magic commands, refer to the <a href="https://ipython.readthedocs.io/en/stable/interactive/magics.html">IPython documentation</a> or run:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">lsmagic</span>
</span></span></code></pre></div><p>To install a module in the current virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="o">%</span><span class="n">pip</span> <span class="n">install</span> <span class="n">module_name</span>
</span></span></code></pre></div><p>It is not possible to create and use a virtual environment directly within a notebook. For example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">!python -m virtualenv myvenv
</span></span><span class="line"><span class="cl">!source myvenv/bin/activate
</span></span><span class="line"><span class="cl">!pip install bs4
</span></span></code></pre></div><p>The first two commands succeed but the third fails with an <code>externally-managed-environment</code> error. This is because a new subprocess is created for each command - the sourced environment is not actually activated when pip is used. This can be resolved by chaining the command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">!python -m virtualenv myvenv<span class="p">;</span> <span class="nb">source</span> myvenv/bin/activate<span class="p">;</span> pip install bs4
</span></span></code></pre></div><p>BeautifulSoup now installs in the <code>myvenv</code> virtual environment, but the notebook does not use this environment. The virtual environment that the IPython kernel is running on is different to the one created - virtual environments cannot be nested!</p>
<h3 id="multiple-virtual-environments">Multiple Virtual Environments</h3>
<p>To make a virtual environments available to Jupyter Notebook, the ipykernel module should be installed inside the virtual environment:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">python3 -m virtualenv --system-site-packages env-name
</span></span><span class="line"><span class="cl"><span class="nb">source</span> env-name/bin/activate
</span></span><span class="line"><span class="cl">pip install ipykernel
</span></span><span class="line"><span class="cl">python3 -m ipykernel install --user --name<span class="o">=</span>env-name --display-name <span class="s2">&#34;Python (env-name)&#34;</span>
</span></span></code></pre></div><blockquote>
<p>The <code>--system-site-packages</code> flag is particularly important if you depend on globally installed modules. For example, <code>matplotlib</code> has to be specially packaged for ARM platforms like Raspberry Pi and installed using the system package manager. This flag ensures that these modules can be found.</p>
</blockquote>
<p>Since the Jupyter Notebook installation is global, the above commands can be easily run from the interface by creating a new terminal.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-50c75" hidden>
    <label for="zoomCheck-50c75">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="jupyter-new-menu.png" alt="jupyter new menu" 
             />
    </label>

</p>
<p>When creating a new notebook, you can select <code>Python (env-name)</code> to use a particular virtual environment.</p>
<p>To list all kernels and remove a particular kernel:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">jupyter-kernelspec list
</span></span><span class="line"><span class="cl">jupyter-kernelspec uninstall env-name
</span></span></code></pre></div><p>There is still a problem - this is running directly on the server. If something goes wrong, it might affect other services. This is also not very easily portable.</p>
<h2 id="install-jupyter-on-docker">Install Jupyter on Docker</h2>
<p>Using docker, this can be containerized!</p>
<p>



    
    <input type="checkbox" id="zoomCheck-aeca6" hidden>
    <label for="zoomCheck-aeca6">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-in-docker.svg" alt="install jupyter in docker" 
             />
    </label>

</p>
<p>This Dockerfile uses Alpine linux as the base image and installs <code>python</code>, <code>pip</code>, <code>virtualenv</code> and <code>jupyter-notebook</code> using the Alpine package manager <code>apk</code>.
The last command starts the notebook. This probably isn&rsquo;t very secure - you might want to modify the flags based on your security model.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="s"> alpine:3.22</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> apk add python3 py3-pip py3-virtualenv jupyter-notebook<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="s"> 8888</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;jupyter-notebook&#34;</span><span class="p">,</span> <span class="s2">&#34;--ip=0.0.0.0&#34;</span><span class="p">,</span> <span class="s2">&#34;--no-browser&#34;</span><span class="p">,</span> <span class="s2">&#34;--allow-root&#34;</span><span class="p">,</span> <span class="s2">&#34;--NotebookApp.token=&#39;your-very-long-and-secure-passphrase&#39;&#34;</span><span class="p">]</span><span class="err">
</span></span></span></code></pre></div><p>Now, creating the following docker-compose.yaml file and running <code>docker compose up</code> will build the image and expose Jupyter Notebook on localhost:4444. To manage the virtual environments, use the terminal and follow the steps in the previous section.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">services:
</span></span><span class="line"><span class="cl">  jupyter:
</span></span><span class="line"><span class="cl">    container_name: jupyter
</span></span><span class="line"><span class="cl">    build: .
</span></span><span class="line"><span class="cl">    ports:
</span></span><span class="line"><span class="cl">      - 4444:8888
</span></span></code></pre></div><p>To reproduce this cionfiguration on a different service, just the <code>Dockerfile</code> and <code>docker-compose.yaml</code> need to be copied.</p>
]]></content>
      </entry>
      <entry>
        <title></title>
        <link rel="alternate" href="/organization/policies/semi-automatic-generation/" />
        <id>/organization/policies/semi-automatic-generation/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">This policy relates to the usage of semi automatic generation - commonly refered to as Large Language Models, diffusion models and related terms. More generally, it defines a framework for when such tools are acceptable.</summary>
          <content type="html"><![CDATA[<p>This policy relates to the usage of semi automatic generation - commonly refered to as Large Language Models, diffusion models and related terms. More generally, it defines a framework for when such tools are acceptable.</p>
]]></content>
      </entry>
      <entry>
        <title>A LaTeX Workflow</title>
        <link rel="alternate" href="/drafts/a-latex-workflow/" />
        <id>/drafts/a-latex-workflow/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Notes are digital, and there is a challenge in it&amp;rsquo;s transition from handwriting. While this transition is in many cases vastly beneficial, the author no longer has the freedom to design their writings exactly as they wish. They are constrained by the limitations of the software systems they choose to use.
Plaintext (&amp;quot;*.txt&amp;quot;) is the most basic of such systems - it simply stores symbols in order as they appear without any other formatting.</summary>
          <content type="html"><![CDATA[<p>Notes are digital, and there is a challenge in it&rsquo;s transition from handwriting. While this transition is in many cases vastly beneficial, the author no longer has the freedom to design their writings exactly as they wish. They are constrained by the limitations of the software systems they choose to use.</p>
<p><strong>Plaintext</strong> (&quot;*.txt&quot;) is the most basic of such systems - it simply stores symbols in order as they appear without any other formatting. These files can be edited directly with any text editor.<br>
<strong>Markup</strong> (&quot;*.md&quot;, &ldquo;*.tex&rdquo;, &ldquo;*.html&rdquo;) languages include additional text to specify some formatting, but can still be reasonably edited with a text editor.
<strong>Richtext</strong> formats (&quot;*.rtf&quot;, &ldquo;*.docx&rdquo;) store content with formatting instructions that typically require specialized software (word processors) to edit and render.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-15c07" hidden>
    <label for="zoomCheck-15c07">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="types-of-documents.svg" alt="types of documents" 
             />
    </label>

</p>
<p>There are two broad categories of editors:</p>
<ul>
<li>Code editors where a source file is edited and seperately rendered into a final document</li>
<li>What You See Is What You Get (WYSIWYG) editors where the document is rendered and edited seamlessly in the same place</li>
</ul>
<p>



    
    <input type="checkbox" id="zoomCheck-22373" hidden>
    <label for="zoomCheck-22373">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="types-of-editors.svg" alt="types of editors" 
             />
    </label>

</p>
<p>Latex typically uses code editors, and this may seem imtimidating and complicated until the initial learning curve is overcome.<br>
If a visual editor is strongly preffered, <a href="https://www.lyx.org">LyX</a> works with latex and is completely free and open source (GPL).<br>
If the goal is to just explore latex without setting it up on your system, <a href="https://www.overleaf.com/">Overleaf</a> is very useful. Note that some features are locked behind a subscription.</p>
<h1 id="latex">LaTeX</h1>
<p>LaTeX is a typesetting system - it is a way of organizing text and images on a page using text commands, templates and composition.</p>
<h2 id="simple-document">Simple Document</h2>
<p>This is a simple latex source file (hello.tex) to show some of the features of latex:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-latex" data-lang="latex"><span class="line"><span class="cl"><span class="k">\documentclass</span><span class="nb">{</span>article<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>document<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\chapter</span><span class="nb">{</span>Title<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>document<span class="nb">}</span>
</span></span></code></pre></div><p>You can use it to create a pdf:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">latexmk -pdf -outdir<span class="o">=</span>build hello.tex
</span></span></code></pre></div><h2 id="quick-reference">Quick Reference</h2>
<h2 id="value">Value</h2>
<ul>
<li>supports scientific and mathematical notation</li>
<li>creates a higher degree of separation of content from formatting</li>
<li>high typographical quality</li>
<li>stable and longevity</li>
<li>free and open source</li>
<li>robust referencing system</li>
</ul>
<p>Extensive use in the scientific community</p>
<h2 id="template">Template</h2>
<p>The following is an example of a template that can be created:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">.
</span></span><span class="line"><span class="cl">├── build
</span></span><span class="line"><span class="cl">├── chapters
</span></span><span class="line"><span class="cl">│   ├── introduction.tex
</span></span><span class="line"><span class="cl">│   ├── chapter1.tex
</span></span><span class="line"><span class="cl">│   ├── chapter2.tex
</span></span><span class="line"><span class="cl">│   ├── references.bib
</span></span><span class="line"><span class="cl">│   └── title.tex
</span></span><span class="line"><span class="cl">├── figures
</span></span><span class="line"><span class="cl">├── main.tex
</span></span><span class="line"><span class="cl">├── Makefile
</span></span><span class="line"><span class="cl">└── style
</span></span><span class="line"><span class="cl">    └── style.tex
</span></span></code></pre></div><p>The entire document does not need to be contained in a single source file - it can be composed from many files.</p>
<p>In this structure, each chapter has it&rsquo;s own <code>tex</code> file. Figures are kept in a dedicated directory (<code>figures/</code>) and the styling is defined in <code>style.tex</code>.</p>
<p><code>main.tex</code> brings everything together with the <code>\input</code> command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-latex" data-lang="latex"><span class="line"><span class="cl"><span class="k">\documentclass</span><span class="nb">{</span>report<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\input</span><span class="nb">{</span>style/style.tex<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\begin</span><span class="nb">{</span>document<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\pagenumbering</span><span class="nb">{</span>roman<span class="nb">}</span>
</span></span><span class="line"><span class="cl"><span class="k">\input</span><span class="nb">{</span>chapters/title<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\tableofcontents</span>
</span></span><span class="line"><span class="cl"><span class="k">\newpage</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\pagenumbering</span><span class="nb">{</span>arabic<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\input</span><span class="nb">{</span>chapters/introduction<span class="nb">}</span>
</span></span><span class="line"><span class="cl"><span class="k">\input</span><span class="nb">{</span>chapters/chapter1<span class="nb">}</span>
</span></span><span class="line"><span class="cl"><span class="k">\input</span><span class="nb">{</span>chapters/chapter2<span class="nb">}</span>
</span></span><span class="line"><span class="cl"><span class="k">\newpage</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\bibliography</span><span class="nb">{</span>chapters/references.bib<span class="nb">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">\end</span><span class="nb">{</span>document<span class="nb">}</span>
</span></span></code></pre></div><p>This should give an idea of how latex can be used to create a powerful and efficient system - once a document format is well defined, it can be reproduced very easily. For most formatting you wish to do, there likely already exists some template. If you are creating one from scratch, or modifying an existing template to meet your requirement, the process may feel tedious and unncessary, but this is a once-off cost that will be rewarded every time it is reused.</p>
<h1 id="the-workflow">The Workflow</h1>
<p>text editor: neovim
document generator: latexmk
viewer: evince</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b6d2c" hidden>
    <label for="zoomCheck-b6d2c">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="the-latex-workflow.svg" alt="the latex workflow" 
             />
    </label>

</p>
<p>Your workflow will depend on your operating system and software preferences. While the workflow defined uses examples for Linux, the general setup should be readily applicable to any platform.</p>
<h2 id="document-generator">Document Generator</h2>
<p>This is the core software that automates the building of latex source files to the desired output document.</p>
<p><a href="https://www.cantab.net/users/johncollins/latexmk/index.html">latexmk</a> supports many operarating systems (including Linux, MacOS and Windows) and, given the source files for a document, it issues the appropriate sequence of commands to generate a <code>pdf</code>, <code>ps</code> or <code>dvi</code> document. <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>continous watching and updates pvc</p>
<h2 id="text-editor">Text Editor</h2>
<p>To edit the latex source files, any text editor will do.</p>
<p>Not much features are required, but the following makes the editing easier:</p>
<ul>
<li>Syntax highlighting (to validate syntax and quickly identify errors)</li>
<li>Autosave (to automatically trigger re-rendering on changes)</li>
</ul>
<p>A terminal based text editor like <code>neovim</code> works perfectly too!</p>
<blockquote>
<p>To autosave for the current file in <code>neovim</code>, run command <code>autocmd TextChanged,TextChangedI &lt;buffer&gt; silent write</code>.</p>
</blockquote>
<h2 id="viewer">Viewer</h2>
<p>Once again, any PDF viewer will work. Some viewers may not automatically update if the output file changes - these are not very nice to work with as they have to be refreshed manally each time.</p>
<p>Evince is the default document view on GNOME. A cool feature is that it supports dark mode (color inversion)!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://ctan.org/pkg/latexmk/">https://ctan.org/pkg/latexmk/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>A New Set of Metrics for Video Productions</title>
        <link rel="alternate" href="/drafts/a-new-metric/" />
        <id>/drafts/a-new-metric/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">&amp;ldquo;When a metric becomes a target, it ceases to be a good metric.&amp;rdquo;
Make no mistake - metrics like views, clickthrough rate, watch time, retention, likes and subscribers are really fascinating. Such data points reveal a lot about the audience and the performance of production outputs. However, linking these metrics as a production feedback results in undesirable influence. Really, it comes done to what you are trying to achieve.
Maximizing these metrics could become the goal, creating an environment that encourages:</summary>
          <content type="html"><![CDATA[<p>&ldquo;When a metric becomes a target, it ceases to be a good metric.&rdquo;</p>
<p>Make no mistake - metrics like views, clickthrough rate, watch time, retention, likes and subscribers are really fascinating. Such data points reveal a lot about the audience and the performance of production outputs. However, linking these metrics as a production feedback results in undesirable influence. Really, it comes done to what you are trying to achieve.</p>
<p>Maximizing these metrics could become the goal, creating an environment that encourages:</p>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Encourages</th>
</tr>
</thead>
<tbody>
<tr>
<td>Views</td>
<td>Videos that many people will watch</td>
</tr>
<tr>
<td>Clickthrough Rate</td>
<td>Titles and thumbnails that attract people</td>
</tr>
<tr>
<td>Watch time</td>
<td>Videos that take long to watch</td>
</tr>
<tr>
<td>Retention</td>
<td>Videos that keep the attention of the people</td>
</tr>
<tr>
<td>Likes</td>
<td>Videos that people do not dislike</td>
</tr>
<tr>
<td>Comments</td>
<td>Videos that promote engagement</td>
</tr>
<tr>
<td>Subscribers</td>
<td>Videos that people want to see more of</td>
</tr>
</tbody>
</table>
<p>These are metrics of the people for the system that connects them to videos - &ldquo;the algorithm&rdquo;. None of these metrics are in direct control of the creator.</p>
<p>Metrics should be incidental.</p>
<table>
<thead>
<tr>
<th>Metric</th>
<th>Encourages</th>
</tr>
</thead>
<tbody>
<tr>
<td>Video count</td>
<td>Producing more videos</td>
</tr>
<tr>
<td>Video quality</td>
<td>Creating videos that improve the self-reported standard</td>
</tr>
<tr>
<td>Video impact</td>
<td>Producing videos that are valuable and benefit people</td>
</tr>
</tbody>
</table>
<p>Titles and thumbnails should be a true representation of what the video presents.
The thumbnail should be a unaltered frame from the video. There is no reason why anything related to the video that is not included in the video would provide more value.</p>
<p>A video production may fall into one of three categories:</p>
<ul>
<li>guide: to do this, the following dependencies are required and these step are to be followed</li>
<li>research: this is a problem, and this is the existing literature</li>
<li>perspective: to see the world differently</li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>A State of Emergency</title>
        <link rel="alternate" href="/drafts/a-state-of-emergency/a-state-of-emergency/" />
        <id>/drafts/a-state-of-emergency/a-state-of-emergency/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html"></summary>
          <content type="html"><![CDATA[]]></content>
      </entry>
      <entry>
        <title>A Visual Guide to Git</title>
        <link rel="alternate" href="/drafts/a-visual-guide-to-git/" />
        <id>/drafts/a-visual-guide-to-git/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">git is a commandline version control tool.
It tracks how content changes and makes different changes happening at once easier to manage.
It offers a few key features:
diffing: comparing two versions branching: working with different versions merging: combining different versions into a new version This guide uses graphs to establish a model of the git workflow. The internal workings of git are explored for a better understanding of how and why certain features work.</summary>
          <content type="html"><![CDATA[<p><code>git</code> is a commandline version control tool.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-d5a40" hidden>
    <label for="zoomCheck-d5a40">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="version-control.svg" alt="version control" 
             />
    </label>

</p>
<p>It tracks how content changes and makes different changes happening at once easier to manage.</p>
<p>It offers a few key features:</p>
<ul>
<li>diffing: comparing two versions</li>
<li>branching: working with different versions</li>
<li>merging: combining different versions into a new version</li>
</ul>
<p>This guide uses graphs to establish a model of the git workflow. The internal workings of git are explored for a better understanding of how and why certain features work. It is not necessary to completely understand how it works internally to use <code>git</code> itself, but the hope is that it provides better clarity.</p>
<hr>
<h1 id="a-typical-workflow">A Typical Workflow</h1>
<p>?</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git pull
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit
</span></span><span class="line"><span class="cl">git push
</span></span></code></pre></div><hr>
<h1 id="installation">Installation</h1>
<blockquote>
<p>Getting the executable.</p>
</blockquote>
<p>



    
    <input type="checkbox" id="zoomCheck-124a3" hidden>
    <label for="zoomCheck-124a3">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="git-executable-extract.png" alt="git executable extract" 
             />
    </label>

</p>
<p>Git can be installed by following the official documentation: <a href="https://git-scm.com/install/">https://git-scm.com/install/</a>.
Installation makes the <code>git</code> command available by storing the binary executable in a particular folder.</p>
<p><code>PATH</code> is a variable in operating systems that lists the folders to look for executables:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$PATH</span>  <span class="c1"># Linux and MacOS</span>
</span></span><span class="line"><span class="cl">path        <span class="c1"># Windows</span>
</span></span></code></pre></div><p>The exact location of the executable can be determined:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">which git   <span class="c1"># On Linux and MacOS</span>
</span></span><span class="line"><span class="cl">where git   <span class="c1"># On Windows</span>
</span></span></code></pre></div><p>If the folder is not in path - for example, by downloading the executable manually to a different folder - the full path to the executable must be used to run <code>git</code>, or that folder must be added to the path.</p>
<p>Running with full path:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">/usr/bin/git --version
</span></span></code></pre></div><hr>
<h1 id="the-repository">The Repository</h1>
<h2 id="new-repository">New Repository</h2>
<blockquote>
<p>Starting tracking.</p>
</blockquote>
<p>A repository is just a folder which has a <code>.git</code> sub folder.</p>
<p>A new repository can be initialized:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git init
</span></span></code></pre></div><p>This creates the hidden <code>.git</code> folder in the current directory and copies the contents of a template folder. The default template folder is <code>/usr/share/git-core/templates</code> <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.</p>
<p>The <code>.git</code> folder is particularly important to how <code>git</code> works. The <code>git</code> executable can be run from any directory, but it will search for the <code>.git</code> folder in the current tree to determine which repository it is working with.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-e915b" hidden>
    <label for="zoomCheck-e915b">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="the-repository.svg" alt="the repository" 
             />
    </label>

</p>
<h2 id="existing-repository">Existing Repository</h2>
<blockquote>
<p>Resuming development.</p>
</blockquote>
<p>An existing repository can be copied with:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git clone https://git.example.com/example/example.git
</span></span></code></pre></div><p>This creates a new directory <code>example</code> and copies the content (along with the <code>.git</code> directory) to this directory. A similar effect can be achieved by manually copying the repository, or using tools like <code>scp</code> or <code>rsync</code>, but <code>git clone</code> does a few extra git specific things:</p>
<ul>
<li>creates remote tracking branches</li>
<li>creates and checks out initial branch</li>
</ul>
<p>These actions are defined in the <a href="/drafts/a-visual-guide-to-git/#promotion-model">promotion model</a>.</p>
<h2 id="internal-structure">Internal Structure</h2>
<hr>
<h1 id="promotion-model">Promotion Model</h1>
<p>



    
    <input type="checkbox" id="zoomCheck-b1993" hidden>
    <label for="zoomCheck-b1993">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="promotion-model.svg" alt="promotion mode" 
             />
    </label>

</p>
<p>To go through each of these stages, a particular command is used.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-773b3" hidden>
    <label for="zoomCheck-773b3">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="promotion-model-commands.svg" alt="promotion mode commands" 
             />
    </label>

</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git commit
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git log --oneline
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git reset --soft HEAD~1
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git status
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git restore --staged
</span></span><span class="line"><span class="cl">git reset
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git diff --cached
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git config user.name <span class="s2">&#34;Name&#34;</span>
</span></span><span class="line"><span class="cl">git config user.email <span class="s2">&#34;name@host.tld&#34;</span>
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;commit message&#34;</span>
</span></span><span class="line"><span class="cl">git push origin master
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">git remote add origin https://git.example.com
</span></span></code></pre></div><hr>
<h1 id="branching-model">Branching Model</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git branch
</span></span></code></pre></div><hr>
<h1 id="collaborative-workflow">Collaborative Workflow</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">git pull
</span></span><span class="line"><span class="cl">git merge
</span></span></code></pre></div><h2 id="merge-conflict">Merge Conflict</h2>
<h2 id="remotes">Remotes</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">git remote
</span></span></code></pre></div><hr>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://git-scm.com/docs/git-init#_template_directory">https://git-scm.com/docs/git-init#_template_directory</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>About</title>
        <link rel="alternate" href="/about/" />
        <id>/about/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">This Website Source code is available at https://git.kistu.dev/kistu/kistu.dev.
Powered by Hugo. Theme is WonderMod (with some modifications). The use of JavaScript is avoided as much as possible.
Illustrations are created with Excalidraw Inkscape.
Updates Webpages are improved after publication. When a published piece is edited, the modified date is updated.
The complete history of changes are recorded because git is used for version control. Refer to the commit history in the source repository: https://git.</summary>
          <content type="html"><![CDATA[<h1 id="this-website">This Website</h1>
<p>Source code is available at <a href="https://git.kistu.dev/kistu/kistu.dev">https://git.kistu.dev/kistu/kistu.dev</a>.</p>
<p>Powered by <a href="https://gohugo.io/">Hugo</a>. Theme is <a href="https://github.com/Wonderfall/hugo-WonderMod/">WonderMod</a> (with some modifications). The use of JavaScript is avoided as much as possible.</p>
<p>Illustrations are created with <del><a href="https://github.com/excalidraw/excalidraw/">Excalidraw</a></del> <a href="https://gitlab.com/inkscape/inkscape">Inkscape</a>.</p>
<h2 id="updates">Updates</h2>
<p>Webpages are improved after publication. When a published piece is edited, the modified date is updated.</p>
<p>The complete history of changes are recorded because <code>git</code> is used for version control. Refer to the commit history in the source repository: <a href="https://git.kistu.dev/kistu/kistu.dev/commits/branch/master">https://git.kistu.dev/kistu/kistu.dev/commits/branch/master</a>.</p>
<h2 id="automatic-generation">Automatic Generation</h2>
<p>None of the content published is automatically generated. See posts under the tag <a href="/tags/automatic-generation/">automatic-generation</a> for more information.</p>
<h1 id="mirrors">Mirrors</h1>
<p>This website is available at:<br>
<a href="https://kistu.dev">https://kistu.dev</a><br>
<a href="https://kistu.neocities.org/">https://kistu.neocities.org</a><br>
<a href="https://kistu.pages.dev/">https://kistu.pages.dev</a><br>
<a href="https://kistu.netlify.app">https://kistu.netlify.app</a></p>
<p>Available on Tor using <a href="https://www.torproject.org/download/">the Tor Browser</a> with these onion addresses:<br>
<a href="http://ovypvbq2p4buxj3fknhuzkn3y42dzye7l4mqqk2dnolofrmpo6cszzid.onion">http://ovypvbq2p4buxj3fknhuzkn3y42dzye7l4mqqk2dnolofrmpo6cszzid.onion</a><br>
<a href="http://46f5l4o254ibgxniv7w644z5ljzrietotcc5jeq7qkaks4nlebfumyid.onion">http://46f5l4o254ibgxniv7w644z5ljzrietotcc5jeq7qkaks4nlebfumyid.onion</a></p>
<h1 id="gpg-public-key">GPG Public Key</h1>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">-----BEGIN PGP PUBLIC KEY BLOCK-----
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">-----END PGP PUBLIC KEY BLOCK-----
</span></span></code></pre></div>]]></content>
      </entry>
      <entry>
        <title>Archiving Webpages</title>
        <link rel="alternate" href="/drafts/archiving-webpages/" />
        <id>/drafts/archiving-webpages/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Webpage A webpage typically consists of 3 types of files:
HyperText Markup Language (HTML) for defining and structuring content Cascading Style Sheet (CSS) for styling content JavaScript (JS) for interactivity Content in this case refers to headings, paragraphs, buttons, links, images and so on. Styling content changes how the content is rendered; this includes typography, spacing, layout and color. JavaScript introduces interactivity - when certain conditions are met, it can change the structure and style of content.</summary>
          <content type="html"><![CDATA[<h1 id="webpage">Webpage</h1>
<p>A webpage typically consists of 3 types of files:</p>
<ul>
<li>HyperText Markup Language (HTML) for defining and structuring content</li>
<li>Cascading Style Sheet (CSS) for styling content</li>
<li>JavaScript (JS) for interactivity</li>
</ul>
<p>Content in this case refers to headings, paragraphs, buttons, links, images and so on. Styling content changes how the content is rendered; this includes typography, spacing, layout and color. JavaScript introduces interactivity - when certain conditions are met, it can change the structure and style of content.</p>
<h2 id="a-simple-webpage">A Simple Webpage</h2>
<p>This is a simple webpage. The HTML boilerplate was copied from the Odin Project <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. An example project from W3Schools was used for the counter <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<h3 id="html">HTML</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="cp">&lt;!DOCTYPE html&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;en&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&#34;UTF-8&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Click Counter<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;stylesheet&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;style.css&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&#34;script.js&#34;</span> <span class="na">defer</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">head</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>What is this?<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">p</span><span class="p">&gt;</span>This is a simple webpage that counts the number of times a button was clicked.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">h2</span><span class="p">&gt;</span>How does it work?<span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">p</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;listing&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		The HTML defines this text, the image above and the button below. <span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		CSS adds styling so it looks nice. <span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		JS changes the text when the button is clicked. <span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">h1</span><span class="p">&gt;</span>Clicker<span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">button</span> <span class="na">onclick</span><span class="o">=</span><span class="s">&#34;increaseCount()&#34;</span><span class="p">&gt;</span>Click me!<span class="p">&lt;/</span><span class="nt">button</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">br</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		<span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;count-display&#34;</span><span class="p">&gt;</span>This button has been pressed <span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		<span class="p">&lt;</span><span class="nt">p</span> <span class="na">id</span><span class="o">=</span><span class="s">&#34;count&#34;</span><span class="p">&gt;</span>0<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">		<span class="p">&lt;</span><span class="nt">p</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;count-display&#34;</span><span class="p">&gt;</span> times.<span class="p">&lt;/</span><span class="nt">p</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">	<span class="p">&lt;/</span><span class="nt">span</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">body</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">html</span><span class="p">&gt;</span>
</span></span></code></pre></div><blockquote>
<p>The background and text color are inverted by the <a href="https://darkreader.org/">DarkReader extension</a>.




    
    <input type="checkbox" id="zoomCheck-4b464" hidden>
    <label for="zoomCheck-4b464">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="webpage-html.png" alt="webpage html" 
             />
    </label>

</p>
</blockquote>
<h3 id="css">CSS</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#181A1B</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h1</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="s2">&#34;Brush Script MT&#34;</span><span class="p">,</span> <span class="kc">cursive</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#D38693</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin-bottom</span><span class="p">:</span> <span class="mi">3</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">h2</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#89B482</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">p</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">color</span><span class="p">:</span> <span class="mh">#7DAEA3</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nt">button</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">font-family</span><span class="p">:</span> <span class="s2">&#34;Impact&#34;</span><span class="p">,</span> <span class="kc">fantasy</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">background-color</span><span class="p">:</span> <span class="mh">#D3869B</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-color</span><span class="p">:</span> <span class="mh">#3C333B</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">padding</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">margin</span><span class="p">:</span> <span class="mi">10</span><span class="kt">px</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">border-radius</span><span class="p">:</span> <span class="mi">8</span><span class="kt">px</span><span class="p">;</span>	
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">#</span><span class="nn">listing</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">text-align</span><span class="p">:</span> <span class="kc">left</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">flex</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">justify-content</span><span class="p">:</span> <span class="kc">center</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">.</span><span class="nc">count-display</span><span class="o">,</span> <span class="p">#</span><span class="nn">count</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">display</span><span class="p">:</span> <span class="kc">inline</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>



    
    <input type="checkbox" id="zoomCheck-de3e5" hidden>
    <label for="zoomCheck-de3e5">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="webpage-css.png" alt="webpage css" 
             />
    </label>

</p>
<h3 id="js">JS</h3>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="cl"><span class="c1">// Declare the counter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">let</span> <span class="nx">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Function to display the counter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">updateCount</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&#34;count&#34;</span><span class="p">).</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">count</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Function to increase the counter
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">increaseCount</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">count</span><span class="o">++</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">updateCount</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>



    
    <input type="checkbox" id="zoomCheck-22e85" hidden>
    <label for="zoomCheck-22e85">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="webpage-js.gif" alt="webpage js" 
             />
    </label>

</p>
<p><strong>simple webpage</strong></p>
<p>Client-Side vs Server-Side
RectJS, Angular, PHP, Springboot</p>
<p>images</p>
<p>inlining - SingleFile</p>
<p>warc</p>
<p>For archiving purposes, one area of complexity is that the webpage is not monolithic; the webpage contains links to assets it requires. A local copy will have to acquire these assets and rewrite the links to refer to the local assets.</p>
<p>Scope: Static webpages and webpages with client-side JS.</p>
<h1 id="server-side">Server Side</h1>
<h1 id="client-side">Client Side</h1>
<p>resolution</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://www.theodinproject.com/lessons/foundations-html-boilerplate">https://www.theodinproject.com/lessons/foundations-html-boilerplate</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://www.w3schools.com/js/js_project_counter.asp">https://www.w3schools.com/js/js_project_counter.asp</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>Artifactory</title>
        <link rel="alternate" href="/projects/software/artifactory/" />
        <id>/projects/software/artifactory/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">A general purpose BitTorrent tracker.</summary>
          <content type="html"><![CDATA[<h1 id="features">Features</h1>
<ul>
<li>GPG Authentication</li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>Audio Link</title>
        <link rel="alternate" href="/projects/software/audio-link/" />
        <id>/projects/software/audio-link/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">E2E encrypted audio chat over the internet.</summary>
          <content type="html"><![CDATA[<p>E2E encrypted audio chat over the internet.</p>
]]></content>
      </entry>
      <entry>
        <title>CharaType</title>
        <link rel="alternate" href="/projects/software/charatype/" />
        <id>/projects/software/charatype/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Learn to write and type with different language or keyboard layout.</summary>
          <content type="html"><![CDATA[<h1 id="concept">Concept</h1>
<p>Learning a new writing system is difficult, particularly when there are two different mediums (physical and digital) to master. Software keyboards are somewhat better adapted for different languages becuase their symbol set can change to match the writing system. Hardware keyboards are limited in this aspect.</p>
<p>This application provides a medium to practise writing and learn the associated keyboard mapping.</p>
<h1 id="prototype-1">Prototype 1</h1>
<h2 id="interface">Interface</h2>
<p>The interface consists of 4 key areas:</p>
<ul>
<li>writing area for touchscreen/tablet input</li>
<li>optical character recognition based on writing area</li>
<li>typing space for keyboard input</li>
<li>typing animation to show keystrokes and keyboard state</li>
</ul>
<h2 id="language-support">Language Support</h2>
<p>Only <code>English (US) en</code> is supported.</p>
<h2 id="complexity">Complexity</h2>
<p>The animation is expected to be the most complex element.<br>
The main complexity for extending language support is expected to be OCR.</p>
]]></content>
      </entry>
      <entry>
        <title>Ctr &#43; C Bug</title>
        <link rel="alternate" href="/drafts/ctrl-c-bug/" />
        <id>/drafts/ctrl-c-bug/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Behaviour: System sometimes logs out when copying text with ctr+c. Reproduce: Run archlinux in a QEMU/KVM virtual machine
This has been occuring infrequenctly enough that the system is not unusable, but it would be great if it didn&amp;rsquo;t happen. This document mainly serves as an exploration into how the cause of similar issues can be identified.
Crashlogs dmesg sudo dmesg coredumpctl coredumpctl list coredumpctl info &amp;lt;pid&amp;gt; </summary>
          <content type="html"><![CDATA[<p>Behaviour: System sometimes logs out when copying text with ctr+c.
Reproduce: Run archlinux in a QEMU/KVM virtual machine</p>
<p>This has been occuring infrequenctly enough that the system is not unusable, but it would be great if it didn&rsquo;t happen. This document mainly serves as an exploration into how the cause of similar issues can be identified.</p>
<h1 id="crashlogs">Crashlogs</h1>
<h2 id="dmesg">dmesg</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo dmesg
</span></span></code></pre></div><h2 id="coredumpctl">coredumpctl</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">coredumpctl list
</span></span><span class="line"><span class="cl">coredumpctl info &lt;pid&gt;
</span></span></code></pre></div>]]></content>
      </entry>
      <entry>
        <title>Digital Data Security</title>
        <link rel="alternate" href="/drafts/digital-data-security/" />
        <id>/drafts/digital-data-security/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Physical backup hashing and verification
link to failure rate study
Processor 3, 2, 1 backup + hashing
Network Control ransomware - backup exfiltration - encryption timebomb - time triggered computation change
Highest threat:
quickest way to delete data timebombs Network Access:
Data Continous: Connectivity, integrity Intermittent: integrity Purge to cold storage [] - [] - []
first is online second is a ferry third is never online second is required to prevent the third from occassionally going online (first is always online)</summary>
          <content type="html"><![CDATA[<h1 id="physical">Physical</h1>
<p>backup
hashing and verification</p>
<p>link to failure rate study</p>
<h1 id="processor">Processor</h1>
<p>3, 2, 1
backup + hashing</p>
<h1 id="network-control">Network Control</h1>
<p>ransomware - backup
exfiltration - encryption
timebomb - time triggered computation change</p>
<p>Highest threat:</p>
<ul>
<li>quickest way to delete data</li>
<li>timebombs</li>
</ul>
<p>Network Access:</p>
<ul>
<li>Data
<ul>
<li>Continous: Connectivity, integrity</li>
<li>Intermittent: integrity</li>
</ul>
</li>
</ul>
<h1 id="purge-to-cold-storage">Purge to cold storage</h1>
<p>[] - [] - []</p>
<ul>
<li>first is online</li>
<li>second is a ferry</li>
<li>third is never online</li>
</ul>
<p>second is required to prevent the third from occassionally going online (first is always online)</p>
<p>what prevents a packet replay attack? I could send the (encrypted) login packet to login somewhere else</p>
]]></content>
      </entry>
      <entry>
        <title>Electric ATV</title>
        <link rel="alternate" href="/projects/transportation/electric-atv/" />
        <id>/projects/transportation/electric-atv/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">All terrain vehicle powered by electricity.</summary>
          <content type="html"><![CDATA[]]></content>
      </entry>
      <entry>
        <title>Entity Language</title>
        <link rel="alternate" href="/drafts/entity-language/" />
        <id>/drafts/entity-language/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Why some things are difficult to define precisely.
The entity experiences a world of colors and forms.
It has limited means of sharing this experience with its peers.
So it uses what means it has.
sound as a representation
not limited to physical, references to abstract concepts
symbols expand reach
ground truth
change in symbols change in entities change in the world</summary>
          <content type="html"><![CDATA[<blockquote>
<p>Why some things are difficult to define precisely.</p>
</blockquote>
<p>The entity experiences a world of colors and forms.</p>
<p>It has limited means of sharing this experience with its peers.</p>
<p>So it uses what means it has.</p>
<p>sound as a representation</p>
<p>not limited to physical, references to abstract concepts</p>
<p>symbols expand reach</p>
<p>ground truth</p>
<p>change in symbols
change in entities
change in the world</p>
]]></content>
      </entry>
      <entry>
        <title>Entity Model</title>
        <link rel="alternate" href="/drafts/entity-model/" />
        <id>/drafts/entity-model/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">relevance
accuracy complexity
flat earth</summary>
          <content type="html"><![CDATA[<p>relevance</p>
<p>accuracy
complexity</p>
<p>flat earth</p>
]]></content>
      </entry>
      <entry>
        <title>FPGAs: A New Way of Programming</title>
        <link rel="alternate" href="/drafts/the-idea-behind-fpgas/" />
        <id>/drafts/the-idea-behind-fpgas/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Data can be stored as binary.
Circuits The exact workings are complex - a level of abstraction is needed to work with components. The complexity can be studied, but it would be counterproductive to do it for every component discovered. Rather, focus on components critical to your design.
Battery Voltage and Current
foundations in chemistry
Sensors detect
Actuators do
Switches Mechanical Electronic Transistor
Micro Controller Memory CPU Clock Logic Circuits NAND</summary>
          <content type="html"><![CDATA[<p>Data can be stored as binary.</p>
<h1 id="circuits">Circuits</h1>
<p>The exact workings are complex - a level of abstraction is needed to work with components. The complexity can be studied, but it would be counterproductive to do it for every component discovered. Rather, focus on components critical to your design.</p>
<h2 id="battery">Battery</h2>
<p>Voltage and Current</p>
<p>foundations in chemistry</p>
<h2 id="sensors">Sensors</h2>
<p>detect</p>
<h2 id="actuators">Actuators</h2>
<p>do</p>
<h2 id="switches">Switches</h2>
<h3 id="mechanical">Mechanical</h3>
<h3 id="electronic">Electronic</h3>
<p>Transistor</p>
<h1 id="micro-controller">Micro Controller</h1>
<h2 id="memory">Memory</h2>
<h2 id="cpu">CPU</h2>
<h2 id="clock">Clock</h2>
<h1 id="logic-circuits">Logic Circuits</h1>
<p>NAND</p>
<h1 id="traditional-computermicrocontroller">Traditional Computer/Microcontroller</h1>
<blockquote>
<p>Computer vs microcontroller.</p>
</blockquote>
<h1 id="fpga">FPGA</h1>
]]></content>
      </entry>
      <entry>
        <title>gpg</title>
        <link rel="alternate" href="/drafts/gpg/" />
        <id>/drafts/gpg/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Difference between gpg and pgp Pretty Good Privacy GNU Privacy Good?
Public key (lock) + private key (unlock) address of mailbox (public) + mailbox key (private) asmetric encryption
two pairs required (private + public)(sender + receiver)
encryption authentication integrity
To generate a key using gpg:
gpg --gen-key This will prompt for a name and email address, as well as a passphrase to encrypt the private key. The --armor flag is used to convert the binary format in which data is stored to base64 encoded text.</summary>
          <content type="html"><![CDATA[<p>Difference between gpg and pgp
Pretty Good Privacy
GNU Privacy Good?</p>
<p>Public key (lock) + private key (unlock)
address of mailbox (public) + mailbox key (private)
asmetric encryption</p>
<p>two pairs required (private + public)(sender + receiver)</p>
<p>encryption
authentication
integrity</p>
<p>To generate a key using <code>gpg</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --gen-key
</span></span></code></pre></div><p>This will prompt for a name and email address, as well as a passphrase to encrypt the private key. The <code>--armor</code> flag is used to convert the binary format in which data is stored to base64 encoded text.</p>
<p>To print the public key:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --armor --export name
</span></span></code></pre></div><p>Where <code>name</code> is the name provided when generating the key. This will print the public key in ascii.</p>
<p>To decrypt some text, provide the filename or pipe the text directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;-----BEGIN PGP MESSAGE----- ...&#34;</span> <span class="p">|</span> gpg --decrypt
</span></span></code></pre></div><p>This automatically selects the correct key and prompts for the passphrase to decrypt the private key.</p>
<p>To encrypt some text, pipe the text directory or provide the filename:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --encrypt --armor -r recepient-name filename
</span></span></code></pre></div><p>Where <code>recepient-name</code> is the user ID of the key and <code>filename</code> is the name of the file to encrypt.</p>
<p>Encryption can also be done without public/private keys:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Hello&#34;</span> <span class="p">|</span> gpg --symmetric --armor --passphrase <span class="s2">&#34;secret-passphrase&#34;</span>
</span></span></code></pre></div><ul>
<li></li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>Handmade Fonts</title>
        <link rel="alternate" href="/projects/software/handmade-fonts/" />
        <id>/projects/software/handmade-fonts/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Create handwritten fonts.</summary>
          <content type="html"><![CDATA[<p>Handwritten notes have a certain appeal that could be explore with handwritten fonts.</p>
]]></content>
      </entry>
      <entry>
        <title>Hardware Adventures with LoRa RA-07H Modules</title>
        <link rel="alternate" href="/drafts/hardware-adventures-with-lora-ra-07h-modules/" />
        <id>/drafts/hardware-adventures-with-lora-ra-07h-modules/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Prelude Don&amp;rsquo;t use this module.
Context P2P long range communication
Interfacing problem
3D printing solution iterations and improvements
PCB designing Flashing hard brick soft brick why brick</summary>
          <content type="html"><![CDATA[<h1 id="prelude">Prelude</h1>
<p>Don&rsquo;t use this module.</p>
<h1 id="context">Context</h1>
<p>P2P long range communication</p>
<h1 id="interfacing">Interfacing</h1>
<p>problem</p>
<h2 id="3d-printing-solution">3D printing solution</h2>
<p>iterations and improvements</p>
<h2 id="pcb-designing">PCB designing</h2>
<h1 id="flashing">Flashing</h1>
<p>hard brick soft brick why brick</p>
]]></content>
      </entry>
      <entry>
        <title>Hardware GPG Keys</title>
        <link rel="alternate" href="/projects/cryptography/hardware-gpg-keys/" />
        <id>/projects/cryptography/hardware-gpg-keys/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Hardware based private keys that software cannot copy.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p>GPG is used for encryption and authentication. It&rsquo;s use case requires easily local access, but this may expose it to a large variety of attacks. The aim is to make software replication of private keys difficult by implementing the key in hardware.</p>
<h1 id="prototype-01">Prototype 01</h1>
<p>More research is required on how GPG works internally. If the algorithm can be easily synthesized on hardware, an FPGA-based approach may be ideal. The private key must be encrypted and stored securely to prevent easy exploitation with hardware access.</p>
<p>For establishing a hardware foundation, a Caesar shift can be implemented. It will take as input a key (shifting constant) and a string of text ([a-z][A-Z]), and output the encrypted text ([a-z][A-Z]). The key should be requested each time a new input is provided and should only be stored for the duration of the encryption process.</p>
]]></content>
      </entry>
      <entry>
        <title>Health Watch</title>
        <link rel="alternate" href="/projects/electronics/health-watch/" />
        <id>/projects/electronics/health-watch/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html"></summary>
          <content type="html"><![CDATA[]]></content>
      </entry>
      <entry>
        <title>HID</title>
        <link rel="alternate" href="/projects/electronics/hid/" />
        <id>/projects/electronics/hid/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Alternate human interface devices.</summary>
          <content type="html"><![CDATA[<h1 id="references">References</h1>
<ul>
<li>MouthPad - touchpad controlled with the mouth, tongue and head gestures: <a href="https://news.mit.edu/2024/mouth-based-touchpad-augmental-0605">MIT News</a></li>
<li>Other works by Tomas Vega: <a href="https://www.tomasvega.com/">Website</a> <a href="https://www.linkedin.com/in/vegatomas">LinkedIn</a></li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>I did not created this</title>
        <link rel="alternate" href="/drafts/i-created-this/" />
        <id>/drafts/i-created-this/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">I created this.
&amp;ldquo;If you wish to make anything from scratch, you must first create the universe.&amp;rdquo;
This makes the &amp;ldquo;I&amp;rdquo; quite the misrepresentation. How much of that creation is truly your own contribution?
I cannot claim to have created anything, for my contribution compared to those before me - those who I depend on - is insignificant.
We stand &amp;ldquo;on the shoulders of giants&amp;rdquo;.
As our specializations increase, it becomes more difficult to determine the extent of that contribution.</summary>
          <content type="html"><![CDATA[<p><strong>I</strong> created this.</p>
<blockquote>
<p>&ldquo;If you wish to make anything from scratch, you must first create the universe.&rdquo;</p>
</blockquote>
<p>This makes the <strong>&ldquo;I&rdquo;</strong> quite the misrepresentation. How much of that creation is truly your own contribution?</p>
<p>I cannot claim to have created anything, for my contribution compared to those before me - those who I depend on - is insignificant.</p>
<p>We stand &ldquo;on the shoulders of giants&rdquo;.</p>
<p>As our specializations increase, it becomes more difficult to determine the extent of that contribution.</p>
<p>Why do?</p>
<ul>
<li>value of time;
<ul>
<li>even though my contribution is insignificant compared to those before</li>
<li>it is quite significant compared to those present</li>
</ul>
</li>
</ul>
<p>Why does it matter?</p>
<ul>
<li>framing: how much do you matter?</li>
</ul>
<p>That &ldquo;I&rdquo; is a standard. And I hope that my claim to a creation is not one of possession, but one of quality. This transformation I present to you meets this standard, and you should expect nothing less.</p>
<ul>
<li>demonstration of capability; I did this, therefore I can do this.</li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>index.md</title>
        <link rel="alternate" href="/cache/" />
        <id>/cache/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">A illustrated guide
git docker motors and generators data structures: Trees &amp;gt; Binary Trees &amp;gt; BST &amp;gt; AVL | RB How does bittorrent work
Is this true? reflection on need for sources in the search for truth
On Principles and Goals
Are you rational? A self fulling prohency of habits and how we polarize No individual has complete information Every few have sufficient information to make a decision
What should I do?</summary>
          <content type="html"><![CDATA[<p>A illustrated guide</p>
<ul>
<li>git</li>
<li>docker</li>
<li>motors and generators</li>
<li>data structures: Trees &gt; Binary Trees &gt; BST &gt; AVL | RB</li>
</ul>
<p>How does bittorrent work</p>
<p>Is this true? reflection on need for sources in the search for truth</p>
<p>On Principles and Goals</p>
<p>Are you rational? A self fulling prohency of habits and how we polarize
No individual has complete information
Every few have sufficient information to make a decision</p>
<p>What should I do? Time scales of good and bad.
I cannot do nothing, for that too is something.
I have limited influence in the present. (Can I do something now?)
I have enourmous influence of the future. (Butterfly effect)
Accept things for as they are now - there is no other way they can be right now.
to see things for as they are
channel desire for change into processes for change, not anger
violence is often the path of most destruction and least sustainability - use it sparingly
Strive for independence by minimizing all dependencies
To minimize some, you may need to fully rely on it (knowledge of the internet)
Whether you will another 100 years or die this instant should make no difference to you</p>
<p>The nature of production (creating unbreakable things)
one of two things must happen: nature must adapt, or we must find a solution</p>
<p>Argument analysis - a verifyable use of advanced prediction technology
Why do we have language? Why do we have jargon?</p>
<p>Any advancement creates a greater capacity for
This can be used for immediate evil, and immediate good
It is up to the innovator whether they believe humanity can control and direct that capacity
This may be why many choose not to write their teachings - it can make greater progress, but it can also make great division and progress in the opposite direction</p>
<p>designing a product
manufacturing
packaging
delivery
decomposition</p>
<p>learning systems
the learning revision problem
system to acquire is different from that to learn
acquisition is a learning process
product of acquisition may not capture that learning process</p>
<p>technology creates a greater capacity for</p>
<p>do you think we can survive without purpose?
there is a scale - it is not absolute (black and white)
it is very difficult to survive on the tipping point
you have survived so far, so at least one of these must be true
you continue if you are distracted from your purpose (we are distracted by novelty and new experience)
you have forgotten your purpose</p>
<p>how many novel applications are there?
looking at the fundamental principle, it is suprisingly simply
there cannot be an infinite number of truly novel solutions with such a simple basis</p>
<p>how do virtual machines work?
define precise layers of separation and sharing</p>
<p>how tor works
regular services
onion services</p>
<p>Logging on Linux (1765043562)
systemd
journalctl (<code>-f</code> flag)</p>
<p>&ldquo;Making of: The Seed&rdquo; (1762973380)
how long it took
blender modes
Effects
Noise
Generate
Follow Curve
Animate Property</p>
<p>what is
language
a model</p>
<p>lorentz attractor and the butterfly effect (proposed 1774496809)
monitoring on linux (proposed 1765055879) regarding node exporter + prometheus + grafana
units of measure (proposed 1776487466) regarding base and relative units
dryk drysa vivkea (proposed 1775666050)</p>
<p>1779984092
title: Pursuing further studies
I do not like what it makes me do. I do not like who it forces me to become, and I feel that the opportunities it provides does not justify this cost.</p>
<p>1779992449
title: Sharing a digital asset
Simply sharing a link is lazy. Perhaps it may be permitted in certain situations, but it should definitely not be the norm. A system of sharing - brief overview, personal statement or something to that effect, should be included.</p>
<p>1780077164
There are a limited number of novel applications. Think of the applications you use everyday.</p>
]]></content>
      </entry>
      <entry>
        <title>Inkling</title>
        <link rel="alternate" href="/projects/electronics/inkling/" />
        <id>/projects/electronics/inkling/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">E-ink book reader and writer.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p>LCD and OLED screens cause eye strain over extended periods of usage. They are not ideal for viewing in outdoor environments (in direct sunlight), and consume a lot of power. The goal is to create an eink device that is gentle on the eyes and mind.</p>
<h1 id="prototype-01">Prototype 01</h1>
<p>The device needs to support two applications:</p>
<ul>
<li>KOReader: an open source book reader with support for various platforms</li>
<li>Neovim: a terminal based text editor</li>
</ul>
<p>While color eink is not a requirement, cost of implementation should be explored.</p>
<h1 id="references">References</h1>
<ul>
<li>60 fps eink monitor with open firmware: <a href="https://www.youtube.com/watch?v=nHbA2-_qzH4">YouTube</a> <a href="https://gitlab.com/zephray/glider">GitLab</a></li>
</ul>
]]></content>
      </entry>
      <entry>
        <title>Is this good?</title>
        <link rel="alternate" href="/drafts/is-this-good/" />
        <id>/drafts/is-this-good/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Is this good? The reason we care is because we want to be good; it should guide our thought and - by extension - our actions.
First, that depends on your perspective.
The common people do not care about the miseries or pleasures of ants - not that it is good, or bad, but it is simply inconsequential. In some perspectives, you may very well be these ants.
An action is good if it benefits you.</summary>
          <content type="html"><![CDATA[<p>Is this good? The reason we care is because we want to be good; it should guide our thought and - by extension - our actions.</p>
<p>First, that depends on your perspective.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-a3662" hidden>
    <label for="zoomCheck-a3662">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="eye.svg" alt="eye" 
             />
    </label>

</p>
<p>The common people do not care about the miseries or pleasures of ants - not that it is good, or bad, but it is simply inconsequential. In some perspectives, you may very well be these ants.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-19bc5" hidden>
    <label for="zoomCheck-19bc5">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="ant.svg" alt="ant" 
             />
    </label>

</p>
<p>An action is good if it benefits you. It is great if it benefits those besides you.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b994d" hidden>
    <label for="zoomCheck-b994d">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="good.svg" alt="good" 
             />
    </label>

</p>
<p>An action is bad if it does not benefit you. It is terrible if you, as the receiver, would wish not to receive and if this is the case for those besides you.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-b4978" hidden>
    <label for="zoomCheck-b4978">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="bad.svg" alt="bad" 
             />
    </label>

</p>
<p>These classifications - of good and bad - only apply to an period in time from a particular perpsective. Perspectives progress, as do times.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-97207" hidden>
    <label for="zoomCheck-97207">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="time.svg" alt="time" 
             />
    </label>

</p>
<p>Thus, our second dimension is temporal. As actions accumulate, while each may be considered good or bad, they as a collective can also be judged.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-bc630" hidden>
    <label for="zoomCheck-bc630">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="collective.svg" alt="collective" 
             />
    </label>

</p>
<p>It is important to not confuse this collective judgement of actions with an individual action, else you may be misguided into thinking good is bad and bad is good, and neither matter so.</p>
<p>



    
    <input type="checkbox" id="zoomCheck-04b65" hidden>
    <label for="zoomCheck-04b65">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="misguided.svg" alt="misguided" 
             />
    </label>

</p>
<p>Herein lies the importance of justification. You do not necessarily need an initial reason to do something (this is natural), but without reason that action may not stand in the perpsective of the future. You must build some framework of reason, else regret or forget.</p>
<p>Here arises a difficulty. To provide a reason, we must judge the benefit of an action apart from that of an instant. Such judgements require a scale to be chosen. Depending on the scale, the collective judgement may waver.</p>
<p>Just because you think that neither matter, does not mean it does not matter. While none may share your exact perspective, there are many who share a similar perspective.</p>
<p>Do no evil. is a Vagueness of these dimensions that could justify almost any action. This state is only resolved by a matter of chance that cannot be depended upon.</p>
]]></content>
      </entry>
      <entry>
        <title>JPEG File Format</title>
        <link rel="alternate" href="/drafts/jpeg-file-format/" />
        <id>/drafts/jpeg-file-format/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">JPEG is a raster format - it is composed of a grid of pixels.
no transparency - 24bit RGB
Lossy compression</summary>
          <content type="html"><![CDATA[<p>JPEG is a raster format - it is composed of a grid of pixels.</p>
<p>no transparency - 24bit RGB</p>
<p>Lossy compression</p>
]]></content>
      </entry>
      <entry>
        <title>Limitations of Excalidraw</title>
        <link rel="alternate" href="/drafts/limitations-of-excalidraw/" />
        <id>/drafts/limitations-of-excalidraw/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Boolean Operations Color </summary>
          <content type="html"><![CDATA[<h1 id="boolean-operations">Boolean Operations</h1>
<h1 id="color">Color</h1>
]]></content>
      </entry>
      <entry>
        <title>logs</title>
        <link rel="alternate" href="/logs/" />
        <id>/logs/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">1764421727 The path of knowledge is collection of explorations which reveal something greater than it&amp;rsquo;s individual elements, but only to those that wish to see it.
What follows is a collection of small, niche and rather unimportant solutions to some challenges we&amp;rsquo;ve faced. Each one may very well be irrelevant by the time it is stumbled upon again, but the hope is that it serves as a remainder of the path travelled and the patterns of thought behind it.</summary>
          <content type="html"><![CDATA[<hr>
<h6 id="1764421727">1764421727</h6>
<blockquote>
<p>The path of knowledge is collection of explorations which reveal something greater than it&rsquo;s individual elements, but only to those that wish to see it.</p>
</blockquote>
<p>What follows is a collection of small, niche and rather unimportant solutions to some challenges we&rsquo;ve faced. Each one may very well be irrelevant by the time it is stumbled upon again, but the hope is that it serves as a remainder of the path travelled and the patterns of thought behind it.</p>
<hr>
<h6 id="1764520956">1764520956</h6>
<p>To reinstall KOReader on a Kindle Paperwhite 2 (PW2) with firmware version 5.12.2 that was <a href="https://www.mobileread.com/forums/showthread.php?t=320564">previously jail broken</a> and then reset (Settings &gt; Device Options &gt; Reset):</p>
<ul>
<li>Install MR Installer (MRI) by copying <code>extensions</code> and <code>mrpackages</code> to the Kindle root folder</li>
<li>Install KUAL (Coplate)
<ul>
<li>Copy <code>Update_KUALBooklet..._install.bin</code> to <code>mrpackages</code></li>
<li>Eject the kindle</li>
<li>Execute the installer by typing <code>:log mrpi</code> on the default searchbar</li>
<li>Reboot twice, then repeat for <code>Update_KUALBooklet_hotfix..._install.bin</code></li>
</ul>
</li>
<li>Install JailBreak&hellip;FW-5.X.hotfix
<ul>
<li>Reboot twice</li>
<li>Copy <code>Update_jailbreak_hotfix...install.bin</code> to <code>mrpackages</code></li>
<li>Eject the kindle</li>
<li>Execute the installer by typing <code>:log mrpi</code> on the default searchbar</li>
</ul>
</li>
<li>Install KOReader
<ul>
<li>Copy <code>koreader</code> to the kindle root</li>
<li>Copy <code>extension/koreader</code> to the <code>extensions</code> folder under kindle root</li>
<li>Eject the kindle and launch KOReader (KUAL &gt; KOReader &gt; Start KOReader)</li>
</ul>
</li>
</ul>
<p>Follow the MobileRead threads very carefully (it is easy to misread instructions or do them in the wrong order). All required files can be found on the <a href="https://www.mobileread.com/forums/showthread.php?t=225030">snapshots page</a>.</p>
<p>If the jail break is not installed properly, KOReader will not launch.</p>
<ul>
<li>Ensure the correct variant of KUAL is installed.</li>
<li>The crash log is stored in the <code>koreader</code> folder in at the kindle root as <code>crash.log</code>.</li>
<li>If the screen flashes but remains on the home page, the hotfix hasn&rsquo;t been installed correctly and will log a <code>could not open framebuffer</code> error. Carefully follow the above instructions - reset to start from scratch if necessary.</li>
</ul>
<p>Kindle root refers to the folder seen when the kindle is plugged in (USB Drive Mode) - it should contain the folders <code>documents</code>, <code>extensions</code>, <code>mrpackages</code> and so on.</p>
<hr>
<h6 id="1764696164">1764696164</h6>
<p>To generate a key using <code>gpg</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --gen-key
</span></span></code></pre></div><p>This will prompt for a name and email address, as well as a passphrase to encrypt the private key.</p>
<p>To print the public key:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --armor --export name
</span></span></code></pre></div><p>Where <code>name</code> is the name provided when generating the key. The <code>--armor</code> flag is used to convert the binary format in which data is stored to base64 encoded text.</p>
<p>To encrypt text, pipe the text directly or provide the filename:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">gpg --encrypt --armor -r recepient-name filename
</span></span></code></pre></div><p>Where <code>recepient-name</code> is the user ID or name of the key pair and <code>filename</code> is the name of the file to encrypt.</p>
<p>To decrypt text, provide the filename or pipe the text directly:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;-----BEGIN PGP MESSAGE----- ...&#34;</span> <span class="p">|</span> gpg --decrypt
</span></span></code></pre></div><p>This automatically selects the correct key and prompts for the passphrase to decrypt the private key.</p>
<p>Encryption can also be done without public/private keys:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;Hello&#34;</span> <span class="p">|</span> gpg --symmetric --armor --passphrase <span class="s2">&#34;secret-passphrase&#34;</span>
</span></span></code></pre></div><hr>
<h6 id="1765043334">1765043334</h6>
<p>To see which process is using a particular port on Linux:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo ss -tulnp <span class="p">|</span> grep :portnumber
</span></span><span class="line"><span class="cl">sudo lsof -i portnumber
</span></span></code></pre></div><p>Use <code>kill</code>(graceful) or <code>kill -9</code>(forceful) to stop the process.</p>
<hr>
<h6 id="1765212283">1765212283</h6>
<p>To move a qbittorrent instance, the following folders/files are sufficient:</p>
<ul>
<li><code>~/.local/share/qBittorrent/BT_backup</code></li>
<li><code>~/.config/qbit/qbitdata.conf</code></li>
<li><code>~/.config/qbit/qbit.conf</code></li>
</ul>
<hr>
<h6 id="1770853147">1770853147</h6>
<p>Forgejo runners can be setup on linux machines to use docker containers to execute jobs. These job containers may have difficulty accessing the internet due to a misconfigured DNS; it may produce errors like <code>unable to resolve host</code> and <code>ping: bad address</code>. To resolve this, add a different DNS nameserver:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;nameserver 8.8.8.8&#34;</span> &gt; /etc/resolv.conf
</span></span></code></pre></div><hr>
<h6 id="1771236153">1771236153</h6>
<p>To compare the output of two commands using git, process substitution can be used:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git diff &lt;<span class="o">(</span>exiftool imageA.jpg<span class="o">)</span> &lt;<span class="o">(</span>exiftool imageB.jpg<span class="o">)</span>
</span></span></code></pre></div><p>This is different from command substitution <code>$(&lt;command&gt;)</code> which executes the command and replaces with it&rsquo;s output.
Process substitution <code>&lt;(&lt;command&gt;)</code> instead creates a temporary file descriptor containing the command&rsquo;s output.</p>
<hr>
<h6 id="1772521864">1772521864</h6>
<p>Bash command chaining:<br>
<code>c1 &amp;&amp; c2</code>: execute c2 <strong>only</strong> if c1 succeeds<br>
<code>c1 || c2</code>: execute c2 <strong>only</strong> if c2 fails<br>
<code>c1  ; c2</code>: execute c2 after c1 (regardless of result)<br>
<code>c1  &amp; c2</code>: execute c1 and c2 in parallel; follow with <code>wait</code></p>
<p>From <a href="https://old.reddit.com/r/commandline/comments/lha15t/bash_execution_tips_the_difference_between_and/">reddit</a>.</p>
<hr>
<h6 id="1772522569">1772522569</h6>
<p>To find and replace in vim (normal mode):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">:%s/oldString/newString/g
</span></span></code></pre></div><p><code>:</code> enters command mode<br>
<code>%</code> sets range to entire file<br>
<code>s</code> invokes substitution command<br>
<code>g</code> replacs all occurrences in each line (omit for first occurrence only)</p>
<p>Use <code>gc</code> to ask for confirmation for each occurrence.</p>
<hr>
<h6 id="1773089881">1773089881</h6>
<p>To reset the root password for a Hetzner VM, navigate to the &lsquo;Rescue&rsquo; tab for the server and &lsquo;Reset Root Password&rsquo;.</p>
<hr>
<h6 id="1773860522">1773860522</h6>
<p>To repopulate the Archlinux keyring (to resolve invalid signature errors) by <a href="https://wiki.archlinux.org/title/Pacman/Package_signing#Resetting_all_the_keys">resetting all the keys</a>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo rm -r /etc/pacman.d/gnupg
</span></span><span class="line"><span class="cl">sudo pacman-key --init
</span></span><span class="line"><span class="cl">sudo pacman-key --populate
</span></span></code></pre></div><p>The <code>error: failed to commit transaction (invalid or corrupted package (PGP signature))</code> has a few causes - in this case, trying to reinstall the <code>archlinux</code> without updating the repository and cancelling the partial installation (after it hanged) caused corruption.</p>
<hr>
<h6 id="1774501304">1774501304</h6>
<p>To create an email alias that doesn&rsquo;t meet the email requirements in Gitea/Forgejo, create an alias that does meet the requirements (&ldquo;<a href="mailto:user1@example1.com">user1@example1.com</a>&rdquo;) in Settings &gt; Account &gt; Add email address.</p>
<p>Then, connect to the database using user gitea or forgejo:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">psql -u &lt;user&gt;
</span></span></code></pre></div><p>Ensure that the alias was created and update the email address:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">uid</span><span class="p">,</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">email_address</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;user1@example1.com&#39;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">UPDATE</span><span class="w"> </span><span class="n">email_address</span><span class="w"> </span><span class="k">SET</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;user1&#39;</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">email</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s1">&#39;user1@example1.com&#39;</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></div><p>The goal was to ensure that commits without a valid email address appeared in the heatmap of Gitea/Forgejo. However, it seems the heatmap is generated from the service actions and not git itself; not commits. <a href="https://github.com/go-gitea/gitea/pull/36469">An attempt</a> was made to use commit details to generate the heatmap, but the issues that arouse were deemed too complex for the added benefit.</p>
<hr>
<h6 id="1774525948">1774525948</h6>
<p>To delete your entire Neocities site using the <code>neocities</code> gem:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">neocities list / <span class="p">|</span> sed <span class="s1">&#39;s/\x1b\[[0-9;]*m//g&#39;</span> <span class="p">|</span> tail -n +2 <span class="p">|</span> grep -v <span class="s1">&#39;/&#39;</span> <span class="p">|</span> <span class="k">while</span> <span class="nb">read</span> -r f<span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;Deleting: </span><span class="nv">$f</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  neocities delete <span class="s2">&#34;</span><span class="nv">$f</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  sleep <span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="k">done</span>
</span></span></code></pre></div><ul>
<li><code>neocities list /</code> lists all files in root recursively</li>
<li><code>sed</code> strips ANSI color codes (deletes fail otherwise)</li>
<li><code>tail</code> skips the first two header lines</li>
<li><code>grep</code> filters out subdirectory files</li>
</ul>
<p>Filtering out the subdirectory files and only deleting top level entries is necessary because <code>neocities delete folder</code> runs before <code>neocities delete folder/file-in-folder.txt</code> - the second delete fails because that folder no longer exists.</p>
<h6 id="1774693997">1774693997</h6>
<p>All of this is unnecesary. Just use the <code>prune</code> flag:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">neocities push --prune public<span class="p">;</span>
</span></span></code></pre></div><hr>
<h6 id="1774532969">1774532969</h6>
<p>Makefiles can use variables from an environment file; this is particularly useful when it is tracked by <code>git</code> but sensitive information needs to be used.</p>
<p>.env</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-txt" data-lang="txt"><span class="line"><span class="cl">USERNAME=&#34;user1&#34;
</span></span><span class="line"><span class="cl">PASSWORD=&#34;pass1&#34;
</span></span></code></pre></div><p>Makefile</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-make" data-lang="make"><span class="line"><span class="cl"><span class="err">-include</span> <span class="err">.env</span>
</span></span><span class="line"><span class="cl"><span class="k">export
</span></span></span><span class="line"><span class="cl"><span class="k">
</span></span></span><span class="line"><span class="cl"><span class="k"></span><span class="err">echo</span> <span class="err">Username</span> <span class="err">is</span> <span class="k">$(</span><span class="nv">USERNAME</span><span class="k">)</span>
</span></span><span class="line"><span class="cl"><span class="err">echo</span> <span class="err">Password</span> <span class="err">is</span> <span class="k">$(</span><span class="nv">PASSWORD</span><span class="k">)</span>
</span></span></code></pre></div><hr>
<h6 id="1774681798">1774681798</h6>
<p>To stop highlighting after a search with <code>/search-term</code> in <code>vim</code>, run <code>:noh</code>.</p>
<hr>
<h6 id="1775718054">1775718054</h6>
<p><code>rsync</code> can be used to both list and copy files.</p>
<p>List files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">rsync -havP --dry-run -e <span class="s1">&#39;ssh -p 8080 -i ~/.ssh/auth-key&#39;</span> user@host:dir1/ .
</span></span></code></pre></div><p>To copy files that match a pattern:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">rsync -havP --dry-run --include<span class="o">=</span><span class="s2">&#34;PATTERN&#34;</span> --exclude<span class="o">=</span><span class="s2">&#34;*&#34;</span> -e <span class="s1">&#39;ssh -p 8080 -i ~/.ssh/auth-key&#39;</span> user@host:dir1/ .
</span></span></code></pre></div><p>This is not particularly novel - dry-run is used to see what files are available and the include and exclude flags are used to select a particular file. This pattern is quite useful because it does not rely on another process to list files and depend on a method of copying results - it is just one terminal based process.</p>
<hr>
<h6 id="1776418143">1776418143</h6>
<p>To reboot a phone from the Termux commandline with superuser privileges:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sudo -c <span class="s2">&#34;reboot&#34;</span>
</span></span></code></pre></div><hr>
<h6 id="1776419122">1776419122</h6>
<p>Onion sites use <code>http</code>, not <code>https</code>. If you try to use <code>https</code>, you will be &ldquo;Unable to connect&rdquo; with an <code>NS_ERROR_CONNECTION_REFUSED</code> error on Firefox.</p>
<hr>
<h6 id="1776456353">1776456353</h6>
<p>Using <code>vim</code> to create a file in a folder that doesn&rsquo;t exist results in an error when saving: <code>E212: Can't open file for writing: no such file or directory</code>. Use the <code>++p</code> flag (from v9.0) when writing to create the parent directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">:w ++p
</span></span></code></pre></div><p>Source: <a href="https://neovim.io/doc/user/editing/#%2B%2Bp">https://neovim.io/doc/user/editing/#%2B%2Bp</a></p>
<hr>
<h6 id="1776680423">1776680423</h6>
<p>Use this pattern to operate on a list in parallel using Bash and <code>xargs</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="cp"></span>
</span></span><span class="line"><span class="cl">process_item<span class="o">()</span> <span class="o">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">item</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$1</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="o">}</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> -f process_item
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">find . -maxdepth <span class="m">1</span> -type d <span class="p">|</span> xargs -P <span class="s2">&#34;</span><span class="si">${</span><span class="nv">JOBS</span><span class="k">:-$(</span>nproc<span class="k">)</span><span class="si">}</span><span class="s2">&#34;</span> -I <span class="o">{}</span> bash -c <span class="s1">&#39;process_item &#34;$@&#34;&#39;</span> _ <span class="o">{}</span>
</span></span></code></pre></div><ul>
<li><code>find</code> returns a list of items that match the specified filters</li>
<li><code>xargs</code> is used to build and execute commands
<ul>
<li><code>-P</code> or <code>--max-procs=N</code> specifies the number of processes to use</li>
<li><code>-I</code> or <code>--replace</code> is used to run the command once per line instead of passing everything as an input to one command</li>
<li><code>{}</code> is a placeholder that is substituted with the input value (from the pipe)</li>
<li><code>${JOBS:-$(nproc)}</code> uses <code>$JOB</code> variable if set; else uses the output from <code>$(nproc)</code></li>
</ul>
</li>
<li><code>bash -c</code> spawns a bash shell and executes the command that follows
<ul>
<li><code>$@</code> is a placeholder that expands positional arguments (just <code>$1={}</code> in this case)</li>
<li><code>_</code> is an empty placeholder; sets the value of <code>$0</code></li>
<li><code>{}</code> is a placeholder that is substituted with the input value (from xargs)</li>
</ul>
</li>
</ul>
<p>GNU parallel supports better output handling (groups and prints on completion instead of interleaving) and progress tracking, but is not installed by default on most Unix systems.</p>
<hr>
<h6 id="1776769776">1776769776</h6>
<p>The main section of a Hugo site is determined based on the section (folder under <code>content</code>) with most posts. This is quite unexpected - the output suddenly changes based on the state of these subfolders. To define sections explicitly, set <code>mainsections=[&quot;section1&quot;, &quot;section2&quot;]</code>. Posts from these sections will then be shown on the homepage. Leave it empty if none should be included.</p>
<hr>
<h6 id="1776785854">1776785854</h6>
<p>To see what commands a package (<code>package_name</code>) provides on Archlinux:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pacman -Ql package_name <span class="p">|</span> grep <span class="s1">&#39;/usr/bin/&#39;</span>
</span></span></code></pre></div><hr>
<h6 id="1777165426">1777165426</h6>
<p>Use <code>jq</code> to create JSON strings in bash; it is particularly useful when the string contains single quotes and newlines. Special characters can usually be ignored using single quotes, but single quotes cannot be escaped.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">data</span><span class="o">=</span><span class="k">$(</span>jq -n <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --arg       keyvar1     <span class="s2">&#34;value&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        --argjson   keyvar2     <span class="s1">&#39;{&#34;subkey&#34;: &#34;value&#34;}&#39;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="s1">&#39;{&#34;key1&#34;: $keyvar1, &#34;key2&#34;: $keyvar2}&#39;</span><span class="k">)</span>
</span></span></code></pre></div><hr>
<h6 id="1778524400">1778524400</h6>
<p>To undo the last commit, but keep the changes staged:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git reset --soft HEAD~1
</span></span></code></pre></div><hr>
<h6 id="1781177936">1781177936</h6>
<p>From <a href="https://unix.stackexchange.com/questions/268460/how-to-mount-qcow2-image">unix stackexchange</a> and <a href="https://askubuntu.com/questions/63594/mount-encrypted-volumes-from-command-line">askubuntu</a>.</p>
<p>To mount a qcow2 disk image:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">modprobe nbd <span class="nv">max_part</span><span class="o">=</span><span class="m">8</span>
</span></span><span class="line"><span class="cl">qemu-nbd --connect<span class="o">=</span>/dev/nbd0 /path/to/your/image.qcow2
</span></span><span class="line"><span class="cl">lsblk /dev/nbd0
</span></span><span class="line"><span class="cl">mount /dev/nbd0p1 /mount/folder
</span></span></code></pre></div><p>To unmount:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">umount /mount/folder
</span></span><span class="line"><span class="cl">qemu-nbd --disconnect /dev/nbd0
</span></span><span class="line"><span class="cl">rmmod nbd
</span></span></code></pre></div><p>If the partition is encrypted, first decrypt the volume:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo cryptsetup luksOpen /dev/nbd0p1 decrypted_volume_name
</span></span><span class="line"><span class="cl">sudo mount /dev/mapper/decrypted_volume_name /mount/folder
</span></span></code></pre></div><p>When unmounting, lock the partition:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo umount /mount/folder
</span></span><span class="line"><span class="cl">sudo cryptsetup luksClose decrypted_volume_name
</span></span></code></pre></div><hr>
<h6 id="1781197828">1781197828</h6>
<p>To apply a CSS style to the parent of a element, use the <code>:has</code> pseudo class:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-css" data-lang="css"><span class="line"><span class="cl"><span class="p">.</span><span class="nc">some_class</span><span class="p">:</span><span class="nd">has</span><span class="o">(</span><span class="p">#</span><span class="nn">some_id</span><span class="o">)</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c">/* style for some_class*/</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><hr>
<h6 id="1782194655">1782194655</h6>
<p>To rename all occurences of a string in a folder (across multiple files):</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">find . -type f -exec sed &#39;s/old-text/next-text/g&#39; {} + | grep new-text
</span></span></code></pre></div><p>To actually perform the replace, remove the pipe to grep and add the &lsquo;-i&rsquo; flag to sed.</p>
<hr>
]]></content>
      </entry>
      <entry>
        <title>M3D</title>
        <link rel="alternate" href="/projects/electronics/m3d/" />
        <id>/projects/electronics/m3d/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Input device with 3 degrees of freedom.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p>Navigation in certain software would be better suited with different input interfaces.</p>
<h1 id="references">References</h1>
<p><a href="https://github.com/FaqT0tum/Orbion_3D_Space_Mouse">https://github.com/FaqT0tum/Orbion_3D_Space_Mouse</a><br>
<a href="https://github.com/KikiHobbyRepair/SpaceRat">https://github.com/KikiHobbyRepair/SpaceRat</a><br>
<a href="https://github.com/pepijndevos/spacefox">https://github.com/pepijndevos/spacefox</a><br>
<a href="https://github.com/sb-ocr/diy-spacemouse">https://github.com/sb-ocr/diy-spacemouse</a></p>
]]></content>
      </entry>
      <entry>
        <title>Model Controller</title>
        <link rel="alternate" href="/projects/software/model-controller/" />
        <id>/projects/software/model-controller/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Embedded environment to explore 3D models.</summary>
          <content type="html"><![CDATA[<p>3D modelling has become very capable. These models are typically controlled only by the creator for a specfic broader goal - like a game or animation. For greater accesibility, it may be better to standardize and expose this interface.</p>
]]></content>
      </entry>
      <entry>
        <title>Money</title>
        <link rel="alternate" href="/drafts/money/" />
        <id>/drafts/money/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Value.</summary>
          <content type="html"><![CDATA[<p>Value.</p>
]]></content>
      </entry>
      <entry>
        <title>Music Player</title>
        <link rel="alternate" href="/projects/electronics/musicplayer/" />
        <id>/projects/electronics/musicplayer/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Compact music player with removable storage.</summary>
          <content type="html"><![CDATA[]]></content>
      </entry>
      <entry>
        <title>Not Invented Here</title>
        <link rel="alternate" href="/drafts/not-invented-here/" />
        <id>/drafts/not-invented-here/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Don&amp;rsquo;t be sad if your idea is already invented! The process from idea to completed invention is one of massive undertaking. There were many difficulties that arouse on the path. The goal of most ideas is to solve a problem, and if that problem is already solved, its great! You can focus on reaching the greater goal.
Now, there is some compromise. Because this invention was not invented here, some elements of the solution may not be as desired.</summary>
          <content type="html"><![CDATA[<p>Don&rsquo;t be sad if your idea is already invented! The process from idea to completed invention is one of massive undertaking. There were many difficulties that arouse on the path. The goal of most ideas is to solve a problem, and if that problem is already solved, its great! You can focus on reaching the greater goal.</p>
<p>Now, there is some compromise. Because this invention was not invented here, some elements of the solution may not be as desired. You need to make a decision here - are these compromises acceptable? If not, your work is not done! You have a reference, but your problem is not truly solved.</p>
<p>One should not simply give up on an idea because something similar has been attempted. One should also not simply pursue an alternate solution without a clear direction.</p>
]]></content>
      </entry>
      <entry>
        <title>Privacy and Security</title>
        <link rel="alternate" href="/drafts/privacy-and-security/" />
        <id>/drafts/privacy-and-security/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">definition
up to develoepers</summary>
          <content type="html"><![CDATA[<p>definition</p>
<p>up to develoepers</p>
]]></content>
      </entry>
      <entry>
        <title>Python Environments</title>
        <link rel="alternate" href="/drafts/python-environments/" />
        <id>/drafts/python-environments/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Managing code is difficult because it is modular and constantly being updated. When you build an application using that code, you have to make certain assumptions. During testing, these assumptions are confirmed for a particular state of that code. However, when that state changes, there is no certainty that these assumptions will remain valid.
There is a wide range of applications built using Python. Python gives developers the power to perform various complex tasks.</summary>
          <content type="html"><![CDATA[<p>Managing code is difficult because it is modular and constantly being updated. When you build an application using that code, you have to make certain assumptions. During testing, these assumptions are confirmed for a particular state of that code. However, when that state changes, there is no certainty that these assumptions will remain valid.</p>
<p>There is a wide range of applications built using Python. Python gives developers the power to perform various complex tasks. A key component of this success is modules - a collection of code that performs a specific task. Modules can be installed using <code>pip</code> and used with <code>import</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pip install requests
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">requests</span>
</span></span></code></pre></div><p>Python is open source, so anyone can contribute. Looking at the <a href="https://github.com/python/cpython">source</a>, the Python programming language has over 3000 contributors. Most of the development is managed by a few developers - these are the core maintainers. 20 people are responsible for almost 60% of commits.</p>
<p>Python modules are separate from the Python programming language itself, and these are maintained by a different set of maintainers.</p>
<p>The reason software is continously being updated is because of fixes and features. Fixes are required for existing features that don&rsquo;t work as expected. More features may be required to solve larger problems, or support more applications.</p>
<p>Modules for python change.</p>
<p>what is python, what are modules?</p>
<p>why can&rsquo;t they be moved?</p>
<h1 id="module-version-conflict">Module version conflict</h1>
<h2 id="virtualenv-venv">virtualenv (venv)</h2>
<h1 id="python-version-conflict">Python version conflict</h1>
<h2 id="pyenv">pyenv</h2>
]]></content>
      </entry>
      <entry>
        <title>Quotes</title>
        <link rel="alternate" href="/drafts/quotes/" />
        <id>/drafts/quotes/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">&amp;ldquo;Choose not to be harmed, and you won&amp;rsquo;t feel harmed. Don&amp;rsquo;t feel harmed, and you haven&amp;rsquo;t been.&amp;rdquo;
&amp;ldquo;We cannot expect what cannot be delivered.&amp;rdquo;
&amp;ldquo;See things for as they are, not as they should be.&amp;rdquo;
&amp;ldquo;Pain is inevitable, suffering is optional.&amp;rdquo;
&amp;ldquo;There is nothing for me do in these worlds, nor do I need or desire anything. &amp;quot;</summary>
          <content type="html"><![CDATA[<blockquote>
<p>&ldquo;Choose not to be harmed, and you won&rsquo;t feel harmed.
Don&rsquo;t feel harmed, and you haven&rsquo;t been.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;We cannot expect what cannot be delivered.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;See things for as they are, not as they should be.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;Pain is inevitable, suffering is optional.&rdquo;</p>
</blockquote>
<blockquote>
<p>&ldquo;There is nothing for me do in these worlds, nor do I need or desire anything. &quot;</p>
</blockquote>
]]></content>
      </entry>
      <entry>
        <title>Rhythmbox Reloaded</title>
        <link rel="alternate" href="/projects/software/rhythmbox-reloaded/" />
        <id>/projects/software/rhythmbox-reloaded/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">A local music player with playback history.</summary>
          <content type="html"><![CDATA[<p>A local music player with playback history.</p>
<h1 id="android">Android</h1>
<h1 id="desktop">Desktop</h1>
]]></content>
      </entry>
      <entry>
        <title>Scriptair</title>
        <link rel="alternate" href="/projects/software/scripair/" />
        <id>/projects/software/scripair/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Memory game. Match two characters in a selected script from a grid.
Inspired by the original game, as well as Pairs by Max Gorin.
Prototype 01 Game states must be stored on a central server. One target is minimal, self-contained dependencies.
Stage 1 A centered grid whose dimensions are set by input fields.
Prototype 02 Perhaps mechanisms can be introduced for learning more about the script - pronounciation, words and so on.</summary>
          <content type="html"><![CDATA[<p>Memory game. Match two characters in a selected script from a grid.</p>
<p>Inspired by the original game, as well as <a href="https://github.com/mxgrn/pairs.one">Pairs</a> by Max Gorin.</p>
<h1 id="prototype-01">Prototype 01</h1>
<p>Game states must be stored on a central server. One target is minimal, self-contained dependencies.</p>
<h2 id="stage-1">Stage 1</h2>
<p>A centered grid whose dimensions are set by input fields.</p>
<h1 id="prototype-02">Prototype 02</h1>
<p>Perhaps mechanisms can be introduced for learning more about the script - pronounciation, words and so on.</p>
]]></content>
      </entry>
      <entry>
        <title>Searchers</title>
        <link rel="alternate" href="/projects/archiving/searchers/" />
        <id>/projects/archiving/searchers/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Local semantic search with query transformations and modular sources.</summary>
          <content type="html"><![CDATA[<h1 id="context">Context</h1>
<p>Popular software is typically accompanied by well-written documentation. This documentation can be easily aquired. However, it&rsquo;s utility is severely restricted because searching a collection of documentation is difficult. The current solution of this problem is to rely on a search engine like Google, which resolves queries - often structured as questions without a direct link to the keywords expected in the solution. The goal of this system is to provide a modular framework for local search operations of this nature. Such a system can be extended to index and search a website crawled and saved locally, acting as a search engine.</p>
<h1 id="existing-solutions">Existing Solutions</h1>
<h2 id="dash">Dash</h2>
<p><a href="https://kapeli.com/dash">Dash</a> is a proprietary browser for MacOS that searches documentation sets generated primarily from various developer API docs and manuals. <a href="https://github.com/zealdocs/zeal">Zeal</a> - inspired by Dash - is an alternative for Linux and Windows machines that uses the same docsets. <a href="https://github.com/freecodecamp/devdocs">Devdocs</a> is an open source API documentation browser that fills a similar role, but does not use the same docset format.</p>
<p>One short coming of these existing systems is that semantic search is not supported - the indexing is limited in this regard (although this could be justified for a documentation browser). The &ldquo;docset&rdquo; feature meets the requirement of modular data sources very well.</p>
<h2 id="hister">Hister</h2>
<p><a href="https://github.com/asciimoo/hister.git">Hister</a> is a search engine that consists of a browser extension, indexer, web app and terminal interface <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. It was developed by Adam Tauber, who also developed <a href="https://github.com/searx/searx">SearX</a> - a metasearch engine that dispatches queries to other search engines. The indexer and user interface should serve as a good reference, but the overall application goals are not the same; the crawling mechanism does not meet the required scale and the index is likely not modular. Some technical aspects are well presented at <a href="https://hister.org/posts">https://hister.org/posts</a>.</p>
<h1 id="prototype-1">Prototype 1</h1>
<p>More research needs to be done on the systems that are suitable for this application. Currently, a language model for query transformation and a fragmented meilisearch index for modular sources is being explored.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://hister.org/posts/how-hister-works">https://hister.org/posts/how-hister-works</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content>
      </entry>
      <entry>
        <title>SmallWeb</title>
        <link rel="alternate" href="/projects/archiving/smallweb/" />
        <id>/projects/archiving/smallweb/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Pipeline to monitor and archive original content on the internet.</summary>
          <content type="html"><![CDATA[<p>This illustration was inspired by <a href="https://hackaday.com/2024/09/10/a-look-at-the-small-web-part-1/">Jenny List</a>.</p>
<h1 id="context">Context</h1>
<p>The internet is fragile.</p>
<h1 id="pipeline">Pipeline</h1>
<h2 id="initial-acquisition">Initial Acquisition</h2>
<ol>
<li>Crawl</li>
<li>Scrape</li>
<li>Verify</li>
</ol>
<p>Each stage typically involes an extraction operation - during crawling, this is extraction of links and during scraping it is the extraction of content. Network requests are expensive, so extraction operations are better suited on local resources.</p>
<h2 id="updates">Updates</h2>
<p>Once content has been aquired, keeping it up to date is a challenge. At scale, some sort of automation is required. Automation logic is fragile, so monitoring is critical.</p>
<p>For certain sources, RSS is available and may be adapted to meet the needs of the archive.</p>
<h1 id="content">Content</h1>
<h2 id="blogs">Blogs</h2>
<h3 id="source-available-git">Source Available (git)</h3>
<p>Git can be used for both initial acquisition and updates for source available websites. This is edit safe (the history of published content that is later modified is preserved).</p>
<p>One difficulty that needs to be considered is if builds are not included in the source - additional software may be required to build the website; for example, a particular version of Hugo may be needed to convert markdown to html (this is not ideal).</p>
<h3 id="update-available-rss">Update Available (RSS)</h3>
<p>RSS can be used for updates. Initial aquisition will still require a custom crawler and scraper. This scraper can be reused for new targets identified by RSS. This is not edit safe.</p>
<h3 id="site-available">Site Available</h3>
<p>For blogs where the published site is the only source and which does not contain RSS capabilites, a custom recrawler needs to be crafted. The recrawler is very suseptible to errors as a result of layout changes and should be monitored for indications of such errors. This is also not edit safe.</p>
<h1 id="youtube">YouTube</h1>
<p>A asset uploaded to YouTube is re-encoded to various formats. TCP guarentees that the delivery of a particular asset is perfect - the source and destination are bitexact. However, various attributes of the asset (including metadata and description) may result in slight variances. For a given quality, it should technically be possible to extract a bitexact copy of the source, regardless of when it was acquired.</p>
]]></content>
      </entry>
      <entry>
        <title>Source of Truth</title>
        <link rel="alternate" href="/drafts/sources-of-truth/" />
        <id>/drafts/sources-of-truth/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Fact Experience
Figures of Authority</summary>
          <content type="html"><![CDATA[<p>Fact
Experience</p>
<p>Figures of Authority</p>
]]></content>
      </entry>
      <entry>
        <title>Technology and Ethics</title>
        <link rel="alternate" href="/drafts/tehcnology-and-ethics/" />
        <id>/drafts/tehcnology-and-ethics/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Technology simply creates a greater capacity for. It is not inherently good or bad.
Ethics determines whether an action is good or bad
Practical Studies The young and porn; age restriction proposal Privacy vs security
Takeaway technology enables something, or allows a greater capacity of without restriction, this capacity can be exploited for good and bad restrictions may not work as expected
Inspired by https://www.youtube.com/watch?v=nAFw5i39m9I</summary>
          <content type="html"><![CDATA[<p>Technology simply creates a greater capacity for. It is not inherently good or bad.</p>
<p>Ethics determines whether an action is good or bad</p>
<h1 id="practical-studies">Practical Studies</h1>
<p>The young and porn; age restriction proposal
Privacy vs security</p>
<h1 id="takeaway">Takeaway</h1>
<p>technology enables something, or allows a greater capacity of
without restriction, this capacity can be exploited for good and bad
restrictions may not work as expected</p>
<h1 id="inspired-by">Inspired by</h1>
<p><a href="https://www.youtube.com/watch?v=nAFw5i39m9I">https://www.youtube.com/watch?v=nAFw5i39m9I</a></p>
]]></content>
      </entry>
      <entry>
        <title>The Internet is Fragile</title>
        <link rel="alternate" href="/drafts/the-internet-is-fragile/" />
        <id>/drafts/the-internet-is-fragile/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Exploring the possible ways that the Internet could fragment by understanding how it works.
When you clicked on this article, it is shown on your computer. This may seem trivial, but there is a whole chain of dependencies that make this possible.
client -| |- server
For any two computers to communicate, they have to be connected.
directly connect each computer group computers by area - emergence of HUBs Client ISP connection to the internet</summary>
          <content type="html"><![CDATA[<p>Exploring the possible ways that the Internet could fragment by understanding how it works.</p>
<p>When you clicked on this article, it is shown on your computer. This may seem trivial, but there is a whole chain of dependencies that make this possible.</p>
<p><em>client -|     |- server</em></p>
<p>For any two computers to communicate, they have to be connected.</p>
<ol>
<li>directly connect each computer</li>
<li>group computers by area - emergence of HUBs</li>
</ol>
<h1 id="client">Client</h1>
<p>ISP
connection to the internet</p>
<h1 id="infrastructure-investments">Infrastructure Investments</h1>
<p>the larger picture</p>
<p>ISP hierarchy</p>
<p>? Even if the some part of the communication is wireless (as is the case for WiFi and LTE), a significant portion includes long, physical cables.?</p>
<p>Terrestrial cables
Undersea cables</p>
<blockquote>
<p>Satellites launched, maintained</p>
</blockquote>
<h1 id="ongoing-service">Ongoing Service</h1>
<p>first, lets get the not directly related to the Internet, but a critical dependency nonetheless.</p>
<p>This complex system depends on another largely complex system.</p>
<p>Electrical power generation and distribution
<em>generation overview</em></p>
<p>Technical expertise for maintainence</p>
<h1 id="software">Software</h1>
<p>evolving</p>
<p>Firmware - routers, NIC
Communication standards (DNS)
Firewalls</p>
<h2 id="server">Server</h2>
<p>hosting provider (ongoing cost)
software updates and configuration</p>
<h1 id="too-much-information">Too Much Information</h1>
<p>There are just too many sources. How many websites/webpages on the internet - Google crawled?</p>
<p>need for search engines</p>
<p>increasing dominance of autonomous search engines powered by data collection - no direct input (social media algorithsm)
follow what I do, not what I say</p>
<h1 id="a-vision-for-the-future">A Vision for the Future</h1>
<p>Give back control to the end user.
Minimize dependencies - become independent.
Encourage freedom within reason.</p>
<p>There are countless valuable resources on the internet. If you find something useful and have the capacity, please archive it. There is no guarantee that it will be available in the future.</p>
]]></content>
      </entry>
      <entry>
        <title>The Uncalibrated Scale</title>
        <link rel="alternate" href="/drafts/the-uncalibrated-scale/" />
        <id>/drafts/the-uncalibrated-scale/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html"></summary>
          <content type="html"><![CDATA[]]></content>
      </entry>
      <entry>
        <title>Timescales of Power</title>
        <link rel="alternate" href="/drafts/timescales-of-power/" />
        <id>/drafts/timescales-of-power/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Are you powerful?
The idea is that even though you may have limited means to influence the present, even a limited means can significantly influence the future.
The question is that of timescales. Accumulating power decreases the timescale of your influence - you can see vast changes happen presently.
But any power, no matter how insignificant - if fact, even just in presense - has such an influence in sufficien time.</summary>
          <content type="html"><![CDATA[<p>Are you powerful?</p>
<p>The idea is that even though you may have limited means to influence the present, even a limited means can significantly influence the future.</p>
<p>The question is that of timescales. Accumulating power decreases the timescale of your influence - you can see vast changes happen presently.</p>
<p>But any power, no matter how insignificant - if fact, even just in presense - has such an influence in sufficien time.</p>
<p>The theory is that their is a point at which the magnitude of change becomes sufficiently large that any two influencers are comparable, and that this point can be made significantly closer &ldquo;in the window of this lifetime or the next&rdquo; without instantly requiring large amounts of power.</p>
<p>The butterfly effect</p>
<div class="tabs">
  







<input type="radio"
       id="my-animation-demo-radio-Animation"
       name="my-animation-demo-selector"
       class="tab-radio"
       tabindex="0"
        checked >

<label for="my-animation-demo-radio-Animation" class="tab-label">Animation</label>

<div class="tab">
  <input type="checkbox" id="zoomCheck-aeca6" hidden>
    <label for="zoomCheck-aeca6">
        <img class="zoomCheck" loading="lazy" decoding="async" 
            src="install-jupyter-in-docker.svg" alt="My animation" 
             />
    </label>
</div>








<input type="radio"
       id="my-animation-demo-radio-Code"
       name="my-animation-demo-selector"
       class="tab-radio"
       tabindex="0"
       >

<label for="my-animation-demo-radio-Code" class="tab-label">Code</label>

<div class="tab">
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span>
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="nn">matplotlib.animation</span> <span class="k">as</span> <span class="nn">animation</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1"># ... your animation code here ...</span>
</span></span></code></pre></div>
</div>



</div>

<p>How does this change? Perspective.
You should not be discouraged from attempting to solve problems that are &ldquo;out of your control&rdquo; because you are powerless - there is reason why it has not been solved by those in power and you are likely to face those same issues if you are in power. Rather, you should focus on action - what can be done now, what can be done in the future and how to plan for those capabilities.</p>
<p>At the same time, you should realise the limits of your capability. If you try and tackle too many things at once, you spread your capability thin and require increase the timescale to resolve. Not all problems that exist need to be resolved. You must decide with ones are meaningful enough to dedicate yourself to.</p>
]]></content>
      </entry>
      <entry>
        <title>Translator</title>
        <link rel="alternate" href="/projects/software/translator/" />
        <id>/projects/software/translator/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Explore possible translations.</summary>
          <content type="html"><![CDATA[<p>The translation between languages is necessarily lossy - some of the authors intention (in terms of style and meaning) is lost. By creating a dataset that maps different translations to a source, a more configureable mapping can be provided that potentially captures the original author&rsquo;s intention.</p>
]]></content>
      </entry>
      <entry>
        <title>Vim Motions</title>
        <link rel="alternate" href="/drafts/vim-motions/" />
        <id>/drafts/vim-motions/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Vim motions is a set of key bindings that allow you to quickly and efficiently edit text.
This is a guide that provides a gentle introduction, with snippets for more advanced features.
Modes Editing Simple editing y - yank (copy) p - paste d - delete x - cut
Complex Editing Replace last 12 characters of each line
s/\(.\{12}\)$// char represents % full document $ end of line ^ start of line </summary>
          <content type="html"><![CDATA[<p>Vim motions is a set of key bindings that allow you to quickly and efficiently edit text.</p>
<p>This is a guide that provides a gentle introduction, with snippets for more advanced features.</p>
<h1 id="modes">Modes</h1>
<h1 id="editing">Editing</h1>
<h2 id="simple-editing">Simple editing</h2>
<p>y - yank (copy)
p - paste
d - delete
x - cut</p>
<h2 id="complex-editing">Complex Editing</h2>
<p>Replace last 12 characters of each line</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-fallback" data-lang="fallback"><span class="line"><span class="cl">s/\(.\{12}\)$//
</span></span></code></pre></div><table>
<thead>
<tr>
<th>char</th>
<th>represents</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>%</code></td>
<td>full document</td>
</tr>
<tr>
<td><code>$</code></td>
<td>end of line</td>
</tr>
<tr>
<td><code>^</code></td>
<td>start of line</td>
</tr>
</tbody>
</table>
]]></content>
      </entry>
      <entry>
        <title>What is programming?</title>
        <link rel="alternate" href="/drafts/what-is-programming/" />
        <id>/drafts/what-is-programming/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">Hardware |- central processing unit
|- storage (ssd, hdd) |- memory (RAM) |- specialized hardware processing |- graphics processing unit |- sound card - wifi card |- battery, power and cooling |- display |- speaker |- HID |- mouse |- keyboard |- touchpad - touch screen
Fundementally created from the principles of chemistry and physics. While the functionality is quite simple, designs can be quite complex to achieve the required performance with limited space and power.</summary>
          <content type="html"><![CDATA[<p>Hardware
|- central processing unit<br>
|- storage (ssd, hdd)
|- memory (RAM)
|- specialized hardware processing
|- graphics processing unit
|- sound card
- wifi card
|- battery, power and cooling
|- display
|- speaker
|- HID
|- mouse
|- keyboard
|- touchpad
- touch screen</p>
<p>Fundementally created from the principles of chemistry and physics. While the functionality is quite simple, designs can be quite complex to achieve the required performance with limited space and power.</p>
<p>Firmware is software that is very closely tied to the hardware.
In a wired mouse, the laser is one of the main sensors. It bounces light of the surface and the sensor registers this. The firmware is run on a microcontroller and translates this sensor reading into a format that can be sent through the transmission medium.</p>
<p>Drivers are software closely tied to the device and interface - it interprets commands received from the firmware and sends it to the OS for performance.</p>
<p>Operating System manages memory and processing.
It allocates memory for applications and provides access to the CPU.</p>
<p>Applications</p>
<p>When a mouse clicks a button, a metal contact mouse. This is wired to a sensor that triggers the firmware to send a signal through the connecting interface. The operating system receives this signal, and uses the driver to determines that a click action must be performed at a specific location. The operating systems propogates this signal which the appliction consumes. The application logic and relevant data defined to be executed when the button is clicked is loaded into the memory and executed by the processor. This then updates the screen, which is sent back down the pipeline.</p>
<p>Dependencies
|- uniquely suited as a means of creation; minimal dependencies (low barrier of entry)
|- field of many domains - not just like computing, but you can be equally intrested in so many other fields
- what sort of problems can you solve?
|- actuators (change the world)
|- sensors (collect data)
- data</p>
<h1 id="novelty">Novelty</h1>
<p>&ldquo;What can you do that hasn&rsquo;t already been done?&rdquo;
- Easiest and most meaningful to address challenges you face
- Check if its done! You don&rsquo;t want to do it all over again. It&rsquo;s likely huge undertaking.</p>
<p>Novelty in hardware design comes mainly through integration of hardware modules.
Novelty in software programming comes from application, or integration of libraries.
DSP?</p>
]]></content>
      </entry>
      <entry>
        <title>Why use open source tools?</title>
        <link rel="alternate" href="/drafts/why-use-open-source-tools/" />
        <id>/drafts/why-use-open-source-tools/</id>
        <published>0001-01-01T00:00:00Z</published>
        <updated>0001-01-01T00:00:00Z</updated>
        <summary type="html">https://www.youtube.com/watch?v=I4mdMMu-3fc</summary>
          <content type="html"><![CDATA[<p><a href="https://www.youtube.com/watch?v=I4mdMMu-3fc">https://www.youtube.com/watch?v=I4mdMMu-3fc</a></p>
]]></content>
      </entry>

</feed>


