<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Shen's Essays]]></title><description><![CDATA[On Philosophy and Computer Science]]></description><link>https://shen.hong.io/</link><image><url>https://shen.hong.io/favicon.png</url><title>Shen&apos;s Essays</title><link>https://shen.hong.io/</link></image><generator>Ghost 5.79</generator><lastBuildDate>Fri, 23 Feb 2024 08:01:36 GMT</lastBuildDate><atom:link href="https://shen.hong.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts]]></title><description><![CDATA[A tutorial on using n8n to automate the uploading of receipts from Firefly III to Paperless-ngx.]]></description><link>https://shen.hong.io/guide-to-workflow-automation-using-n8n-to-manage-receipts/</link><guid isPermaLink="false">62a1c7080b2cc80001ef7bf7</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Sun, 12 Jun 2022 19:50:26 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/nail-factory.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/nail-factory.webp" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts"><p>Have you ever had a repetitive task, which involves downloading files from one location, and uploading them to another? Are there &quot;computer chores&quot; in your routine, which simply consists of being the intermediary between two different online services? Whether it&apos;s entering spreadsheets from one program into another, or catching the output of one website and reformatting it for another tool &#x2013; the proliferation of <a href="https://en.wikipedia.org/wiki/Information_silo">online services which do not talk to each other</a> means that humans are left to fill the gaps. But we are more than glorified data-pipelines: to shuttle files from one place to another is the <a href="https://www.perseus.tufts.edu/hopper/text?doc=Perseus%3Atext%3A1999.01.0054%3Abook%3D1">characteristic activity</a> of an <a href="https://en.wikipedia.org/wiki/USB_flash_drive">USB</a>, not a human being. Thankfully, we can automate these tasks away using workflow automation software.</p><p>My name is <a href="https://shen.hong.io/about/">Shen</a>, and I am a Philosophy student. In this tutorial, I showcase <a href="https://n8n.io/">n8n</a>: an open source (<a href="https://github.com/n8n-io/n8n">Github link</a>), self-hosted workflow automation platform. I will walk us through the process of authenticating with a <a href="https://en.wikipedia.org/wiki/Representational_state_transfer">REST API</a>, setting up <a href="https://requestbin.com/blog/working-with-webhooks/">Webhooks</a>, and finally the automation code in n8n itself. Although this tutorial is specifically concerned with two tools (Firefly III and Paperless-ngx), the general concepts are universal and broadly applicable. By automating the mundane, repetitive tasks of uploading receipts, I can devote more time to Philosophy. And it is my hope that by sharing this knowledge, you too will find more leisure to devote to <a href="https://iep.utm.edu/philo/#H4">contemplation</a>. </p><h2 id="my-receipt-management-workflow">My Receipt Management Workflow</h2><p>I use two tools regularly to keep track of my spending, and to manage paper documents in general:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/firefly-plus-paperless.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1000" height="298" srcset="https://shen.hong.io/content/images/size/w600/2022/06/firefly-plus-paperless.png 600w, https://shen.hong.io/content/images/2022/06/firefly-plus-paperless.png 1000w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">I use Firefly III and Paperless-ngx to manage my receipts!</span></figcaption></figure><ul><li><a href="https://www.firefly-iii.org/">Firefly III</a>: an open source, self-hosted personal finance tool. I use this to keep track of my financial transactions and daily spending.</li><li><a href="https://github.com/paperless-ngx/paperless-ngx">Paperless-ngx</a>: an open source, self-hosted document management platform. It offers powerful search, categorisation, and OCR capabilities that I utilise to manage all my paper documents.</li></ul><p>Whenever I get home from the day, I scan and digitise the receipts that I have accrued. Then I enter their transaction information into Firefly: adding the description and value, before finally attaching the PDF of the relevant receipt into the transaction. Afterwards, I upload again the PDFs into Paperless, making sure once again to give them the correct description. Having to upload and enter the transaction information twice was repetitive and time consuming, and I quickly started wondering if there was some way to have the two systems talk to each other.</p><p><em>Was there any way to integrate Firefly with Paperless-ngx? What could I do, so that every time I uploaded a new transaction on Firefly, it automatically sent the information and attached PDF to Paperless-ngx?</em></p><p>Both Firefly and Paperless-ngx are open source tools, with well-documented APIs. Surely it wouldn&apos;t be too difficult to create an integration, I thought. My first approach was to <a href="https://github.com/firefly-iii/firefly-iii/issues/6135">open a suggestion</a> on the <a href="https://github.com/firefly-iii/firefly-iii">Firefly III Github page</a>. James Cole, the maintainer of Firefly III <a href="https://github.com/firefly-iii/firefly-iii/issues/6135#issuecomment-1146814974">responded</a>, declining to add a native integration due to maintenance concerns. However, he encouraged me to build my own &#x2013; and with that suggestion, I stumbled upon <a href="https://n8n.io/">n8n</a>.</p><h2 id="why-i-choose-to-use-n8n">Why I Choose to Use n8n</h2><p><a href="https://n8n.io/">n8n</a> (<a href="https://docs.n8n.io/#about-n8n">pronounced as &quot;n-eight-n&quot;)</a> is an open source workflow automation tool. It allows one to &quot;glue&quot; together different services, automating the transfer of information between tools. n8n uses a visual, flow-chart-like metaphor to allow users to easily create workflows (i.e. scripts). n8n is essentially the open source version of <a href="https://zapier.com/">Zapier</a> or <a href="https://ifttt.com/">IFTTT</a>. I decided to use n8n instead of an ad-hoc setup with <a href="https://en.wikipedia.org/wiki/Shell_script">shell scripts</a> and <a href="https://en.wikipedia.org/wiki/Cron">crontab entries</a> because I wanted something easy to use and understandable at a glance. After all, if you spend much more time automating a task than you&apos;d spend performing it, your efforts would be inefficient. Once I realised it was the tool for me, I quickly <a href="https://docs.n8n.io/hosting/options/">installed it on to my server</a>.</p><h2 id="creating-an-n8n-workflow-for-automatic-receipt-management">Creating an n8n workflow for automatic receipt management</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/workflow.webp" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1873" height="575" srcset="https://shen.hong.io/content/images/size/w600/2022/06/workflow.webp 600w, https://shen.hong.io/content/images/size/w1000/2022/06/workflow.webp 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/workflow.webp 1600w, https://shen.hong.io/content/images/2022/06/workflow.webp 1873w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The complete automation workflow</span></figcaption></figure><p>How was it like creating the automation workflow? In my case, there were four steps:</p><ol><li>Generate the API authentication tokens for both services.</li><li>Register Webhooks on Firefly III using the Firefly API.</li><li>Creating the automation workflow on n8n</li><li>Testing, and deploying the workflow to production.</li></ol><h2 id="generating-authentication-tokens">Generating Authentication Tokens</h2><p>Generally, the first step towards any workflow automation, is to get access to the <a href="https://en.wikipedia.org/wiki/API">API</a> of the service(s) that you are trying to use. There is usually some means to authenticate yourself to the API. In my case, both Firefly III and Paperless-ngx support the use of token-based authentication. An API token is essentially a long, computer-memorised password that must be sent along with API requests. <strong>Be sure to keep these tokens safe </strong>&#x2013; they allow the bearer to access your account!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1072" height="500" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image.png 1000w, https://shen.hong.io/content/images/2022/06/image.png 1072w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example: generating authentication tokens at the paperless Admin page.</span></figcaption></figure><p>We will need API tokens from both Firefly III and Paperless-ngx in order to interact with their APIs. In fact, the next step is for us to register webhooks using the Firefly III API directly.</p><h2 id="registering-and-managing-webhooks-with-firefly-iii">Registering and Managing Webhooks with Firefly III</h2><p><a href="https://requestbin.com/blog/working-with-webhooks/">Webhooks</a> are the foundation of our automation workflow. A webhook is like a custom <a href="https://en.wikipedia.org/wiki/Callback_(computer_programming)">function callback</a>: you can give Firefly a special URL that it will send data to upon a certain trigger. The <a href="https://docs.firefly-iii.org/firefly-iii/pages-and-features/webhooks/">Firefly III documentation page on Webhooks</a> has a more extensive description of the functionality available. We will be creating two webhooks, using the following triggers:</p><ul><li><code>TRIGGER_STORE_TRANSACTION</code> : For when we store a new transaction.</li><li><code>TRIGGER_UPDATE_TRANSACTION</code>: For when an existing transaction is updated.</li></ul><p>Each time a transaction is added or updated, we want Firefly to take a certain action: namely send the data of the transaction involved to our Webhook URL. This action is defined in Firefly as:</p><ul><li><code>RESPONSE_TRANSACTIONS</code>: Respond to the Webhook URL with the transaction that was added/updated.</li></ul><p>Where do we get the Webhook URL? These are provided by n8n! Now is the time to create a new n8n Workflow. Be sure to create two Webhook nodes using the graphical GUI. We will need two, as we want our Workflow to be triggered both when a new transaction is added, and when an existing transaction is updated:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-2.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1896" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-2.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-2.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-2.png 1600w, https://shen.hong.io/content/images/2022/06/image-2.png 1896w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Creating two Webhook nodes in n8n. Don&apos;t worry about the start node&#x2013; we will not be using it at all, since the workflow will be triggered solely via Webhooks coming from Firefly III</span></figcaption></figure><p>Each of these webhook nodes will give you a unique webhook URL. Save those. Make sure to use the Test URL for now instead of the production URL, while we still build the workflow. With these URLs, it is now time to register the webhooks with Firefly III.</p><p>Most applications have an admin page where you will be able to register new webhooks. Unfortunately as of 2022-06-09, Firefly III does not have such a page. Instead, in order to register new webhooks, <a href="https://docs.firefly-iii.org/firefly-iii/pages-and-features/webhooks/#create-or-edit-webhooks-using-the-api">we must interact directly with it&apos;s API</a>. We will be using the <a href="https://curl.se/">CURL</a> command to do so, via the command line. You&apos;ll need the following information handy:</p><ul><li><code>[Firefly III domain]</code>: The domain name of your Firefly III installation.</li><li><code>[Auth Token]</code> The API Authentication Token for Firefly III.</li><li><code>[Webhook URL]</code>: The URL from your webhook nodes. Make sure to use the test URL, I will show you how to switch them later.</li></ul><h3 id="registering-webhooks">Registering Webhooks</h3><p>We will be registering two webhooks, one for <code>TRIGGER_STORE_TRANSACTION</code> and one for <code>TRIGGER_UPDATE_TRANSACTION</code>, respectively. Here is the command to register the first one:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">curl -X POST &apos;https://[Firefly III domain]/api/v1/webhooks&apos; \
  -H &apos;accept: application/vnd.api+json&apos; \
  -H &apos;Authorization: Bearer [Auth Token] \
  -H &apos;Content-Type: application/json&apos; \
  -d &apos;{
  &quot;active&quot;: true,
  &quot;title&quot;: &quot;Paperless-ngx Integration (via n8n) On New&quot;,
  &quot;trigger&quot;: &quot;TRIGGER_STORE_TRANSACTION&quot;,
  &quot;response&quot;: &quot;RESPONSE_TRANSACTIONS&quot;,
  &quot;delivery&quot;: &quot;DELIVERY_JSON&quot;,
  &quot;url&quot;: &quot;[Webhook #1 URL]&quot;
}&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">The first webhook, which will fire on new transactions. Make sure to give it the correct webhook URL from the first n8n webhook node!</span></p></figcaption></figure><p>And the command to register the second one:</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">curl -X POST &apos;https://[Firefly III domain]/api/v1/webhooks&apos; \
  -H &apos;accept: application/vnd.api+json&apos; \
  -H &apos;Authorization: Bearer [Auth Token] \
  -H &apos;Content-Type: application/json&apos; \
  -d &apos;{
  &quot;active&quot;: true,
  &quot;title&quot;: &quot;Paperless-ngx Integration (via n8n) On Update&quot;,
  &quot;trigger&quot;: &quot;TRIGGER_UPDATE_TRANSACTION&quot;,
  &quot;response&quot;: &quot;RESPONSE_TRANSACTIONS&quot;,
  &quot;delivery&quot;: &quot;DELIVERY_JSON&quot;,
  &quot;url&quot;: &quot;[Webhook #1 URL]&quot;
}&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">The second webhook, which will fire on transaction updates. Make sure to give it the correct webhook URL from the second n8n webhook node!</span></p></figcaption></figure><h3 id="getting-list-of-existing-webhooks">Getting List of Existing Webhooks</h3><p>Once you have registered both webhooks successfully, you should be able to query the <code>/api/v1/webhooks</code> endpoint to get a list for all of them. Here is the command to list all webhooks.</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">curl -X GET &apos;https://[Firefly III domain]/api/v1/webhooks&apos; \
  -H &apos;accept: application/vnd.api+json&apos; \
  -H &apos;Authorization: Bearer [Auth Token]&apos; \
  -H &apos;Content-Type: application/json&apos;
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to retrieve a list of all webhooks.</span></p></figcaption></figure><p>Every successful command will return information in the form of an minified json string. You can &quot;prettify&quot; the information using an online tool like <a href="http://jsonprettify.com/">json prettify</a>, which will make it easier to read. Next, I will also go through some other useful API calls to manage these webhooks, for future reference.</p><h3 id="updating-existing-webhooks">Updating Existing Webhooks</h3><p>In order to update an existing webhook, you must make a <code>PUT</code> request towards the correct <code>{id}</code> of the webhook that you want to modify. You can get webhook IDs by using the previous API call to list webhooks.</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">curl -X PUT &apos;https://[Firefly III domain]/api/v1/webhooks/{id}&apos; \
  -H &apos;accept: application/vnd.api+json&apos; \
  -H &apos;Authorization: Bearer [Auth Token] \
  -H &apos;Content-Type: application/json&apos; \
  -d &apos;{
  &quot;active&quot;: true,
  &quot;title&quot;: &quot;Paperless-ngx Integration (via n8n) On New&quot;,
  &quot;trigger&quot;: &quot;TRIGGER_STORE_TRANSACTION&quot;,
  &quot;response&quot;: &quot;RESPONSE_TRANSACTIONS&quot;,
  &quot;delivery&quot;: &quot;DELIVERY_JSON&quot;,
  &quot;url&quot;: &quot;[Webhook URL]&quot;
}&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to update a webhook.</span></p></figcaption></figure><p>Whatever values you put in <code>-d &apos;{ ... }&apos;</code> field will overwrite existing values. Keep this update command in mind, as we will be using it to set the <code>url</code> to the production webhook URLs later.</p><h3 id="deleting-webhooks">Deleting Webhooks</h3><p>In order to delete a webhook, you simply make a <code>DELETE</code> request towards the <code>{id}</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-shell">curl -X DELETE &apos;https://[Firefly III domain]/api/v1/webhooks/{id}&apos; \
  -H &apos;accept: application/vnd.api+json&apos; \
  -H &apos;Authorization: Bearer [Auth Token] \
  -H &apos;Content-Type: application/json&apos;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to delete a webhook.</span></p></figcaption></figure><h2 id="saving-authentication-tokens-from-firefly-and-paperless-ngx-into-n8n">Saving authentication tokens from Firefly and Paperless-ngx into n8n</h2><p>Now that we registered the webhooks, Firefly III is able to send information <em>to</em> n8n. But right now, n8n has no way to send or retrieve information <em>from</em> Firefly, nor from Paperless-ngx. In order for n8n to use their respective APIs, we must first save our authentication credentials into n8n. To do this, go to the <em>Credentials</em> tab on the left, and click <em>New</em>. n8n offers some pre-built authentication types to work with popular services, but it does not have one for Firefly III nor Paperless-ngx yet. Hence, we&apos;ll use the <a href="https://docs.n8n.io/integrations/credentials/httprequest/#using-header-auth">generic Header Auth credential type</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-3.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-3.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-3.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-3.png 1600w, https://shen.hong.io/content/images/2022/06/image-3.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Accessing the Header Auth generic credential type from n8n. To get to this dialogue box, click on the </span><i><em class="italic" style="white-space: pre-wrap;">New</em></i><span style="white-space: pre-wrap;"> button under the orange </span><i><em class="italic" style="white-space: pre-wrap;">Credentials</em></i><span style="white-space: pre-wrap;"> tab on the left.</span></figcaption></figure><p>n8n&apos;s generic Header Auth credential is a reference to authenticating a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods">HTTP request</a> by using a <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers">HTTP Header</a>. Any manner of header can be used, but for both Firefly III and Paperless-ngx, they use the standard <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization">HTTP Authorization Header</a>. </p><p>Go ahead and name your credential appropriately (e.g. &quot;Firefly III Header Auth&quot;), and add <code>Authorization</code> as the name of the Header used. The value has to be <code>Bearer [Auth Token</code>, which is the string <code>Bearer</code> followed by your authentication token.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-4.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-4.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-4.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-4.png 1600w, https://shen.hong.io/content/images/2022/06/image-4.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The entry for Firefly III header auth in n8n.</span></figcaption></figure><p>These values in the n8n form correspond to the line <code>-H &apos;Authorization: Bearer [Auth Token] \</code> from our earlier CURL commands.</p><p>Similarly, we&apos;ll have to create another set of Header Authentication credentials for Paperless-ngx. The only difference is that the value of the header is slightly different, <a href="https://paperless-ngx.readthedocs.io/en/latest/api.html#authorization">as the Paperless-ngx documentation specifies</a>. We will enter <code>Authorization</code> for <em>Name</em>, and <code>Token [Auth Token]</code> for <em>Value</em>. Make sure that you are using the authentication token for Paperless-ngx here, of course!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-5.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-5.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-5.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-5.png 1600w, https://shen.hong.io/content/images/2022/06/image-5.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The entry for Paperless-ngx header auth in n8n.</span></figcaption></figure><p>After saving both authentication methods into n8n, we are ready to begin constructing the workflow itself.</p><h2 id="building-the-n8n-workflow-to-retrieve-attachments-from-firefly-and-upload-them-to-paperless-ngx">Building the n8n Workflow to Retrieve Attachments from Firefly and Upload them to Paperless-ngx</h2><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/workflow.webp" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1873" height="575" srcset="https://shen.hong.io/content/images/size/w600/2022/06/workflow.webp 600w, https://shen.hong.io/content/images/size/w1000/2022/06/workflow.webp 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/workflow.webp 1600w, https://shen.hong.io/content/images/2022/06/workflow.webp 1873w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">The complete automation workflow. We need to build this today.</span></figcaption></figure><p>Now we must construct the automation workflow. The remainder of this article will be a tutorial that walks us through the main parts of building such a workflow. You are welcome to either follow along, or otherwise download the complete <code>.json</code> code for the workflow at the <a href="https://s.hong.io/snippets/firefly-to-paperless-n8n-workflow.json">following link</a>. Be aware that the code is not &quot;plug-and-play&quot; &#x2013; you will have to edit it to specify your own URLs and endpoints.</p><p>Our goal is that every time a transaction is added (or updated) on Firefly, the new (or updated) transaction is checked for any attachments. If the transaction has attachments, it will be downloaded by n8n, and then uploaded to Paperless-ngx. This sounds like a simple, and completely linear task&#x2013; however, there are two complexities that we must handle:</p><ol><li>The workflow must account for multiple attachments in the same transaction, and upload them all correctly to Paperless-ngx.</li><li>The workflow must preserve the transaction description, and send it to Paperless-ngx.</li></ol><p>In order to do the first, we must create a loop. In order to perform the second, we will be renaming the downloaded attachments before <code>POST</code>-ing them to Paperless.</p><p>I will walk us through the basic steps involved in building this workflow, as understanding the process will be helpful in creating future workflows. The bulk of the nodes that we use are to make HTTP requests (the ones with the blue &apos;@&apos;). Hence, the techniques we learn here are fairly universal and generally applicable to all services that have REST-ful APIs. </p><h2 id="retrieving-new-or-updated-transactions-and-checking-them-for-attachments">Retrieving New or Updated Transactions, and Checking them for Attachments</h2><p>First, every time a webhook fires, we must retrieve the transaction that was associated with the webhook. Remember, Firefly will only send data to n8n if a transaction is added or updated. However, the information that it sends is only a reference to what transaction was added/updated, and does not contain much information on the transaction itself. Hence, the first step that we must take, is to retrieve the full information of the webhook transaction.</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/06/image-6.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-6.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-6.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-6.png 1600w, https://shen.hong.io/content/images/2022/06/image-6.png 1907w" sizes="(min-width: 720px) 720px"></figure><p>To do this, we add two new HTTP Get nodes (which have the blue &apos;@&apos;). Then, click on the node itself, and configure it to use the header authentication credentials that we added earlier.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-8.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1908" height="1368" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-8.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-8.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-8.png 1600w, https://shen.hong.io/content/images/2022/06/image-8.png 1908w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Filling out the information in the HTTP Request node.</span></figcaption></figure><p>Now, when this node makes an HTTP request towards the Firefly III API, it will have the right credentials to be authenticated. What kind of request should this node make? This is the moment when we get into the heart of building automation workflows. We must consult the Firefly III API to see what kinds of information can be retrieved:</p><ul><li><a href="https://docs.firefly-iii.org/firefly-iii/api/">Firefly III API Intro</a></li><li><a href="https://api-docs.firefly-iii.org/">Firefly III API Reference (List of endpoints)</a></li></ul><p>In our case, we must retrieve the information associated with the transaction that triggered the webhook. According to <a href="https://api-docs.firefly-iii.org/#/transactions/getTransaction">the Transactions section of the Firefly III API Reference</a>, a request made to the following endpoint:</p><pre><code class="language-http">https://[Firefly III domain]/api/v1/transactions/{id}</code></pre><p>Will return with the JSON-formatted information associated with the transaction bearing the ID of <code>{id}</code>. Hence, that&apos;s the call that we must make.</p><p>At this point, we still don&apos;t know what information is given to us by the webhooks. Hence, we need to call the webhook at least once, in order to capture it&apos;s input. Press the large, orange &quot;Execute Workflow&quot; button at the bottom of the n8n editor so we may begin capturing data. Now go ahead and create a new (test) transaction on Firefly III. If you registered the webhooks correctly, the following information should be sent to your HTTP node.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/Screenshot-2022-06-09-at-18-40-15-n8n------Firefly-III-to-Paperless-ngx-Integration.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/Screenshot-2022-06-09-at-18-40-15-n8n------Firefly-III-to-Paperless-ngx-Integration.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/Screenshot-2022-06-09-at-18-40-15-n8n------Firefly-III-to-Paperless-ngx-Integration.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/Screenshot-2022-06-09-at-18-40-15-n8n------Firefly-III-to-Paperless-ngx-Integration.png 1600w, https://shen.hong.io/content/images/2022/06/Screenshot-2022-06-09-at-18-40-15-n8n------Firefly-III-to-Paperless-ngx-Integration.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The input to the node (as captured from the webhook) is visible on the left. The output of this node executing is visible on the right.</span></figcaption></figure><p>One of the JSON fields that the Webhook gives us is the <code>id</code> of the transaction. We now have the information that we need to make the request! Now, all we have to do is to craft an expression in the URL field. We do this by clicking on the grey gear icon to the right of the URL field (not visible in the above screenshot), and clicking &quot;Add expression.&quot;</p><p>The window that now opens up is n8n&apos;s expression editor. Here, using a combination of JavaScript and templating, you can craft expressions that take information from nodes which feed into your current node, or any node executed so far.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-9.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-9.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-9.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-9.png 1600w, https://shen.hong.io/content/images/2022/06/image-9.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">An example of how the expression editor will look like. Don&apos;t mind the red &quot;not found&quot; warnings &#x2013; this screenshot is just an illustration.</span></figcaption></figure><p>In order to find the right values to insert into the expression, browse the expandable value list to the left of the expression editor. Find the value labeled <code>id</code> and click on it&#x2013; it will insert the appropriate expression into the editor. You should see the value become highlighted in green (unlike my screenshot), and the resulting substitution will be displayed on the Result field of the screen.</p><p>Finally, combine the outputs of both nodes into an IF node, to check if the transactions contain attachments. If they do, then we must get the attachment information, and begin downloading all attachments from the transaction. To do this, you will also use the expression editor&#x2013; taking the value from the inputs of the IF node and comparing if there <code>has_attachments</code> attribute is true. The expression that I used is:</p><pre><code class="language-json">{{$json[&quot;data&quot;][&quot;attributes&quot;][&quot;transactions&quot;][0][&quot;has_attachments&quot;]}}</code></pre><p>This process of creating expressions in different nodes forms the core of the n8n workflow creation process. Feel free to experiment and understand the expression editor! You will use it in ways greater or less to create all the remaining nodes of the workflow.</p><h2 id="downloading-attachments-from-the-transaction-in-a-loop">Downloading Attachment(s) from the Transaction in a Loop.</h2><p>After getting the information about the attachment on the Firefly III transaction, we must be able to download the attachment and send it on to Paperless-ngx. However, there can be multiple attachments in a transaction &#x2013; hence we must create a loop that downloads every attachment in the transaction.</p><p>Just like in regular programming, every loop in n8n has three elements:</p><ol><li>A <code>Set</code> node (with the blue pencil icon) which sets a loop counter before the loop.</li><li>A second <code>Set</code> node which increments the counter for every iteration of the loop.</li><li>An <code>IF</code> node which checks whether the counter is greater than a chosen value. If false, it repeats the loop.</li></ol><p>Here&apos;s a screenshot of how a simplified loop looks like. We would implement the actual &quot;business logic&quot; of what we want the loop do in the middle of it.</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/06/image-12.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-12.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-12.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-12.png 1600w, https://shen.hong.io/content/images/2022/06/image-12.png 1907w" sizes="(min-width: 720px) 720px"></figure><p>Do you see the similarity between the loop above, and the loop in our workflow implementation? The only flourish that our workflow contains is that a second path branches out from the <em>Download Attachment(s)</em> node.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-10.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-10.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-10.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-10.png 1600w, https://shen.hong.io/content/images/2022/06/image-10.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The download every attachments loop in our n8n workflow.</span></figcaption></figure><p>Our loop instructs the <em>Download Attachment(s)</em> node to download every <code>i</code>-th attachment, starting from 0, until all attachments have been downloaded. This is accomplished by having the <em>If Done</em> node compare the current value of the counter, against the overall length of the attachment information array, returned by the <em>Get Attachment Information</em> node.</p><h2 id="renaming-attachments-using-a-function-node">Renaming Attachments Using a Function Node</h2><p>For every attachment, we utilise a function node to rename the attachment, before <code>POST</code>-ing them to Paperless. A function node basically receives information from the preceeding nodes as an JavaScript object, to which we can modify with code.</p><ul><li><a href="https://docs.n8n.io/integrations/core-nodes/n8n-nodes-base.function/#2-function-node">Function node documentation.</a></li><li><a href="https://community.n8n.io/t/how-to-modify-filename-when-obtaining-file-from-http/9521/2"><em>&quot;How to modify filename?&quot;</em></a> n8n community forum post.</li></ul><p>Our renaming code has some logic to check if this attachment is the only attachment of the transaction, or if it is only one of a series. Depending on the condition, it appends an appropriate suffix.</p><figure class="kg-card kg-code-card"><pre><code class="language-JavaScript">// Rename the input binary file to new_file_name. Contains some logic to determine how to
// name the files, depending on if there are multiple attachments.
if ($node[&quot;Get Attachment Information&quot;].json[&quot;data&quot;].length === 1) {
  // If the transaction only has one attachment
  var new_file_name = 
  $node[&quot;If Transaction has Attachment(s)&quot;].json[&quot;data&quot;][&quot;attributes&quot;][&quot;transactions&quot;][0][&quot;description&quot;];
} else {
  // Else if the transaction has multiple attachments
  // We will name each file with a number, starting from 1. The number comes from the counter.
  let suffix = String($json[&quot;counter&quot;] + 1);
  var new_file_name =
  $node[&quot;If Transaction has Attachment(s)&quot;].json[&quot;data&quot;][&quot;attributes&quot;][&quot;transactions&quot;][0][&quot;description&quot;]
  + &quot; &quot; + suffix;
}

const data = Object.assign(items[0].binary.data, {fileName:new_file_name});
return [
  {
    json:{},
    binary: {
      data:data
    }
  }
]</code></pre><figcaption><p><span style="white-space: pre-wrap;">Code used to rename attachments inside the Function node.</span></p></figcaption></figure><h2 id="posting-renamed-documents-to-paperless-ngx">Posting Renamed Documents to Paperless-ngx</h2><p>Finally, the attachment is sent to Paperless using a <code>POST</code> request. The way that this is done in n8n is slightly non-intuitive, so it&apos;s helpful for us to consult the documentation first to understand how <code>POST</code> requests are made.</p><ul><li><a href="https://docs.n8n.io/integrations/core-nodes/n8n-nodes-base.httprequest/#node-reference"><em>HTTP Request Node</em></a>, n8n documentation</li><li><a href="https://docs.n8n.io/integrations/core-nodes/n8n-nodes-base.httprequest/#send-a-binary-file-to-an-api-endpoint"><em>Send a binary file to an API endpoint</em></a><em>, </em>n8n documentation</li></ul><p>We will use the same HTTP Request Node, but instead of a <code>GET</code> request, we must use a POST request. Additionally, as per the example in the documentation, set the <code>JSON/RAW Parameters</code> toggle to true (green), and select <code>Form-Data Multipart</code> for the body content type. Also set the <code>Send Binary Data</code> toggle to true (Green).</p><p>Finally, add <code>document:data</code> into the <code>Binary Property</code> field. Note that this does <em>not</em> have to be a expression, just the string <code>document:data</code>. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/image-11.png" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/06/image-11.png 600w, https://shen.hong.io/content/images/size/w1000/2022/06/image-11.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/image-11.png 1600w, https://shen.hong.io/content/images/2022/06/image-11.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Screenshot of the dialogue for </span><code spellcheck="false" style="white-space: pre-wrap;"><span>POST</span></code><span style="white-space: pre-wrap;">-ing a binary document to the Paperless API.</span></figcaption></figure><p>Now all that remains is to test out the workflow. Click on the orange &quot;Execute Workflow&quot; button once more, and create a test transaction on Firefly III with an attachment. You should see the workflow trigger, and your attachment automatically appear on Paperless-ngx!</p><p>Once you have verified that the workflow works correctly in Development mode, you must activate it on n8n so it runs automatically upon triggers. Then, you must update the webhook URLs on Firefly III to point towards the production URL, instead of the test URL. To do this, simply use the update command listed earlier.</p><h2 id="conclusion">Conclusion</h2><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/06/workflow.webp" class="kg-image" alt="Guide to Workflow Automation: How I use Firefly III, Paperless-ngx, and n8n to Painlessly Manage Receipts" loading="lazy" width="1873" height="575" srcset="https://shen.hong.io/content/images/size/w600/2022/06/workflow.webp 600w, https://shen.hong.io/content/images/size/w1000/2022/06/workflow.webp 1000w, https://shen.hong.io/content/images/size/w1600/2022/06/workflow.webp 1600w, https://shen.hong.io/content/images/2022/06/workflow.webp 1873w" sizes="(min-width: 1200px) 1200px"><figcaption><span style="white-space: pre-wrap;">The complete automation workflow</span></figcaption></figure><p>We have created an n8n automation workflow which allows attachments to be automatically uploaded to Paperless-ngx. I hope my tutorial was helpful in covering the steps towards creating such workflows. As of always, the the complete <code>.json</code> code for the workflow at the <a href="https://s.hong.io/snippets/firefly-to-paperless-n8n-workflow.json">following link</a>. </p><p>Tools like n8n are useful for automating mundane tasks, and I hope that the time it saves us will allow us to do more purposeful, and interesting things.</p><hr><p><em>Thank you for reading my tutorial. For further content, feel free to subscribe to my </em><a href="https://shen.hong.io/rss/"><em>RSS feed</em></a><em>. If you have any thoughts, feedback, or got stuck in any way, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em>. I always enjoy meeting interesting strangers! Be sure to check out my other content: I also write about </em><a href="https://shen.hong.io/role-of-friendship-in-inquiry-reflections-on-galileo/"><em>Galileo</em></a><em>, </em><a href="https://shen.hong.io/philosophical-contemplation-on-relativity-part-1/"><em>Special Relativity</em></a><em>, and </em><a href="https://shen.hong.io/nixos-for-philosophy-installing-firefox-latex-vscodium/"><em>NixOS</em></a><em>.</em></p>]]></content:encoded></item><item><title><![CDATA[How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit]]></title><description><![CDATA[A guide to using EOTK (The Enterprise Onion Toolkit) to make websites available over the Tor Network as a Onion Service.]]></description><link>https://shen.hong.io/making-websites-on-tor-using-eotk/</link><guid isPermaLink="false">623bba9476647600017c8cfb</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Sun, 27 Mar 2022 20:51:24 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/onions-1.webp" medium="image"/><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2><img src="https://shen.hong.io/content/images/2023/12/onions-1.webp" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"><p>Hello there. My name is Shen, and I&apos;m a <a href="https://shen.hong.io/about/">Philosophy and Computer Science student</a>. I write articles about <a href="https://shen.hong.io/intro-to-metaphysics-for-compsci-and-scientists/">Metaphysics</a> and <a href="https://shen.hong.io/dialogue-on-the-questions-of-being/">Ontology</a>. Recently, a reader contacted me about making my website available as an <a href="https://community.torproject.org/onion-services/">Onion Service</a> over the <a href="https://en.wikipedia.org/wiki/Tor_(network)">Tor Network</a>. Using <a href="https://github.com/alecmuffett/eotk">EOTK</a> (the Enterprise Onion Toolkit), I was able to mirror <a href="https://shen.hongio267dx4o2ofkn4ddsztu4ok2vq4euc7sxumbi7kcfd64ije62ad.onion/">my blog as a <code>.onion</code> site</a>, and make it accessible to users over Tor.</p><p>This article is a guide to the process of using EOTK to mirror an existing website as an Onion Service. It is be applicable to any small website, whether a static site, or one running common <a href="https://en.wikipedia.org/wiki/Content_management_system">Content Management System</a> like <a href="https://wordpress.org/">WordPress</a>, <a href="https://www.drupal.org/project/drupal/releases/8.9.1">Drupal</a>, or <a href="https://ghost.org/">Ghost</a>. Both this guide, and EOTK in general are designed to be self-contained, and &quot;drop-in&quot; &#x2013; you do not need to modify your existing website configuration. </p><p>This guide is suitable for the intermediate Linux user or hobbyist web admin. You do not need to know any technical details about Tor. My intended audience is someone who is comfortable managing a WordPress site on the command line, or self-hosting a webapp using a VPS, but otherwise does not know about <a href="https://en.wikipedia.org/wiki/Unix_domain_socket">Unix sockets</a>, <a href="https://www.nginx.com/resources/datasheets/nginx-modules/">Nginx modules</a>, or advanced networking.</p><h3 id="what-is-tor-what-are-onion-sites">What Is Tor? What Are Onion Sites?</h3><p>The <a href="https://www.torproject.org/">Tor Network</a> is a free and open source volunteer network that mitigates against tracking, surveillance, and internet censorship. Users connect to the Tor Network using the Tor Browser, which lets them browse websites anonymously. Tor users are often people with advanced privacy and security needs &#x2013; such as journalist, whistleblowers, and citizens of politically oppressive regimes.</p><p>People can visit regular internet websites (i.e. the &quot;clearnet&quot;) using the Tor Browser, but there are special sites called onion sites, that are only visible using Tor. These <a href="https://community.torproject.org/onion-services/">Onion Services</a> provide the highest degree of anonymity and protection for Tor users. This is why <a href="https://community.torproject.org/onion-services/#featured-onions">many news organisations run onion sites</a>. The New York Times, Washington Post, BBC, and others all maintain onion sites for users in countries where internet censorship is pervasive and freedom of speech is limited. Even <a href="https://en.wikipedia.org/wiki/Facebook_onion_address">Facebook</a> and <a href="https://help.twitter.com/en/using-twitter/twitter-supported-browsers">Twitter</a> have onion sites. One can think of an onion site as the &quot;shortwave radio&quot; equivalent of a regular internet website.</p><h3 id="why-should-i-make-my-website-available-as-an-onion-site-over-tor">Why Should I Make My Website Available as an Onion Site over Tor?</h3><p>Onion sites are typically used by people with exceptional privacy and cybersecurity requirements. These users may come from politically oppressive regimes, or marginalised communities where surveillance is widespread and dangerous. By making your regular, &quot;clearnet&quot; website accessible as a onion site, you can help readers and visitors who may otherwise be unable to reach your content. I believe that even if only a single individual benefits from the onion mirror of my blog, it would be all worth it &#x2013; because the pursuit of Philosophy is a universal, human activity &#x2013; that should be accessible to all.</p><h3 id="what-is-the-difference-between-making-my-website-an-onion-site-and-visiting-my-website-over-tor">What Is the Difference between Making My Website an Onion Site, and Visiting My Website over Tor?</h3><p>For Tor Network users, native onion sites offer far better security, privacy, as well as performance &#x2013; than simply visiting a clearnet website over Tor. In order for a Tor Network user to visit a clearnet site, her connection must exit the Tor Network through an <a href="https://community.torproject.org/relay/setup/exit/">Exit Node</a>, which makes the final connection over the Internet to the clearnet website. This last step can be subject to surveillance and monitoring, as well as interception if the clearnet site does not use <a href="https://en.wikipedia.org/wiki/Transport_Layer_Security">TLS (i.e. SSL, HTTPS)</a>. Finally, the capacity of the Tor Network&apos;s exit nodes is limited &#x2013; congestion at Exit Nodes typically make visits to clearnet web pages slow. </p><p>Alec Muffet, the creator and maintainer of EOTK has made a <a href="https://youtu.be/Rd7YKamsliI?t=1681">video comparison</a> on the performance between visiting Facebook over Tor, and Facebook as a native Onion Service. The Facebook Onion Service loads around an order of magnitude faster.</p><h2 id="making-an-existing-website-available-as-an-onion-site">Making an Existing Website Available as an Onion Site</h2><p>We now begin the technical portion of the guide. We will be using <a href="https://alecmuffett.com/about">Alec Muffet</a>&apos;s <a href="https://github.com/alecmuffett/eotk">EOTK</a> (The Enterprise Onion Toolkit) to create a transparent mirror for our existing clearnet website. EOTK is a best-in-class, industry standard toolkit for creating onion sites for existing clearnet websites. It has been used by the BBC, the Intercept, and many other large organisations. This tutorial will guide you in deploying EOTK for your existing website, to create a secure and low-maintenance onion mirror. This tutorial will also guide you in the process of generating a &quot;vanity&quot; <code>.onion</code> domain name using <a href="https://github.com/cathugger/mkp224o">mkp224o</a>, adding a <code>Onion-Location</code> header for your clearnet site, and installing signed TLS certificates with HARICA.</p><p>As an overview, the process of making a onion mirror will be as follows:</p><ol><li><strong>Generate a &quot;vanity&quot; <code>.onion</code> domain name using <code>mkp2240</code></strong>.<br>We do this first because it can take a long time &#x2013; this task can run in the background while you complete the subsequent steps.</li><li><strong>Install and configure EOTK on our remote web server</strong>.<br>This step will include daemonising the process and running via systemctl.</li><li><strong>Install trusted TLS certificates from HARICA into EOTK</strong>.<br>This step is optional. It is only applicable if you decide to purchase signed certificates from the HARICA certificate authority.</li><li><strong>Configure the <code>Onion-Location</code> header in Nginx for our clearnet site</strong>.<br>This better facilitates discovery for your onion site.</li></ol><p>This process will take roughly 2 hours to complete. It is a good choice for a weekend-project for the hobbyist web admin. We will begin with the first step of the process, by using the mkp2240 tool to generate a vanity onion domain name.</p><h2 id="using-mkp224o-to-generate-%E2%80%9Cvanity%E2%80%9D-onion-domain-names">Using mkp224o to Generate &#x201C;Vanity&#x201D; Onion Domain Names</h2><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/cathugger/mkp224o"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - cathugger/mkp224o: vanity address generator for tor onion v3 (ed25519) hidden services</div><div class="kg-bookmark-description">vanity address generator for tor onion v3 (ed25519) hidden services - GitHub - cathugger/mkp224o: vanity address generator for tor onion v3 (ed25519) hidden services</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">cathugger</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/3bc7dfaaa5a4853d77ed7c6f80f3848e0fc2fa1c6aa4128dd1ba73a67f34211c/cathugger/mkp224o" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"></div></a><figcaption><p><span style="white-space: pre-wrap;">Link to the mkp224o Github Repository</span></p></figcaption></figure><p>The Tor Network is a decentralised network. As a result, there is no central authority to issue or register <code>.onion</code> domain names. Rather, all <a href="https://en.wikipedia.org/wiki/.onion#.exit_(defunct_pseudo-top-level_domain)"><code>.onion</code> domain names</a> consist of a 56 character-long <a href="https://en.wikipedia.org/wiki/Cryptographic_hash_function">hash</a> of letters and numbers, which is the base32 encoding of the site&apos;s public key. In practice, this means typical onion domain names look something like this:</p><figure class="kg-card kg-code-card"><pre><code>http://5ekxbftvqg26oir5wle3p27ax3wksbxcecnm6oemju7bjra2pn26s3qd.onion/</code></pre><figcaption><p><span style="white-space: pre-wrap;">The Onion Site Domain of the Debian Project (debian.org)</span></p></figcaption></figure><p>This is of course, rather ugly and entirely unmemorable. But it is possible to generate partially human-readable onion domain names, by repeatedly creating many candidate domain names, and filtering them for patterns. This is how organisations like Twitter, or the New York Times have <code>.onion</code> domains which look like this:</p><figure class="kg-card kg-code-card"><pre><code>https://twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion/</code></pre><figcaption><p><span style="white-space: pre-wrap;">Onion Site Domain of Twitter (twitter.com)</span></p></figcaption></figure><figure class="kg-card kg-code-card"><pre><code>https://nytimesn7cgmftshazwhfgzm37qxb44r64ytbb2dj3x62d2lljsciiyd.onion/</code></pre><figcaption><p><span style="white-space: pre-wrap;">Onion Site Domain of the New York Times (nytimes.org)</span></p></figcaption></figure><p>These are called &quot;<a href="https://en.wikipedia.org/wiki/Vanity_plate">vanity</a>&quot; onion domain names, and the brute-force process to create them is analogous to cryptocurrency mining &#x2013; since we are repeatedly making many hashes, and filter them according to an arbitrary criteria. </p><h3 id="security-considerations-of-using-vanity-onion-domain-names">Security Considerations of Using Vanity <code>.onion</code> Domain Names</h3><p>Although vanity <code>.onion</code> domain names are more recognisable, they may also have potential security disadvantages. If your users become accustomed to simply remembering your domain name as <code>name&lt;random-hash&gt;.onion</code>, an attacker may potentially <a href="https://en.wikipedia.org/wiki/IDN_homograph_attack">impersonate your site by generating a similar onion domain name</a> &#x2013; relying on how users will likely only glance at the first part of the domain name and ignore the unreadable hash. For instance, the following two domain names look indistinguishable at the first glance.</p><figure class="kg-card kg-code-card"><pre><code>legiteun3agp5zpw2zgcrjih4flq4ipp4dbpveqhlcyuas67wfqwmmyd.onion
legiteun3abbemz2u6zkfhiqemr6gurrekbjfppmdg2ud57sz6dr4mqd.onion</code></pre><figcaption><p><span style="white-space: pre-wrap;">Which is the &quot;legit&quot; onion site?</span></p></figcaption></figure><p>Hence, it is up to the implementer to decide whether or not the user-friendliness of a vanity <code>.onion</code> domain name will outweigh the small, but possible security disadvantage. I personally believe that the risk is quite small, so I choose to use a vanity <code>.onion</code> domain name &#x2013; but it is important to make an informed choice.</p><p>We can generate these vanity onion domain names using a tool called <a href="https://github.com/cathugger/mkp224o"><code>mkp224o</code> (Github Link)</a>. We will do this step first, since it can take some time &#x2013; we can run it in the background while we accomplish the subsequent steps.</p><h3 id="downloading-mkp2240">Downloading mkp2240</h3><p>Let us download and compile mkp224o from its authors <a href="https://github.com/cathugger/mkp224o">Github Repository</a>. You&apos;ll want to do this on a local machine (i.e. your desktop), preferably one which has a powerful, multi-core CPU.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">git clone https://github.com/cathugger/mkp224o.git</code></pre><figcaption><p><span style="white-space: pre-wrap;">Cloning from git repository</span></p></figcaption></figure><p>The utility is a C program, which we must configure and then compile on our machine. First, let us download some tools necessary to compile it:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">sudo apt install gcc libsodium-dev make autoconf</code></pre><figcaption><p><span style="white-space: pre-wrap;">Downloading compiler tools</span></p></figcaption></figure><p>Now we run <code>./autogen.s</code> in order to generate a <code>./configure</code> <a href="https://www.gnu.org/software/autoconf/">autoconf</a> script. <code>./configure</code> is the script which generates the <a href="https://en.wikipedia.org/wiki/Make_(software)#Makefile">Makefile</a> that allows us to compile the C program.</p><pre><code class="language-Shell">cd mkp224o
./autogen.s</code></pre><h3 id="configuring-and-compiling-mkp224o-with-optimal-settings">Configuring and Compiling mkp224o with Optimal Settings</h3><p>This is the point where we must consider carefully the different configuration options that we can compile the program with. The options are documented in the <code>OPTIMISATIONS.txt</code> file available in the project root (<a href="https://github.com/cathugger/mkp224o/blob/master/OPTIMISATION.txt">here&apos;s a link to it on Github</a>).</p><p>The mkp224o program is essentially an &quot;onion miner&quot; which mines onion domain names. We must ensure that we compile it with the optimal options, so that it performs as fast as possible. Specifically, mkp224o uses a library from <a href="https://nacl.cr.yp.to/">NaCl</a> to generate Ed25519 signatures very quickly. This library (called SUPERCOP) has <a href="https://ed25519.cr.yp.to/software.html">different versions</a> (i.e. implementations) that are better optimised for different CPU architectures. The mkp224o tool supports the following implementations:</p><table>
<thead>
<tr>
<th><strong>Implementation</strong></th>
<th><strong>Enable Flag</strong></th>
<th><strong>Notes (from the maintainer)</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>ref10</td>
<td><code>--enable-ref10</code></td>
<td>SUPERCOP&apos; ref10, pure C, very portable, previous default</td>
</tr>
<tr>
<td>amd64-51-30k</td>
<td><code>--enable-amd64-51-30k</code></td>
<td>SUPERCOP&apos; amd64-51-30k, only works on x86_64</td>
</tr>
<tr>
<td>amd64-64-24k</td>
<td><code>--enable-amd64-64-24k</code></td>
<td>SUPERCOP&apos; amd64-51-24k, only works on x86_64</td>
</tr>
<tr>
<td>ed25519-donna</td>
<td><code>--enable-donna</code></td>
<td>based on amd64-51-30k, C, portable, current default</td>
</tr>
<tr>
<td>ed25519-donna</td>
<td><code>--enable-donna-sse2</code></td>
<td>uses SSE2, needs x86 architecture</td>
</tr>
</tbody>
</table>
<p>The author offers advise and a discussion of the various options in <code>OPTIMISATIONS.txt</code>. In practical terms, if you are using a modern, 64-bit (x86_64) Intel or AMD CPU, you should use the <code>--enable-amd64-30k</code> configure flag. If you don&apos;t know what x86_64 means, you should certainly use  <code>--enable-amd64-30k</code>.</p><p>Likewise, there are an additional set of configure options for mkp224o&apos;s filtering algorithm. The options are described in detail in <code>OPTIMISATIONS.txt</code> &#x2013; and a proper selection is important, if you have hundreds of filters. However, if you are using only a few, the following flag will suffice:</p><pre><code class="language-Shell">--enable-intfilter=native</code></pre><p>This enables simple integer filters, which is fastest if you have only a dozen filters.</p><p>We the above considerations in mind, we can now configure mkp224o with the following flags:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">./configure --enable-amd64-51-30k --enable-intfilter</code></pre><figcaption><p><span style="white-space: pre-wrap;">Configuring mkp224o with optimal feature flags for a modern CPU</span></p></figcaption></figure><p>Now we run <code>make</code> to compile the program, and now <code>mkp224o</code> should be available as a native binary within the git repository.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">make

# Run mkp224o and print help
./mkp224o -h</code></pre><figcaption><p><span style="white-space: pre-wrap;">Compiling mkp224o and running the compiled binary</span></p></figcaption></figure><h3 id="mining-onions-with-mkp224o">Mining Onions with mkp224o</h3><p>Now we are ready to mine onions with mkp224o! This process is CPU bound, so more threads your CPU has, the faster your process is. Before we begin, make sure to check out all the available options for mkp224o by running <code>./mkp224o -h</code>. In particular, we&apos;ll be using the following flags:</p><table>
<thead>
<tr>
<th><strong>Flag</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>-f filename</code></td>
<td>Specify filter file which contains filters separated by newlines</td>
</tr>
<tr>
<td><code>-d dirname</code></td>
<td>Output directory</td>
</tr>
<tr>
<td><code>-B</code></td>
<td>Use batching key generation method (&gt;10x faster than -z, current default)</td>
</tr>
<tr>
<td><code>-s</code></td>
<td>Print statistics each 10 seconds</td>
</tr>
<tr>
<td><code>-p passphrase</code></td>
<td>Use passphrase to initialize the random seed with</td>
</tr>
<tr>
<td><code>--checkpoint filename</code></td>
<td>Load/save checkpoint of progress to specified file (requires passphrase)</td>
</tr>
</tbody>
</table>
<p>First, we&apos;ll create a <code>./my-filters.txt</code> file with a list of &quot;filters&quot; that we want our onion domain to be. For my website https://shen.hong.io/, my filters were the following:</p><figure class="kg-card kg-code-card"><pre><code>hongonion
shenhong
hongblog
shenblog
shensite
hongio
shenio</code></pre><figcaption><p><span style="white-space: pre-wrap;">Example </span><code spellcheck="false" style="white-space: pre-wrap;"><span>my-filters.txt</span></code><span style="white-space: pre-wrap;"> file for https://shen.hong.io/</span></p></figcaption></figure><p>With these filters, you will be able to find candidate onion domain names like:</p><figure class="kg-card kg-code-card"><pre><code>hongio267dx4o2ofkn4ddsztu4ok2vq4euc7sxumbi7kcfd64ije62ad.onion</code></pre><figcaption><p><span style="white-space: pre-wrap;">This is the domain name for my real onion site</span></p></figcaption></figure><p>Alec Muffet, the Facebook Engineer who created EOTK, has written some excellent advise about the process of mining and choosing onions. For inspiration and guidence, I highly recommend <a href="https://github.com/alecmuffett/eotk/blob/master/docs.d/TIPS-FOR-MINING-ONIONS.md">checking out his post</a> (available as within the EOTK git repository as <code>docs.d/TIPS-FOR-MINING-ONIONS.md</code>).</p><p>Create a directory called <code>./candidates/</code> which will contain all the matching domains. We will also specify a <code>--checkpoint</code> and a passphrase <code>-p</code>. This is to allow us to cancel and resume the search process conveniently. For the passphrase, make sure it is a long, &gt; 64 character random string. We will need it to resume the search! Finally, we will be using the <code>-B</code> flag to enable batched mode, which speeds up performance significantly. With the above settings, we may begin the search process with the following command:</p><pre><code class="language-Shell">./mkp224o -s --checkpoint checkpoint.txt -d candidates -f my-filters.txt -B -p PASSPHRASE</code></pre><p>You are now mining onions! Candidate domain names which match your filter criteria will be stored in the <code>./candidates/</code> directory. Depending on how many characters your filters are, this process can take a long time. Keep it going in the background, while we continue the rest of the tutorial.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-25-15-12-15.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1252" height="982" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-25-15-12-15.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-25-15-12-15.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-25-15-12-15.png 1252w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>mkp224o</span></code><span style="white-space: pre-wrap;"> in action. Note the onion domain names it already discovered!</span></figcaption></figure><h3 id="how-long-does-it-take-to-find-a-onion-domain-name-of-a-given-length">How Long Does It Take to Find a Onion Domain Name of a Given Length?</h3><p>I&apos;m glad you asked! There is a <a href="https://github.com/cathugger/mkp224o/issues/27">very informative discussion</a> in the Issues section of mkp224o&apos;s Github page, which essentially asks the same question. As <a href="https://github.com/cathugger/mkp224o/issues/27#issuecomment-568291087">one commentor has presented</a>, number of calculations (hashes) \(t\) needed to find an onion address with \(n\) characters with probability \(p\) is governed by the following equation:</p><p>\[t = \frac{\log(1-p)}{\log(1-\frac{1}{32^n})}\]</p><p>Each additional character in our vanity onion domain increases the term \(32^n\), where 32 is the base32 encoding of a onion domain. When we run <code>./mkp224o</code>, it outputs its own performance statistics, including its <code>calc/sec</code> rate which represents the number of hashes \(t\) which it performs every second. Hence, given the <code>calc/sec</code> value, you will know the \(t\) that your computer is capable of performing every second.</p><p>I&apos;ve recreated a table of \(t\), given some values of \(n\) and \(p\):</p><table>
<thead>
<tr>
<th><strong>n</strong></th>
<th><strong>p=0.50</strong></th>
<th><strong>p=0.75</strong></th>
<th><strong>p=0.90</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>22</td>
<td>44</td>
<td>73</td>
</tr>
<tr>
<td>2</td>
<td>709</td>
<td>1419</td>
<td>2357</td>
</tr>
<tr>
<td>3</td>
<td>22713</td>
<td>45425</td>
<td>75450</td>
</tr>
<tr>
<td>4</td>
<td>726817</td>
<td>1453634</td>
<td>2414434</td>
</tr>
<tr>
<td>5</td>
<td>23258160</td>
<td>46516319</td>
<td>77261934</td>
</tr>
<tr>
<td>6</td>
<td>744261118</td>
<td>1488522235</td>
<td>2472381917</td>
</tr>
<tr>
<td>7</td>
<td>23816355774</td>
<td>47632711548</td>
<td>79116221364</td>
</tr>
<tr>
<td>8</td>
<td>762123384785</td>
<td>1524246769571</td>
<td>2531719083689</td>
</tr>
<tr>
<td>9</td>
<td>24387948313146</td>
<td>48775896626291</td>
<td>81015010678099</td>
</tr>
<tr>
<td>10</td>
<td>780414346020670</td>
<td>1560828692041339</td>
<td>2592480341699210</td>
</tr>
</tbody>
</table>
<p>Here are some typical values of \(t\) which I have measured:</p><ul><li><strong>Framework Laptop</strong> with 11th Gen Intel Core i7-1185G7 (8 threads): \(\approx 13,417,732\) calc/sec.</li><li><strong>Hetzner Dedicated CPU Server</strong> with AMD EPYC 7003 Milan (48 threads): \(\approx 93,313,394\) calc/sec.</li></ul><p>(Feel free to <a href="https://shen.hong.io/contact/">contact me</a> if you have your own measurements! I&apos;ll add them to this list)</p><p>As you can see, for a 75% chance to &quot;mine&quot; a single 8-character vanity onion domain name, you will need approximately 31 hours on a modern Intel i7 CPU. That same 8-character vanity onion domain name will take less than 5 hours on a high-end AMD EPYC 7003 server CPU with 24 cores. But even that same powerful server CPU will take more than 4 days to mine a 9-character onion, and more than 191 days to mine a 10-character onion!</p><p>Hence, keep your onions mining, while we proceed on to the next part of the guide: installing and configuring EOTK.</p><h2 id="introduction-to-eotk-the-enterprise-onion-toolkit">Introduction to EOTK: The Enterprise Onion Toolkit</h2><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/alecmuffett/eotk"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - alecmuffett/eotk: Enterprise Onion Toolkit</div><div class="kg-bookmark-description">Enterprise Onion Toolkit. Contribute to alecmuffett/eotk development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">alecmuffett</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/b0703c2fdab15e705499e13ca84760804c0a62ec56e9879106c221425ce01b0e/alecmuffett/eotk" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"></div></a><figcaption><p><span style="white-space: pre-wrap;">Link to the EOTK Github Repository</span></p></figcaption></figure><p>We&apos;ll be using Alec Muffet&apos;s <a href="https://github.com/alecmuffett/eotk">EOTK</a> in order to make our website available transparently over the Tor Network. This section of the guide will introduce EOTK, install it on the server, and go through common configuration options and settings. </p><p>EOTK is the best approach to making an existing clearnet website available as an onion site. It offers the following advantages:</p><ul><li><strong>It is a transparent, drop-in solution:</strong> You do not need to alter your existing web app configuration or stack.</li><li><strong>It is widely used and actively developed:</strong> The toolkit has been deployed by the <a href="https://github.com/alecmuffett/eotk#eotk-in-the-news">BBC, The New York Times, and other large organisations</a>. The Git project has a <a href="https://github.com/alecmuffett/eotk/commits/master">consistent history of commits</a>.</li><li><strong>It packages the tor service with it:</strong> You do not need to separately install and configure tor. The solution installs a standalone tor service that is preconfigured with secure defaults.</li></ul><p>Hence, we will use EOTK to make our website available over the Tor Network as an onion site.</p><h3 id="how-eotk-works">How EOTK Works</h3><p>EOTK works as a man-in-the-middle <em>rewriting proxy</em>. It accepts inputs from the Tor Network, rewrites them for your web application, and then sends them to the application itself. Likewise, when the application responds, it will rewrite the response and then route it to the correct user in the Tor Network. Essentially, EOTK serves as a <em>translator</em> which allows Tor Network users to <em>visit</em> your site as an onion site, and for your site to <em>respond</em> as an onion site.</p><p>Here&apos;s a diagram of a regular web setup, where you have the web application behind a Nginx reverse proxy. The web application can be any CRM such as WordPress, Drupal, or Ghost &#x2013; and the Nginx reverse proxy performs traditional tasks such as compression and TLS.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Untitled-Diagram-4-.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1602" height="255" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Untitled-Diagram-4-.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Untitled-Diagram-4-.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Untitled-Diagram-4-.png 1600w, https://shen.hong.io/content/images/2022/03/Untitled-Diagram-4-.png 1602w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example of a regular website architecture. The application sits behind an Nginx reverse proxy.</span></figcaption></figure><p>When we install and configure EOTK successfully, it sits between our Nginx reverse proxy, and the Tor Network.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Untitled-Diagram-3-.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1602" height="555" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Untitled-Diagram-3-.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Untitled-Diagram-3-.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Untitled-Diagram-3-.png 1600w, https://shen.hong.io/content/images/2022/03/Untitled-Diagram-3-.png 1602w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example of EOTK in action. EOTK sits between the application and Nginx, dynamically rewriting requests that cross the Tor Network.</span></figcaption></figure><p>Note the substantive similarity between the role and position of EOTK, and our regular &quot;clearnet&quot; Nginx reverse proxy. EOTK is effectively a modified Nginx reverse proxy, which &quot;translates&quot; requests between the Tor Network, and your application&apos;s regular reverse proxy. In fact, we will also have to install TLS certificates at the EOTK level, in order to allow our onion sites to be served over HTTPS. But also observe that the connection between EOTK and your Web Application is not direct, but rather EOTK is a layer &quot;on top of&quot; your regular Nginx reverse proxy. This means that all the benefits and optimisations from your regular Nginx configuration is preserved. For example, if you configured Nginx to use compression, your site will likewise be compressed as an onion site.</p><p>Now that we understand the the architecture of EOTK within our existing web stack, let us begin with the installation process.</p><h3 id="installing-eotk-on-ubuntu">Installing EOTK on Ubuntu</h3><p>EOTK&apos;s installation instructions are available within the <code>./docs.d</code> folder of its git repository (<a href="https://github.com/alecmuffett/eotk/blob/master/docs.d/HOW-TO-INSTALL.md">link here</a>). The toolkit is available on a variety of different platforms, such as Ubuntu, Debian, CentOS, Mac OS, and FreeBSD. We&apos;ll be following the latest Ubuntu 20.04 instructions. First, clone the EOTK repository onto your remote webserver. </p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">git clone https://github.com/alecmuffett/eotk.git
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Cloning the EOTK from its git repository</span></p></figcaption></figure><p><strong>Be very mindful where you place the <code>./eotk</code> directory.</strong> The folder is not disposable, but rather it will serve as the root which all of our site-specific configuration will reside. The <code>./eotk</code> directory that we just cloned is analogous to our <code>/etc/nginx</code> directory &#x2013; it will later contain all of our site-specific configuration, TLS certificates, and <code>.onion</code> site keys. Make sure to move it to a safe place! Consider placing it in <code>/etc/</code>.</p><p>Once you are ready, we run <a href="https://github.com/alecmuffett/eotk/blob/master/opt.d/build-ubuntu-20.04.sh">the included build script</a>.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">cd ./eotk
./opt.d/build-ubuntu-20.04.sh</code></pre><figcaption><p><span style="white-space: pre-wrap;">Run the EOTK build script for Ubuntu 20.04</span></p></figcaption></figure><p>This build script does the following:</p><ol><li>It first downloads and installs a set of platform-specific build dependencies, such as <code>build-essential</code>, <code>libssl-dev</code>, and <code>curl</code>. </li><li>It then calls out to the <code>./opt.d/lib.sh</code> script, which contains instructions common to all platforms. </li><li>Within the <code>./opt.d/lib.sh</code> script, it downloads, configures, and builds two programs. The <code>tor</code> daemon itself (which provides us access to the Tor Network), as well as <a href="https://openresty.org/en/">OpenResty</a>, a Nginx plugin that provides Lua scripting support for Nginx configuration. OpenResty is the core of the EOTK &#x2013; we use OpenResty to &quot;translate&quot; requests to and from the Tor network for our web application.</li></ol><p>The build process can take some time. Once it is complete, you should have a <code>~/eotk/eotk</code> binary that is ready to use, as well as a directory structure that is populated like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">root@remotehost:~/eotk# tree -L 3
.
&#x2502;   # Directories
&#x251C;&#x2500;&#x2500; demo.d
&#x251C;&#x2500;&#x2500; docs.d
&#x251C;&#x2500;&#x2500; lib.d
&#x251C;&#x2500;&#x2500; opt.d
&#x251C;&#x2500;&#x2500; projects.d
&#x251C;&#x2500;&#x2500; secrets.d
&#x251C;&#x2500;&#x2500; templates.d
&#x251C;&#x2500;&#x2500; tools.d
&#x2502;   &#x251C;&#x2500;&#x2500; openresty.d
&#x2502;   &#x251C;&#x2500;&#x2500; tor -&gt; /root/eotk/opt.d/tor.d/bin/tor
&#x2502;   &#x2514;&#x2500;&#x2500; tor.d
&#x2502;
&#x2502;   # Executables
&#x251C;&#x2500;&#x2500; eotk
&#x251C;&#x2500;&#x2500; eotk-housekeeping.sh
&#x251C;&#x2500;&#x2500; eotk-init.sh
&#x2502;
&#x2502;   # Other files
&#x251C;&#x2500;&#x2500; LICENSE
&#x251C;&#x2500;&#x2500; Makefile
&#x2514;&#x2500;&#x2500; README.md
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Directory tree of the ./eotk folder</span></p></figcaption></figure><h3 id="eotk-directory-structure-and-reference">EOTK Directory Structure and Reference</h3><p>Here&apos;s a brief explanation of the EOTK directory structure. Many of the directories are only generated once you build the <code>eotk</code> binary.</p><table>
<thead>
<tr>
<th><strong>Directory</strong></th>
<th><strong>Contents</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>demo.d</code></td>
<td>Example site configuration files for demonstration. The <code>wikipedia.conf</code> file is a good source of inspiration (<a href="https://github.com/alecmuffett/eotk/blob/master/demo.d/wikipedia.tconf">Github Link</a>)</td>
</tr>
<tr>
<td><code>docs.d</code></td>
<td>Documentation (<a href="https://github.com/alecmuffett/eotk/tree/master/docs.d">GitHub link</a>)</td>
</tr>
<tr>
<td><code>lib.d</code></td>
<td>Shell and Perl scripts used by the <code>eotk</code> binary.</td>
</tr>
<tr>
<td><code>opt.d</code></td>
<td>Contains the Tor daemon, and OpenResty binaries.</td>
</tr>
<tr>
<td><strong><code>projects.d</code></strong></td>
<td>This is equivalent to your <code>/etc/nginx/sites-available/</code> folder. All of your site configuration, and TLS keys will be held here.</td>
</tr>
<tr>
<td><strong><code>secrets.d</code></strong></td>
<td>This directory exclusively holds the public-key pairs for your <code>.onion</code> domain. We will be copying our mined onion files here.</td>
</tr>
<tr>
<td><code>templates.d</code></td>
<td>Using in the building process of the <code>eotk</code> binary, as well as to generate project configuration files.</td>
</tr>
<tr>
<td><code>tools.d</code></td>
<td>Contains tools.</td>
</tr>
</tbody>
</table>
<p>Out of all of these directories, the most relevant are the <code>projects.d</code> and the <code>secrets.d</code> folders. These will contain the configuration files for your site, and the public key pair for your onion domain name. Every website you choose to make available as an onion site will have its own folder within <code>projects.d</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">projects.d
&#x251C;&#x2500;&#x2500; website1.com.d # Includes all subdomains of website1.com
&#x251C;&#x2500;&#x2500; website2.net.d
&#x2514;&#x2500;&#x2500; website3.org.d
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Example of the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>projects.d</span></code><span style="white-space: pre-wrap;"> directory. These folders are created automatically by the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>eotk</span></code><span style="white-space: pre-wrap;"> binary once we configure a project.</span></p></figcaption></figure><p>Now that our EOTK binary is built and available, we are ready to make our website available on Tor. </p><h3 id="understanding-the-eotk-binary-and-command-reference">Understanding the EOTK Binary and Command Reference</h3><p>The EOTK binary, available within its folder as <code>./eotk</code> is equivalent to our <code>nginx</code> command. Just as you might run <code>nginx -t</code> to test your nginx configuration file, you will run <code>./eotk -a</code> to check your EOTK configuration. However, it is important to note that you must always run EOTK locally, within the root folder (e.g. <code>~/eotk/eotk</code>, or <code>/etc/eotk/eotk</code>), because unlike the <code>nginx</code> executable, <code>./eotk</code> is <em>not</em> available globally. The entirety of our EOTK installation is self-contained within its folder, and we must always be inside that folder when we interact with it.</p><p>For a full listing of the available commands, run <code>./eotk -h</code>. Partial documentation for the command syntax is also available at <code>eotk/docs.d/EOTK-COMMAND-SYNTAX.md</code> (<a href="https://github.com/alecmuffett/eotk/blob/master/docs.d/EOTK-COMMAND-SYNTAX.md">Github link)</a>. For our purposes, the most relevant and common commands will be the following:</p><table>
<thead>
<tr>
<th><strong>Command</strong></th>
<th><strong>Explanation</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>./eotk config &lt;website.com.conf&gt;</code></td>
<td>Generates a Nginx configuration file from a given EOTK configuration file.</td>
</tr>
<tr>
<td><code>./eotk syntax &lt;project&gt;</code></td>
<td>Checks the configuration syntax of a given project. Analogous to <code>nginx -t</code>.</td>
</tr>
<tr>
<td><code>./eotk nxreload &lt;project&gt;</code></td>
<td>Reloads the configuration file of a given project. Analogous to <code>systemctl reload nginx</code>.</td>
</tr>
<tr>
<td><code>./eotk status &lt;project&gt;</code></td>
<td>Displays the status of a given project. To list the status of all projects, call with <code>-a</code>.</td>
</tr>
<tr>
<td><code>./eotk start &lt;project&gt;</code></td>
<td>Starts a given project. Analogous to linking a Nginx file from <code>/etc/nginx/sites-available</code> to <code>/etc/nginx/sites-enabled</code>.</td>
</tr>
<tr>
<td><code>./eotk restart &lt;project&gt;</code></td>
<td>Restarts a given project. Analogous to <code>systemctl restart nginx</code>.</td>
</tr>
<tr>
<td><code>./eotk stop &lt;project</code></td>
<td>Stops a given project. Analogous to <code>systemctl stop nginx</code>.</td>
</tr>
<tr>
<td><code>./eotk projects</code></td>
<td>Lists all projects.</td>
</tr>
</tbody>
</table>
<p>As you can see, there is a strong similarity between EOTK and our regular Nginx reverse proxy. And just like Nginx, every website (and its subdomains) begin as a configuration file. The websites that we wish to make available over Tor are <em>projects</em>, in EOTK&apos;s terminology. Given a website <code>example.com</code> with its subdomains <code>www.example.com</code>, <code>blog.example.com</code>, <code>portal.example.com</code>, we must create a EOTK configuration file called <code>example.com.conf</code>, within which we will configure all the details.</p><h3 id="creating-eotk-configuration-files-for-projects">Creating EOTK Configuration Files for Projects</h3><p>Within the main <code>~/eotk/</code> folder, create a <code>.conf</code> file with the name of your project. Your project can be anything, but I suggest naming it after the domain name of your clearnet website. I will use the domain name <code>example.com</code> for the purpose of our tutorial. Within the file, write:</p><figure class="kg-card kg-code-card"><pre><code>set project example.com</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> EOTK configuration file.</span></p></figcaption></figure><p>The <code>set project</code> directive is essential for EOTK to know which project this configuration file belongs to. EOTK will use the value we set to create a folder within <code>~/eotk/projects.d/</code>, such as <code>~/eotk/projects.d/example.com.d</code>. Save and exit out of that file for now, we are not done with it.</p><p>We must next configure a series of <code>hardmap</code> directives to map our <code>.onion</code> domain name to the clearnet domain and subdomains of our website. But before we can do that, we must install the cryptographic keys of our vanity onion domain name, which we just mined. At this point, hopefully you have already mined some memorable vanity onion domain names!</p><h3 id="installing-mined-vanity-onion-domain-names">Installing Mined Vanity Onion Domain Names</h3><p>Back on your local (and powerful) machine, you should stop <code>mkp224o</code> (Ctrl + C) and examine the <code>candidates/</code> directory. It should contain folders with the name <code>&lt;domain&gt;.onion/</code>, each one being a domain name which matches your filters. Select one which you wish to keep, and move the folder to a safe place.</p><p>Within it, you will find the following files:</p><pre><code>hostname
hs_ed25519_public_key
hs_ed25519_secret_key</code></pre><p>The <code>hostname</code> is a text file containing your <code>.onion</code> domain name (in case you forget it). The <code>hs_ed25519</code> files are the public key pairs of your domain. <strong>Make sure to protect these files</strong>. If you lose your <code>hs_ed25519_secret_key</code>, you will effectively lose control over your domain name. <strong>Do not under any circumstances lose or publish the <code>hs_ed25519_secret_key</code>. If an adversary obtains your private (secret key), they will be able to take over your domain name.</strong></p><p>We will need to upload both the public key, and the private key to our remote web server (where we installed EOTK). A simple and secure way to do this is to use <code>rsync</code>. On your local machine, where these files are located, run:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">rsync -a hs_ed25519_*_key username@remote:/path/to/destination</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command to upload file from local machine to remote.</span></p></figcaption></figure><p>This command will let <code>rsync</code> to upload both files to your remote machine to a destination of your choice. Once they are on your remote machine, we must rename them and move them to the <code>~/eotk/secrets.d</code> directory. </p><p><strong>Note: </strong>If you have been following this guide along with the EOTK project documentation (<code>~/eotk/docs.d/HOW-TO-INSTALL.md</code>, <a href="https://github.com/alecmuffett/eotk/blob/master/docs.d/HOW-TO-INSTALL.md">Github link</a>), please note that as of 2022-03-26 (<a href="https://github.com/alecmuffett/eotk/tree/e4a4ca429f64035451af563affdf673a940a2147">commit <code>e4a4ca4</code></a>), <strong>the </strong><a href="https://github.com/alecmuffett/eotk/blob/e4a4ca429f64035451af563affdf673a940a2147/docs.d/HOW-TO-INSTALL.md#slightly-harder-with-manually-mined-onions"><strong>instructions for installing pre-mined onion domain keys</strong></a><strong> are incorrect</strong>. Until the documentation is updated, follow the instructions given here.</p><p>We must take the <code>hs_ed25519_public_key</code> and <code>hs_ed25519_secret_key</code> files, and move them into the <code>~/eotk/secrets.d</code> directory, with the following names: </p><ul><li>Rename <code>hs_ed25519_public_key</code> to <code>&lt;domain&gt;.v3pub.key</code></li><li>Rename <code>hs_ed25519_secret_key</code> to <code>&lt;domain&gt;.v3sec.key</code></li></ul><p>The <code>&lt;domain&gt;</code> must be your onion domain name <strong>without the .onion</strong> suffix. For example, if the vanity onion domain of your site was <code>exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion</code>, then your <code>~/eotk/secrets.d</code> directory looks like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">secrets.d
&#x251C;&#x2500;&#x2500; exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.v3pub.key
&#x2514;&#x2500;&#x2500; exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.v3sec.key</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>~/eotk/secrets.d</span></code><span style="white-space: pre-wrap;">.</span></p></figcaption></figure><p>The <code>~/eotk/secrets.d/</code> directory will contain the public-key pairs of all of your onion domain names for all of your projects, in the forms of <code>&lt;domain&gt;.v3pub.key</code> and <code>&lt;domain&gt;.v3sec.key</code> files. If you wish to commission any future sites, simply mine the onion domain names with <code>mkp224o</code> and install them in <code>~/eotk/secrets.d</code>. We are now ready to go back to our project configuration file <code>~/eotk/example.com.conf</code>, and map our domain name to the project.</p><h3 id="configuring-hardmaps-for-eotk-projects">Configuring Hardmaps for EOTK Projects</h3><p>Move back to our main <code>~/eotk/</code> folder, and reopen the <code>example.com.conf</code> EOTK configuration file. We will now define a <code>hardmap</code> which maps the onion domain name to our clearnet domain. Fun fact: according to Alec Muffet, the main developer of EOTK, the reason that the mapping directive is called hardmap is due to reasons of backward compatibility:</p><blockquote>&apos;Hardmap&apos; originally was just &apos;map&apos; and expected a filename, and then &apos;softmap&apos; arrived and took just an onion address ... which is nicer; but I don&apos;t want to change the code very much, so if you are &apos;hardmap&apos; and specify only an onion address, let&apos;s fill in the filename and then continue by stripping it away again. Now we just clean it up and assume the key materials are in secrets.d; le huge sigh, hindsight is 20-20.<br><br>&#x2013; Alec Muffet, <code>~/eotk/lib.d/do_configure.pl</code> comments (<a href="https://github.com/alecmuffett/eotk/blob/7b3cb492fd86ad605c0d476cfaff1cd97df92af4/lib.d/do-configure.pl#L282-L290">Source</a>).</blockquote><p>The hardmap directive takes the following form:</p><pre><code>hardmap &lt;onion domain&gt; &lt;clearnet domain&gt; &lt;subdomains&gt;</code></pre><p>Where the <code>&lt;onion domain&gt;</code> is the domain name (including the <code>.onion</code> TLD) of your newly mined vanity domain name. Hence, our project configuration file will look like:</p><figure class="kg-card kg-code-card"><pre><code>set project example.com

hardmap exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion example.com</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> EOTK configuration file. With hardmap to </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com</span></code><span style="white-space: pre-wrap;">, and no subdomains.</span></p></figcaption></figure><p>Subdomains are listed after the clearnet domain, seperated by whitespace. If our <code>example.com</code> website has <code>www</code>, <code>blog</code>, and <code>portal</code> as subdomains, then our <code>example.com.conf</code> file will look like the following:</p><figure class="kg-card kg-code-card"><pre><code>set project example.com

hardmap exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion example.com www blog portal</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> EOTK configuration file. With hardmap to </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com</span></code><span style="white-space: pre-wrap;">, and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>www</span></code><span style="white-space: pre-wrap;">, </span><code spellcheck="false" style="white-space: pre-wrap;"><span>blog</span></code><span style="white-space: pre-wrap;">, and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>portal</span></code><span style="white-space: pre-wrap;"> as subdomains.</span></p></figcaption></figure><p>An individual project configuration file may contain multiple hardmap directives, potentially mapping multiple different .onion domain names to the same, or different clearnet domain names. The <code>~/eotk/demo.d</code> directory (<a href="https://github.com/alecmuffett/eotk/tree/master/demo.d">Github link</a>) contains multiple example configuration files. In particular, the <code>wikipedia.conf</code> file has an example with multiple hardmaps.</p><p>The above <code>example.com.conf</code> EOTK configuration file is now sufficient for us to bring our onion site online. </p><h3 id="activating-deactivating-and-reloading-eotk-configuration">Activating, Deactivating, and Reloading EOTK configuration</h3><p>We will now generate our example.com project from the <code>example.com.conf</code> file. This is where the magic happens. Run the following:</p><pre><code class="language-Shell">./eotk config example.com.conf</code></pre><p>The EOTK binary will now parse the provided project configuration file, and setup the project. It will do the following:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/eotk-1-1-.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="2000" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/eotk-1-1-.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/eotk-1-1-.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/eotk-1-1-.png 1600w, https://shen.hong.io/content/images/2022/03/eotk-1-1-.png 2172w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Diagram showing what the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>./eotk config example.com.conf</span></code><span style="white-space: pre-wrap;"> command does. When we execute the command, EOTK generates a </span><code spellcheck="false" style="white-space: pre-wrap;"><span>tor.conf</span></code><span style="white-space: pre-wrap;"> and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>nginx.conf</span></code><span style="white-space: pre-wrap;"> file for us, then uses them to configure our project-specific EOTK Tor and Nginx daemons.</span></figcaption></figure><ul><li>EOTK will create a directory in <code>~/eotk/projects.d</code> named <code>example.com.d</code>, which will contain all our project-specific configuration, and startup/shutdown scripts.</li><li>EOTK will instantiate a project-specific Nginx instance, with an autogenerated Nginx configuration file at <code>~/eotk/projects.d/example.com.d/nginx.conf</code>. This configuration file will contain the OpenResty rewriting rules that &quot;translate&quot; our clearnet site to an onion site, and vice versa.</li><li>EOTK will configure the tor daemon service with the provided onion domain keyfiles, to create a hardmap to our EOTK Nginx instance. It will autogenerate a Tor configuration file at <code>~/eotk/projects.d/example.com.d/tor.conf</code>. Incoming Tor Network requests for our onion site will be handed over to the EOTK Nginx instance, translated, and passed through to our regular Nginx instance (and vice versa).</li></ul><p>Now our <code>example.com</code> project is ready! To start it, all we have to do is to run:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">./eotk start example.com</code></pre><figcaption><p><span style="white-space: pre-wrap;">Starting the example.com project</span></p></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-16-41-53.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1252" height="922" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-26-16-41-53.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-26-16-41-53.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-16-41-53.png 1252w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Running </span><code spellcheck="false" style="white-space: pre-wrap;"><span>./eotk configure example.com.conf</span></code><span style="white-space: pre-wrap;">, starting, and checking the status of the resulting </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com</span></code><span style="white-space: pre-wrap;"> project.</span></figcaption></figure><p>And our project is live! All you have to do is to navigate to the resulting <code>.onion</code> URL in the Tor Browser, and after accepting the self-signed certificate warning, you will be able to see the site. In the case of our tutorial, I actually deployed a real onion mirror for the IANA <a href="https://example.com/">Example.com</a> site. If you visit the following link on the Tor Browser, you can see that it is live!</p><pre><code>https://exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/</code></pre><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-16-45-59.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1188" height="854" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-26-16-45-59.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-26-16-45-59.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-16-45-59.png 1188w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Tor Browser screenshot of my example.com mirror. (</span><a href="https://exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/"><span style="white-space: pre-wrap;">Onion link</span></a><span style="white-space: pre-wrap;">)</span></figcaption></figure><h3 id="but-wait-how-did-you-deploy-an-onion-mirror-for-iana%E2%80%99s-examplecom">But Wait, How Did You Deploy an Onion Mirror for IANA&#x2019;s Example.com?</h3><p>Recall that EOTK functions by essentially running a tor service, in conjunction with a Nginx rewriting proxy. This makes EOTK analogous to an open proxy server which translates requests between a Tor Network client, and any remote host. You may in fact point EOTK to any clearnet website, and it will be able to translate requests from your hardmapped onion domain name to the clearnet domain.</p><p>In practice, you should almost <em>never</em> deploy EOTK to websites or web applications that you do not own. For one, an EOTK installation on a different host from the clearnet service will achieve lower performance, as proxied requests must traverse through the open internet, instead of only internally. Secondly, your EOTK server will function effectively as an open proxy for Tor users, and you will be responsible for any malicious traffic which passes through your EOTK deployment. In fact, your server will be the <em>source</em> of all Tor traffic, malicious or otherwise. And of course, it will be impossible to find the ultimate source of any malicious Tor traffic, since the very nature of the Tor Network makes Tor clients anonymous.</p><p>Dealing with potentially malicious Tor traffic is an important consideration with any EOTK deployment. In particular, we would want to protect the administrative portions of our website, and render them inaccessible via Tor. We will discuss how to accomplish this with EOTK in the following section.</p><h3 id="whitelisting-blacklisting-and-access-control-with-eotk">Whitelisting, Blacklisting, and Access Control with EOTK</h3><p>Our website (<code>example.com</code>) is now accessible on its onion domain over the Tor Network. EOTK seamlessly and transparently translates requests back and forth between Tor Network users and our webserver. However, oftentimes we do not wish to make the entire functionality of our website available over Tor. For example, we may not want to expose our admin login portal to the Tor Network, as it will be impossible to ban malicious login attempts. There may be certain subdomains that contain functionality (typically relating to <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">CORS</a> (cross-origin resource sharing) that will not function once rewritten.</p><p>EOTK comes with a set of whitelisting and blacklisting directives that will allow us to selectively manage access to our site from the EOTK project configuration file (e.g. <code>example.com.conf</code>). EOTK provides three different types of syntax for access control,:</p><ul><li><strong>Blacklist:</strong> Denies access to the specified resources. Blocked resources will return a <code>500 Internal Server Error</code> response code.</li><li><strong>Whitelist: </strong>Allows access <em>only</em> to the specified resources. If you add whitelist directives, all requests that do not fit the whitelist will be rejected with a <code>500 Internal Server Error</code> response code.</li><li><a href="https://github.com/alecmuffett/eotk/blob/e4a4ca429f64035451af563affdf673a940a2147/demo.d/example.tconf#L84-L107"><strong>Block</strong></a><strong>: </strong> Rejects specified resources with a <code>403 Access Forbidden</code> response code. They allow the admin to specify a <code>block_err</code> message which will be displayed on the blocked request. However, <strong>block directives can only accept a single value.</strong></li></ul><p>I don&apos;t recommend using block directives, as the EOTK project maintainer states:</p><blockquote>The [block] variables are single-valued so be careful of polluting multiple projects.  If your site needs different blocking for different onions, consider splitting your config into multiple files and using &quot;foreignmap&quot; to stitch the hostname rewrites together.<br><br>&#x2013; Alec Muffet, <code>~/eotk/demo.d/example.tconf</code> comments (<a href="https://github.com/alecmuffett/eotk/blob/e4a4ca429f64035451af563affdf673a940a2147/demo.d/example.tconf#L86-L91">Source</a>).</blockquote><p>Instead, we will use the blacklist and whitelist directives, which perform the same function, and as they take multiple values, are much more powerful.</p><p>Block, blacklist, and whitelist directives allow the administrator to specify matches based upon multiple criteria. The following three are probably the most useful and relevant:</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host"><strong>Host</strong></a><strong>:</strong> A HTTP Header. The requested domain name or subdomain. We will specify host directives if we want to whitelist/blacklist domain names or subdomains.</li><li><a href="https://en.wikipedia.org/wiki/Query_string"><strong>Parameter</strong></a><strong>:</strong> The HTTP query string (i.e. what comes after a <code>?</code> symbol in the requested URL.</li><li><strong>Path:</strong> The path of the requested resource.</li></ul><p>Furthermore, EOTK also provides the additional criteria, which are probably less applicable.</p><ul><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin"><strong>Origin</strong></a><strong>:</strong> A HTTP Header. The origin of the request. This is rarely useful, unless you are working with CORS, e.g. loading resources over a onion CDN.</li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer"><strong>Referer</strong></a><strong>:</strong> A HTTP Header. This is rarely useful for the purpose of access control, since the Referrer header is easily spoofed.</li><li><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent"><strong>User-Agent</strong></a><strong>:</strong> The User-Agent. This is likewise rarely useful, since the User-Agent can be easily spoofed.</li></ul><p>All of these directives also have an alternate form with the <code>_re</code> suffix, which accepts <a href="https://en.wikipedia.org/wiki/Regular_expression">regular expressions</a> as a matching parameter.</p><p>In summary, the EOTK blacklist and whitelist access control directives are:</p><h4 id="eotk-blacklist-directives">EOTK Blacklist Directives</h4><table>
<thead>
<tr>
<th><strong>Directive</strong></th>
<th><strong>Explanation</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>set host_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on host header.</td>
</tr>
<tr>
<td><code>set host_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on host header (regular expression).</td>
</tr>
<tr>
<td><code>set param_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on parameter/query string.</td>
</tr>
<tr>
<td><code>set param_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on parameter/query string. (regular expression).</td>
</tr>
<tr>
<td><code>set path_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on path.</td>
</tr>
<tr>
<td><code>set path_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on path (regular expression).</td>
</tr>
<tr>
<td><code>set origin_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on origin header.</td>
</tr>
<tr>
<td><code>set origin_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on origin header (regular expression).</td>
</tr>
<tr>
<td><code>set referer_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on referer header.</td>
</tr>
<tr>
<td><code>set referer_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on referer header (regular expression).</td>
</tr>
<tr>
<td><code>set user_agent_blacklist &lt;value&gt;</code></td>
<td>Blocks request based on user agent header.</td>
</tr>
<tr>
<td><code>set user_agent_blacklist_re &lt;regexp&gt;</code></td>
<td>Blocks request based on user agent header (regular expression).</td>
</tr>
</tbody>
</table>
<h4 id="eotk-whitelist-directives">EOTK Whitelist Directives</h4><table>
<thead>
<tr>
<th><strong>Directive</strong></th>
<th><strong>Explanation</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>set host_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with host header.</td>
</tr>
<tr>
<td><code>set host_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with host header (regular expression).</td>
</tr>
<tr>
<td><code>set param_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with the parameter/query string.</td>
</tr>
<tr>
<td><code>set param_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with the parameter/query string (regular expression).</td>
</tr>
<tr>
<td><code>set path_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with the path.</td>
</tr>
<tr>
<td><code>set path_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with the path (regular expression).</td>
</tr>
<tr>
<td><code>set origin_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with the origin header.</td>
</tr>
<tr>
<td><code>set origin_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with the origin header (regular expression).</td>
</tr>
<tr>
<td><code>set referer_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with the referer header.</td>
</tr>
<tr>
<td><code>set referer_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with the referer header (regular expression).</td>
</tr>
<tr>
<td><code>set user_agent_whitelist &lt;value&gt;</code></td>
<td>Accepts requests <em>only</em> with the user agent header.</td>
</tr>
<tr>
<td><code>set user_agent_whitelist_re &lt;regexp&gt;</code></td>
<td>Accepts requests <em>only</em> with the user agent header (regular expression).</td>
</tr>
</tbody>
</table>
<p>Keep in mind that all of the above whitelist and blacklist directives accept multiple values, separated by a whitespace. Value lists can also be split over multiple lines using the <code>\</code> character. With the above directives as a reference, let us take a look at an example configuration.</p><h4 id="example-access-control-configuration-for-examplecom">Example Access Control Configuration for Example.com</h4><p>Let us go through a practical example of using the EOTK access control directives to limit our website&apos;s exposure over Tor. Given our <code>example.com</code> project, let us suppose our website exposes the following sensitive functionality:</p><ul><li>An admin portal at <code>admin.example.com</code> and <code>remote.example.com</code></li><li>A staff-only login page at <code>example.com/login/</code></li><li>A rate-limited API at <code>example.com/api/</code></li></ul><p>We can <code>blacklist</code> all of the above by writing the following in our EOTK configuration file. Note that we may split parameters over separate lines using the forward slash (escape) character: <code>\</code>:</p><figure class="kg-card kg-code-card"><pre><code>set project example.com

set host_blacklist \
admin.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion \
remote.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion

set path_blacklist /login/ /api/

hardmap exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion example.com www blog portal</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> EOTK configuration file. Using access control directives</span></p></figcaption></figure><p>Observe carefully how the <code>set host_blacklist</code> parameter <strong>must accept full host strings</strong>, in the form of <code>&lt;subdomain&gt;.&lt;onion domain&gt;.onion</code>. As the blacklist functions based on matches from the <code>Host</code> header in the incoming request, specifying only the subdomain <em>will not work</em>. This was an unexpected source of confusion when I implemented hong.io&apos;s onion mirror.</p><figure class="kg-card kg-code-card"><pre><code># set host_blacklist admin             # WILL NOT WORK
# set host_blacklist admin.example.com # WILL NOT WORK

# Only the following works
set host_blacklist admin.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion
</code></pre><figcaption><p><span style="white-space: pre-wrap;">Illustration of a confusing idiosyncrasy in how we must specify </span><code spellcheck="false" style="white-space: pre-wrap;"><span>host_blacklist</span></code><span style="white-space: pre-wrap;"> (and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>host_whitelist</span></code><span style="white-space: pre-wrap;"> directives.</span></p></figcaption></figure><p>After making these changes, we must regenerate our configuration by running <code>./eotk configure &lt;filename.conf&gt;</code>, and then reload the built-in Nginx server in EOTK using <code>./eotk nxreload &lt;project name&gt;</code>:</p><figure class="kg-card kg-code-card"><pre><code>./eotk configure example.com.conf
./eotk nxreload example.com</code></pre><figcaption><p><span style="white-space: pre-wrap;">Regenerate our newly modified </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> file, and then reload the project.</span></p></figcaption></figure><p>Be sure to not forget to <code>configure</code> and then <code>nxreload</code> every time you make changes in your project configuration file. Otherwise, whatever modifications you have made will not take affect! Likewise, carefully note that the <code>./eotk nxreload</code> command takes the <em>project name</em> as its argument, <em>not</em> the configuration file name. The project name is defined by the <code>set project &lt;projectname&gt;</code> directive in your configuration file!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-00-04-34.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1252" height="922" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-27-00-04-34.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-27-00-04-34.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-00-04-34.png 1252w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Regenerating, reloading, and checking the status on our project in EOTK.</span></figcaption></figure><p>Now that we have implemented blacklist directives for our mirror, whenever a Tor Network client attempts to visit one of the specified resources, EOTK will reject the request with a <code>500 Internal Server Error</code> response code. This will yield the following error page:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-20-03-10.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1190" height="854" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-26-20-03-10.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-26-20-03-10.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-26-20-03-10.png 1190w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example of a Tor Network client attempting to visit a blacklisted resource.</span></figcaption></figure><p>You may attempt to visit those links yourself. Open the Tor Browser, and try navigating to the following addresses. They will all result in a <code>500 Internal Server Error</code> response!</p><figure class="kg-card kg-code-card"><pre><code>https://admin.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/
https://remote.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/
https://exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/login/
https://exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion/api/

</code></pre><figcaption><p><span style="white-space: pre-wrap;">Examples of blacklisted resources from example.com&apos;s onion mirror.</span></p></figcaption></figure><p>As per the reason why blacklisted resources return a <code>500 Internal Server Error</code> response code, we may view Alex Muffet&apos;s explanation, located once again as a comment in the EOTK git repository:</p><blockquote>[Access] failures are generally 500 because it presents the least attack surface to a penetration tester.<br><br>&#x2013; Alec Muffet, <code>~/eotk/demo.d/example.tconf</code> comments (<a href="https://github.com/alecmuffett/eotk/blob/e4a4ca429f64035451af563affdf673a940a2147/demo.d/example.tconf#L114-L116">Source</a>).</blockquote><h3 id="host-based-whitelisting-and-allowing-only-http-get-requests">Host-based whitelisting and Allowing only HTTP GET Requests</h3><p>In its default configuration, EOTK naively forwards <em>all</em> requests with the specified hardmap domain name, regardless of subdomain. When we configure our example.com website with the hardmap directive:</p><pre><code>hardmap exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion example.com www blog portal</code></pre><p>We would have expected only requests to <code>example.com</code>, and the subdomains <code>www</code>, <code>blog</code>, and <code>portal</code> be forwarded on to our web application. However, in my testing, this was not the case &#x2013; but instead, EOTK translates all requests made to <code>*.example.com</code>. Hence, all services available as a subdomain under <code>example.com</code> will be available as an onion site on the Tor Network &#x2013; even if these services are hosted on entirely different servers.</p><p>This default behaviour may be undesirable, particularly if you only intend to expose one service as an onion site. In order to prevent this behaviour, we must explicitly whitelist subdomains using the <code>host_whitelist</code> directive. Here is a modified <code>example.com.conf</code> file which uses the <code>host_whitelist</code> directive:</p><figure class="kg-card kg-code-card"><pre><code>set project example.com

set host_whitelist \
exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion \
www.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion \
blog.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion \
portal.exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion

set path_blacklist /login/ /api/

set suppress_methods_except_get 1

hardmap exmple27tdg5nesxaao6khfuk5ac723n7tpqkzhyytnjm2ltt6tgkhyd.onion example.com www blog portal</code></pre><figcaption><p><span style="white-space: pre-wrap;">Contents of </span><code spellcheck="false" style="white-space: pre-wrap;"><span>example.com.conf</span></code><span style="white-space: pre-wrap;"> EOTK configuration file. Explicitly whitelisting hosts.</span></p></figcaption></figure><p>Make sure that the list of whitelisted hosts matches the list of subdomains in the <code>hardmap</code> directive. Finally, do not forget to add the root (subdomain-less) host to the whitelist as well!</p><p>There is one final EOTK directive that is relevant for some use-cases. On line 9, we added <code>set suppress_methods_except_get 1</code>. This effectively makes our onion site a read-only mirror of our clearnet website. It informs EOTK to only allow HTTP GET requests. This will disable any functionality on the website which takes user input, such as search bars, file uploads, and form submissions. This directive will only make sense if your website does not require any server-side interaction at all for Tor Network clients.</p><p>In my use case, I know for certain that the administrative interfaces of my blog will never be accessed over the Tor Network. Hence, I set <code>suppress_methods_except_get</code> to <code>1</code> for my blog. </p><p>No matter your choices, remember to run <code>./eotk config &lt;filename.conf&gt;</code> on your configuration file, followed by <code>./eotk nxreload &lt;projectname&gt;</code>. Now your changes are live!</p><h3 id="other-eotk-configuration-options">Other EOTK Configuration Options</h3><p>As of the time of this article&apos;s writing (2022-03-28), the EOTK configuration file documentation is incomplete. A listing and partial explanation of the different options can be found in the <code>~/eotk/demo.d/example.tconf</code> file (<a href="https://github.com/alecmuffett/eotk/blob/master/demo.d/example.tconf#L114-L116">Github link</a>). I have gone through the directives with the most common use-cases in this article, but I highly encourage any administrator to review the <code>example.tconf</code> file for further information.</p><p>Some interesting, and potentially useful options that this article does not cover include:</p><ul><li>Injecting request headers for upstream use.</li><li>Managing the rewriting process, mangling SRI tags.</li><li>Caching requests, redirects, and Nginx tuning.</li></ul><p>Note: Do not worry about the TLS-related directives for now. This tutorial will also present a complete guide to purchasing, and installing a TLS certificate later.</p><h3 id="activating-eotk-as-a-systemd-service-and-starting-automatically-on-boot">Activating EOTK as a Systemd Service and Starting Automatically on Boot</h3><p>By following the above instructions, you should have a secure, nearly-production-ready Onion version of your website, accessible over a custom vanity <code>.onion</code> domain over the Tor Network. We must make sure that our EOTK service runs automatically as a <a href="https://systemd.io/">Systemd daemon</a>, which will ensure that it always starts up when our server boots, and restarts if the service stops or crashes. </p><p>We do this by generating, and then installing the EOTK startup scripts. These scripts are generated with each installation, because they contain hardcoded paths to our EOTK binary. To generate them, simply run:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">./eotk make-scripts</code></pre><figcaption><p><span style="white-space: pre-wrap;">Generate installation-specific startup and housekeeping scripts</span></p></figcaption></figure><p>This will create the <code>eotk-init.sh</code> and <code>eotk-housekeeping.sh</code> files in the same location as your <code>./eotk</code> binary. The <code>eotk-init.sh</code> file is a <a href="https://en.wikipedia.org/wiki/Init#SysV-style">System V-style init script</a> which we can move to the <code>/etc/init.d</code> directory, and then convert it automatically into a Systemd-style service unit. To do this, run:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">cp eotk-init.sh /etc/init.d
update-rc.d eotk-init.sh defaults</code></pre><figcaption><p><span style="white-space: pre-wrap;">Installing the System V style init script for EOTK</span></p></figcaption></figure><p>Now, all we have to do is to <code>start</code> it via Systemd.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">systemctl start eotk
systemctl status eotk</code></pre><figcaption><p><span style="white-space: pre-wrap;">Start the daemonised EOTK service.</span></p></figcaption></figure><p>The status for the <code>eotk-init.service</code> should show as <code>active (running)</code> with a green status semaphore. Now we are certain that our EOTK installation will autostart on boot, and always be available. We can still manage our specific EOTK projects using <code>./eotk start &lt;project&gt;</code> or <code>./eotk stop &lt;project&gt;</code> within the <code>/eotk</code> directory, and our workflow for updating project-specific configuration files is the same.</p><p>Note that we do not do <code>systemctl enable eotk</code> as we might with a native Systemd service unit. <code>start</code>-ing the service is enough to have it autostart on boot, since the EOTK is using a System V-style init script which is only managed by Systemd through a compatibility layer.</p><p>Our EOTK installation is almost production-ready. We only have one last task left, which is purchasing, and installing a TLS certificate.</p><h2 id="purchasing-and-installing-a-trusted-tls-certificate">Purchasing, and Installing a Trusted TLS Certificate</h2><p>By now, your website&apos;s <code>.onion</code> mirror is nearly production-ready. However, we must address one final issue. EOTK creates <a href="https://en.wikipedia.org/wiki/Self-signed_certificate">self-signed TLS certificates</a> by default, which are not trusted by the Tor Browser. When any visitor visits your website&apos;s <code>.onion</code> link, they will be greeted with a prominent certificate warning. This warning persists upon every restart of the Tor Browser, even if the user accepts to continue.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-00-31-37.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1842" height="1108" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-27-00-31-37.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-27-00-31-37.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-from-2022-03-27-00-31-37.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-00-31-37.png 1842w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Tor Browser&apos;s self-signed TLS certificate warning. Our onion sites will prominently display this warning on every visit, unless we purchase and install a properly signed TLS certificate from a trusted Certificate Authority.</span></figcaption></figure><h4 id="self-signed-tls-certificates-break-cross-domain-resource-loading">Self-Signed TLS Certificates Break Cross-Domain Resource Loading</h4><p>The warnings prompted by the self-signed certificate are not just cosmetic in nature, but can signifcantly break site functionality. If your website loads resources for any subdomain (such as <code>static.example.com</code>), then resources from the subdomain will not load unless the user manually navigates to the subdomain, and adds an exception. This means that an onion site mirror with self-signed certificates can present a broken experience, with images, scripts, and resources not loading at all.</p><p>Hence, for any production-quality onion mirror, we must deploy a signed TLS certificate for use with EOTK.</p><h3 id="there-are-only-two-options-for-trusted-tls-certificates">There Are Only Two Options for Trusted TLS Certificates</h3><p>Unfortunately, as of the time of this article&apos;s writing (March 2022), <a href="https://letsencrypt.org/">LetsEncrypt</a> <a href="https://tor.stackexchange.com/a/21761">does not support</a> TLS certificates for <code>.onion</code> domain names. Hence, we must purchase a commercial TLS certificate. As of March 2022, there are only two commercial certificate vendors which provide TLS certificates for <code>.onion</code> domain names:</p><ul><li><strong>DigiCert</strong>: Offers only <a href="https://en.wikipedia.org/wiki/Extended_Validation_Certificate">Extended Validation (EV)</a> Certificates (<a href="https://www.digicert.com/blog/ordering-a-onion-certificate-from-digicert">Link to Press Release</a>)</li><li><a href="https://www.harica.gr/"><strong>HARICA</strong></a>: Offers <a href="https://en.wikipedia.org/wiki/Domain-validated_certificate">Domain Validation (DV)</a> Certificates, including wildcard Certificates (<a href="https://news.harica.gr/article/onion_announcement/">Link to Press Release</a>).</li></ul><p><a href="https://www.digicert.com/">DigiCert</a> only offers EV certificates, which must be purchased via an order form, and are only available to registered organisations. Although the cost is not stated, EV certificates generally cost thousands of dollars per year. This makes DigiCert generally inaccessible to small websites and hobbyists like myself.</p><p>The only vendor of DV certificates is <a href="https://www.harica.gr/">HARICA</a>, which is a much more accessible and affordable option. HARICA has been <a href="https://blog.torproject.org/tls-certificate-for-onion-site/">featured on the Tor Project&apos;s blog</a> as an option for <code>.onion</code> TLS certificates, and we will be using them for this tutorial. <em>Note: I do not have any affiliation with HARICA, and this article is not sponsored by them in any way.</em></p><h3 id="about-the-harica-certificate-authority">About the HARICA Certificate Authority</h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/image.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="543" height="100"><figcaption><span style="white-space: pre-wrap;">The HARICA Certificate Authority Logo.</span></figcaption></figure><p>HARICA is a Greek-based Certificate Authority founded by a <a href="https://www.harica.gr/en/About/About">non-profit civil organisation</a>. HARICA is an acronym which stands for the Hellenic Academic and Research Institutions Certification Authority. They seem to be active in the Greek academic sector and work with Greek Universities. Although there are not any choices, I felt comfortable about using HARICA as a certificate authority, because they come from a familiar academic background.</p><h3 id="purchasing-a-onion-tls-certificate-from-harica">Purchasing a Onion TLS Certificate from HARICA</h3><p>I&apos;ll take us on a quick walkthrough of the process it takes to purchase, validate, and install a TLS certificate from HARICA. I have documented this process in unusual detail, because it was entirely unfamiliar to me. I&apos;m young enough that I only ever used <a href="https://letsencrypt.org/">Lets Encrypt</a>, so I never had the experience of purchasing and deploying a commercial TLS certificate &#x2013; I grew up using Let&apos;s Encrypt&apos;s <code>certbot</code> tool. For other hobbyist web admins who are likewise unfamiliar with the certificate purchasing process, I hope the following guide will be helpful.</p><p>All screenshots were taken on March 2022.</p><p>First, the user must navigate to <a href="https://www.harica.gr/">HARICA&apos;s website</a>, and register for an account. Once registration is complete, you will be directed to login to the HARICA CertManager (<a href="https://cm.harica.gr/Login?ReturnUrl=%2F">direct link</a>). Before you begin, make sure to check your settings and enable <a href="https://en.wikipedia.org/wiki/Multi-factor_authentication">Two Factor Authentication</a> (2FA), which is good practice for any important service.</p><p>Next, you&apos;ll want to create a Certificate Request for a &quot;Server Certificate,&quot; which is HARICA&apos;s term for a server-side TLS certificate. It will present a screen where you can add domain names. Simply paste in the <code>.onion</code> domain name directly. You can add additional domain names, as well as wildcard domains:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-23-43-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-23-43-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-23-43-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-23-43-Harica-CertManager---Server-Certificates.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-23-43-Harica-CertManager---Server-Certificates.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Step 1: Request TLS certificates for the onion domain and subdomains. Keep in mind that wildcard certificates are also supported!</span></figcaption></figure><p>HARICA does not seem to have a public pricing page. Prices are displayed in the requesting process. As of March 2022, the pricing for DV TLS Certificates appear to be the following:</p><ul><li>DV Certificate: &#x20AC;30.00 EUR per year.</li><li>DV Wildcard Certificate: &#x20AC;150.00 EUR per year</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-24-20-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-24-20-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-24-20-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-24-20-Harica-CertManager---Server-Certificates.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-24-20-Harica-CertManager---Server-Certificates.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Select &quot;Domain Validated&quot; (DV) for Certificate Type.</span></figcaption></figure><p>They offer an additional 10% discount for every additional year, up to 4 years (30% discount).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-24-33-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-24-33-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-24-33-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-24-33-Harica-CertManager---Server-Certificates.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-24-33-Harica-CertManager---Server-Certificates.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Choose the certificate duration.</span></figcaption></figure><p>Next, you&apos;ll be asked to either generate, or submit a <a href="https://en.wikipedia.org/wiki/Certificate_signing_request">Certificate Signing Request (CSR)</a>. This is a text file which contains the information about your certificate, such as the Common Name, Organisational Name, et cetera. These were the questions that Let&apos;s Encrypt&apos;s <code>certbot</code> tool would ask, when you provision a new certificate. We will select the <em>Auto-generate CSR</em> option. This will instruct HARICA to generate a CSR in your browser, using a private key for your certificate that it will also create.</p><p>For the private key, it is important to <strong>choose </strong><a href="https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm"><strong>ECDSA</strong></a><strong> as your algorithm, and <code>384</code> as your key size.</strong> These are the most secure options, followed by <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA</a> with <code>4096</code> key size. Between ECDSA and RSA, you should always choose ECDSA which is the more secure option, especially since you don&apos;t have to worry about backward-compatibility as all recent versions of Tor Browser support ECDSA.</p><p>You will also be prompted for a passphrase used to encrypt your certificate&apos;s private key. This is similar to the kind of passphrase that you might set for your SSH key. Set a long passphrase, and keep it safe &#x2013; we will need it later when we install these certificates.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-29-11-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1201" height="935" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-29-11-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-29-11-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-29-11-Harica-CertManager---Server-Certificates.png 1201w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Select the certificate type. For the algorithm you should either use ECDSA with a </span><code spellcheck="false" style="white-space: pre-wrap;"><span>384</span></code><span style="white-space: pre-wrap;"> bit key size, or RSA with an </span><code spellcheck="false" style="white-space: pre-wrap;"><span>4096</span></code><span style="white-space: pre-wrap;"> bit key size. For maximum security, go with ECDSA and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>384</span></code><span style="white-space: pre-wrap;"> bit.</span></figcaption></figure><p>Your browser will freeze for a few seconds, as it generates the CSR and a private key. Then, you will be prompted to download your private key, as a <code>privateKey.pem</code> file. Make sure to save this file &#x2013; we will need it when we install the certificate into EOTK. <strong>Protect your private key carefully &#x2013; losing it will allow your certificate to be compromised!</strong></p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-37-32-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-37-32-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-37-32-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-37-32-Harica-CertManager---Server-Certificates.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-37-32-Harica-CertManager---Server-Certificates.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Download and save CSR Private Key. Keep this file, and the passphrase safe &#x2013; you will need them later when we install the signed TLS certificate into EOTK.</span></figcaption></figure><p>We have now successfully submitted a request for a DV TLS Certificate. Now we must prove to HARICA that we own and control the <code>.onion</code> domain name. This is the next step.</p><h3 id="obtaining-an-onion-domain-proof-of-ownership">Obtaining an Onion Domain Proof of Ownership</h3><p>When using Let&apos;s Encrypt, domain ownership is proven automatically when <code>certbot</code> places a file in the <code>./well-known</code> (defined by the <a href="https://datatracker.ietf.org/doc/html/rfc5785">RFC 5785</a> IETF standard). With HARICA, the process is a little different. You may either manually place a file in a similar location, which is the <em>Validate via &quot;Agreed-Upon Change to Website,</em>&quot; or use the <em>Validate via &quot;Onion CSR&quot; </em>option.</p><p>We will go with the second <em>Validate via &quot;Onion CSR&quot;</em> option. This is because if you are attempting to purchase a certificate with multiple subdomains, the first validation option will not be available anyways. Likewise, the first option is not available for wildcard certificates. Finally, it requires making modifications to our EOTK project config (e.g. <code>~/eotk/example.com.conf</code>) which may be annoying, if you have multiple projects.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-38-46-Harica-CertManager---Server-Certificates-1.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1201" height="940" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-38-46-Harica-CertManager---Server-Certificates-1.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-38-46-Harica-CertManager---Server-Certificates-1.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-38-46-Harica-CertManager---Server-Certificates-1.png 1201w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Choose &quot;Onion CSR&quot; as the method of domain validation.</span></figcaption></figure><p>When we do this, we will be asked to confirm and submit our  validation request. Then, we must <a href="https://github.com/HARICA-official/onion-csr">download and compile a tool</a> called <code>onion-csr</code> from the official HARICA Github repository.</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-39-14-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-39-14-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-39-14-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-39-14-Harica-CertManager---Server-Certificates.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-39-14-Harica-CertManager---Server-Certificates.png 1907w" sizes="(min-width: 720px) 720px"></figure><p>We will do this in the following section.</p><h4 id="cloning-and-building-haricas-onion-csr-tool">Cloning and Building HARICA&apos;s Onion CSR Tool</h4><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/HARICA-official/onion-csr"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - HARICA-official/onion-csr: Tool to create CSRs signed with Tor Hidden Service private keys</div><div class="kg-bookmark-description">Tool to create CSRs signed with Tor Hidden Service private keys - GitHub - HARICA-official/onion-csr: Tool to create CSRs signed with Tor Hidden Service private keys</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">HARICA-official</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/25f6618369b8256eee50b62800f7f09166b6a0abffe8e809e8d3f7c30480f1c9/HARICA-official/onion-csr" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit"></div></a><figcaption><p><span style="white-space: pre-wrap;">Link to the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>onion-csr</span></code><span style="white-space: pre-wrap;"> tool from HARICA</span></p></figcaption></figure><p>We must download and build the <code>onion-csr</code> tool. We must do this on the remote machine, where we run EOTK. This is a source-available Ruby script which essentially creates and signs a CSR using the private key of your <code>.onion</code> domain name. By signing the CSR using your <code>.onion</code> domain&apos;s private key, HARICA can confirm that you possess ownership over the domain by verifying it using the domain&apos;s public key.</p><p>The entirety of <code>onion-csr</code>&apos;s source code is located in the <code>onion-csr.rb</code> file (<a href="https://github.com/HARICA-official/onion-csr/blob/master/onion-csr.rb">Github link</a>). The code is only 100 lines long, and is understandable enough to be audited and read. The instructions for using the tool are included in the <code>README.md</code> file of the repository (<a href="https://github.com/HARICA-official/onion-csr/blob/master/README.md">Github link</a>), but I will present the process here in this guide as well.</p><p>First, we install dependencies for the <code>onion-csr</code> tool. This is only the ruby intepreter, and some compilation dependencies. We will need <code>build-essential</code> because we will build the ED25519 library from the original C source code.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">apt install ruby build-essential</code></pre><figcaption><p><span style="white-space: pre-wrap;">Install dependencies for HARICA&apos;s onion-csr tool</span></p></figcaption></figure><p>Fun fact: the ED25519 library that we will be building is actually the same library that was used by <code>mkp224o</code> from earlier! HARICA&apos;s <code>onion-csr</code> repository includes it as a submodule which will also be downloaded when we clone the repository. The ED25519 library can be found here (<a href="https://github.com/orlp/ed25519/tree/7fa6712ef5d581a6981ec2b08ee623314cd1d1c4">Github link</a>), and if desired the code can be audited as well.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">git clone --recurse-submodules https://github.com/HARICA-official/onion-csr.git
cd onion-csr
gem install ffi
gcc -shared -o libed25519.so -fPIC ed25519/src/*.c</code></pre><figcaption><p><span style="white-space: pre-wrap;">Clone and build the onion-csr tool</span></p></figcaption></figure><p>We are done. All that we need now is to run <code>./onion-csr.rb</code> with the appropriate <em>CA Signing Nonce</em>, that is provided by HARICA in the preceding step. Likewise, we specify the path to our tor directory. EOTK creates project-specific tor directories at the following location:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">~/eotk/projects.d/&lt;projectname&gt;.d/&lt;truncated onion domain&gt;-v3.d/</code></pre><figcaption><p><span style="white-space: pre-wrap;">Location of the tor directory in EOTK</span></p></figcaption></figure><p>For our <code>example.com</code> project, we would run the following command:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">./onion-csr.rb -n &lt;NONCE&gt; -d ~/eotk/projects.d/example.com.d/exmple27tdg5nesxaao6-v3.d/</code></pre><figcaption><p><span style="white-space: pre-wrap;">Generate the &quot;onion CSR&quot; file by running the executable with the HARICA-provided CSR nounce, and the path to EOTK&apos;s project-specific tor service directory.</span></p></figcaption></figure><p>The utility should immediately output the onion CSR to the screen. We copy this and submit it to HARICA. When pasting the onion CSR, make sure that there are no extra newlines at the end, which will cause it to be rejected.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-46-32-Harica-CertManager---Server-Certificates.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1208" height="866" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-46-32-Harica-CertManager---Server-Certificates.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-46-32-Harica-CertManager---Server-Certificates.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-46-32-Harica-CertManager---Server-Certificates.png 1208w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Upload our generated certificate signing request.</span></figcaption></figure><p>Once our &quot;Onion CSR&quot; is submitted, domain ownership validation is complete. All that&apos;s left is to accept the certificate, make payment, and then download the signed TLS certificate files.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-49-25-Harica-CertManager---Acceptance.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1907" height="1367" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-2022-03-27-at-01-49-25-Harica-CertManager---Acceptance.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-2022-03-27-at-01-49-25-Harica-CertManager---Acceptance.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/03/Screenshot-2022-03-27-at-01-49-25-Harica-CertManager---Acceptance.png 1600w, https://shen.hong.io/content/images/2022/03/Screenshot-2022-03-27-at-01-49-25-Harica-CertManager---Acceptance.png 1907w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Once the Onion CSR is uploaded, HARICA will generate the signed TLS certificate for our onion domain name. Upon acceptance and payment, the certificate </span><code spellcheck="false" style="white-space: pre-wrap;"><span>.pem</span></code><span style="white-space: pre-wrap;"> files will be available for download.</span></figcaption></figure><p>Naturally, I pressed &quot;decline&quot; at this stage, since I was going through the process only for example. But once you click accept, you&apos;ll be able to pay for, and then download the certificate &#x2013; which will be made available as a <code>certificate.pem</code> file. </p><p>We will now go through the final step of this guide, which is installing these signed, trusted TLS certificates into EOTK.</p><h3 id="installing-signed-tls-certificates-into-eotk">Installing Signed TLS Certificates into EOTK</h3><p>You should have the signed certificate available as a <code>.pem</code> file. It will have a 32-digit hexidecimal name like <code>AF6EC6C9FEE27EB9769B47B040A71832.pem</code> (a random example, not a real key). I&apos;ll call it <code>certificate.pem</code> for easier reference. You should also have a password-protected private key file named <code>privateKey.pem</code>. We must first transfer both of these files to our remote server:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">rsync -a certificate.pem username@remote:/path/to/destination
rsync -a privateKey.pem username@remote:/path/to/destination</code></pre><figcaption><p><span style="white-space: pre-wrap;">Transfer the newly-purchased certificate and its corresponding (password-protected) private key </span><code spellcheck="false" style="white-space: pre-wrap;"><span>.pem</span></code><span style="white-space: pre-wrap;"> files to the remote server.</span></p></figcaption></figure><p>These files must be installed within the <code>ssl.d</code> folder of our EOTK project directory. <strong>These files must be renamed to the truncated 20-character long onion domain name, followed by the <code>-v3</code> suffix.</strong> Your public key must be given the <code>.cert</code> file ending, and the private key must be given the <code>.pem</code> file ending. <strong>The specific naming of these files is important</strong> &#x2013; if you misname them, EOTK will not be able to use them!</p><p>The best way to not mess up is to simply copy them on top of (i.e. replace) the existing self-signed TLS certificates which EOTK automatically generates. We can do this with our certificate file first:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">cp certificate.pem eotk/projects.d/example.com.d/ssl.d/exmple27tdg5nesxaao6-v3.cert</code></pre><figcaption><p><span style="white-space: pre-wrap;">Install the certificate (i.e. public key) for our onion domain.</span></p></figcaption></figure><p>Our <code>privateKey.pem</code> is passphrase protected. Before we copy it, we must first unlock it with our passphrase. We do this by using the <code>openssl</code> command to create an <code>unlockedKey.pem</code> file. The command will prompt you for your passphrase.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">openssl ec -in privateKey.pem -out unlockedKey.pem
# Enter private key passphrase</code></pre><figcaption><p><span style="white-space: pre-wrap;">Unlock the passphrase protected private key file.</span></p></figcaption></figure><p>Once that&apos;s done, we can copy <code>unlockedKey.pem</code> on top of the existing self-signed private key.</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">cp unlockedKey.pem eotk/projects.d/example.com.d/ssl.d/exmple27tdg5nesxaao6-v3.pem</code></pre><figcaption><p><span style="white-space: pre-wrap;">Install the certificate private key for our onion domain.</span></p></figcaption></figure><p>Now, our signed TLS certificates are installed! In order for them to take effect, all we have to do is to instruct EOTK to reload its internal Nginx server:</p><figure class="kg-card kg-code-card"><pre><code class="language-Shell">./eotk nxreload example.com</code></pre><figcaption><p><span style="white-space: pre-wrap;">Instruct EOTK to reload the Nginx configuration for the example.com project.</span></p></figcaption></figure><p>Now navigate to our <code>.onion</code> domain in the Tor Browser. As you can see, there are no longer any certificate warnings, and our connection is verified by the Hellenic Academic and Research Institution Cert. Authority.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/cert-2.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1404" height="1107" srcset="https://shen.hong.io/content/images/size/w600/2022/03/cert-2.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/cert-2.png 1000w, https://shen.hong.io/content/images/2022/03/cert-2.png 1404w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Trusted TLS Certificate for hong.io&apos;s onion mirror.</span></figcaption></figure><p>Now, with a trusted TLS certificate installed, your website&apos;s Onion Service is truly production-quality. By using EOTK, you have made your website available as a native onion site using the same framework that large organisations and media outlets use!</p><h2 id="configuring-the-onion-location-header-in-nginx">Configuring the Onion-Location Header in Nginx</h2><p>One last fun step. Now that your website is available as an Onion Service, how do you publicise this information? You can put a link in the footer of your website (as I have done), but there exists a standard called the <code>Onion-Location</code> header. You can configure your clearnet website to pass the <code>Onion-Location</code> header with every request. Browsers which know how to inteprete the header (such as the Tor Browser, and the Brave Browser), will detect the header and inform the user that an <code>.onion</code> link is available.</p><p>The Tor Project maintains <a href="https://community.torproject.org/onion-services/advanced/onion-location/">instructions for both Nginx and Apache</a>. For Nginx, all you have to do is to add the following directive to your clearnet <code>nginx.conf</code> file:</p><pre><code class="language-Nginx">add_header Onion-Location https://&lt;your-onion-address&gt;.onion$request_uri;</code></pre><p>Likewise, you can also add a <code>&lt;meta&gt;</code> tag to your website&apos;s HTML code, should that be more convenient:</p><pre><code class="language-HTML">&lt;meta http-equiv=&quot;onion-location&quot; content=&quot;https://&lt;your-onion-service-address&gt;.onion&quot; /&gt;</code></pre><p>Make sure to specify the protocol as <code>https://</code>, since EOTK always enforces TLS connections. No matter the method, when a user navigates to your website using either the Tor Browser or Brave, they will see the following button:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-15-50-43.png" class="kg-image" alt="How to Make Your Website Available Over Tor : A Complete Guide To EOTK, The Enterprise Onion Toolkit" loading="lazy" width="1444" height="1106" srcset="https://shen.hong.io/content/images/size/w600/2022/03/Screenshot-from-2022-03-27-15-50-43.png 600w, https://shen.hong.io/content/images/size/w1000/2022/03/Screenshot-from-2022-03-27-15-50-43.png 1000w, https://shen.hong.io/content/images/2022/03/Screenshot-from-2022-03-27-15-50-43.png 1444w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The </span><code spellcheck="false" style="white-space: pre-wrap;"><span>Onion-Location</span></code><span style="white-space: pre-wrap;"> header in action. Observe the purple button in the upper-right corner of the address bar.</span></figcaption></figure><p>Congratulations, now your website&apos;s Onion Service is complete!</p><h2 id="conclusion">Conclusion</h2><p>By following this guide, you should have been able to create a production-quality mirror which enables you to make your website available as a native Onion Service over the Tor Network. The Onion Service mirror will allow users to access your website securely and anonymously, delivering your content to audiences that are previously inacessible. </p><p>For me, I know that on a practical sense, few readers will access my blog over the Tor Network. After all, my website is a small, independent blog dedicated to Philosophy (and the odd Computing article), with a readership measuring in the dozens. But I believe in empowering my readers whenever I can, and even if a single reader is to benefit from <a href="https://shen.hongio267dx4o2ofkn4ddsztu4ok2vq4euc7sxumbi7kcfd64ije62ad.onion/">my Onion Service</a>, all this effort will have been worth it. For the pursuit of philosophy is an universal activity, among the <a href="https://www.perseus.tufts.edu/hopper/text?doc=Perseus%3Atext%3A1999.01.0054%3Abook%3D1"><em>characteristic activities of the human being</em></a>.</p><p>I hope this guide has been useful to you, and that it was able to help you deploy your own instances of EOTK. More Onion Service sites will help empower users, especially those who face censorship and oppression.</p><hr><p><em>Thank you for reading my tutorial. For further content, feel free to subscribe to my </em><a href="https://shen.hong.io/rss/"><em>RSS feed</em></a><em>. If you have any thoughts, feedback, or got stuck in any way, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em>. I always enjoy meeting interesting strangers! Be sure to check out my other content: I also write about </em><a href="https://shen.hong.io/role-of-friendship-in-inquiry-reflections-on-galileo/"><em>Galileo</em></a><em>, </em><a href="https://shen.hong.io/philosophical-contemplation-on-relativity-part-1/"><em>Special Relativity</em></a><em>, and </em><a href="https://shen.hong.io/nixos-for-philosophy-installing-firefox-latex-vscodium/"><em>NixOS</em></a><em>. </em></p>]]></content:encoded></item><item><title><![CDATA[Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX]]></title><description><![CDATA[A guide to creating fully reproducible, PDF/A Compliant documents in LaTeX.]]></description><link>https://shen.hong.io/reproducible-pdfa-compliant-latex/</link><guid isPermaLink="false">621ac8845947890001a2e1cc</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Mon, 28 Feb 2022 03:08:05 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/scribe.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/scribe.webp" alt="Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX"><p>Hello there. I am <a href="https://shen.hong.io/about/">Shen</a>, and I study Philosophy and Computer Science. This is a guide on creating <em>fully reproducible</em>, <a href="https://en.wikipedia.org/wiki/PDF/A"><em>PDF/A-compliant</em></a> documents in <a href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a>. LaTeX documents are the gold-standard in academic publishing and typesetting, and this guide will help you make your documents <a href="https://www.adobe.com/accessibility/pdf/pdf-accessibility-overview.html"><em>more accessible</em></a><em>, better structured, and suitable for </em><a href="https://en.wikipedia.org/wiki/Digital_preservation"><em>long-term preservation</em></a><em> for the future.</em></p><p>This guide is suitable for the intermediate LaTeX user. You do not need to know any TeX programming at all. The method described here is compatible with existing documents and templates.</p><h2 id="introduction-to-pdfa-compliance-and-fully-reproducible-pdfs">Introduction to PDF/A-Compliance, and Fully Reproducible PDFs.</h2><p>We will begin with an introduction on PDF/A compliance, reproducible builds, and why they are beneficial for LaTeX documents. If you are familiar with these topics already, feel free to visit the next section instead.</p><h3 id="what-is-the-pdfa-standard-and-why-should-my-document-be-pdfa-compliant">What is the PDF/A standard, and why should my document be PDF/A-compliant?</h3><p>The PDF/A standard is an ISO standard that aims to make PDF-documents better suited for <em>long-term preservation and archiving</em>. A PDF is supposed to be a paper-replacement, after all. And documents (whether digital, or paper) must be readable, even decades after they are created. LaTeX is used for many documents those long-term preservation is important, such as theses, dissertations, and documentation. This is especially true for documents that are submitted to digital libraries and university archives, which make them available for posterity. </p><p>Unfortunately, regular PDF documents are unsuitable for long-term preservation. Fonts used in the document are often linked from the operating system, and media files could be compressed using compression algorithms that are proprietary, with intellectual property constraints. If you have any colours in your document at all, they can only be displayed with a device-dependent color-profile, which may change or become unavailable decades down the line. All of these factors mean that <em>a PDF created today is not guaranteed to look the same, or even be readable at all, in the future.</em></p><p>The PDF/A standard solves this issue, <em>by requiring the PDF to include all components necessary for its display.</em> Font files are embedded into the PDF, and only open source compression algorithms are used for media. A device-independent color profile is embedded into the document, so the PDF-viewer of the future will know how to render the content, even if we stop using <a href="https://en.wikipedia.org/wiki/RGB_color_spaces">RGB</a>.</p><p>More importantly, PDF/A compliance is the first step towards creating <em>more accessible documents</em>. PDF/A compliant documents include rich metadata, which allows the authorship, topic, and cataloguing information of the document to be inferred automatically. Users of screen-readers and braille displays will be better accommodated by PDF/A compliant PDFs. Although this guide does not present the creation of fully-accessible (i.e. structured) PDFs, a compliant-PDF is the first step towards universal accessibility.</p><h3 id="what-are-fully-reproducible-builds-and-what-benefits-does-it-have-for-latex-documents">What are fully reproducible builds, and what benefits does it have for LaTeX documents?</h3><p>A fully reproducible build, is a compilation (i.e. build) process that guarantees a specific output, given a specific input (i.e. source code). The professional discipline of software development has made significant efforts towards reproducible builds for software, due to many well-founded security benefits. However, for LaTeX we are interested in reproducible PDFs primarily for the purposes of long-term preservation and archiving.</p><p>How can you be sure that your LaTeX source code will generate the exact same PDF, both on your laptop, and on a colleague&apos;s computer? If a historian of the future were to compile your document, could she be certain that her result was the same as your PDF from the past? LaTeX documents are not bit-for-bit reproducible. If you build your document twice, <em>the resulting PDFs will have different hashes, even if the source code remained unchanged.</em></p><p>There can be many reasons why a PDF will need to be re-compiled from it&apos;s original LaTeX. If we know that the build process is fully reproducible, we can be certain that we will have the same document as the original, no matter how many years have passed.</p><h2 id="creating-pdfa-compliant-documents-in-latex">Creating PDF/A Compliant documents in LaTeX</h2><p>This tutorial will take the form of a dialectic, where I walk the reader through the steps and decisions involved. A complete configuration will only be provided at the end. This way, you will understand the <em>reasoning</em> behind the choices made, and <a href="http://www.perseus.tufts.edu/hopper/text?doc=Perseus%3Atext%3A1999.01.0178%3Atext%3DMeno%3Apage%3D97">gain knowledge, rather than merely correct opinion</a>.</p><h3 id="levels-of-pdfa-compliance">Levels of PDF/A Compliance:</h3><p>There are three different levels of PDF/A compliance:</p><ul><li><code>PDF/A-3a</code> The <code>a</code> level denotes the highest level of compliance. These are structured, tagged, and fully-accessible documents capable of text-reflowing (e.g. like an ebook). LaTeX is not capable of creating these types of PDFs at this point in time.</li><li><code>PDF/A-3b</code> The <code>b</code> level denotes a basic level of compliance. These documents are self-contained, and archival grade. </li><li><code>PDF/A-3u</code> The <code>u</code> level is simply <code>PDF/A-3b</code>, with the additional bonus of keeping the text of the PDF as Unicode. This is a good idea, so we will be using <code>pdfx</code> with the <code>a-3u</code> option, to create this type of document.</li></ul><p>We will be using LuaLaTeX as the underlying LaTeX engine. Here&apos;s a quick overview of the <a href="https://tex.stackexchange.com/a/72">differences between LuaLaTeX and other engines</a> (XeLaTeX, etc.) LuaLaTeX is the successor to pdfLaTeX, and offers native Unicode support, and packages like <code>microtype</code> offers more features for it.</p><p>We will be loading the <code>pdfx</code> package in order to add PDF/A support to our document.</p><h3 id="note-on-mmap-cmap-and-why-we-do-not-need-them">Note on <code>mmap</code>, <code>cmap</code>, and why we do not need them.</h3><p>Please note that <em>unlike</em> many other guides, we will <em>not</em> be using <code>mmap</code> or <code>cmap</code>. These packages are used to provide character map files for our documents. However, lualatex and <code>fontspec</code> support modern OpenType fonts in the first place, which comes with character-maps embedded within the font files themselves. <code>mmap</code> and <code>cmap</code> do not even support the modern <code>TU</code> encoding (TeX Unicode) which lualatex uses.</p><p>What is the <code>mmap</code> package, and why do some people use it? Have you ever had the experience where you tried to copy some text from a PDF, but when you pasted the selected text, the paste was incorrect? This is because documents that contain ligatures (i.e. &apos;th,&apos; &apos;fl,&apos; or &apos;ae&apos;) render them as a single <em>glyph</em>, or &apos;character.&apos;</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/02/Caslon_Pro_Ligatures.svg" class="kg-image" alt="Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX" loading="lazy" width="480" height="410"><figcaption><span style="white-space: pre-wrap;">Examples of ligatures from the font Adobe Caslon Pro. Image Source: </span><a href="https://commons.wikimedia.org/wiki/File:Caslon_Pro_Ligatures.svg"><span style="white-space: pre-wrap;">Max Naylor, Wikimedia Commons.</span></a></figcaption></figure><p>If the PDF does not contain a <em>character map table</em>, than when you select the text, it will not know that the ligature represents a combination of two actual characters. Having character-map tables is especially important for screen-reader support, or automatic text-search. Once again, we will not encounter this problem, using LuaLaTeX and modern unicode OpenType fonts.</p><h3 id="finding-the-correct-load-order-for-the-pdfx-package">Finding the Correct Load Order for the <code>pdfx</code> Package</h3><p>Let&apos;s begin with a regular LaTeX document, that is similar to ones that you may have right now.</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\documentclass[
  12pt,
  a4paper
]{article}

% Packages
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{geometry}
\usepackage{microtype}
\usepackage{amsmath}
\usepackage{xcolor}
% ...
\usepackage[style=mla, backend=biber]{biblatex}

% Hyperref must be loaded last!
\usepackage{hyperref}

\title{A LaTeX Document}
\author{Shen}
\date{\today}

\begin{document}
\maketitle
\tableofcontents

\section{Introduction}
The body of the document begins here.

\printbibliography
\end{document}
</code></pre><figcaption><p><span style="white-space: pre-wrap;">A generic LaTeX document</span></p></figcaption></figure><p>This is the kind of document that any intermediate LaTeX user will have. We use common packages like <code>geometry</code>, <code>microtype</code>, or <code>amsmath</code> &#x2013; and we manage our citations using <code>biblatex</code>. We enable unicode and font support with <code>fontspec</code>. This is not some contrived minimal example, but rather a <em>realistic</em> example &#x2013; one that reflects the complexity of a paper or article. We even use <code>hyperref</code> and <code>xcolor</code>, to give our citations and footnotes blue-colored hyperlinks.</p><p>How do we make this document PDF/A compliant? Do we simply load the <code>pdfx</code> package at the top of our load-chain?</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">% Packages
\usepackage[a-3u]{pdfx}

\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{geometry}
\usepackage{microtype}
\usepackage{amsmath}
\usepackage{xcolor}
% ...
\usepackage[style=mla, backend=biber]{biblatex}

% Hyperref must be loaded last!
\usepackage{hyperref}</code></pre><figcaption><p><span style="white-space: pre-wrap;">A naive attempt to add PDF/A compatibility to an existing document</span></p></figcaption></figure><p>The above attempt will <em>not</em> work. The document will compile, but if your paper has any footnotes, figures, or internal links at all, <code>hyperref</code> would experience errors in creating hyperlinks. This issue occurs because the <code>pdfx</code> package loads <code>hyperref</code> internally, and by loading <code>pdfx</code> at the top of our load-chain, <em>we end up loading <code>hyperref</code> first.</em></p><p>This is a problem. The <code>hyperref</code> package modifies and patches many LaTeX commands (such as <code>\section</code>, <code>\footnote</code>, etc), and this is why it <em>must</em> be loaded last:</p><blockquote>Make sure  comes last of your loaded packages, to give it a fighting chance of not being over-written, since its job is to redefine many LATEX commands<br>&#x2013; Section 3: Implicit Behaviour, <a href="https://mirrors.mit.edu/CTAN/macros/latex/contrib/hyperref/doc/hyperref-doc.pdf">Hyperref manual</a></blockquote><p>The <code>pdfx</code> package also loads <code>xcolor</code> internally, hence we have further duplicated our efforts with loading <code>xcolor</code>. Let&apos;s change our code now, to account for <code>pdfx</code>, and move it towards the end of our load chain. </p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">% Packages
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{geometry}
\usepackage{microtype}
\usepackage{amsmath}
% ...
\usepackage[style=mla, backend=biber]{biblatex}

% pdfx loads both hyperref and xcolor internally
% \usepackage{hyperref}
% \usepackage{xcolor}
\usepackage[a-3u]{pdfx}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Moving </span><code spellcheck="false" style="white-space: pre-wrap;"><span>pdfx</span></code><span style="white-space: pre-wrap;"> to the bottom, which loads both </span><code spellcheck="false" style="white-space: pre-wrap;"><span>hyperref</span></code><span style="white-space: pre-wrap;"> and </span><code spellcheck="false" style="white-space: pre-wrap;"><span>xcolor</span></code><span style="white-space: pre-wrap;"> implicitly.</span></p></figcaption></figure><p>The above attempt will solve issues with <code>hyperref</code>, and now footnotes, citations, and labels will be linked properly. Hence, the first complete, working configuration, would look like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\documentclass[
  12pt,
  a4paper
]{article}

% Packages
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{geometry}
\usepackage{microtype}
\usepackage{amsmath}
% ...
\usepackage[style=mla, backend=biber]{biblatex}

% pdfx loads both hyperref and xcolor internally
% \usepackage{hyperref}
% \usepackage{xcolor}
\usepackage[a-3u]{pdfx}

\title{A LaTeX Document}
\author{Shen}
\date{\today}

\begin{document}
\maketitle
\tableofcontents

\section{Introduction}
The body of the document begins here.

\printbibliography
\end{document}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Complete, working example for PDF/A-compliant document</span></p></figcaption></figure><h3 id="managing-pdf-metadata-and-hyperref-configuration">Managing PDF Metadata, and <code>hyperref</code> Configuration</h3><p>The above example will work, but our document is not complete yet. PDF/A requires us to specify certain document metadata, which will be used to help cataloguing, as well as make it easier for automated tools to injest our PDF. The <code>pdfx</code> documentation manual states:</p><blockquote>Standards-compliant PDF documents require document-level metadata to be included. This, known as an &#x2018;XMP packet,&#x2019; is like having a library catalog card included within the PDF itself.<br>...<br>[The advantages of this metadata are:]<br> <br>For a librarian: cataloguing information is available within the file itself, without the need to search explicitly in the visual layout of the content or elsewhere; <br><br>All actual libraries cataloguing this PDF can have consistent information; including web-based indexing sites such as Google.<br><br>For the author(s): who can specify the kind of information most appropriate to help readers understand the nature and purpose of the document.</blockquote><p>This metadata is also read by PDF-viwers, which allows them to display the title of the document in the window bar of the PDF viewer, for example.</p><p>So how do we include this XMP-packet of metadata? Users who are familiar with <code>hyperref</code> may specify their document&apos;s metadata like this, alongside other <code>hyperref</code>-specific configuration options:</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\usepackage[
    % First specify PDF metadata with hyperref
    pdftitle   = {A PDF-A Tutorial},
    pdfauthor  = {Shen},
    pdfsubject = {A guide on creating PDF-A compliant documents},
    pdfcreator = {LuaLaTeX},
    % Now configure settings for hyperref itself
    colorlinks = true,
    allcolors  = blue,
]{hyperref}</code></pre><figcaption><p><span style="white-space: pre-wrap;">An example of how PDF-metadata is commonly added using </span><code spellcheck="false" style="white-space: pre-wrap;"><span>hyperref</span></code></p></figcaption></figure><p>You might also use <code>hyperref</code>&apos;s command <code>\hypersetup</code> to specify these options separately from the package load (which is good practice):</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\usepackage{hyperref}

% ...

\hypersetup{
    % First specify PDF metadata with hyperref
    pdftitle   = {A PDF-A Tutorial},
    pdfauthor  = {Shen},
    pdfsubject = {A guide on creating PDF-A compliant documents},
    pdfcreator = {LuaLaTeX},
    % Now configure settings for hyperref itself
    colorlinks = true,
    allcolors  = blue,
}</code></pre><figcaption><p><span style="white-space: pre-wrap;">Another example of a common way that PDF-metadata is defined, this time using </span><code spellcheck="false" style="white-space: pre-wrap;"><span>\hypersetup</span></code><span style="white-space: pre-wrap;">.</span></p></figcaption></figure><p>Some clever people may even the <code>pdfusetitle</code> option in <code>hyperref</code> at load, which automatically defines the <code>pdftitle</code> and <code>pdfauthor</code>, attributes from the document&apos;s <code>\title</code> and <code>\author</code> declarations:</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\usepackage[pdfusetitle, colorlinks=true, allcolors=blue]{hyperref}</code></pre><figcaption><p><span style="white-space: pre-wrap;">A third example of how PDF-metadata is defined with </span><code spellcheck="false" style="white-space: pre-wrap;"><span>hyperref</span></code><span style="white-space: pre-wrap;">.</span></p></figcaption></figure><p>Unfortunately, none of the above methods will work using the <code>pdfx</code> package. The <code>pdfx</code> package requires the PDF&apos;s metadata to be specified in a separate <code>.xmpdata</code> file, because it needs to do transform the metadata into a specific XMP packet that is then embedded into the PDF.</p><p>This <code>.xmpdata</code> is given the same name and in the same location as our primary <code>.tex</code> file. If your document is called <code>paper.tex</code>, than you&apos;ll have to create a <code>paper.xmpdata</code> file in the same location. An example <code>.xmpdata</code> file will look like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\Author{Shen}
\Title{
  Default PDF Document Title (For PDF/A Compatibility)
}
\Language{English}
\Keywords{keyword1\sep keyword2\sep keyword3}
\Publisher{Self-Published}
\Subject{
  Description of the PDF&apos;s subject
}
\Date{2022-02-26}
\PublicationType{pamphlet}
\Source{https://github.com/ShenZhouHong/latex-essay}
\URLlink{https://github.com/ShenZhouHong/latex-essay}
</code></pre><figcaption><p><span style="white-space: pre-wrap;">An example of how a </span><code spellcheck="false" style="white-space: pre-wrap;"><span>.xmpdata</span></code><span style="white-space: pre-wrap;"> file looks like</span></p></figcaption></figure><p>It uses the same syntax as TeX, and we specify our metadata in this document. The <a href="http://mirrors.ctan.org/macros/latex/contrib/pdfx/pdfx.pdf">pdfx documentation</a> on CTAN.org lists other fields and options. Now, when we compile the document, the <code>pdfx</code> package will automatically seek out and find the <code>.xmpdata</code> file.</p><p>It is important to keep in mind that we cannot use the <code>pdfusetitle</code> option for <code>hyperref</code> anymore, and attempting to pass that option will result in an error.</p><p>Now, the only thing we must do is to pass the regular options we use for <code>hyperref</code> through <code>\hypersetup</code>, since we are not loading <code>hyperref</code> directly anymore, and cannot pass options to it from the <code>\usepackage</code> line.</p><h2 id="complete-example-of-pdfa-compliant-document">Complete Example of PDF/A-Compliant Document</h2><p>Hence, the complete example of our PDF-A compliant document will look like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\documentclass[
  12pt,
  a4paper
]{article}

% Packages
\usepackage{fontspec}
\usepackage{libertinus-otf}
\usepackage{geometry}
\usepackage{microtype}
\usepackage{amsmath}
% ...
\usepackage[style=mla, backend=biber]{biblatex}

% pdfx loads both hyperref and xcolor internally
% \usepackage{hyperref}
% \usepackage{xcolor}
\usepackage[a-3u]{pdfx}

% We use \hypersetup to pass options to hyperref
\hypersetup{
  colorlinks = true,
  allcolors  = blue
  % ...
}

\title{A LaTeX Document}
\author{Shen}
\date{\today}

\begin{document}
\maketitle
\tableofcontents

\section{Introduction}
The body of the document begins here.

\printbibliography
\end{document}</code></pre><figcaption><p><span style="white-space: pre-wrap;">The </span><code spellcheck="false" style="white-space: pre-wrap;"><span>paper.tex</span></code><span style="white-space: pre-wrap;"> file</span></p></figcaption></figure><p>And here is the corresponding <code>paper.xmpdata</code> file. Remember to place it in the same directory as your main <code>.tex</code> file (i.e. the one that you run <code>lualatex</code> on).</p><figure class="kg-card kg-code-card"><pre><code class="language-LaTeX">\Author{Shen}
\Title{
  Default PDF Document Title (For PDF/A Compatibility)
}
\Language{English}
\Keywords{keyword1\sep keyword2\sep keyword3}
\Publisher{Self-Published}
\Subject{
  Description of the PDF&apos;s subject
}
\Date{2022-02-26}
\PublicationType{pamphlet}
\Source{https://github.com/ShenZhouHong/latex-essay}
\URLlink{https://github.com/ShenZhouHong/latex-essay}
</code></pre><figcaption><p><span style="white-space: pre-wrap;">The </span><code spellcheck="false" style="white-space: pre-wrap;"><span>paper.xmpdata</span></code><span style="white-space: pre-wrap;"> file</span></p></figcaption></figure><p>Congratulations! Now you have created a fully PDF-A compliant document!</p><h3 id="checking-for-pdf-a-compliance-using-pdf-validation-tools">Checking for PDF-A Compliance using PDF Validation Tools</h3><p>The last step that we can take is to check for our document&apos;s compliance. Adobe Acrobat Pro&apos;s Preflight tool is still the gold standard in this area, as Adobe has been instrumental in developing the PDF standard as a whole. However, there are also quality open-source validators that function nearly as well, if not identically.</p><p>We will be checking our resulting PDF document for PDF/A-compliance using the <a href="https://openpreservation.org/">Open Preservation Foundation</a>&apos;s VeraPDF tool. <a href="https://openpreservation.org/products/verapdf/">VeraPDF</a> is an open source (GPLv3+), cross-platform PDF standard validator that&apos;s available on Windows, Mac OS, and Linux. It&apos;s a Java program, so you will have to install a Java Runtime. I recommend using the OpenJDK JRE. The latest version is available on Ubuntu simply as the <code>default-jre</code> apt package:</p><pre><code class="language-bash">sudo apt install default-jre</code></pre><p>The VeraPDF tool can be downloaded from the Open Preservation Foundation&apos;s website, <a href="https://openpreservation.org/products/verapdf/">which is linked here</a>. It comes as a .zip file with an <code>verapdf-install</code> shell script and <code>verapdf-install.bat</code> file. For linux, you&apos;ll want to execute the <code>verapdf-install</code> script, by first granting it executable permissions:</p><pre><code class="language-bash">chmod +x verapdf-install
./verapdf-install</code></pre><p>This would start the GUI installer, and allow you to install the validator into a directory <code>/verapdf</code> at a location of your choice. We can run the GUI validator by simply executing it from the terminal like before:</p><pre><code class="language-bash">chmod +x verapdf-gui
./verapdf-gui</code></pre><p>The only slightly tricky part left is that if you are using a modern, high-resolution display, the Java program might not have a GUI that&apos;s large enough. The first time I ran the program, it looked like this, and was unusable:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/02/Screenshot-from-2022-02-27-20-43-09.png" class="kg-image" alt="Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX" loading="lazy" width="714" height="374" srcset="https://shen.hong.io/content/images/size/w600/2022/02/Screenshot-from-2022-02-27-20-43-09.png 600w, https://shen.hong.io/content/images/2022/02/Screenshot-from-2022-02-27-20-43-09.png 714w"><figcaption><span style="white-space: pre-wrap;">Cramped GUI due to old Java programs unable to support high-resolution displays</span></figcaption></figure><p>A simple fix for that is to open the <code>~/verapdf/verapdf-gui</code> file. It&apos;s a simple shell script that launches the Java GUI program, and all you have to do is to change the <code>SCALE_FACTOR=1.0</code> line to a larger integer, like <code>1.5</code> or <code>2.0</code>.</p><p>Now the GUI is appropriately sized. All we have to do is to select our PDF, and click Execute. VeraPDF will tell us if the document is compliant with our variant of the PDF/A standard.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/02/Screenshot-from-2022-02-27-20-37-21.png" class="kg-image" alt="Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX" loading="lazy" width="1424" height="744" srcset="https://shen.hong.io/content/images/size/w600/2022/02/Screenshot-from-2022-02-27-20-37-21.png 600w, https://shen.hong.io/content/images/size/w1000/2022/02/Screenshot-from-2022-02-27-20-37-21.png 1000w, https://shen.hong.io/content/images/2022/02/Screenshot-from-2022-02-27-20-37-21.png 1424w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">The PDF document from this guide is validated by veraPDF</span></figcaption></figure><p>Congratulations on creating your first PDF/A-compliant LaTeX document!</p><h2 id="fully-reproducible-latex-documents-using-makefile">Fully Reproducible LaTeX Documents using Makefile</h2><p>All that remains for us to do now, is to make this document fully reproducible, so that compilations of the same source code will always result in a PDF with the same hash.</p><p>Creating fully bit-by-bit reproducible programs is quite difficult for regular software programming, but for LaTeX it is quite easy. First, we must make sure we are not using any LaTeX commands that change over time. The only common one would be the <code>\date{\today}</code> line. We&apos;ll remove that to specify a specific date.</p><p>Finally, the <code>pdfx</code> package embeds the creation date and modification date of the file into the PDF itself. As that changes every time we run <code>lualatex</code>, this will result in PDF files with different hashes. Thankfully, <code>lualatex</code> respects the <code>SOURCE_DATE_EPOCH</code> environment variable.</p><p><a href="https://reproducible-builds.org/docs/source-date-epoch/"><code>SOURCE_DATE_EPOCH</code></a> is a standardised environment variable that is read by compilers and build toolchains to set the modification date of a binary (or a PDF file) to a specific date and time. It&apos;s a specification made by the <a href="https://reproducible-builds.org/docs/source-date-epoch/">Reproducible Builds project</a>.</p><p>The variable takes an integer that represents the number of seconds elapsed since the <a href="https://en.wikipedia.org/wiki/Unix_time">Unix epoch</a>, and we can get that integer for today by simply running:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">date +&quot;%s&quot;</code></pre><figcaption><p><span style="white-space: pre-wrap;">Getting the Unix epoch time (in seconds) of the current date and time.</span></p></figcaption></figure><p>If you are running <code>lualatex</code> directly in the terminal, all you have to do to make your document reproducible, is to export this variable (for a set value, of course), and have it be available every time you run <code>lualatex</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">export SOURCE_DATE_EPOCH=1646013411
lualatex paper.tex</code></pre><figcaption><p><span style="white-space: pre-wrap;">Defining the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>SOURCE_DATE_EPOCH</span></code><span style="white-space: pre-wrap;"> on a fixed time.</span></p></figcaption></figure><p>This is a little burdensome, and besides, you&apos;ll have to write down whatever epoch you started with. Furthermore, it would be strange having the creation-date and modification-date of the document fixed permanently.</p><p>A much better way to use <code>SOURCE_DATE_EPOCH</code> to make our LaTeX documents reproducible, is to use a <code>Makefile</code>, and tie the <code>SOURCE_DATE_EPOCH</code> to the time of our <em>latest git commit</em>. </p><figure class="kg-card kg-code-card"><pre><code class="language-bash">SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)</code></pre><figcaption><p><span style="white-space: pre-wrap;">Defining the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>SOURCE_DATE_EPOCH</span></code><span style="white-space: pre-wrap;"> based on the time of the latest git commit</span></p></figcaption></figure><p>This way, we&apos;ll always have a realistic modification date for our PDF documents, that&apos;ll remain constant for every given commit. By doing so, you&apos;ll be certain that your git repository will produce definite PDFs for every commit in it&apos;s history, that will be bit-for-bit identical. We&apos;ll create a <code>Makefile</code> in the directory where our <code>paper.tex</code> and <code>paper.xmpdata</code> files are located:</p><figure class="kg-card kg-code-card"><pre><code class="language-makefile">COMMIT_EPOCH = $(shell git log -1 --pretty=%ct)

# Makes sure latexmk always runs
.PHONY: paper.pdf all clean
all: paper.pdf

paper.pdf: paper.tex
	SOURCE_DATE_EPOCH=$(COMMIT_EPOCH) latexmk -pdf -lualatex -use-make $&lt;
clean:
	latexmk -c
delete:
	latexmk -C</code></pre><figcaption><p><span style="white-space: pre-wrap;">Example </span><code spellcheck="false" style="white-space: pre-wrap;"><span>makefile</span></code><span style="white-space: pre-wrap;"> for </span><code spellcheck="false" style="white-space: pre-wrap;"><span>paper.tex</span></code><span style="white-space: pre-wrap;"> that uses the date of the latest git commit</span></p></figcaption></figure><p>Now, every time you run <code>make</code> to compile your PDF, you can be certain that the resulting document is fully reproducible. And of course, if you are using this makefile in a project that does not use git, simply set <code>COMMIT_EPOCH</code> to a fixed value.</p><h2 id="conclusion">Conclusion</h2><p>Now your document is both PDF/A compliant, as well as fully reproducible. You can be reassured that your documents will be available, even many decades into the future. Should you ever need to recompile the PDF from source, you&apos;ll get to verify that the outputs are the same by comparing the hashes of the new PDF, with the old PDF.</p><p>Creating reproducible, PDF/A compliant documents is increasingly important. As organisations and universities expand their digital archives, PDF/A compliance is increasingly becoming a requirement for many types of documents.</p><h3 id="latex-template-for-the-humanities">LaTeX Template for the Humanities</h3><p>If you are interested in creating PDF/A compliant, fully-reproducible documents, make sure to also check out my LaTeX essay template! Over the course of four years, I have created and refined a <a href="https://github.com/ShenZhouHong/latex-essay">simple LaTeX template for writing in the academic humanities</a>. It implements the contents of this tutorial, and more, offering features such as:</p><ul><li><strong>Custom Microtype Protrusion</strong> Settings for Hanging Punctuation.</li><li><strong>Typography Tweaks</strong>, Adjustments, and Custom Headers and Footers.</li><li><strong>BibLaTeX Integration</strong> for Citation Management</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/02/collage-4.jpg" class="kg-image" alt="Creating Fully Reproducible, PDF/A Compliant Documents in LaTeX" loading="lazy" width="2000" height="2589" srcset="https://shen.hong.io/content/images/size/w600/2022/02/collage-4.jpg 600w, https://shen.hong.io/content/images/size/w1000/2022/02/collage-4.jpg 1000w, https://shen.hong.io/content/images/size/w1600/2022/02/collage-4.jpg 1600w, https://shen.hong.io/content/images/size/w2400/2022/02/collage-4.jpg 2400w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">A photocollage of LaTeX documents created using my template</span></figcaption></figure><p>The template is <a href="https://github.com/ShenZhouHong/latex-essay">available on Github</a>, and it&apos;s open source. Feel free to check it out for inspiration!</p><hr><p><em>Thank you for reading my LaTeX guide! I also write essays on </em><a href="https://shen.hong.io/dialogue-on-the-questions-of-being/"><em>Philosophy</em></a><em>, </em><a href="https://shen.hong.io/intro-to-metaphysics-for-compsci-and-scientists/"><em>Metaphysics</em></a><em>, and </em><a href="https://shen.hong.io/nixos-for-philosophy-installing-firefox-latex-vscodium/"><em>NixOS</em></a><em>. </em><a href="https://shen.hong.io/contact/"><em>Let me know</em></a><em> if you have any suggestions, comments, or advice &#x2013; I always enjoy conversations with interesting strangers! If you want to be notified of future content, feel free to subscribe to my </em><a href="https://shen.hong.io/rss/"><em>RSS feed.</em></a></p>]]></content:encoded></item><item><title><![CDATA[Philosophical Contemplation on Einstein's Relativity: An Introduction]]></title><description><![CDATA[Thoughts, reflections, and a review on Relativity, by Albert Einstein. An examination of physics by a student of philosophy.]]></description><link>https://shen.hong.io/philosophical-contemplation-on-relativity-part-1/</link><guid isPermaLink="false">621796c85947890001a2de03</guid><category><![CDATA[Science]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Thu, 24 Feb 2022 17:02:35 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/portal.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/portal.webp" alt="Philosophical Contemplation on Einstein&apos;s Relativity: An Introduction"><p><em>Hello there. I&apos;m </em><a href="https://shen.hong.io/about/" rel="noreferrer noopener"><em>Shen</em></a><em>, and I study Philosophy and Computer Science. This is the start of a three-part series on the Theory of Relativity, from a philosophical perspective.</em></p><hr><p>All Philosophy is a pursuit towards being. Whether it is political philosophy (what is legitimacy?), epistemology (what is the truth?) or even just the personal contemplations of an individual life (see Montaigne&apos;s <em>Essays</em>), we must have some sense of the ontology of our <a href="https://en.wikipedia.org/wiki/Dasein">human-reality</a>, in order to conduct philosophical inquiry in the first place. But yet, it is important to <em>ground</em> our inquiries in the <em>facticity</em> of our human-reality &#x2013; for if we let ourselves get carried away, we might end up talking about a metaphysics which have no correspondence to our human-reality at all &#x2013; in other words, we would be simply &quot;building castles in the clouds.&quot;</p><p>Hence, any rigorous metaphysical inquiry of metaphysics must at minimum acknowledge the insights of the empirical sciences. In particular, in order to investigate <em>space</em> and <em>time,</em> we must consult not only our phenomenological experience of spatiality and temporality &#x2013; but also the <em>physical</em> foundation of space and time. After all, Physics <em>is</em> a component of our human-reality, for we live in an objective reality in which the phenomena that we experience are not simply subordinate to our will. With these thoughts in mind, I began a study of Einstein&apos;s Relativity &#x2013; so that I can better understand the principles of our physics, and derive insights that will better inform me on further inquiries in metaphysics as a whole.</p><p>In this process, I completed a reading of Einstein&apos;s 1917 book <a href="https://press.princeton.edu/books/hardcover/9780691166339/relativity"><em>Relativity: The Special and The General Theory</em></a>, as well as a study of his 1905 paper <a href="https://www.physics.umd.edu/courses/Phys606/spring_2011/einstein_electrodynamics_of_moving_bodies.pdf"><em>On the Electrodynamics of Moving Bodies</em></a>. These were highly satisfying readings, which I enjoyed greatly &#x2013; and with these thoughts on my mind, I find myself ready to present some contemplations on Einstein&apos;s Special and General Relativity. This article is partly an explanation of Relativity, partly a review of Einstein&apos;s book &#x2013; and wholly a reflection of my contemplations, as a Philosophy student reviewing Physics. </p><h2 id="what-is-the-theory-of-relativity">What is the Theory of Relativity?</h2><p>To begin, what is the Theory of Relativity? What solutions does it offer, and what problems does it aim to solve? </p><p>Our human-reality is a spatial and temporal reality. We exist as beings with definite extension in space, as well as a location in time. Our existence is subject to empirical principles which apply to us irrespective of our internal desires &#x2013; hence we live in an <em>objective</em> reality. One of the necessary conditions of this objectivity, is for the same empirical principles to apply to <em>any</em> human being, irrespective of their perspective. We must all be subject to the same natural laws, in order for these laws to be <em>objective</em> in the first place.</p><p>The student of Physics can surely agree to this conclusion, for it is essentially a formulation of a more familiar principle. This is the principle of <strong>Galilean Relativity</strong>, which states that the laws of Newtonian mechanics are true for observers in <em>any</em> reference frame. Whether you are &quot;at rest,&quot; on a train, or jetting across the globe on an aircraft at 300 kilometers per hour &#x2013; the kinematic equations and laws of motion are equally true for all of us. </p><p>Why do we call this principle Galilean Relativity? It is a principle of relativity, insofar that the events (i.e. the phenomena, for the student of Philosophy)  of a system in any reference frame is directly <em>relatable</em> to any other reference frame. There will always exist some method of transformation, some function, that will allow you to take the event(s) of one reference frame, and translate it into the terms of another &#x2013; in a way where this transformation will preserve the underlying laws and equations of Newton. And it is Galilean, because this principle was first expressed by Galileo Galilei, in his <em>Dialogues Concerning the Two Chief World Systems &#x2013; </em>in the most charming little thought experiment, that I will reproduce for the pleasure of my readers below:</p><blockquote>Shut yourself up with some friend in the main cabin below decks on some large ship, and have with you there some flies, butterflies, and other small flying animals.  Have a large bowl of water with some fish in it; hang up a bottle that empties drop by drop into a wide vessel beneath it. <br><br>With the ship standing still, observe carefully how the little animals fly with equal speed to all sides of the cabin.  The fish swim indifferently in all directions; the drops fall into the vessel beneath; and, in throwing something to your friend, you need throw it no more strongly in one direction than another, the distances being equal; jumping with your feet together, you pass equal spaces in every direction.<br><br>When you have observed all these things carefully (though doubtless when the ship is standing still everything must happen in this way), have the ship proceed with any speed you like, so long as the motion is uniform and not fluctuating this way and that.  You will discover not the least change in all the effects named, nor could you tell from any of them whether the ship was moving or standing still.<br><br>In jumping, you will pass on the floor the same spaces as before, nor will you make larger jumps toward the <a href="https://en.wikipedia.org/wiki/Stern">stern</a> than toward the <a href="https://en.wikipedia.org/wiki/Prow">prow</a> even though the ship is moving quite rapidly, despite the fact that during the time that you are in the air the floor under you will be going in a direction opposite to your jump. In throwing something to your companion, you will need no more force to get it to him whether he is in the direction of the <a href="https://en.wikipedia.org/wiki/Bow_(ship)">bow</a> or the stern, with yourself situated opposite.  The droplets will fall as before into the vessel beneath without dropping toward the stern, although while the drops are in the air the ship runs many spans.  The fish in their water will swim toward the front of their bowl with no more effort than toward the back, and will go with equal ease to bait placed anywhere around the edges of the bowl. Finally the butterflies and flies will continue their flights indifferently toward every side, nor will it ever happen that they are concentrated toward the stern, as if tired out from keeping up with the course of the ship, from which they will have been separated during long intervals by keeping themselves in the air. <br><br>And if smoke is made by burning some incense, it will be seen going up in the form of a little cloud, remaining still and moving no more toward one side than the other.  <strong>The cause of all these correspondences of effects is the fact that the ship&apos;s motion is common to all the things contained in it, and to the air also.</strong><br><br>&#x2013; Galileo, Dialogues Concerning the Two Chief World Systems.</blockquote><h2 id="what-is-the-problem-of-galilean-relativity-which-einstein-wishes-to-solve">What is the Problem of Galilean Relativity, which Einstein wishes to Solve?</h2><p>Galilean Relativity is reasonable and convincing, for it broadly agrees with both our lived experience, as well as rigorous (and accessible) experiments. But yet, at the turn of the 19th century, Physicists began to encounter seemingly inexplicable phenomena, which Galilean Relativity is unable to account for. What were these difficulties, which pointed towards the insufficiency of Galilean Relativity? They laid chiefly in the field of <em>electrodynamics</em> &#x2013; the study of the behaviour of moving electromagnetic bodies. And the two most apparent difficulties were on <strong>the electrodynamics of moving bodies</strong>, and <strong>the isotropy of the speed of light</strong>.</p><h3 id="the-electrodynamics-of-moving-bodies">The Electrodynamics of Moving Bodies</h3><p>Move a magnet in motion past a conductor at rest, and you&apos;ll generate a moving <em>magnetic field.</em> As the moving field perturbs the conductor, the moving magnetic field will induce an <em>electric field</em> in the conductor. The electric field in the conductor becomes observable as a measurable current on our multimeter. This is <em>Faraday&apos;s Law of Induction</em> in effect.</p><p>\[\nabla\times\vec E=-\frac{\partial \vec B}{\partial t}\]</p><p>But yet, there exists a puzzling asymmetry. For when we move a <em>conductor in motion</em> past a <em>magnet at rest</em>, there is no moving magnetic field. And yet, an electrical current is induced in the moving conductor nonetheless. There is no electric field anywhere to be found, as there is no magnetic field in motion.</p><p>Now, it seems reasonable for us to observe a current in the second case as well as the first, for moving a magnet past a conductor and a conductor past the magnet are the same interaction, seen from different <em>perspectives</em> (i.e. reference frames, for the Physics student). And yet, classical electrodynamics was unable to provide a singular account for both cases. For the latter case, a different mechanism of action has to be proposed, relating to the electromotive force instead. The details of both cases are not necessary for our investigation &#x2013; but the key takeaway is that <em>the theory of classical electrodynamics required two different accounts for the same underlying phenomena.</em> The laws of our physical reality are not the same irrespective of our reference frame, but somehow, <em>we have fundamentally different laws for what is essentially the same phenomena</em>.</p><h3 id="the-anti-isotropy-of-the-speed-of-light">The (Anti-)Isotropy of the Speed of Light</h3><p>Imagine a train, an embankment, and two observers &#x2013; one on the train, and one on the ground. The observer on the train will perceive herself to be stationary, while the observer on the ground will perceive the observer on the train to move at the speed of the train itself.</p><p>If the observer on the train were to throw a ball towards the front of the train, the speed of the ball will appear to be the speed of the train <em>plus</em> the speed of the ball, for the observer on the ground. And likewise, if her ball were to be thrown towards the rear of the train, than the observer on the ground will perceive the ball to be the speed of the train <em>minus</em> the speed of the ball.</p><p>We use the word <em>anti-isotropic </em>to describe this phenomena, where the speed of the ball (as observed from the ground) is different, depending on whether the ball is thrown in one direction, or another. Naturally, for the observer on the train, the ball will move at the same velocity no matter which direction she throws it &#x2013; but that is only true if we were on the reference frame of the train. As observers on the ground, the ball&apos;s velocity has to be anti-isotropic.</p><p>This anti-isotropy is <em>not</em> a violation of Galilean Relativity. For the velocities of the ball <em>in respect to the train</em> and <em>in respect to the ground</em> are merely quantities that refer to different reference frames. And there exists definite functions to &quot;translate&quot; the values from one reference frame to another &#x2013; namely adding or subtracting the velocity of the train itself. This transformation preserves the kinematic equations and Newtonian laws of motion, so this anti-isotropy is a welcome sign that all is well in our physics.</p><p>At the close of the 19th century, Physicists tried to discover this anti-isotropy in the speed of light, and failed. Light, as an electromagnetic wave &#x2013; must propagate within a definite medium, an electromagnetic field. This is the train from our analogy. And whatever our velocity is in relation to this field, we must be able to observe a similar anti-isotropy in the speed of light depending on whether we are moving <em>with</em> the motion of the field, or <em>against</em> the motion of the field.</p><p>Are we moving with the motion of the electromagnetic field, or against the motion of the electromagnetic field? Well, since the Earth orbits the sun in a circle, we would be moving both with the field, and against the field, at different points of our orbit.</p><p>Hence, a good way to uncover the anti-isotropy of the speed of light, is to measure it during different times of the year. This way, we will get to measure the speed of light while we are moving in different directions within this &quot;lumiferous aether,&quot; and whenever we are aligned with it, light would move slightly faster, or slower, if we are moving against the aether.</p><p>But yet, despite the best and most exacting experiments of the Physcists of the time, we were <em>unable to discover any anti-isotropy in the speed of light</em>. The speed of light was the same, no matter our reference frame, in respect to the &quot;aether.&quot; This culminated in the famous <a href="https://en.wikipedia.org/wiki/Michelson%E2%80%93Morley_experiment">Michelson-Morley experiment of 1887</a>, in which the existence of the &quot;aether wind&quot; was conclusively proven to be false.</p><p>This is a fatal problem for the Galilean Relativity of Classical Mechanics. Because now, there is a phenomena &#x2013; the speed of light &#x2013; which is the same <em>no matter </em>the observer&apos;s reference frame, or perspective. You could be traveling on a rocket at 95% the speed of light, and shoot a laser beam forwards &#x2013; and that laser beam will move at exactly \(c\), for both you on the rocket &#x2013; as well as me, an observer on the ground. Somehow, the speed of light is no longer objective, insofar Classical Mechanics is concerned. It has become a constant those constancy seems subjective to the observer&apos;s perspective &#x2013; a metaphysical solipsism that is deeply troubling for our system of physics as a whole.</p><p>These are the two difficulties of Classical Mechanics, at the end of the 19th century. Somehow, <strong>the Galilean Relativity of Newton are unable to account for the theory of electrodynamics at all.</strong></p><p>This is the background behind the project of Einstein&apos;s theory. We are faced with an incomplete system of physics, in which our laws of motion are invariant in respect to reference frame, but yet our laws of electrodynamics are not. But yet, it cannot possibly be the case that our laws are not invariant in respect to reference frame, for the laws of physics privilege no perceptual <em>subject</em> &#x2013; it is an <em>objective</em> science.</p><blockquote>Examples of this sort, together with the unsuccessful attempts to discover any motion of the earth relatively to the &#x201C;light medium,&#x201D; suggest that the phenomena of electrodynamics as well as of mechanics possess no properties corresponding to the idea of absolute rest. They suggest rather that, as has already been shown to the first order of small quantities, <strong>the same laws of electrodynamics and optics will be valid for all frames of reference for which the equations of mechanics hold good</strong>.<br>&#x2013; Einstein, On the Electrodynamics of Moving Bodies</blockquote><h2 id="concluding-remarks">Concluding Remarks</h2><p>What does it even <em>mean</em> to have physical laws which are not invariant in respect to a reference frame? Is this a possibility that we can tolerate in <em>any</em> case?</p><p>We cannot tolerate it. It would be a disastrous flaw within the ontology of our physics, for it destroys all possibility of objectivity. Physical laws <em>must</em> be the same no matter our perspective &#x2013; both on the practical grounds of experimental physics, as well as on <em>a priori</em> principles of metaphysics too. To paraphrase Jean-Paul Sartre, any theory of knowledge presupposes a metaphysics. Ontologically, to state that physical laws are variant in respect to perspective, is to reduce our system of metaphysics to a dangerous solipsism &#x2013; the world no longer has properties which are independent of my will.</p><p>This is where Einstein begins his pursuit. An attempt to furnish an account of physics, <em>one in which both the laws of electrodynamics, as well as the laws of kinematics, are valid and invariant, for all reference frames</em>. This is the problem of Galilean Relativity which Einstein&apos;s Relativity aims to solve.</p><p>We will examine Einstein&apos;s <strong>Special Relativity</strong> in the next installment of this series, followed by an account of Einstein&apos;s <strong>General Relativity</strong> as a whole. In particular, we will look at what Relativity means, in terms of the spatial and temporal condition of our human-reality.</p><hr><p><em>Thank you for reading my article. For updates on the series, feel free to subscribe to my </em><a href="https://shen.hong.io/rss/"><em>RSS feed</em></a><em>. If you have any thoughts, comments, or feedback, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em>. I am particularly interested in conversations with Physicists and Physics students, who can help me further my understanding of Einstein. Write to me, and let me know of your thoughts!</em></p>]]></content:encoded></item><item><title><![CDATA[The Necessity of Metaphysics: An Introduction for Computer Scientists and the Physical Sciences]]></title><description><![CDATA[An introduction to the Philosophy of Metaphysics for Computer Scientists and other STEM perspectives.]]></description><link>https://shen.hong.io/intro-to-metaphysics-for-compsci-and-scientists/</link><guid isPermaLink="false">6205354c24fdb30001ac65b6</guid><category><![CDATA[Philosophy]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Fri, 11 Feb 2022 17:50:04 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/classical.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/classical.webp" alt="The Necessity of Metaphysics: An Introduction for Computer Scientists and the Physical Sciences"><p>Hello there. I&apos;m <a href="https://shen.hong.io/about/" rel="noreferrer noopener">Shen</a>, and I&apos;m a student of Philosophy and Computer Science. The subject of my undergraduate thesis is on Metaphysics, and I often come across friends and colleagues in the Sciences, who are unfamiliar with the topic. <em>What is Metaphysics? What sort of questions does it investigate? What is its purpose and utility, as a subject in the first place?</em> These are all worthy questions, so let us explore them today.</p><p>As fellow explorers of the world, we are all united by our dedication to the pursuit and understanding of our <a href="https://en.wikipedia.org/wiki/Dasein" rel="noreferrer noopener">human-reality.</a> Hence, the work of a Mathematician, Computer Scientist, Physicist, or Philosopher all stem from our fascination with the world, and its details.</p><p>This article is an introduction and an overview of metaphysics as a Philosophical discipline. But more importantly, I hope that through this understanding, it will present an understanding and <em>appreciation</em> of the beauty and unique perspective that metaphysics offers, as a means to <em>comprehend the world.</em></p><h2 id="metaphysics-is-a-study-of-being">Metaphysics is a study of Being</h2><p>Metaphysics is the study of <em>Being</em>. What is the study of Being, you ask? That is an excellent question, and for us to come to an answer, it will be the most useful to examine the study of Being from the perspective of different fields.</p><p>We live in a world of &quot;things&quot;, which exist as components of our reality. These &quot;things&quot; can be physical objects, such as a chair, a rock, a tree, or the laptop in front of you. But these &quot;things,&quot; (which at this moment, we can only afford to speak loosely and imprecisely about) do not <em>necessarily</em> have to be physical. In fact, many of them are not. A computer algorithm, a mathematical equation, even your friend&apos;s phone number &#x2013; all of these are examples of &quot;things&quot; which have no <em>physical</em> existence. But yet, they are all definite components of our human reality &#x2013; for we can think of them, act with them, and reason about them in definite, and well-defined manners.</p><p>So, what do all of these &quot;things&quot; share in common? An equation is quite different from a table or a tree, after all! At this stage of our discussion, we can only discern one commonality between all of them, which is that they <em>exist</em> as components of our reality &#x2013; even though this existence is not always physical. They exist, because they <em>are.</em></p><p>Hence, these &quot;things&quot; are <em>beings</em> &#x2013; components of our reality. An explanation or theory about the nature or behaviour of being is called <a href="https://en.wikipedia.org/wiki/Ontology" rel="noreferrer noopener"><em>ontology</em></a> &#x2013; the study of the <em>ontos &#x2013; </em>being, or &quot;that which <em>is</em>.&quot;</p><p>Now, we only have to ask: what is a study of being like?</p><h2 id="the-sciences-as-an-empirical-study-of-being">The Sciences as an (Empirical) study of Being</h2><p>The study of being may sound abstract and metaphysical, but it does not have to be. To begin, let us look at the empirical Sciences. Science, in its own way, is a study of being! In fact, each Science has a category of being that it investigates, and different methods to which this investigation is pursued.</p><p>Let us take the humble rock, a being that was first presented in our examples. What <em>is</em> the being of this rock? Hand it over to a Geologist, and she will examine it&#x2013; and using the methods of her discipline, come to a rigorous account of the rock&apos;s being.</p><p>She could begin with an examination of the rock&apos;s <em>material</em>, by holding it up to the light. And marvel at the way that it&apos;s rough surface glimmers, as its details appear. There will be feldspar and muscovite, wholesome in their dullness. Amidst tiny inclusions of quartz, and amphibole, which lends a subtle sparkle, to the texture of the stone. A sample could be chiselled and pulverised for analysis &#x2013; yielding realms of graphs and data, showing the exact distribution of silicates and carbons within. This is a <a href="https://plato.stanford.edu/entries/aristotle-causality/#FouCau" rel="noreferrer noopener"><em>material</em> account</a> of the rock&apos;s being &#x2013; and the Geologist can conclude from it that the rock&apos;s being <em>is</em> granite &#x2013; as determined by its constitutive minerals and chemistry.</p><p>But this is not the only account possible. She may reach the same conclusion, through entirely different means. Lacking the means and facility of a field laboratory, the Geologist can equally attempt an account by looking at the source of the rock instead, by discerning clues on how it <em>came to be</em>. She could look at the site of the quarry, situated against weathered bedrock of a weary continent. And examine the slopes of the landscape, and see the ruins of an ancient <a href="https://en.wikipedia.org/wiki/Caldera" rel="noreferrer noopener">caldera</a> in the formations around. With that, she can conclude that the <a href="https://plato.stanford.edu/entries/aristotle-causality/#FouCau" rel="noreferrer noopener"><em>efficient</em> cause</a> of the rock is through volcanic activity &#x2013; that this sample was once a part of a lava column, of an eruption long ago, lost in geologic time. Hence, she will conclude that the rock&apos;s being <em>is</em> <a href="https://en.wikipedia.org/wiki/Igneous_rock" rel="noreferrer noopener">igneous</a>, specifically igneous granite. The same being, but accounted for differently, in different means.</p><p>Both of these accounts are valid, even though they take on slightly different characteristics, and address facets of the rock&apos;s being in slightly different ways. It is a demonstration on how the study of being is one of the primary concerns of the Sciences, and not just the being of material objects either.</p><p>For is it not the task of a Computer Scientist, to discern the being of an algorithm? Do we not talk about different categories of <a href="https://en.wikipedia.org/wiki/Big_O_notation" rel="noreferrer noopener"><em>algorithmic complexity</em></a>, and rank programs by their behaviour? Given the same end, a programmer may use either a recursive function, or an iterative one &#x2013; and in order to understand the differences in their performance, we must know the <em>being</em> of the algorithm, sometimes in <a href="https://en.wikipedia.org/wiki/P_versus_NP_problem" rel="noreferrer noopener">deep, information-theoretic</a> ways.</p><p>It is clear now, that we pursue the study of being in all of the constitutive sciences. So now it remains for us to ask: how does metaphysics differ, when it comes to the study of being?</p><h2 id="metaphysics-as-a-study-of-all-beings">Metaphysics as a study of all beings</h2><p>Now, we approach the essential difference between metaphysics and the physical sciences. Metaphysics is a study of not only <em>a</em> being, within the method and discipline of <em>a</em> specific science. But rather, <em>metaphysics is a study of the nature of all beings, </em>in our human-reality.</p><p>What does it mean to be a discipline which aims to study <em>all</em> beings? After all, the being of a rock, and a table, seems fundamentally different from the being of an algorithm or equation. But yet, the fact that they all exist, implies some deep and fundamental commonalities.</p><p>Thus lies the difficulty and abstractness of metaphysical inquiry. If we want to provide an account for being, it must be able to satisfy not just one being, or a category of beings &#x2013; but all beings, from oak trees to <a href="https://en.wikipedia.org/wiki/Search_tree" rel="noreferrer noopener">search trees</a>, and everything in between. To quote the words of Martin Heidegger (a metaphysician of the first order):</p><blockquote><strong>The question is the broadest in scope</strong>. It comes to a halt at no being of any kind whatsoever. The question embraces all that is, and that means not only what is now present at hand in the broadest sense, but also what has previously been and what will be in the future. The domain of this question is limited only by what simply is not and never is: by Nothing<br>...<br>Given the unrestricted range of the question, <strong>every being counts as much as any other. Some elephant in some jungle in India is in being just as much as some chemical oxidation process on the planet Mars</strong>, and whatever else you please.<br>&#x2013; Martin Heidegger, <a href="https://yalebooks.yale.edu/book/9780300083286/introduction-metaphysics" rel="noreferrer noopener">Introduction to Metaphysics</a>.</blockquote><p>Wow! Isn&apos;t it audacious, to think that we can attempt to account, or theorise, about the nature of all beings? Isn&apos;t it both scary, but also exciting? To think that there is something which puts me into a common relation, with a tree, with a boulder, with equations and phone numbers and elephants too &#x2013; some elusive foundation behind our human-reality.</p><p>This is the pursuit of metaphysics. It is the study of the broadest question, the question of <em>all</em> beings. But along with it, metaphysics is also the study of the deepest question. The question of <em>why are there beings at all</em>. To quote Heidegger once again:</p><blockquote>Why are there beings at all? Why&#x2014;that is, what is the ground? From what ground do beings come? On what ground do beings stand? To what ground do beings go? The question does not ask this or that about beings&#x2014;what they are in each case, here and there, how they are put together, how they can be changed, what they can be used for, and so on. <strong>The questioning seeks the ground for what is, insofar as it is in being.</strong><br>&#x2013; Martin Heidegger, <a href="https://yalebooks.yale.edu/book/9780300083286/introduction-metaphysics" rel="noreferrer noopener">Introduction to Metaphysics</a>.</blockquote><p>That question is the &quot;elusive foundation&quot; behind our human-reality, that metaphysics aims to pursue. We are all united by our being, by the fact that we <em>are</em>, as opposed to <em>we are not</em>. Why are there beings, asks Metaphysics. <em>Why are there any beings at all?</em></p><p><strong>The question of being is the fundamental question of metaphysics.</strong></p><p>And just like with the <a href="https://en.wikipedia.org/wiki/Riemann_hypothesis" rel="noreferrer noopener">Riemann hypothesis</a> or the <a href="https://en.wikipedia.org/wiki/Theory_of_everything" rel="noreferrer noopener">Standard model</a>, or the apogee of any art and scientific endeavour, it remains an open question.</p><p>It is, one of the <a href="https://en.wikipedia.org/wiki/List_of_unsolved_problems_in_philosophy#Metaphysics" rel="noreferrer noopener">Unsolved Problems of Philosophy.</a></p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://en.wikipedia.org/wiki/List_of_unsolved_problems_in_philosophy#Metaphysics"><div class="kg-bookmark-content"><div class="kg-bookmark-title">List of unsolved problems in philosophy - Wikipedia</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://en.wikipedia.org/static/apple-touch/wikipedia.png" alt="The Necessity of Metaphysics: An Introduction for Computer Scientists and the Physical Sciences"><span class="kg-bookmark-author">Wikimedia Foundation, Inc.</span><span class="kg-bookmark-publisher">Contributors to Wikimedia projects</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://upload.wikimedia.org/wikipedia/commons/c/cf/Philbar_3.png" alt="The Necessity of Metaphysics: An Introduction for Computer Scientists and the Physical Sciences"></div></a></figure><h2 id="the-practical-necessity-of-metaphysics">The Practical Necessity of Metaphysics</h2><p>We will conclude today&apos;s discussion, with a quick examination of the utility of metaphysics <em>in practice</em>. I hope my sketch of metaphysics has awakened your wonder and curiosity, the same aesthetic appreciation that unites all of us in our studies and inquiries. At the same time, metaphysics &#x2013; like any abstract scientific pursuit &#x2013; is also a bountiful source of practical utility and applications. And although metaphysics is more abstract than most &#x2013; the pursuit of its study opens up more derivative fields of inquiry, that can have surprising relevance to our day-to-day lives.</p><p>For you see, the relationship between metaphysics and philosophy, is akin to the relationship between physics and engineering &#x2013; or computer science, and software development. The abstract, formal laws of the former, will allow us to derive the facts and methods of the latter.</p><p>The relationship between philosophy and metaphysics is keener than most, for many of the most interesting and practical questions of philosophy, find their ground eventually, in the question of being.</p><p>Consider the question of happiness, for one! <em>What does it take, to live a happy live</em>? If you were to ask Aristotle, <a href="https://plato.stanford.edu/entries/aristotle-ethics/" rel="noreferrer noopener">he would have said</a> that <a href="https://en.wikipedia.org/wiki/Eudaimonia" rel="noreferrer noopener"><em>eudaemonia</em></a> (a sense of virtuous flourishing, happiness) comes from acting according to the &quot;characteristic activity of the human being.&quot;<em> What is the characteristic activity of a human being?</em> This is, ultimately &#x2013; a question of ontology. It is a question about the <em>being</em> of a human!</p><p>Likewise, consider the question of politics. <em>How does one create a good government? </em>For Thomas Hobbes, a good government is one held by a strong, authoritarian sovereign, <a href="https://en.wikipedia.org/wiki/Leviathan_(Hobbes_book)" rel="noreferrer noopener">who holds absolute power</a> over all men. How did he come to this conclusion? It&apos;s because Hobbes believes that the being of man is fundamentally selfish, with &quot;the life of man [in nature being] solitary, poor, nasty, brutish, and short.&quot; But someone like Rousseau would have <a href="https://plato.stanford.edu/entries/rousseau/#PoliPhil" rel="noreferrer noopener">entirely different views on government</a>, all due to a different conception of the ontology of humanity.</p><p>Finally, consider our personal lives, and our struggles with living. What is authenticity? What is true love? All of these are questions, which we will face in our own journey. And the answer to these questions, all have a foundation, in examining the being of our nature, as well as the <a href="https://en.wikipedia.org/wiki/Stendhal" rel="noreferrer noopener">being of love</a>.</p><p>This is why metaphysics is so exciting for me. It is the theoretical physics of Philosophy, the understanding of which will further our exploration in other fields of the human condition.</p><p>Metaphysics is a study of being, which serves not only as the broadest question, but also the deepest question.</p><p>And through this study of being, not only will we come to a better understanding of the other beings in our reality.</p><p>But also examine our own beings, and learn what it means <em>to be.</em></p><hr><p><em>Thank you for reading my article on metaphysics. If you have any comments questions, or philosophical musings of your own, feel free to </em><a href="https://shen.hong.io/contact/"><em>reach out and contact me</em></a><em>! I am always looking for conversations with interesting strangers, and pen-pals of philosophy and contemplation.</em></p>]]></content:encoded></item><item><title><![CDATA[Building a Philosophy Workstation with NixOS: Installing Firefox, VSCodium, and LaTeX]]></title><description><![CDATA[A NixOS tutorial on creating a Philosophy Workstation with Firefox, VSCodium, and LaTeX.]]></description><link>https://shen.hong.io/nixos-for-philosophy-installing-firefox-latex-vscodium/</link><guid isPermaLink="false">61ecda2e5d1f270001b13c6d</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Mon, 31 Jan 2022 02:00:00 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/women-writing-a-letter.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/women-writing-a-letter.webp" alt="Building a Philosophy Workstation with NixOS: Installing Firefox, VSCodium, and LaTeX"><p>Hey there! I&apos;m <a href="https://shen.hong.io/about/">Shen</a>, and I&apos;m a Philosophy and Computer Science student. My area of interest is in <a href="https://plato.stanford.edu/entries/metaphysics/">metaphysics and ontology</a> &#x2013; and in this tutorial, I will show you how to setup a workstation for Philosophy using <a href="https://nixos.org/">NixOS</a>. </p><p>This tutorial is designed for the Philosophy student who is familiar and comfortable with Linux in general, but new to NixOS. It may also be suitable for Computer Science students and Software Developers, with some modifications. The tutorial will walk you through the process step-by-step and provide ample links and explanations. We will be continuing from where the <a href="https://shen.hong.io/nixos-home-manager-wayland-sway/">previous guide</a> left off, starting with wireless networking, Home Manager, and a basic Sway graphical environment. In this guide (Part II), we will install and configure the software needed to <em>practice</em> philosophy:</p><ul><li><a href="https://www.mozilla.org/en-US/firefox/new/"><strong>Firefox</strong></a> &#x2013; For reading papers, and writing blog posts! <a href="https://en.wikipedia.org/wiki/Tractatus_Logico-Philosophicus#Description_and_context">Unlike Wittgenstein</a>, we can (thankfully) publish more than one work in our lifetime. </li><li><a href="https://www.latex-project.org/"><strong>TeX/LaTeX</strong></a> &#x2013; Used to write essays and longer-form content. LaTeX allows us to focus on the <a href="https://plato.stanford.edu/entries/aristotle-causality/"><em>formal cause</em></a> of our essay, instead of grappling with the material cause. Plato didn&apos;t write his dialogues on Microsoft Word, and <a href="https://shen.hong.io/dialogue-on-the-questions-of-being/">neither should you</a>.</li><li><a href="https://vscodium.com/"><strong>VSCodium</strong></a> &#x2013; We will use VSCodium (an open-source fork of VSCode) to write LaTeX documents. We use VSCodium because it&apos;s extensibility allows us to use a <a href="https://valentjn.github.io/ltex/index.html">custom language engine</a>. The syntax highlighting will be helpful for students of the <a href="https://en.wikipedia.org/wiki/Analytic_philosophy">Analytic tradition</a> as well. (<em>Full disclosure: I am a student of the </em><a href="https://en.wikipedia.org/wiki/Existentialism"><em>Continental</em></a><em> tradition.</em>)</li></ul><p>For Part III, we will also do the following:</p><ul><li><a href="https://element.io/"><strong>Element Messanger</strong></a> &#x2013; Needed for correspondance with other philosophers and students of philosophy. I&apos;m available on Matrix &#x2013; feel free to <a href="https://shen.hong.io/contact/">reach out to me</a> about this post!</li><li><strong>Bluetooth</strong> (+HiFi Music Support) &#x2013; Music is philosophy set to rhymth, and the contemplation of harmony is essential for <a href="https://en.wikipedia.org/wiki/Boethius#De_institutione_musica">the attunement of our soul</a>. Just remember <a href="http://www.perseus.tufts.edu/hopper/text?doc=urn:cts:greekLit:tlg0059.tlg030.perseus-eng1:3.398">the advice of Socrates</a>, and avoid listening to the <a href="https://en.wikipedia.org/wiki/Musical_system_of_ancient_Greece#The_oldest_harmoniai_in_three_genera">Ionian and Lydian modes</a>!</li></ul><p>The ontology of this tutorial is dialectical in nature. Hence we will not provide a complete configuration (which would only be <a href="http://www.perseus.tufts.edu/hopper/text?doc=Perseus%3Atext%3A1999.01.0178%3Atext%3DMeno%3Apage%3D97">true opinion, and not knowledge</a>) &#x2013; but take the reader step-by-step through the process of building a NixOS workstation.</p><h2 id="installing-and-configuring-firefox">Installing and Configuring Firefox</h2><p>Let&apos;s begin with installing and configuring Firefox. We&apos;ll be adding Firefox to our <code>configurations.nix</code> file using Home Manager. Having Firefox available on our NixOS system is as easy as adding:</p><pre><code class="language-nix">programs.firefox = {
    enable = true;
};</code></pre><p>Followed by running <code>sudo nixos-rebuild --switch</code>. But wait! We are not done yet. For this configuration would only give us a bare-bones, default Firefox installation. Could we manage our settings (and even browser plugins) through our NixOS config as well? That is certainly possible. In fact, Home Manager offers a <a href="https://nix-community.github.io/home-manager/options.html#opt-programs.firefox.enable">wide array of options</a> for configuring Firefox. All of these settings are held within the (Firefox) user profile, so we can begin by setting up a default profile for Firefox:</p><pre><code class="language-nix">programs.firefox = {
    enable = true;
    profiles.default = {
        id = 0;
        name = &quot;Default&quot;;
    };
};</code></pre><p>It is within this <code>profiles.default</code> scope where we can customise our Firefox installation. Here, <a href="https://nix-community.github.io/home-manager/options.html#opt-programs.firefox.profiles._name_.settings">you can add directives</a> such as <code>userChrome</code> or <code>userContent</code>, which allows you to change the look and interface of the Firefox UI in powerful ways. For example, you can use the userChrome directive to add custom CSS styles which can <a href="https://superuser.com/a/1261661">remove the Firefox tab bar</a>. This would be useful if you are using a browser extension such as <a href="https://addons.mozilla.org/en-US/firefox/addon/tree-style-tab/">Tree Style Tabs</a>.</p><h3 id="configuring-firefox-settings-through-nixos">Configuring Firefox Settings Through NixOS</h3><p>For our tutorial, we will add the <code>settings</code> directive within <code>profiles.default</code>. This allows us to configure Firefox with our settings and preferences &#x2013; which can be both as mundane as setting a homepage, to disabling the default telemetry, debugging, and the <a href="https://help.getpocket.com/article/913-pocket-for-firefox-faq">sponsored Pocket integration</a>. Previously, you would have had to dig through Firefox&apos;s settings page, but with the power of NixOS, we can configure it all in one place! Let&apos;s begin by setting our homepage:</p><pre><code class="language-nix">programs.firefox = {
    enable = true;
    profiles.default = {
        id = 0;
        name = &quot;Default&quot;;
        settings = {
            # Browser settings go here
            &quot;browser.startup.homepage&quot; = &quot;https://shen.hong.io/&quot;;
            
        };
    };
};</code></pre><p>Note how each setting directive is a string, followed by an equal sign that points to it&apos;s value, and the line ends with a semicolon. Don&apos;t mess this up! The rest of the settings will all go within this scope.</p><p>Next, let&apos;s enable HTTPS Everywhere. This will ensure that our browser connects to websites only via HTTPS, and show a warning should it not be available. Don&apos;t worry, you can still visit (and whitelist) HTTP sites, but it&apos;s a good security measure for 2022.</p><pre><code class="language-nix"># Enable HTTPS-Only Mode
&quot;dom.security.https_only_mode&quot; = true;
&quot;dom.security.https_only_mode_ever_enabled&quot; = true;
</code></pre><p>Continuing on this theme of security and privacy, lets enable some privacy settings such as the <a href="https://en.wikipedia.org/wiki/Do_Not_Track">Do Not Track header</a>, as well as configure Firefox&apos;s <a href="https://developer.mozilla.org/en-US/docs/Web/Privacy/Tracking_Protection">built-in tracking protection</a>. These measures are no replacement for a dedicated browser extension such as <a href="https://ublockorigin.com/">UBlock Origin</a>, but it&apos;s a good start.</p><pre><code class="language-nix"># Privacy settings
&quot;privacy.donottrackheader.enabled&quot; = true;
&quot;privacy.trackingprotection.enabled&quot; = true;
&quot;privacy.trackingprotection.socialtracking.enabled&quot; = true;
&quot;privacy.partition.network_state.ocsp_cache&quot; = true;</code></pre><p>Remember! Privacy is essential to the conduct and practice of true philosophy. </p><pre><code class="language-nix"># Disable all sorts of telemetry
&quot;browser.newtabpage.activity-stream.feeds.telemetry&quot; = false;
&quot;browser.newtabpage.activity-stream.telemetry&quot; = false;
&quot;browser.ping-centre.telemetry&quot; = false;
&quot;toolkit.telemetry.archive.enabled&quot; = false;
&quot;toolkit.telemetry.bhrPing.enabled&quot; = false;
&quot;toolkit.telemetry.enabled&quot; = false;
&quot;toolkit.telemetry.firstShutdownPing.enabled&quot; = false;
&quot;toolkit.telemetry.hybridContent.enabled&quot; = false;
&quot;toolkit.telemetry.newProfilePing.enabled&quot; = false;
&quot;toolkit.telemetry.reportingpolicy.firstRun&quot; = false;
&quot;toolkit.telemetry.shutdownPingSender.enabled&quot; = false;
&quot;toolkit.telemetry.unified&quot; = false;
&quot;toolkit.telemetry.updatePing.enabled&quot; = false;

# As well as Firefox &apos;experiments&apos;
&quot;experiments.activeExperiment&quot; = false;
&quot;experiments.enabled&quot; = false;
&quot;experiments.supported&quot; = false;
&quot;network.allow-experiments&quot; = false;</code></pre><p>I admit, number of directives in the previous section was distressingly long. And I&apos;m not even sure if the options listed are complete. If you happen to know of any other settings that will help improve the privacy of a Firefox user, please <a href="https://shen.hong.io/contact">reach out</a> and let me know! In any case, we are not done yet. The last thing that we must do in our Firefox configuration, is to remove and disable the Pocket extension. These settings should completely neutralise it &#x2013; we both remove Pocket from the new tabs page, as well as delete it&apos;s API URL and the unique per-user authentication key.</p><pre><code class="language-nix"># Disable Pocket Integration
&quot;browser.newtabpage.activity-stream.section.highlights.includePocket&quot; = false;
&quot;extensions.pocket.enabled&quot; = false;
&quot;extensions.pocket.api&quot; = &quot;&quot;;
&quot;extensions.pocket.oAuthConsumerKey&quot; = &quot;&quot;;
&quot;extensions.pocket.showHome&quot; = false;
&quot;extensions.pocket.site&quot; = &quot;&quot;;
</code></pre><p>With all of the above settings, you should have a Firefox installation that&apos;s secure, privacy-friendly, and ready for use. Remember to run <code>sudo nixos-rebuild --switch</code>!</p><h3 id="where-do-i-find-additional-settings-for-firefox">Where do I find additional settings for Firefox?</h3><p>All of the settings above came from Firefox. Specifically, they are entries from Firefox&apos;s <code>about:config</code> page. If you enter that into your Firefox URL bar, it should open a special internal page which lists all the settings and their values. As far as I understand, these settings are how Firefox configures itself internally. What is an easy way to browse, or find more settings?</p><p>Unfortunately, I can&apos;t seem to find an official listing or directory of all the settings and explanations for their effects. I was able to find an user-contributed Wiki <a href="https://kb.mozillazine.org/About:config_entries">which lists many settings</a>, but the entries appear to be a little dated. If someone can help me find an official list of all <code>about:config</code> entries and their usage, I would be very grateful. <a href="https://shen.hong.io/contact">Let me know</a> if you know where to look!</p><h3 id="how-do-i-configure-extensions-like-ublock-origin-through-nixos">How do I configure extensions like uBlock Origin through NixOS?</h3><p>This... isn&apos;t actually a rhetorical question. I genuinely wish to know! Right now, I have simply installed my Firefox extensions manually from within Firefox. I&apos;d like to be able to manage my extensions through NixOS and Home Manager as well, but I&apos;m not sure how. If you know of a way to manage Firefox extensions through Nix, I would be grateful if you <a href="https://shen.hong.io/contact">can write to me</a>.</p><h2 id="installing-latex-on-nixos">Installing LaTeX on NixOS</h2><p>After FireFox, installing LaTeX is a relatively simple affair. LaTeX is a document typesetting engine used to produce academic and technical documents, and it is an essential part of my personal philosophy workflow. LaTeX under NixOS is offered by the <code>texlive</code> scheme, with <a href="https://nixos.wiki/wiki/TexLive">different schemes for different numbers of LaTeX packages</a>. If space or installation time is a constraint, it may be useful to go with a smaller scheme like <code>texlive.combined.scheme-medium</code>, but I personally didn&apos;t worry and went with the full installation with all LaTeX packages:</p><pre><code class="language-nix"># Packages installed (under Home Manager)
home.packages = with pkgs; [
    # Full LaTeX installation with all packages
    texlive.combined.scheme-full
];</code></pre><p>The above directive, followed by a quick <code>sudo nixos-rebuild --switch</code> &#x2013; and LaTeX is installed! It&apos;s available via <code>latex</code> or <code>lualatex</code>. Isn&apos;t NixOS magical?</p><p>LaTeX support in NixOS is important to me. I write <a href="https://github.com/ShenZhouHong/kant-metaphysics/releases">all my papers</a> in LaTeX &#x2013; as it allows me to focus on the content of my discourse, rather than it&apos;s layout. That&apos;s not to say LaTeX documents aren&apos;t beautiful on their own, however! Over the years, I&apos;ve made my <a href="https://github.com/ShenZhouHong/latex-essay">own LaTeX template</a> which comes with all the typographic bells and whistles of professional typesetting. It&apos;s also <a href="https://en.wikipedia.org/wiki/PDF/A">PDF/A compliant</a> (archival grade), for the day I die and <a href="https://dialektika.org/en/2018/04/10/on-freedom-and-death-in-sartre/">become one with the facticity of historicity</a>. And of course, LaTeX&apos;s powerful <a href="https://www.ctan.org/pkg/tkz-euclide">TKZ-Euclide package</a> allows the <a href="https://en.wikipedia.org/wiki/Dialogue_Concerning_the_Two_Chief_World_Systems">Galileo&apos;s <em>Geometra filosofico</em></a><em> </em>to construct demonstrations of the same apodeictic certainty as <a href="https://en.wikipedia.org/wiki/Euclid%27s_Elements">Euclid</a>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/01/example-hjelmslev.png" class="kg-image" alt="Building a Philosophy Workstation with NixOS: Installing Firefox, VSCodium, and LaTeX" loading="lazy" width="1468" height="1282" srcset="https://shen.hong.io/content/images/size/w600/2022/01/example-hjelmslev.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/example-hjelmslev.png 1000w, https://shen.hong.io/content/images/2022/01/example-hjelmslev.png 1468w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Example of a TKZ-Euclide Construction in LaTeX. Excerpt from my essay on the Hjelmslev transformation (which is ironically, on non-Euclidean Geometry).</span></figcaption></figure><p>The completely text-based format of LaTeX makes it easy to commit one&apos;s work to version control, which makes it easy to keep track of <a href="https://github.com/ShenZhouHong/kant-metaphysics/commits/master">the progression of one&apos;s thoughts and changes</a> through time. Immanuel Kant certainly wished that he had <code>git</code> while he was working on his <a href="https://en.wikipedia.org/wiki/Critique_of_Pure_Reason">Critique of Pure Reason</a>. Alas, he didn&apos;t &#x2013; and that&apos;s why <a href="https://www.hackettpublishing.com/critique-of-pure-reason">modern editions of his text</a> have to put up with very strange marginal page numbering schemes like <code>14[b]</code> and <code>188[a]</code>, just to keep track of the differences between the first and second editions. <a href="https://en.wikipedia.org/wiki/Prolegomena_to_Any_Future_Metaphysics"><em>Whatever</em> the metaphysics of the future is</a>, it will be version-controlled with <code>git</code> and paginated with <code>diff</code>.</p><h2 id="installing-vscodium-and-configuration-for-the-java-language-engine">Installing VSCodium, and Configuration for the Java Language Engine</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://shen.hong.io/content/images/2022/01/Screenshot-from-2022-01-29-14-36-08.png" class="kg-image" alt="Building a Philosophy Workstation with NixOS: Installing Firefox, VSCodium, and LaTeX" loading="lazy" width="2000" height="1333" srcset="https://shen.hong.io/content/images/size/w600/2022/01/Screenshot-from-2022-01-29-14-36-08.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/Screenshot-from-2022-01-29-14-36-08.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/01/Screenshot-from-2022-01-29-14-36-08.png 1600w, https://shen.hong.io/content/images/2022/01/Screenshot-from-2022-01-29-14-36-08.png 2256w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Working with a LaTeX Document (my thesis on Sartre) with VSCodium</span></figcaption></figure><p>Now that we have LaTeX installed, it&apos;s time to install and configure VSCodium so we have an editor to work with. VSCodium is an open-source fork of Microsoft&apos;s VSCode, a powerful and highly configurable text editor. Named in honor of <a href="https://en.wikipedia.org/wiki/Charles_Cotin">Charles Cotin</a>, the 15th century French philosopher and polyglot, both Cotin and VSCode are known for their <a href="https://code.visualstudio.com/api/language-extensions/overview">support of many languages</a>. VSCodium is used for academic writing, but it is also popular amongst Programmers and Software Developers as well.</p><pre><code class="language-nix"># Install VSCodium under Home Manager
programs.vscode = {
    enable = true;
    # Make sure to use the open source vscodium
    package = pkgs.vscodium;
};</code></pre><p>Add the above within Home Manager&apos;s scope in <code>configurations.nix</code>, and VSCodium should be available. You can verify it by running <code>codium</code> in the command line to launch it. However, we are not finished with it yet. We still have two plugins to install:</p><ul><li>LaTeX Workshop for LaTeX Syntax Highlighting and Support</li><li>LTeX for Grammar and Spell-checking.</li></ul><p>We&apos;ll install both plugins in the next step through VSCodium&apos;s GUI. Although Home Manager does offer <a href="https://nix-community.github.io/home-manager/options.html#opt-programs.vscode.extensions">a way to define VSCodium plugins</a> through the <code>configurations.nix</code> file, the two plugins above do not appear to be packaged. If the situation has changed, and the plugins are available &#x2013; <a href="https://shen.hong.io/contact">please let me know</a>!</p><h3 id="installing-the-latex-workshop-and-ltex-plugin-through-vscodium-gui">Installing the LaTeX Workshop and LTeX Plugin through VSCodium GUI</h3><p>First, we will install the LaTeX Workshop plugin. This plugin adds syntax highlighting for <code>.tex</code> files, as well as useful autocompletion for LaTeX and packages. Simply search for it in the Extensions tab of VSCodium. It should appear with the name <code>LaTeX Workshop</code>, with the extension ID of <code>james-yu.latex-workshop</code>. Install it and we&apos;re done &#x2013; it does not require any additional configuration.</p><p>Next, we install the <a href="https://valentjn.github.io/ltex/index.html">LTeX VSCode plugin</a>. LTeX is a <em>powerful</em> grammar and spelling checker, with a deep integration with LaTeX and LaTeX packages. This is not a simple dictionary by any means &#x2013; it uses the same syntax engine that Software Developers and Programmers use for computer code. Furthermore, advanced users can configure LTeX to use n-Gram based <a href="https://valentjn.github.io/ltex/settings.html#ltexadditionalruleslanguagemodel">language models</a>, pre-trained <a href="https://valentjn.github.io/ltex/settings.html#ltexadditionalrulesneuralnetworkmodel">neural networks</a>, and <a href="https://valentjn.github.io/ltex/settings.html#ltexadditionalrulesword2vecmodel">Word2Vec language models</a>. It&apos;s certainly a plugin that the younger Wittgenstein would be proud of &#x2013; just imagine how useful symbol definition lookups and keyword autocompletion would have been, when writing the <a href="https://people.umass.edu/klement/tlp/tlp-hyperlinked.html#bodytextOgden">Tractatus Logico-Philosophicus</a>!</p><p>For the actual installation, likewise navigate to the Extensions tab of VSCodium, and search for <code>LTeX</code>. It should appear with the name <code>LTeX &#x2013; LanguageTool grammar/spell checking</code> and the extension ID is <code>valentjn.vscode-ltex</code>. Install this now &#x2013; we will have to configure it next.</p><h3 id="configuring-java-for-the-ltex-plugins-local-lsp-server">Configuring Java for the LTeX plugin&apos;s Local LSP Server</h3><p>The LTeX plugin works by running a local instance of a Language Server that implements the <a href="https://en.wikipedia.org/wiki/Language_Server_Protocol">LSP (Language Server Protocol)</a>, which requires Java. Unfortunately, due to <a href="https://en.wikipedia.org/wiki/POSIX">NixOS&apos;s idiosyncratic ontology</a>, the included Java executable does not work, so we must first install Java using Home Manager and have it available to our user.</p><pre><code class="language-nix"># Install the Open Source Java Runtime under Home Manager
programs.java = {
    enable = true;
    package = pkgs.jdk11;
};</code></pre><p>We use the <code>package</code> directive to simply tell NixOS to use the open source implementation of the Java Runtime, instead of Oracle&apos;s version. Run a <code>sudo nixos-rebuild --switch</code>. Now, we must restart our workstation. <strong>Restarting is essential in this process:</strong> NixOS must not only install Java, but the Java executable must be included in the <code>$PATH</code> variable of our user. The <code>$PATH</code> is essentially a list of places that your computer searches through, when you run a program like <code>vim</code> or <code>git</code>. Likewise, NixOS must set the <code>$JAVA_HOME</code> variable, which is a variable that Java Programs expect. As far as I can tell, these variables are only set properly when the computer is restarted after installation.</p><p>Once your computer has rebooted, we must retrieve the <code>$JAVA_HOME</code> path. Do this by simply running:</p><pre><code class="language-bash">echo $JAVA_HOME</code></pre><p>In the terminal. You should see a path which leads to your Java executable. Copy this path, and navigate into the settings page of LTeX in VSCodium. Find the setting listed as <code>ltex.java.path</code>, and paste your <code>$JAVA_HOME</code> output there.</p><p>Now restart VSCodium, and the LTeX plugin should work! Now, you should have completed the configuration of your writing space. With VSCodium and LTeX, you have a solid tool for writing essays &#x2013; the material of philosophical inquiry. And oh, what wonderful essays you will write! </p><h3 id="but-what-kind-of-essays-can-i-write-i-dont-know-any-philosophy-i-dont-know-anything-at-all">But what kind of essays can I write? I don&apos;t know any philosophy! I don&apos;t know anything at all!</h3><p>Socrates knew nothing, and he was the wisest man in Athens.</p><h3 id="no-but-seriously-%E2%80%93-what-should-i-write-about">No, but seriously &#x2013; what should I write about?</h3><p>Why not write about yourself? Philosophy is fundamentally an act of contemplation &#x2013; and there is no better form of contemplation than the contemplation of the self. <a href="https://en.wikipedia.org/wiki/Know_thyself">Know thyself</a>, was the inscription at the Temple of the Oracle of Delphi. There can be great pleasure and virtue, in writing essays about your world and what you know.</p><p>This is not a sophistry &#x2013; I genuinely mean it. Write about <em>yourself</em>. There&apos;s no topic that you know better than your own being. And in this process, you will follow the path of Michel de Montaigne, who wrote essays too. Montaigne sat down one day, in the evening of his life. In front of a philosophy workstation not too dissimilar to our own. He had also pondered, about the question of what to write about.</p><p>He realised very quickly that he knew little. He saw himself before all the experts of the world &#x2013; the geometers and theologians, and realised he can hope to offer nothing useful in that regard. He was a courtier and a statesman &#x2013; an occupation usual for a nobleman of his rank. But his career is uninteresting, and his hair is greying, and he knew he was not going to create a treatise of political philosophy or any lofty work of grandeur and acclaim.</p><p>&quot;What do I know?&quot; Asked Michel de Montaigne. And so he decided to write, about the only thing that he knew well. He wrote about himself. He wrote <em>for</em> himself. He examined the world through his eyes, and tried to contemplate about its questions &#x2013; without thought as to approval or greatness. He wrote essays <a href="https://en.wikisource.org/wiki/The_Essays_of_Montaigne/Book_I/Chapter_XXVII">On Friendship</a>, and <a href="https://en.wikisource.org/wiki/The_Essays_of_Montaigne/Book_I/Chapter_XXXVIII">On Solitude</a>. <a href="https://en.wikisource.org/wiki/The_Essays_of_Montaigne/Book_I/Chapter_XXV">On the Education of Children</a>. He wrote about the little things that he knew well. And throughout this all, he wrote with humility and contemplation, with genuine curiosity and goodwill. Above all, he wrote with frankness and sincerity. When the court was abuzz with sensation and scandal, upon hearing stories <a href="https://en.wikisource.org/wiki/The_Essays_of_Montaigne/Book_I/Chapter_XXX">On Cannibals</a> and &quot;savages&quot; from the New World, Montaigne wrote about how:</p><blockquote>I do not believe, from what I have been told about this people, that there is anything barbarous or savage about them.</blockquote><p>But directed the eye of his fellows towards the cruelty of the Inquisition, and the affairs of their own &quot;civilised&quot; states.</p><blockquote>I consider it more barbarous to eat a man alive than to eat him dead; to tear by rack and torture a body still full of feeling, ... a practice which we have not only read about but seen within recent memory, not between ancient enemies, but between neighbours and fellow-citizens and, what is worse, under the cloak of piety and religion - than to roast and eat a man after he is dead.</blockquote><p>It&apos;s hard to write, because we all ask ourselves Montaigne&apos;s question: &quot;What do I know?&quot; And just like Montaigne, we find ourselves facing the experts of the world, the <a href="https://en.wikipedia.org/wiki/Terence_Tao">great mathematicians</a> and <a href="https://en.wikipedia.org/wiki/Donald_Knuth">computer scientists</a> &#x2013; who have written volumes and monographs, irreproachable in their perfection. But you are still the foremost expert of yourself, if you strive towards contemplation and curiosity. And like Michel de Montaigne, you can write about your world, and what you know &#x2013; in bits and dabbles and little blog posts too.</p><p>Ultimately, <a href="https://en.wikipedia.org/wiki/Essays_(Montaigne)">Montaigne&apos;s writings</a> &#x2013; his attempts at understanding his world &#x2013; <a href="https://en.wikipedia.org/wiki/Essay#Definitions">gave us the literary genre of the </a><a href="https://en.wikipedia.org/wiki/Essay#Definitions"><em>essay</em></a><a href="https://en.wikipedia.org/wiki/Essay#Definitions"> itself</a> (<em>essayer</em>: French, &quot;to try&quot;) . It catapulted him to fame, and he is considered one of the foremost philosophers and humanists of the European Renaissance. But this all began very slowly &#x2013; with curious person, sitting in front of a workstation. A Philosophy workstation.</p><p>Who picks up their pen, and began to write.</p><pre><code class="language-bash">sudo nixos-rebuild --switch
touch  my-essay.tex
codium my-essay.tex</code></pre><hr><p><em>Thank you for reading Part II of my guide in building a Philosophy Workstation with NixOS. If you have any questions or comments, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em> &#x2013; I would love to know if my guide has helped you in any way! I enjoy exchanging emails with interesting strangers, and look forwards to your correspondence.</em></p>]]></content:encoded></item><item><title><![CDATA[Building a Philosophy Workstation with NixOS: Learning Home Manager and Configuring Sway with Wayland]]></title><description><![CDATA[A NixOS tutorial on using Network Manager, Home Manager, and setting up a Graphical Environment with Wayland and Sway]]></description><link>https://shen.hong.io/nixos-home-manager-wayland-sway/</link><guid isPermaLink="false">61ec9fef5d1f270001b1394b</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Sun, 23 Jan 2022 04:29:44 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/writing-a-letter.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/writing-a-letter.webp" alt="Building a Philosophy Workstation with NixOS: Learning Home Manager and Configuring Sway with Wayland"><p>This tutorial is Part I in a series on configuring a default <a href="https://nixos.org/download.html#nixos-iso">Minimal installation</a> of NixOS to a state where productive philosophy can be done. We will start from a text-only environment that has minimal amenities (e.g. where the <a href="https://shen.hong.io/installing-nixos-with-encrypted-root-partition-and-seperate-boot-partition/">previous installation guide</a> left off), and slowly build up our <code>configuration.nix</code> file until we have a graphical user interface, as well as all the applications that are conductive towards producing philosophy. In this series, we will complete the following:</p><ul><li>Configure networking using NetworkManager (e.g <code>nmcli</code>)</li><li>Install low-level systems applications (e.g. <code>git</code>, <code>wget</code>, <code>parted</code>, and <code>vim</code>)</li><li>Learn about and use Home Manager to manage our user-level applications.</li><li>Install and configure the Sway display manager with the Wayland display server</li><li>Fine tune the Sway graphical environment, enabling buttons for brightness and volume control, and enabling audio (Part II).</li><li>Install and configure VSCodium, LaTeX, Firefox, and other tools needed to produce philosophy (Part II).</li></ul><p>This tutorial is designed for the <a href="https://shen.hong.io/about/">philosophy student</a> who&apos;s familiar and comfortable with Linux in general, but new to NixOS. It may also be suitable for Computer Science students and Software Developers, with some modifications. It will walk you through the process step-by-step and provide ample links and explanations.</p><p>The ontology of this tutorial is dialectical in nature. Hence we will not provide a complete configuration (which would only be <a href="http://www.perseus.tufts.edu/hopper/text?doc=Perseus%3Atext%3A1999.01.0178%3Atext%3DMeno%3Apage%3D97">true opinion, and not knowledge</a>) &#x2013; but take the reader step-by-step through the process of building a NixOS workstation.</p><h2 id="installing-and-using-networkmanager-for-wifi-connections">Installing and Using NetworkManager for WiFi Connections</h2><p>The first, and one of the most important steps of any Linux installation, is to ensure that we have WiFi connectivity. After all, the Internet is one of the most direct embodiments of our <a href="https://en.wikipedia.org/wiki/Heideggerian_terminology#Being-with"><em>Mitsein</em></a>! </p><h3 id="adding-networkmanager-to-configurationsnix">Adding NetworkManager to <code>configurations.nix</code></h3><p>There are many ways to manage Internet connectivity in Linux. We will use <a href="https://wiki.archlinux.org/title/NetworkManager">NetworkManager</a>, a high-level command line interface that simplifies the process &#x2013; as well as comes with many GUI interfaces which we can add later on. In your <code>/etc/nixos/configuration.nix</code>, add the appropriate options in the <em>first</em> level of nested brackets (right after imports and account configuration). </p><figure class="kg-card kg-code-card"><pre><code class="language-Nix">{ config, pkgs, ... }:

{
  imports =
    [ # Include the results of the hardware scan.
      ./hardware-configuration.nix 
    ];

  # ...
  
  # Networking Configuration
  networking.hostName = &quot;my-philosophy-workstation&quot;; # Define your hostname.
  networking.networkmanager.enable = true;
  # networking.networkmanager.wifi.backend = &quot;iwd&quot;;

  # The global useDHCP flag is deprecated, therefore explicitly set to false here.
  # Per-interface useDHCP will be mandatory in the future, so this generated config
  # replicates the default behaviour.
  networking.useDHCP = false;
  networking.interfaces.eno0.useDHCP = true;
  networking.interfaces.wls6.useDHCP = true;
};</code></pre><figcaption><p><span style="white-space: pre-wrap;">The default </span><code spellcheck="false" style="white-space: pre-wrap;"><span>configuration.nix</span></code><span style="white-space: pre-wrap;"> file.</span></p></figcaption></figure><p>We will enable DHCP on both of our wired <code>en0</code> as well as wireless <code>wls6</code> interfaces &#x2013; these options should already be there from your original installation.</p><p>Note how all of our options (e.g. such as <code>hostname</code>, <code>networkmanager.enable</code>, etc) are within the <code>networking</code> namespace. This makes sense, as they all relate to configuring the networking of your NixOS installation. Do you wonder what other options are available? NixOS has a wonderful <a href="https://search.nixos.org/options?channel=unstable&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=networking.networkmanager">options search on their website,</a> where you can look up any options. This is a good resource to keep in mind as you progress through this guide!</p><p>Now run a quick rebuild to allow NixOS to install <code>NetworkManager</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">sudo nixos-rebuild --switch</code></pre><figcaption><p><span style="white-space: pre-wrap;">Command used to &quot;rebuild&quot; the system after a configuration change.</span></p></figcaption></figure><p>Now Network Manager should be available on the terminal via the <code>nmcli</code> command. If you run <code>sudo nmcli</code>, you should receive a colorful status output, where it lists various devices that are available (such as ethernet, wifi, and the odd <a href="https://en.wikipedia.org/wiki/Loopback#Virtual_network_interface">loopback interface</a>).</p><h3 id="adding-the-networkmanager-group-to-our-user-account">Adding the <code>networkmanager</code> group to our User Account</h3><p>Is using <code>sudo</code> (or <code>root</code>) a <a href="https://plato.stanford.edu/entries/necessary-sufficient/#PhilCond">neccessary condition</a> for accessing <code>nmcli</code>? Or is the need privilege escalation only contingent on our account&apos;s lack of permission, which can be remedied in other ways? In fact, you can add the <code>networkmanager</code> group to your user account, so you can access <code>nmcli</code> without needing elevated privileges. Simply modify the <code>extraGroups</code> directive in your <code>users.users.[username]</code> option, and add <code>networkmanager</code> as a group for your account:</p><pre><code class="language-nix">  users.users.myUserAccount = {
    isNormalUser = true;
    shell = pkgs.zsh;
    extraGroups = [ &quot;wheel&quot; &quot;networkmanager&quot;];
  };</code></pre><h3 id="connecting-to-wifi-using-nmcli-practical-steps">Connecting to Wifi using <code>nmcli</code>: Practical Steps</h3><p>Now our configuration of NetworkManager is complete. All that remains is to learn the practical steps of connecting to wifi. I&apos;ll guide you through the commands that I use to connect to WiFI &#x2013; but keep in mind that you&apos;ll always have more certain references at hand, through <code>nmcli --help</code> and <code>man nmcli</code>.</p><p><strong>Scan for WiFi networks</strong> in a new location:</p><pre><code class="language-bash">nmcli device wifi rescan</code></pre><p><strong>List available WiFi networks</strong> (these are the ones you just scanned!):</p><pre><code class="language-bash">nmcli device wifi list</code></pre><p>You should now see a colorful array of WiFI names (SSIDs), as well as their signal strength. Find the name of the network you wish to connect to, and then type:</p><pre><code class="language-bash">nmcli device wifi connect WiFiName --ask</code></pre><p>NetworkManager should ask for your password, and now you should be successfully connected to the network. You can test your connectivity using <code>nmcli networking connectivity check</code>, or even just by pinging a nearby server (i.e. <code>ping 8.8.8.8</code>).</p><p>Why do we invoke the command with the option <code>--ask</code>? Check out the <a href="https://en.wikipedia.org/wiki/Man_page">Linux manual</a> entry for <code>nmcli</code> in order to find out!</p><pre><code class="language-bash">man nmcli</code></pre><p>NetworkManager auto-magically saves all of your WiFi connections. In the future, when you return to a location where you have connected before, you don&apos;t need to enter the credentials again. Simply run the following in order to list all your previous connections:</p><pre><code class="language-bash">nmcli connection show</code></pre><p>And then run:</p><pre><code class="language-bash">nmcli connection up &lt;WiFi Name&gt;</code></pre><p><code>NetworkManager</code> should actually connect to known networks automatically &#x2013; the above instructions are useful as a backup.</p><h3 id="connecting-to-enterprise-education-and-other-networks-a-complication">Connecting to Enterprise, Education, and Other Networks: A Complication</h3><p>The above workflow works well for <em>simple</em> networks, namely the sort that you&apos;ll find at a coffee shop, or a friend&apos;s apartment. That&apos;s because they utilise a form of authentication called <a href="https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access">WPA-Personal</a> (or WPA2, WP3). That&apos;s the type of WiFi network where you only need to enter a password.</p><p>Unfortunately, most Universities, Colleges, and Institutions of Philosophy utilise a more sophisticated WiFi authentication method, called WPA-Enterprise. This is the type that requires an username and password, or even additional settings like domain.</p><p>NetworkManager supports these more complicated types of connections, but you&apos;ll need to <a href="https://github.com/wylermr/NetworkManager-WPA2-Enterprise-Setup">setup a connection profile</a>. I&apos;ll write a seperate guide detailing this more involved procedure at a later date. </p><p>(If you really need help with this step, <a href="https://shen.hong.io/contact/">feel free to email me</a>, and I&apos;ll let you know how I made my connection profile)</p><h2 id="installing-low-level-systems-applications">Installing Low-level Systems Applications </h2><p>Congratulations! If you have successfully completed the previous steps, you should be able to access the internet. We&apos;re still in the command line right now, but we can make it more friendly, by installing some familiar tools. Right after your networking configuration, add the following stanza:</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">  # List packages installed in system profile.
  environment.systemPackages = with pkgs; [
    vim
    parted
    git
    wget
  ];</code></pre><figcaption><p><span style="white-space: pre-wrap;">Installing packages in the system profile</span></p></figcaption></figure><p>After a quick <code>sudo nixos-rebuild --switch</code>, these additional packages should become installed, and available <em>system-wide.</em> Now all users will get to access <code>vim</code>, <code>parted</code>, <code>git</code>, and <code>wget</code>. This means we&apos;ll have a powerful editor available in the command line, as well as a way to check out git repositories and download files. Any number of programs can be installed on Nix this way! In fact, the NixOS website has a <a href="https://search.nixos.org/packages?channel=unstable&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=fortune">search function for packages</a> analogous to the options search. It&apos;s a good way to find out what programs are available!</p><h2 id="introduction-to-home-manager">Introduction to Home Manager</h2><p>But wait, is it really a good idea to install <em>all</em> programs on a system-wide basis? Perhaps there are programs that should only be available for the user who installs it. Likewise, many of the more advanced programs like <code>git</code> or <code>firefox</code> comes with a dizzying array of configuration options that are personal (such as setting an account name or email address). Is there any way we can install and configure programs only for the individual user?</p><p>The NixOS ecosystem does provide a powerful tool for managing settings and programs (in other words, an <em>environment</em>) on a per-user basis. This tool is called <a href="https://github.com/nix-community/home-manager">Home Manager</a>, and it has many sophisticated uses. For our purpose, we will only use it to manage programs and configuration files for our user. Here&apos;s how to use Home Manager in your <code>configurations.nix</code>.</p><h3 id="installing-and-configuring-home-manager">Installing and Configuring Home Manager</h3><p>First, install Home Manager <a href="https://nix-community.github.io/home-manager/index.html#sec-install-nixos-module">following the instructions in it&apos;s manual</a>. As with both Computing and Philosophy, we always follow upstream sources. You wouldn&apos;t read someone else&apos;s opinion about <a href="https://www.loebclassics.com/view/plato_philosopher-symposium/1925/pb_LCL166.81.xml">a Platonic dialogue</a>, would you? After adding the Home Manager channel, we can then modify our <code>configurations.nix</code> accordingly, adding the following directives:</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">  # Begin home-manager directives
  home-manager.useUserPackages = true;
  home-manager.useGlobalPkgs = true;
 
  home-manager.users.myUserAccount = { pkgs, ... }: {
    # Everything inside here is managed by Home Manager!
    
    programs.home-manager = {
      enable = true;
    };
  };</code></pre><figcaption><p><span style="white-space: pre-wrap;">Loading Home Manager</span></p></figcaption></figure><p>Why do we set <code>home-manager.useUserPackages</code> and <code>home-manager.useGlobalPkgs</code> to <code>true</code>? Check out the <a href="https://nix-community.github.io/home-manager/#sec-install-nixos-module">reasoning in the Home Manager documentation</a> to find out. You may set them both to <code>false</code>, if you like &#x2013; Philosophy is practiced by contrarians, after all.</p><p>You should have noticed by now that all directives in your <code>configurations.nix</code> file have different levels of scope, sort of like a domain name or <a href="https://plato.stanford.edu/entries/aristotle-logic/#SpeGenDif">Aristotelian category</a>. That&apos;s because NixOS configuration is written in a programming language called the <a href="https://nixos.wiki/wiki/Nix_Expression_Language">Nix Expression Language</a>. It&apos;s closely related to <a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)">Lisp and Scheme</a>, which are other programming languages that are very keen on brackets, parantheses, and Philosophy. In practical terms, this means that you can group together, and re-arrange the Nix expressions as you wish. The above configuration can also be written like this, for example:</p><pre><code class="language-nix">  # Begin home-manager directives
  home-manager = {
  	useUserPackages = true;
    useGlobalPackages = true;
    users.users.myUserAccount = { pkgs, ... }: {
      # Everything inside here is managed by Home Manager!
      programs.home-manager = {
        enable = true;
      };
      
    };
  };</code></pre><p>As you work on and build up your <code>configurations.nix</code>, keep this perculiar property in mind. It&apos;ll help you re-order your configuration files as you amass more options in time.</p><h3 id="how-to-use-home-manager-to-install-and-configure-programs-for-your-user">How to use Home Manager to Install and Configure Programs for your User</h3><p>Now that we&apos;ve setup NixOS to use home-manager, we can begin installing programs and configuring their settings on a per-user basis. Make sure to have rebuilt your system (<code>sudo nixos-rebuild --switch</code>!), and follow along as we add some programs with Home Manager. Keep in mind that the following directives will be added within the <code>home-manager.users.users.myUserAccount</code> brackets (i.e. where the <code># Everything inside here is managed by Home Manager!</code> comment lies). I&apos;ll let you know once we add files in the other bracket (where we originally installed the systems packages).</p><p><strong>Installing using <code>home.packages</code></strong></p><p>As far as I can tell, there are two ways to install programs using Home Manager. Some programs like <code>fortune</code> or <code>gnumake</code> are simple, low-level, and do not have configuration options associated with them. These programs are installed just like the system-wide packages we installed earlier, but they will only be available for our current user:</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">home.packages = with pkgs; [ 
  fortune
  gnumake
];</code></pre><figcaption><p><span style="white-space: pre-wrap;">Installing user-specific packages with Home Manager</span></p></figcaption></figure><p>From my observations, it seems that we use <code>home.packages</code> to install these programs, because they have not been yet &quot;wrapped up&quot; by Home Manager contributors in a form that exposes all of their settings. These programs might be so simple and low-level that they don&apos;t have any options to be made available in the first place. </p><p><strong>Installing via a <code>program</code> directive</strong></p><p>For more sophisticated programs, we install them by declaring a program directive, where we state that they are enabled (<code>enable = true;</code>). I do not know why <em>a priori</em> some programs are available this way, and others not &#x2013; but assume it is because the Home Manager contributors have not packaged everything in this manner yet.</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">    programs.git = {
      # Install git
      enable = true;
      
      # Additional options for the git program
      package = pkgs.gitAndTools.gitFull; # Install git wiith all the optional extras
      userName = &quot;myGitUsername&quot;;
      userEmail = &quot;myGitEmailAddress@users.noreply.github.com&quot;;
      extraConfig = {
        # Use vim as our default git editor
        core.editor = &quot;vim&quot;;
        # Cache git credentials for 15 minutes
        credential.helper = &quot;cache&quot;;
      };
    };</code></pre><figcaption><p><span style="white-space: pre-wrap;">Configuring user-specific packages with Home Manager</span></p></figcaption></figure><p>As you can see, programs that have been packaged this way have all of their configuration options exposed. This means we can use Home Manager to manage their settings for us. This is very powerful, because if you configure your programs with all their customisations using Home Manager, then you can replicate your setup <em>anywhere </em>by simply copying your <code>configurations.nix</code> file and running a <code>sudo nixos-rebuild --switch</code>. Imagine making a very fancy, sophisticated setup &#x2013; and even putting your configuration file into version control. Wouldn&apos;t that be a dream?</p><p>Putting our two examples together, this is how the Home Manager snippet of your <code>configurations.nix</code> would look like, with both <code>git</code> and some packages installed:</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">  # Begin home-manager directives
  home-manager.useUserPackages = true;
  home-manager.useGlobalPkgs = true;
 
  home-manager.users.myUserAccount = { pkgs, ... }: {
    # Everything inside here is managed by Home Manager!
    programs.home-manager = {
      enable = true;
    };
    programs.git = {
      enable = true;      
      # Additional options for the git program
      package = pkgs.gitAndTools.gitFull; # Install git wiith all the optional extras
      userName = &quot;myGitUsername&quot;;
      userEmail = &quot;myGitEmailAddress@users.noreply.github.com&quot;;
      extraConfig = {
        # Use vim as our default git editor
        core.editor = &quot;vim&quot;;
        # Cache git credentials for 15 minutes
        credential.helper = &quot;cache&quot;;
      };
    };
    home.packages = with pkgs; [ 
      fortune
      gnumake
    ];
  };</code></pre><figcaption><p><span style="white-space: pre-wrap;">The </span><code spellcheck="false" style="white-space: pre-wrap;"><span>configurations.nix</span></code><span style="white-space: pre-wrap;"> file with Home Manager</span></p></figcaption></figure><p>Remember to rebuild you configuration, using <code>nixos-rebuild --switch</code>!</p><h3 id="finding-more-home-manager-programs">Finding More Home Manager Programs</h3><p>How do we know what programs can be installed using the <code>programs.[name].enable</code> directive? For a given program like <code>program.git</code>, how do we find all of it&apos;s options? Regular NixOS has it&apos;s <a href="https://search.nixos.org/options?">options search</a>, and <a href="https://nix-community.github.io/home-manager/options.html">package search</a>, as we&apos;ve seen earlier. Does Home Manager possess a similar search function?</p><p>Yes, it does! There is the <a href="https://mipmip.github.io/home-manager-option-search/">Home Manager Options Search</a>, a community project maintained by other Home Manager users. It works the same way like the options and package search for regular NixOS.</p><p>Home Manager&apos;s manual also has an <a href="https://nix-community.github.io/home-manager/options.xhtml" rel="noreferrer">appendix</a> which lists all the programs that it has packaged, along with their options. You can visit the link above and take a look! It&apos;s not as easy to navigate, but you can simply use Ctrl+F to search within the page.</p><p>For any new program that you want to install in your Home Manager, a good workflow seems to be:</p><ol><li>Search via the Options Search or look through the manual appendix, to see if it&apos;s available as a packaged <code>program</code> that you can <code>enable</code>, and set it&apos;s configuration options.</li><li>If it is not within the Appendix, find it in NixOS&apos;s <a href="https://search.nixos.org/packages">packages search</a>, and then simply add it in the <code>home.packages</code> list.</li></ol><p>I developed this workflow through a process of a trial and error. I&apos;m still very new to NixOS myself, and this guide documents my understanding of Nix &#x2013; I may be wrong in regards to best practices. If you know of a better way to find programs for Home Manager, <a href="https://shen.hong.io/contact/">please let me know</a>!</p><p>Now you should be reasonably confident in adding new programs using Home Manager. Why not add <code>tmux</code>, and <code>neovim</code>? Here&apos;s how they might look like &#x2013; this snippet is copied out of my configuration, but don&apos;t let my settings inform yours &#x2013; check out their options in the Home Manager appendix and modify them your own way!</p><pre><code class="language-nix">    programs.tmux = {
      enable = true;
      keyMode = &quot;vi&quot;;
      shortcut = &quot;a&quot;;
      terminal = &quot;screen-256color&quot;;
      clock24 = true;
      shell = &quot;/etc/profiles/per-user/myUserAccount/bin/zsh&quot;;
    };

    programs.neovim = {
      enable = true;
      vimAlias = true;
    }; </code></pre><p>Likewise, I installed the following packages. Do you recognise them?</p><pre><code class="language-nix">    home.packages = with pkgs; [ 
      fortune 
      file
      htop
      exa
      zenith
      neofetch
      irssi
      gnumake
    ];</code></pre><p>With all of these, you should have a pretty comfortable command-line environment with NixOS. Make sure to run <code>nixos-rebuild --switch</code>!</p><h2 id="installing-and-configuring-sway-with-wayland">Installing and Configuring Sway with Wayland</h2><p>Now that we are confident with configuring NixOS on the command line, let&apos;s move on towards installing and building a graphical environment. There are as many schools of Desktop environments as there are Philosophy &#x2013; but for this tutorial, we will go with <a href="https://swaywm.org/">Sway</a>. Sway is a <em>tiling</em> window manager that uses the <a href="https://en.wikipedia.org/wiki/Wayland_(display_server_protocol)">Wayland display server</a>. </p><p>Adding a display server and window manager to our NixOS installation would give us a graphical user interface, and allow us to take our first steps away from the analytic consoles and command-prompts of yesteryear &#x2013; and move into the brave new world of <a href="https://en.wikipedia.org/wiki/Postmodernism">Postmodernism</a>!</p><p>There are various steps involved here. Many of which I have sourced from different guides and mailing-lists. I admit I do not understand all the configuration &#x2013; unfortunately, I can only offer the <a href="https://en.wikipedia.org/wiki/Sophist"><em>appearance of knowledge</em></a> here, and not knowledge itself. If my explanations are lacking in any way, or you have some clarifications &#x2013; please reach out and let me know.</p><h3 id="enable-hardware-support-for-wayland">Enable Hardware Support for Wayland</h3><p>First, we must enable some hardware support options for Wayland and Sway. <a href="https://en.wikipedia.org/wiki/OpenGL">OpenGL</a> is a graphics API that our hardware uses to render accelerated graphics. If we do not enable this option, we will run into <a href="https://discourse.nixos.org/t/sway-from-home-manager/8703">a bug and Sway will not run</a>.</p><p><strong>Please note that these directives go into the top-level (i.e. OUTSIDE) of your home-manager bracket. </strong>Place them on the same level as your other system-wide configuration.</p><pre><code class="language-nix">  # Hardware Support for Wayland Sway
  hardware = {
    opengl = {
      enable = true;
      driSupport = true;
    };
  };</code></pre><h3 id="xdg-integration">XDG Integration</h3><p>Next, <strong>also in the top-level</strong> we must add the following XDG directives. XDG is a <a href="https://en.wikipedia.org/wiki/Freedesktop.org">standard originating from freedesktop.org</a> which formalises a set of common variables and methods for desktop applications to interact with each other. The following settings enable what&apos;s called a XDG Portal, which <a href="https://github.com/flatpak/xdg-desktop-portal/blob/master/README.md">improves communication between bundled flatpak apps</a>, and Wayland.</p><pre><code class="language-nix">  xdg = {
    portal = {
      enable = true;
      extraPortals = with pkgs; [
        xdg-desktop-portal-wlr
        xdg-desktop-portal-gtk
      ];
    };
  };</code></pre><h3 id="adding-packages-to-homemanager-in-support-of-sway">Adding packages to HomeManager in Support of Sway</h3><p>Now, it is time to <strong>go to the HomeManager level</strong> and add some packages that Sway will need to use. I found the list of these packages from the <a href="https://nixos.wiki/wiki/Sway">NixOS Wiki page on Sway</a>, which you should also review as it contains additional information.</p><pre><code class="language-nix">    home.packages = with pkgs; [ 
      # ...
      # All of the below is for sway
      swaylock
      swayidle
      wl-clipboard
      mako
      alacritty
      wofi
      waybar
    ];</code></pre><p>Each of these packages serve a specific purpose.</p><ul><li><code>swaylock</code> is a idle screen locker.</li><li><code>swayidle</code> is an idle timer.</li><li><code>wl-clipboard</code> allows you to copy-paste (i.e. a clipboard).</li><li><code>mako</code> is a &quot;notification daemon,&quot; which creates little floating popups when you receive messages.</li><li><code>alacritty</code> is a rust-based terminal. This is <em>very</em> important, as it is the terminal that you&apos;ll be using within the Sway graphical environment.</li><li><code>wofi</code> is an application launch menu. It&apos;s like the dash on Mac OS, where you get to search for the name of a program, and run it. <code>wofi</code> is a Wayland descendent of <code>rofi</code>, which was a descendent of <code>dmenu</code>. Isn&apos;t Open Source interesting?</li><li><code>waybar</code> is a status bar. Ultimately I did not use it, but uncomment it for your own configuration</li></ul><p>Please be very careful with <code>swaylock</code> right now. If you run it in the command line, you will get locked out of your computer. Even if you type your password correctly, it will not let you in. That&apos;s because <code>swaylock</code> must be properly configured to accept your password and authenticate to your computer using <a href="www.linux-pam.org">PAM</a>, which is like an API but for authentication. In order to properly get swaylock working, <a href="https://github.com/swaywm/sway/issues/2773">add the following directive</a> <strong>to your system-wide configuration </strong>(i.e. <em>not</em> inside home-manager!):</p><pre><code class="language-nix">  # Allow swaylock to unlock the computer for us
  security.pam.services.swaylock = {
    text = &quot;auth include login&quot;;
  };</code></pre><p>Later on, once you are inside Sway &#x2013; you&apos;ll be able to lock your computer by running <code>swaylock</code> on any terminal!</p><h3 id="adding-and-configuring-sway-with-wayland-as-a-program-in-home-manager">Adding and Configuring Sway with Wayland as a Program in Home Manager</h3><p>Now we are ready to add and configure sway with Wayland. The instructions here are sourced from the <a href="https://nixos.wiki/wiki/Sway">NixOS Sway wiki page</a>, once more. The following snippet goes inside your Home Manager (i.e. on the same level as <code>programs.git</code>). </p><p>This is a lot of configuration, and it&apos;s not easy to understand at first. So just like with <a href="https://en.wikipedia.org/wiki/The_Phenomenology_of_Spirit">reading Hegel</a>, we&apos;ll go through it line-by-line and unpack it.</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">    # Use sway desktop environment with Wayland display server
    wayland.windowManager.sway = {
      enable = true;
      wrapperFeatures.gtk = true;
      # Sway-specific Configuration
      config = {
        terminal = &quot;alacritty&quot;;
        menu = &quot;wofi --show run&quot;;
        # Status bar(s)
        bars = [{
          fonts.size = 15.0;
          # command = &quot;waybar&quot;; You can change it if you want
          position = &quot;bottom&quot;;
        }];
        # Display device configuration
        output = {
          eDP-1 = {
            # Set HIDP scale (pixel integer scaling)
            scale = &quot;1&quot;;
	      };
	    };
      };
      # End of Sway-specificc Configuration
    };</code></pre><figcaption><p><span style="white-space: pre-wrap;">Configuring the </span><code spellcheck="false" style="white-space: pre-wrap;"><span>sway</span></code><span style="white-space: pre-wrap;"> desktop environment with </span><code spellcheck="false" style="white-space: pre-wrap;"><span>wayland</span></code></p></figcaption></figure><p>First, we <code>enable</code> sway. We also enable the <code>wrapperFeatures</code> for gtk, because it lets applications decorate their windows using the <a href="https://www.gtk.org/"><code>gtk</code> toolkit</a>, which is a common library used to make GUIs for programs.</p><p>Next, we begin the sway-specific configuration in <code>config</code>. Sway has a large number of configuration options, all of <a href="https://nix-community.github.io/home-manager/options.html#opt-wayland.windowManager.sway.config">which are documented</a> in the Home Manager manual&apos;s appendix. If you have time, I highly recommend browsing it &#x2013; but the following three are the most important:</p><p>We set the default terminal as <code>alacritty</code>, otherwise you&apos;ll be faced with a horribly ugly default terminal. We&apos;ll also set the application launcher menu to <code>wofi</code>. We specifically call <code>wofi</code> with the <code>--show run</code> option, since that&apos;s for listing applications and running them. <code>wofi</code> has <a href="https://hg.sr.ht/~scoopta/wofi">plenty of other options</a>, such as <code>--show ssh</code> for starting an SSH menu &#x2013; but that&apos;s not what we need. The <code>bars</code> option is a list <code>[ ]</code> of multiple brackets <code>{ }</code>, each of which contains the configuration for a separate status bar. We will only need one status bar, which will be located towards the bottom of the screen. My configuration does not use <code>waybar</code>, but you are welcome to uncomment it for yours.</p><p>Finally, we set some settings for our laptop&apos;s default display <code>eDP-1</code>. Specifically, we set the pixel-scaling factor to <code>1</code>. If you have a high-resolution screen, you may wish to set it to <code>2</code> (so that the text is not too small).</p><h3 id="adding-and-configuring-alacritty-with-themes">Adding and Configuring Alacritty with Themes</h3><p>Finally, the last thing we need to do is to configure our graphical terminal emulator, <code>alacritty</code>. Although the configuration looks difficult at first, you can see that it is all entirely contained within the <code>settings</code> brackets. The <a href="https://nix-community.github.io/home-manager/options.html#opt-programs.alacritty.enable">options are documented</a> in Home Manager&apos;s manual, once again.</p><figure class="kg-card kg-code-card"><pre><code class="language-nix">    # Rust-based terminal emulator
    programs.alacritty = {
      enable = true;
      settings = {
        env.TERM = &quot;alacritty&quot;;
        window = {
          decorations = &quot;full&quot;;
          title = &quot;Alacritty&quot;;
          dynamic_title = true;
          class = {
            instance = &quot;Alacritty&quot;;
            general = &quot;Alacritty&quot;;
          };
        };
        font = {
          normal = {
            family = &quot;monospace&quot;;
            style = &quot;regular&quot;;
          };
          bold = {
            family = &quot;monospace&quot;;
            style = &quot;regular&quot;;
          };
          italic = {
            family = &quot;monospace&quot;;
            style = &quot;regular&quot;;
          };
          bold_italic = {
            family = &quot;monospace&quot;;
            style = &quot;regular&quot;;
          };
          size = 14.00;
        };
        colors = {
          primary = {
            background = &quot;#1d1f21&quot;;
            foreground = &quot;#c5c8c6&quot;;
          };
        };
      };
    };</code></pre><figcaption><p><span style="white-space: pre-wrap;">Configuring </span><code spellcheck="false" style="white-space: pre-wrap;"><span>alacritty</span></code><span style="white-space: pre-wrap;">, a rust-based terminal emulator for Sway and Wayland.</span></p></figcaption></figure><p>The most important setting here is probably the <code>size</code> setting &#x2013; it determines how big the text is in the terminal. If you have a high-resolution screen, the default size will probably be too small. As you build up your <code>configuration.nix</code> file, this would be a good place to theme and make a really nice-looking terminal.</p><h3 id="rebuilding-nixos-and-running-sway">Rebuilding NixOS and Running Sway</h3><p>Now everything should be all done! If your configuration file is correct (make sure that the top-level and home-manager-level directives are in their proper places), you should be able to rebuild your system and run now.</p><pre><code class="language-bash">sudo nixos-rebuild --switch</code></pre><p>After this is done, I recommend restarting your computer, just in case.</p><pre><code class="language-bash">sudo reboot now</code></pre><p>After logging in (making sure to select the newest version of you NixOS install from the boot manager), you will be greeted once again by a command line environment. But don&apos;t fear! All you need to do now is to run:</p><pre><code class="language-bash">sway</code></pre><p>And you should be in a graphical environment! Congratulations, you have successfully transcended the command prompt, and reached a <a href="https://en.wikipedia.org/wiki/Critique_of_Pure_Reason#Transcendental_Aesthetic">transcendental aesthetic</a>!</p><h3 id="help-im-stuck-how-do-i-get-around-in-sway">Help! I&apos;m Stuck! How do I... get around in Sway?!</h3><p>If you&apos;re like me, and you have never used a tiling window manager before, you probably have <em>no idea</em> how to get around. At all. And even worse, when you search for &quot;sway documentation&quot;, you&apos;ll find no instructions on how to control the Window Manager! </p><p>Fortunately, Sway is based on i3, a venerable tiling Manager hailing from the Archlinux days. And it has both <a href="https://i3wm.org/">excellent documentation</a> on it&apos;s project site, as well as a <a href="https://i3wm.org/docs/refcard.html">quick-reference cheat sheet</a> to help you get around. Print out a copy and keep it with you &#x2013; you&apos;ll become an expert in tiling window managers in no time!</p><h2 id="conclusion-of-part-i">Conclusion of Part I</h2><p>You should now have a graphical environment in NixOS, that you put together yourself. It&apos;s still a very bare and basic space, but should serve as a good foundation for further customisation. In Part II of this guide, we will take this foundation and build upon it further. Specifically, we will install and configure FireFox together, as well as other applications like Element (Matrix Client) and Signal Desktop. I will also show you how to configure audio using the Pipewire service, and most importantly &#x2013; install LaTeX and VSCodium.</p><p><em>I hope you enjoyed my tutorial. If you have any questions or comments, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em> &#x2013; I would love to know if my guide has helped you in any way. I also </em><a href="https://shen.hong.io/"><em>write about Philosophy</em></a><em> in my own time &#x2013; let me know of your thoughts!</em></p>]]></content:encoded></item><item><title><![CDATA[The Role of Friendship in Inquiry: Reflections on Galileo's Dialogues]]></title><description><![CDATA[I recently finished reading Galileo Galilei’s Dialogue Concerning the Two Chief World Systems. Why is this ancient text still relevant today?]]></description><link>https://shen.hong.io/role-of-friendship-in-inquiry-reflections-on-galileo/</link><guid isPermaLink="false">61e5fe4f8d361100013c83ae</guid><category><![CDATA[Philosophy]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Mon, 17 Jan 2022 23:48:19 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/galileo.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/galileo.webp" alt="The Role of Friendship in Inquiry: Reflections on Galileo&apos;s Dialogues"><p>I recently finished reading <a href="https://en.wikipedia.org/wiki/Galileo_Galilei">Galileo Galilei</a>&#x2019;s (1564 &#x2013; 1642) <a href="https://en.wikipedia.org/wiki/Dialogue_Concerning_the_Two_Chief_World_Systems"><em>Dialogue Concerning the Two Chief World Systems</em></a>, a treatise on geocentric and heliocentric astronomy. Published in Italy during the February of 1632, the <em>Dialogue </em>was both revolutionary and controversial for its time &#x2013; earning Galileo a condemnation at the hands of the Italian Inquisition, and a life-long house-arrest that lasted until his death. But yet, despite it&#x2019;s contemporaneous infamy &#x2013; it&#x2019;s valid to ask ourselves:</p><p>&#x201C;Why are the <em>Dialogues </em>relevant today? What can we gain from reading it?&#x201D; As a purely scientific work of observational astronomy, the <em>Dialogues </em>would only be relevant as a matter of historical interest. It may be a milestone or a landmark in the studies of the history of science, but heliocentrism as a doctrine is neither new nor controversial today. For a book to be considered perennial, it generally has to touch upon some truth or thesis that is <em>universal </em>to the human experience &#x2013; to transcend the material (and ephemeral) circumstances of it&#x2019;s historicity. That&#x2019;s why the <em>Iliad</em>, with its themes of rage and justice &#x2013; is just as valid and relevant today, as it was during the days of Homer. But as a work of scientific non-fiction, it is not readily apparent how Galileo&#x2019;s <em>Dialogue </em>has appeal to universal validity &#x2013; when the subject-matter of the treatise itself is a field which has advanced in the 400 years since. Why do we read Galileo&#x2019;s <em>Dialogues</em>? To what extent can it teach us, as students of a culture nearly four centuries antecedent to Galileo&#x2019;s death? These were the questions that puzzled me, as I engaged in a month-long preceptorial of the <em>Dialogue </em>with my colleagues.</p><p>The <em>Dialogue Concerning the Two Chief World Systems </em>is an unusual work for a scientific monograph. The <em>Dialogue </em>is first and foremost, a dialogue. Set as a conversation between <em>Salviati, Sagredo, </em>and <em>Simplicio</em>, the three characters meet over the course of four days, and converse amongst themselves like characters in a play. The book is organised not by subject or theme, but chronologically &#x2013; literally divided into four days: <em>Sagredo </em>and <em>Simplicio </em>arrive <em>Salviati</em>&#x2019;s house in the morning &#x2013; and refreshments are served at the afternoon, when the hot Venetian summers conclude the conversation. Yet, despite its casual form, Galileo&#x2019;s work is a serious, empirical presentation of the merits of Heliocentric astronomy, complete with geometrical proofs and demonstrations &#x2013; accoutrements of the highest rigour by the standards of Renaissance science. Why did Galileo choose the form of a dialogue for his presentation? Was the structure of the dialogue <em>essential </em>to Galileo&#x2019;s demonstration, or only an incidental trapping of fancy and style? Modern authors have flirted with different forms of presentation for scientific inquiry, after all. Shalev Ben-David&#x2019;s 2017 paper on quantum cryptography featured an abstract in the form of an Aesopian fable, and Rovelli&#x2019;s <em>Dialogue on Quantum Gravity</em> even re-imagines the familiar characters of Salviati, Sagredo, and Simplicio in the cafetaria of an American university. The key difference, which I realised as I immersed myself in Galileo&#x2019;s treatise &#x2013; is that its <em>formal nature </em>as a dialogue is not incidental to its presentation, but rather <em>essential </em>to the very <em>philosophy </em>of scientific inquiry that Galileo&#x2019;s <em>Dialogues </em>embody.</p><p>What do I mean by philosophy of scientific inquiry? Does a work of academic literature necessarily embody an underlying philosophy at all? I think it is fairly uncontroversial to claim that all forms of inquiry must assume some wider set of principles or axioms which serve as a foundation for its study &#x2013; even if such a philosophy is unstated, and as omnipresent as the water to which a fish calls home. To quote Sartre, &#x201C;If any metaphysics presupposes a theory of knowledge, it is equally true that any theory of knowledge presupposes a metaphysics.&#x201D; And as a philosophy of scientific inquiry, these underlying assumptions define the ways we gather knowledge, the ways we reach valid truths. The claim of my essay is that in our study of Galileo&#x2019;s <em>Dialogue</em>, <em>we come to a better understanding about the philosophy of scientific inquiry. </em>Not just <em>a </em>method of scientific inquiry in the Renaissance, or the particular scientific inquiry as it was conducted by Galileo &#x2013; but <em>scientific inquiry as we know it. </em>I cannot apodictically assert whether Galileo&#x2019;s <em>Dialogue Concerning the Two Chief World Systems </em>was the first presentation of the scientific method, but it is decidedly the most influential example &#x2013; and it&#x2019;s publication, controversy, and subsequent condemnation set the stage for the development of science as a distinct field from Philosophy and Theology.</p><p>What is this philosophy of scientific inquiry which Galileo&#x2019;s <em>Dialogue </em>embody? In order to answer this question, we must start by taking a look at the text itself. Galileo&#x2019;s dialogue is the gathering of three close friends at Sagredo&#x2019;s house, in the midst of a hot Venetian summer. The book prefaces the beginning of the first Day with the following:</p><blockquote>It has happened that several discussions had taken casually at various times among these gentlemen, and had rather whetted than satisfied their thirst for learning. Hence very wisely they resolved to meet together on certain days during which, setting aside all other business, they might apply themselves more methodically to the contemplations of the wonders of God in the heavens and upon the earth. They met in the palace of the illustrious Sagredo; and, after the customary but brief exchange of compliments, Salviati commenced as follows.</blockquote><p>Following that introduction, we meet the three characters. The worldly and successful Salviati, listens actively and with admiration, as his friends Sagredo and Simplicio take on the roles of advocates for the titular &#x201C;two chief world systems.&#x201D; Sagredo is presented to us as a keen and sophisticated advocate for heliocentrism, substantiating his claims with both experiments and geometrical demonstrations. Simplicio takes on the mantle of the Ptolemaic geocentric model, quoting learnedly from Latin as he supports his claims with Aristotelian doctrine.</p><p>I admit, for the longest time I could not understand the character of Simplicio. At first, knowing his role as the speaker for geocentricism &#x2013; my perceptions of him were coloured by the fact that he was the proverbial Devil&#x2019;s advocate &#x2013; the very <em>absurdum </em>in the <em>reductio ad absurdum </em>that the work seemed to be. Biased by my modern sensibilities of universal gravitation, Simplicio&#x2019;s quaint, spirited quotations from Aristotle seemed like hidebound dogmatisms. In brief, my perception of him was summed up by his very name: a <em>Simpleton</em>. How could his interlocutors not feel the my same feelings of frustration, as they argued with him over the course of several days? His dogged following of Aristotelian physics, and the way he reluctantly and begrudgingly conceded points only after rigorous argumentation, made him appear foolish and asinine. Was Simplicio&#x2019;s very inclusion in the text, merely a rhetorical strawman, a personified concession to the Church?</p><p>It was only much later, did I realise my misjudgement of Simplicio&#x2019;s character &#x2013; and his place in the dialogue. Simplicio&#x2019;s no simpleton &#x2013; but rather, he is a young man trained and educated by the finest standards of Classical education in the Italian Renaissance. Named after <a href="https://plato.stanford.edu/entries/simplicius/">Simplicius of Cilicia</a>, a noted Neoplatonic commentator and scholar; Simplicio quotes and refers to Aristotle with a scholastic fluency worthy of his namesake. The arguments that Simplicio makes in refutation of Heliocentricism may <em>appear </em>unusual to our modern-day empiricism &#x2013; but they are entirely consisted with an Aristotelian system of physics and substance. Simplicio is <em>not </em>a simpleton. If anything, his comprehensive and intimate knowledge of Aristotle would earn him a professorship in the Classics department of any University in the world today. I had misjudged Simplicio&#x2019;s character out of no advantage other than the benefit of hindsight.</p><p>So what <em>is </em>the role of Simplicio in Galileo&#x2019;s <em>Dialogue</em>? How does he fit in the rhetorical narrative of the work as a whole? I think it is perhaps my own initial misunderstanding of Simplicio&#x2019;s character, which may best shed light on Galileo&#x2019;s underlying method. We are all products of our environment, our path a trajectory of the sum of the forces acting upon us. And it is my own bias, as a member of a face-paced, results-oriented society &#x2013; to be interested only in the <em>answers</em>, and not the process that leads us to them. Simplicio is <em>wrong</em>, empirically speaking &#x2013; the Earth is not the center of the universe, nor the Sun a satellite of our globe. But Galileo&#x2019;s <em>Dialogue </em>is not a mere presentation of facts as they stand &#x2013; but rather, a work that presents the <em>scientific method</em>, as a process of discursive inquiry.</p><p>Science is fundamentally dialectical in nature, where findings are not totems of authority to be appealed to &#x2013; but components of a longer, overarching conversation &#x2013; a dialogue between scholars and scientists as the field progresses. It would be unthinkable for a modern Physicist to base their findings on naught but the authority of Einstein &#x2013; but such a underlying conception of science as an <em>empirical </em>pursuit was not always the case in our past. A Natural Philosopher of the 15th century can publish a paper explicating the aetiology of a novel phenomena based solely upon the authority and dogma of Aristotle, and such a work would be considered a valid method of inquiry. Its merits would be analysed on the validity of the interpretation alone.</p><p>Galileo&#x2019;s <em>Dialogue </em>is a proposal and demonstration, of an alternative system. Where the validity of any unit of scientific truth is not assessed based on its consistency with established authorities &#x2013; but on empirical experiments and demonstration. More importantly, Simplicio&#x2019;s role in the dialogue is to illustrate the discursive role of scientific inquiry in action. Sagredo presents his arguments to Salviati, and Simplicio in turn &#x2013; and Salviati plays his own role in the resulting synthesis of thesis and antithesis. As key players in this dance of scientific inquiry, the characters embody qualities required for the very process to work.</p><p>What are these qualities? Sagredo, Salviati, and Simplicio are many things &#x2013; they are educated, leisurely, perhaps even aristocratic. But the theme that was most present on my mind, as I worked through the <em>Dialogue</em>, as the fact that all three characters &#x2013; despite their differences &#x2013; are <em>friends</em>. Friendship: or at the very least, a good-natured benevolence and goodwill &#x2013; seem to be amongst the essential qualities necessary for the discursive process of inquiry to work. Throughout the dialogue, no matter how much the characters disagree with each other &#x2013; an atmosphere of good faith and understanding prevails. Simplicio may <em>misunderstand </em>the reasoning of Sagredo, but his friends are quick to correct him. And despite his own dogged nature and faith in Aristotle, Simplicio does accept the counterarguments of his friends, and profess the need for further study and contemplation, with every concession that he makes. This genuine friendship between the three characters &#x2013; is more than just a narrative scene-setting or facade. They are truly friends. Salviati and Sagredo fret and worry over their companion, when he shows up late to their meeting due to the tides. And likewise, Sagredo, who leans towards the side of Salviati&#x2019;s arguments throughout the work &#x2013; is quick to spring to Simplicio&#x2019;s defense, when Salviati misunderstands or critiques Simplicio on unfair grounds.</p><p>In conclusion, as I read Galileo&#x2019;s <em>Dialogue Concerning the Two Chief World Systems</em>, I realised that there are many lessons to be learnt from it &#x2013; that are valid not just in it&#x2019;s own historical context, but universally valid today. The Dialogues are a great presentation of the scientific system &#x2013; a process and method of inquiry based on neither Gods nor Man, but evidence alone. And it is a heartening and sincere rendition on the necessity of goodwill and friendship, in the process of any intellectual inquiry. There are many things that I had wished to address in this essay which I ultimately left unspoken, due to a lack of time of my own. But I would like to conclude with the following passage from the latter half of the <em>Dialogue</em>:</p><blockquote><strong>Salviati</strong>: &#x201C;Sagredo, please let us weary ourselves no longer with these particulars, especially since you know that our goal is not to judge rashly or accept as true either one opinion or the other, <em>but merely to set forth for our own pleasure </em>those arguments and counter-arguments which can be adduced for one side and for the other.&#x201D;</blockquote><p>Let us remind ourselves that Sagredo, Salviati, and Simplicio &#x2013; busy individuals with personal and professional lives full of happenings of their own &#x2013; agreed to take a break from their worldly affairs, and set aside four days amongst themselves for a philosophical discussion. During which, the friends argued not for profit, or even to necessarily change each other&#x2019;s opinions. But rather gathered in the company of each other&#x2019;s friendship, to set forth opinions and arguments for the sake of <em>the pleasure of inquiry </em>alone.</p>]]></content:encoded></item><item><title><![CDATA[Dialogue on the Questions of Being, through an Analogy with Chess]]></title><description><![CDATA[Imagine one day, a Visitor arrives to your office. You offer them tea, and after all the introductions and customary salutations, they inquire of you the following question: “What is chess?”]]></description><link>https://shen.hong.io/dialogue-on-the-questions-of-being/</link><guid isPermaLink="false">61e5f5b28d361100013c82a2</guid><category><![CDATA[Philosophy]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Tue, 28 Dec 2021 12:30:00 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/chess-players.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/chess-players.webp" alt="Dialogue on the Questions of Being, through an Analogy with Chess"><p>There&#x2019;s a question on my mind, that is perhaps best represented in the manner of a dialogue. It&#x2019;s a question on the nature of consciousness, where we ask not only: <em>What is consciousness?</em> and <em>Where is consciousness located?</em> But in order to do so, we must first explore adjacent questions in ontology, like what is being? or what is meaning? I will develop these questions in the course of this dialogue, which I present as follows.</p><p>Imagine one day, a <strong>Visitor</strong> arrives to your office. You offer them tea, and after all the introductions and customary salutations, they inquire of you the following question: &#x201C;What is chess?&#x201D;, they ask.</p><p>Of course, you would be astonished &#x2013; for how could they not know of such a common game? But your Visitor hails from a faraway land, with no knowledge of your customs. So despite your bemusement, you proceed with an impromptu demonstration. You find a dusty chess-board beneath some papers, and a box of chess-pieces behind a shelf. Presenting these materials to your Visitor, you arrange the pieces in their starting order, and explain the movements of the chess-pieces. You showcase the forward marching of the Pawns, and the odd, L-shaped pounces of the Knight. Thus you demonstrate the pieces one-by-one, as well as other nuances of the rules, such as checkmate and <em>en passant</em>.</p><p>After you conclude your explanation, you look expectantly at your visitor. They seem surprised. &#x201C;What a marvellous invention of yours, this &#x2018;chess!&#x2019;&#x201D;, they exclaim. </p><p>&#x201C;And surely, you have a prized possession indeed, for many strangers to speak of its renown. But should anyone else wishes to see this game, must they also come visit you at your office, just as I have done?&#x201D;</p><p>You laugh, bemused by your Visitor&#x2019;s peculiar misunderstanding. You quickly explain. No, you were not the inventor of chess (if only!). Nor is your worn-out chess board the &#x2018;Chess&#x2019; of the world. In fact, you clarify &#x2013; your visitor has confused the singular with the general. There are such things which are singular, such as You and I (or the Eiffel Tower). And in English, we often denote their singularity with the definite article &#x2013; &#x201C;<em>the</em> Eiffel tower, not <em>an</em> Eiffel tower.&#x201D; But in any case, chess is a game that is enjoyed by millions, of which you only possess a particular instance &#x2013; namely that battered chess-board which is now in front of you both.</p><p>Your Visitor looks thoughtful, and glances at the chess-board, with an odd intensity. After they moment, state pensively: &#x201C;So chess is not this <em>particular</em> board and these <em>particular</em> pieces which you have in front of you.&#x201D; You nod, encouragingly. They pick up a Rook, and examine it &#x2013; before proceeding: &#x201C;But rather, chess is a game of black and white pieces, made from ebony and boxwood. It is played on a varnished, mahogany board, with black and white squares made from oak and birch.&#x201D;</p><p>&#x201C;And hence, anyone may possess a particular instance of chess, should they own a board and a piece-set constructed by these materials and dimensions,&#x201D; they conclude.</p><p>You object, stating that they have misunderstood once again. No, chess is not defined by these material characteristics. A game of chess is not defined by ebony and boxwood pieces, nor must a chessboard be made from mahogany, oak, and birch. Rather, there can be chessboards made from plastic or canvas, chess-pieces from metal or glass. Indeed, there is no necessity for the pieces to be black and white either &#x2013; all of these are merely material characteristics which are accidental to the essence of chess altogether. To define the being of chess as a set of material properties would miss what is essential about chess. To drive forth the point, you make sure to recount a rather charming miniature chess-set you saw, made from frosted and transparent crystal &#x2013; at a colleague&#x2019;s office down the hall.</p><p>The Visitor remains silent for a few moments, taking this all in. &#x201C;Chess is not defined by its material characteristics, but rather it&#x2019;s material characteristics are accidents from any particular embodiment of it&#x2019;s being.&#x201D; You nod along, reasonably. They look up at you, with a look of increasing bafflement. &#x201C;But then, what is chess?!&#x201D;</p><p>You lean back in your armchair, taking a moment this time to ponder. You arrive at an answer, presenting it this time with more care. Chess is not defined by any material characteristics of its components, you admit. But rather, a board game is chess, when its pieces follows certain definite rules. A chess-game is a game where there is a board composed of 64 alternating squares, arranged in an eight-by-eight grid. Where each player controls sixteen pieces, eight of which are Pawns. You generalise even further, making sure to point out that it is not necessary for the pieces and squares to be black and white &#x2013; nor any colours at all, provided that they are differentiated from each other. And likewise, it is not necessary for the pieces to be &#x2018;Pawns,&#x2019; &#x2018;Bishops,&#x2019; &#x2018;Knights,&#x2019; and &#x2018;Kings&#x2019; &#x2013; the customary names that we give them are also accidental. Instead, their true natures are defined by their movements.</p><p>A contemplative silence settles over the both of you. Your Visitor considers your explanation with an unusual attentiveness. After a while, they speak.</p><p>&#x201C;You state that chess a general being, which manifests in particular instances. It is not defined by the material characteristic of these instances, but rather by a set of underlying rules which define certain behaviours.&#x201D;</p><p>The Visitor looks puzzled, and pauses for a moment before they exclaim: &#x201C;But now that you divorce the definition of chess from the material basis of its embodiment, you risk a progression into absurdity!&#x201D;</p><p>Astonished, you interject &#x2013; and ask what them what they mean.</p><p>&#x201C;If chess were to be defined as a set of logical rules, these rules may be present under any representation. You would not need a chess board, or chess pieces at all, in order to have chess!&#x201D;</p><p>You laugh, surprised by the Visitor&#x2019;s particular objection. Why, you say&#x2013; that is not a problem at all. Indeed, chess is represented &#x2013; and played &#x2013; often without chess boards nor chess pieces. You mention the existence of correspondence chess &#x2013; matches played via post or internet forums, where nary a chess-board is in sight. You present the concept of Descriptive Chess Notation, a terse, succinct language that&#x2019;s often used to represent chess matches in print. To better illustrate this, you even print off an example, of the opening moves from a match between two Grandmasters in Berlin:</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.12.00.png" class="kg-image" alt="Dialogue on the Questions of Being, through an Analogy with Chess" loading="lazy" width="1538" height="962" srcset="https://shen.hong.io/content/images/size/w600/2022/01/Screenshot-2022-01-17-at-18.12.00.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/Screenshot-2022-01-17-at-18.12.00.png 1000w, https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.12.00.png 1538w" sizes="(min-width: 720px) 720px"></figure><p>Your Visitor studies the printout closely. &#x201C;I do not see the chess-pieces,&#x201D; they state. &#x201C;Nor how there is any movement in space.&#x201D;</p><p>Ah, but you are simply unaccustomed to reading Descriptive Chess Notation, you reassure. For you see, each numbered line in the printout represents a pair of moves, the first belonging to white, the second to black. Each move contains a letter and a destination square, where P represents &#x2018;Pawn&#x2019;, N represents &#x2018;Knight&#x2019;, and so on. Given the expression &#x201C;P-K4&#x201D;, it would mean &#x201C;move the (white) Pawn to the fourth square in front of the (white) King&#x201D;. It&#x2019;s all a little obscure, but to an experienced chess player reading such notation is as clear as seeing a movie play out in their mind.</p><p>&#x201C;I see,&#x201D; nods the Visitor. They revisit the printout, which now appears with a newfound significance.</p><p>&#x201C;From what I understand so far, the essence of chess is not in its material embodiment. But rather, it is a set of rules, and relations, which may manifest within an arbitrary set of symbols which represent them. These symbols may be totems of carved ebony and boxwood, or simply letters upon a page.&#x201D;</p><p>&#x201C;But in both cases, the simple symbols by themselves are not enough to be chess. To a person that does not know about Descriptive Chess Notation, your printout will have the appearance of random, meaningless characters. Likewise, boxwood chess- pieces would look like simple wood-turnings to a person ignorant of their use.&#x201D;</p><p>You agree to your Visitor&#x2019;s elaboration. Perhaps, you muse provisionally &#x2013; there are two components to the being of chess. There is, by necessity &#x2013; a set of symbols which represent the state of the game. But there must also be a corresponding interpretation, which inscribes meaning to the symbols. In fact, you hypothesise, there is probably a formal method to define the being of chess, as a set of symbols and a suitable interpretation.</p><p>&#x201C;Like a formal language. Where the alphabet is a set composed of the chess pieces, and a grammar which defines valid combinations of moves.&#x201D;</p><p>Or a geometrical system, you reply. One which contains definitions of the chess-pieces, and postulates which allow their moves.</p><p>(Or even a Turing-machine, I add, narrating from the margins. But that is neither here nor there. The dialogue continues on.)</p><p>You feel there&#x2019;s a certain oddity with this definition of chess, and your Visitor notices it too, who takes it upon themselves to elaborate:</p><p>&#x201C;We&#x2019;ve arrived at a puzzling stopping-point, in this progression of abstraction,&#x201D; they state. &#x201C;We began with wooden chess-pieces, physical and embodied &#x2013; as real objects with extension in space and time. We than divorce chess from it&#x2019;s material representation, and ultimately conclude that it&#x2019;s essence lays in a set of symbols and a suitable interpretation.&#x201D;</p><p>The Visitor picks up a pen and makes a few notes on the printout. &#x201C;For the sake of convenience, let us denote the set of symbols as \[\{\text{Symbols}\}\], and their interpretation as a function \[I(x)\]. The process of interpreting symbols to yield meaning can be written as:&#x201D;</p><p>\[{\text{Symbols}} \Rightarrow I(x) \Rightarrow {\text{Meaning}}\]</p><p>So given a collection of symbols like the printout from above (Figure 1), there exists a suitable interpretation that yields meaning from otherwise obscure lines. You grant this step of reasoning.</p><p>&#x201C;But these symbols and their interpretation may be of arbitrary complexity,&#x201D; they frown. &#x201C;By going further in abstraction, we encounter the same absurdity that I feared earlier.&#x201D;</p><p>What sort of absurdity? You ask the Visitor to clarify, and they provide an example:</p><p>&#x201C;Let me take your earlier representation of a chess match, in Descriptive Chess Notation (Figure 1). I will make some minor changes to the representation, for the sake of demonstration.&#x201D; They pick up your printout and a proffered pen, and begin to write. &#x201C;I will replace the arabic numbers with their numerals, and remove any dashes and space. With these minor, purely notational substitutions, the match takes on the following form.&#x201D;</p><p>You glance at their amendations:</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.22.43.png" class="kg-image" alt="Dialogue on the Questions of Being, through an Analogy with Chess" loading="lazy" width="1212" height="548" srcset="https://shen.hong.io/content/images/size/w600/2022/01/Screenshot-2022-01-17-at-18.22.43.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/Screenshot-2022-01-17-at-18.22.43.png 1000w, https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.22.43.png 1212w" sizes="(min-width: 720px) 720px"></figure><p>&#x201C;With this string of characters (i.e. symbols), there exists an interpretation such that they yield a chess game. This is not difficult to grant, for the string is a simple, idiosyncratic variation on Descriptive Chess Notation,&#x201D; the Visitor explains.</p><p>What an ungainly representation. But it can be done, you allow.</p><p>&#x201C;But now I will do the following. I will take the above string and encrypt them with a password according to a cypher, yielding an encrypted output.&#x201D;</p><p>You raise a puzzled eyebrow.</p><p>&#x201C;A cypher can be understood as a simple, reversible function, which takes a key and an input.&#x201D; The Visitor clarifies. &#x201C;If you input plaintext and the key, it&#x2019;s output will be ciphertext. If you input ciphertext and the (correct) key, it will output the original plaintext.&#x201D; They helpfully scribble down some notation:</p><p>\[C(k, i) = O\]<br>\[C(k, \text{plaintext}) = \text{ciphertext}\]<br>\[C(k, \text{ciphertext}) = \text{plaintext}\]<br></p><p>What does this have to do with symbols and interpretation, you ask.</p><p>&#x201C;The philosophical import of this example comes from the following cryptographic principle. Any good cypher is capable of producing seemingly random ciphertext, given an input and an appropriate key. Given the modified notation as input, and a well-chosen cipher and key , I can produce the following ciphertext as output:&#x201D;</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.22.53.png" class="kg-image" alt="Dialogue on the Questions of Being, through an Analogy with Chess" loading="lazy" width="1184" height="600" srcset="https://shen.hong.io/content/images/size/w600/2022/01/Screenshot-2022-01-17-at-18.22.53.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/Screenshot-2022-01-17-at-18.22.53.png 1000w, https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.22.53.png 1184w" sizes="(min-width: 720px) 720px"></figure><p>You examine the result, comparing to the earlier plaintext string (Figure 2). It does look pretty random, you admit. Any appearance of structure and meaning seems to be obliterated.</p><p>&#x201C;But here&#x2019;s the thing. This string does not only have the mere appearance of randomness. But it is truly random, by all mathematical measures. You can give it to a Mathematician, who could subject it to exacting statistical analysis. They will find that the frequency distribution (i.e. occurrences) of letters will be uniform and evenly distributed. The encrypted string will be mathematically indistinguishable from random noise.&#x201D;</p><p>You let the Visitor&#x2019;s words sink in for a moment.</p><p>But this is such a pedestrian objection! You protest.</p><p>You still have a set of symbols, and a corresponding interpretation. The only difference is that the symbols are obfuscated, by adding a cryptographic key and decryption step to the interpretation. Sure, one may add any number of steps and contrivances in the interpretation, but it doesn&#x2019;t change the essential nature of chess as symbols and a means to interpret them.</p><p>&#x201C;I apologise, but you misunderstood my point&#x201D;, replies the Visitor good- naturedly.</p><p>&#x201C;For the essential structure of my syllogism is thus. Given a set of symbols and an interpretation, I can yield meaning, as agreed upon before.&#x201D;</p><p>\[{\text{Symbols}} \Rightarrow I(x) \Rightarrow {\text{Meaning}}\]</p><p>&#x201C;With the deciphering process, I essentially add an additional step in the process. First, the cipher-function \[C(x)\] takes a meaningless input, which yields the symbols that are then interpreted by \[I(x)\].&#x201D;</p><p>\[{\text{No Meaning}} \Rightarrow C(x) \Rightarrow {\text{Symbols}} \Rightarrow I(x) \Rightarrow {\text{Meaning}}\]</p><p>&#x201C;You are right to admit, however &#x2013; that the additional deciphering process is not essentially different from interpretation. It is merely a continuation (or rather, an extension) of the interpretive act. Indeed, it can be simplified into it, where \[I&apos;(x) = I(C(x))\], just like functions can be composed (i.e. nested)&#x201D;</p><p>\[{\text{No Meaning}} \Rightarrow \underbrace{ \big[ C(x) \Rightarrow {\text{Symbols}} \Rightarrow I(x) \big] }_{I&apos;(x)} \Rightarrow {\text{Meaning}}\]</p><p>&#x201C;And thus, we yield the conclusion of our <em>reductio ad absurdum.</em> Given something that is meaningless, there exists a suitable interpretation \[I&apos;(x)\] which yields a meaning.&#x201D;</p><p>\[\therefore {\text{No Meaning}} \Rightarrow I&apos;(x) \Rightarrow {\text{Meaning}}\]</p><p>&#x201C;Not only that, but given something which has no meaning, there will always exist a suitable interpretation of it which can yield an arbitrary meaning of one&apos;s choice. For any string of random characters, there can be an interpretation that yields a riveting novella. Within any noise of radio static, there is a suitable interpretation that yields a lost concerto of Bach.&#x201D;</p><p>But this is an absurdity! You protest. By stretching the nature of symbols and their interpretation to such an extreme, we risk losing meaning entirely. There has to be some paralogism in this chain of reasoning, or some illusion at hold. Otherwise, it seems that the very structure of being loses its shape. How can we affect an explanation?</p><p>&#x201C;We can try. For one, the absurdity of this conclusion comes an imbalance between the symbols and interpretation.&#x201D; The Visitor sighs. &#x201C;We hollow out the symbols until there is nothing left within them, just as we simultaneously over-stuff the interpretation so that it does all the work. But yet, somehow meaning is conserved in the two parts as a whole &#x2013; it is merely distributed unevenly.&#x201D;</p><figure class="kg-card kg-image-card"><img src="https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.30.47.png" class="kg-image" alt="Dialogue on the Questions of Being, through an Analogy with Chess" loading="lazy" width="1966" height="830" srcset="https://shen.hong.io/content/images/size/w600/2022/01/Screenshot-2022-01-17-at-18.30.47.png 600w, https://shen.hong.io/content/images/size/w1000/2022/01/Screenshot-2022-01-17-at-18.30.47.png 1000w, https://shen.hong.io/content/images/size/w1600/2022/01/Screenshot-2022-01-17-at-18.30.47.png 1600w, https://shen.hong.io/content/images/2022/01/Screenshot-2022-01-17-at-18.30.47.png 1966w" sizes="(min-width: 720px) 720px"></figure><p>You nod. There&#x2019;s a similar meta-theorem in the study of Hilbert-style logic systems, you recall. Given a logical system with axioms, and rules of inference &#x2013; there is always some tenuous balance in play. You can either have a system that has few axioms, but many rules of inference. Or a system that has many axioms, but few rules of inference. It seems like a similar dynamic is at play here.</p><p>But still, there is no reconciliation yet. If anything, all this goes to illustrate is that the conception of chess (or anything) as a set of symbols and an interpretation is an illusion. After all, meaning is still conserved &#x2013; no matter how you distribute the share of work between symbols and the interpretative function (Figure 4).</p><p>You both ponder now, in silence. All of a sudden, the office feels chilly and strange. There is such a thing as chess, that is certain. Chess is a well-defined being which exists. It&#x2019;s trivial to dismiss the <em>material cause</em> as a definition, but now it seems that the formal cause is just as useless in explaining what being (or chess, for that matter) is. You feel a sense of <em>aporia</em> set in, as your Visitor politely shifts in the armchair.</p><p>&#x201C;If I may,&#x201D; the Visitor offers gently. &#x201C;I think there&#x2019;s something hidden here. Not in the interpretative function itself, but rather &#x2013; in <em>the very act of interpretation</em>. The interpretive process is not a static one, but an active one of some sort. We have not explored it properly, so we can only speak loosely about it. But perhaps the being of chess is neither in its materials, nor in its rules. But in the mind of a person who desires to play &#x2013; in the interpreter, not interpretation.&#x201D;</p><p>&#x201C;This is not an answer, for the terms I use are nebulous, and ill-defined. It would take another dialogue to properly explicate this hypothesis, let alone test it for validity. But to stake the foundation of <em>being</em> behind the interpreter, is to claim that being is teleological in nature.&#x201D;</p><p>Chess is chess, because two players want to play, you surmise. The final cause. It&#x2019;s not a satisfying answer by any means, especially since there exists a similar progression into absurdity with this answer. To say that chess is endowed with meaning because meaning comes from the mind of the interpreter, is just as awkward of a contortion. <em>What is being? What is meaning? Where are they located?</em></p><p>Ultimately, these questions trouble me, just as it troubles the two interlocutors of my dialogue. For while I presented them in the form of an analogy with chess, the true object of my investigation is actually <em>consciousness</em>. <em>What is consciousness? Where is it located?</em> These are the questions that I seek to know. To state the consciousness is the emergent phenomena of an interaction between neurotransmitters and synapses, is to likewise recourse to the material explanation of being &#x2013; one that is unsatisfying, as illustrated above. Likewise, there seems to be a similar absurdity if we were to abstract consciousness away as a construction of logic, or a sterile computational act.</p><p>There are several potentially fruitful avenues of discussion, that I have not explored in this dialogue. I could examine the role of the interpreter, behind meaning and being. The role of desire (the teleological aim) in ascribing meaning to non-meaning can be important. Likewise, even though the symbol-interpretation hypothesis of meaning is inadequate, there may be a different (perhaps more nuanced) presentation of it that solves the absurdity that I critique. Maybe there is a self-referential component to the being of consciousness, which I have not addressed at all. I would like to be able to explore these questions in greater precision and detail, over the course of a subsequent essay. But this is a concern of mine alone, and it will not be fitting to end the dialogue on such a conclusion. So we turn back, to the Visitor, for the final time.</p><p>&#x201C;Thank you so much for the tea,&#x201D; the Visitor remarks gratefully. &#x201C;And the explanations. I am very grateful for your time.&#x201D;</p><p>Would you, like to play some chess? You ask. The Visitor smiles warmly, and as the sun sets outside your office window, you begin a game.</p><p></p><p><em>I hope you enjoyed my dialogue. If you have any comments, or remarks, feel free to </em><a href="https://shen.hong.io/contact/"><em>contact me</em></a><em> &#x2013; I&apos;m always happy to have conversations with interesting strangers!</em></p>]]></content:encoded></item><item><title><![CDATA[How to Install NixOS With Full Disk Encryption (FDE) using LUKS2, Detached LUKS Header, and A Separate Boot Partition on an USB/MicroSD Card]]></title><description><![CDATA[This tutorial is a guide on installing NixOS, with a separate /boot partition and full-disk-encryption (FDE) using LUKS2 with a detached header.]]></description><link>https://shen.hong.io/installing-nixos-with-encrypted-root-partition-and-seperate-boot-partition/</link><guid isPermaLink="false">61e5f4678d361100013c828b</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Fri, 17 Dec 2021 22:59:00 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/encryption.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/encryption.webp" alt="How to Install NixOS With Full Disk Encryption (FDE) using LUKS2, Detached LUKS Header, and A Separate Boot Partition on an USB/MicroSD Card"><p>This tutorial is a guide on installing <a href="https://nixos.org/">NixOS</a>, with a separate <code>/boot</code> partition and full-disk-encryption (FDE) using <a href="https://gitlab.com/cryptsetup/cryptsetup">LUKS2</a> with a detached header. This guide is different from other tutorials, because it offers the following features:</p><ul><li>Full disk encryption using the newer <a href="https://gitlab.com/cryptsetup/LUKS2-docs/blob/master/luks2_doc_wip.pdf">LUKS2</a> container format, instead of LUKS1.</li><li>Separate <code>/boot</code> partition on portable device (i.e. USB, MicroSD Card).</li><li>Detached LUKS2 header on separate portable device, offering <a href="https://en.wikipedia.org/wiki/Deniable_encryption">deniable encryption</a>.</li></ul><h2 id="example-setup">Example Setup</h2><p>Using this guide, I installed NixOS on a <a href="https://puri.sm/products/librem-14/">Purism Librem 14</a> laptop, running Coreboot with <a href="https://www.tianocore.org/">Tianocore</a>, an open-source implementation of UEFI. Using the laptop&apos;s on-board MicroSD Card reader, I installed both the <code>\boot</code> partition and the LUKS2 header located on a MicroSD card.</p><p>When the MicroSD card is removed from the laptop, the hard drive appears to be unformatted, without the appearance of an operating system at all.</p><h2 id="prerequisites">Prerequisites</h2><p>You must have an USB stick imaged with the appropriate <a href="https://nixos.org/download.html#nixos-iso">NixOS installation image</a>.</p><p>In order to follow this tutorial, two devices are necessary. You will need a primary device (e.g. hard drive, SSD) - which we will denote as <code>/dev/sda</code>. This device holds the LUKS2 container.</p><p>You will also need a second, portable device (e.g. USB, MicroSD card) - which we will denote as <code>/dev/sdb</code>. This device will hold the <code>/boot</code> partition, as well as the detached LUKS2 header.</p><p>If you plan to use a SD or MicroSD card, make sure to get a high-endurance version <a href="https://www.delkin.com/blog/slc-vs-mlc-performance-quick-guide/">that uses SLC or MLC flash</a>. These cards are slower, but allows more write-cycles.</p><p>This tutorial is written for a user that has an intermediate familiarity with Linux operating systems, but who is new to NixOS specifically. You do not need to know how to manually install an operating system. Every single step will be presented in this tutorial, including steps for partitioning.</p><p><strong>Warning: be extremely careful with disk labels when following this tutorial.</strong> Devices and partitions may appear under different labels on your system. Using commands like <code>dd</code> or <code>cryptsetup luksFormat</code> will cause permanent data loss! Use <code>lsblk</code> in order to check disk labels if at all unsure.</p><p>All commands must be run as the <code>root</code> user. This may be done using <code>sudo</code>, or by opening a root shell. In order to open a root shell, simply run:</p><pre><code class="language-bash">sudo su root</code></pre><h2 id="prepare-disks">Prepare Disks</h2><p>This step is entirely optional. We will prepare the disk devices by erasing them. We will first <em>zeroise</em> <code>/dev/sdb</code>, in order to completely wipe it.</p><pre><code class="language-bash">dd if=/dev/zero of=/dev/sdb status=progress
</code></pre><p>Next, we will wipe <code>/dev/sda</code> by filling it with random data.</p><pre><code class="language-bash">dd if=/dev/urandom of=/dev/sda status=progress
</code></pre><p>The reason we wipe the primary device by filling it with random data, instead of simply zero-ising it, is to make the size of the encrypted content undeterminable.</p><p>Now <code>/dev/sda</code> and <code>/dev/sdb</code> are fully prepared. We are ready to create partition tables, and partitions within them.</p><h2 id="create-partitions">Create Partitions</h2><p>We will have the following partition scheme. The specific <a href="https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#Overview">method (or scenario)</a> that we will implement for our Full Disk Encryption (FDE) is to mount an <a href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_%28Linux%29">LVM (Logical Volume Manager)</a> on top of our LUKS2 Container. LVM volumes are then created to reflect the different partitions on our root filesystem. This method is referred to as <a href="https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system#LVM_on_LUKS">LVM on LUKS</a> and is a common method of implementing FDE on Linux devices.</p><p><code>/dev/sda</code> <strong>Primary device</strong></p><ul><li><code>/dev/sda1</code> LUKS2 Container <code>crypted</code></li><li>LVM Volume Group <code>vg</code></li><li>LVM Logical Volume <code>vg-swap</code></li><li>LVM Logical Volume <code>vg-nixos</code></li><li>and any other partitions...</li></ul><p><code>/dev/sdb</code> <strong>Portable device</strong></p><ul><li><code>/dev/sdb1</code> <code>boot</code> Partition</li><li><code>/dev/sdb2</code> LUKS2 Detached Header</li></ul><p>The underlying root <code>\</code>, <code>\home</code>, and swap partitions will be created as LVM virtual volumes within the LUKS2 container.</p><p>We will first proceed to make the partition table and partition for the primary <code>/dev/sda</code> device, then for the portable <code>/dev/sdb</code> device. We will be using <code>parted</code> as the tool to create partitions.</p><h3 id="make-root-partition-on-primary-device-devsda">Make root partition on primary device <code>/dev/sda</code></h3><p>We will be using the <a href="https://www.gnu.org/software/parted/manual/parted.html"><code>parted</code> command-line tool</a> to create all of our partitions.</p><p>Create a <a href="https://en.wikipedia.org/wiki/GUID_Partition_Table"><code>gpt</code> partition table</a> for <code>/dev/sda</code>. Other partition table formats like <code>msdos</code> are not necessary.</p><pre><code class="language-bash">parted /dev/sda -- mklabel gpt
</code></pre><p>Create primary partition for <code>/dev/sda</code>. This will take up all of the space on the primary <code>/dev/sda</code> device, since it will hold a LUKS2 encryption container. Other partitions (such as swap, or <code>/home</code>) will be created within the encryption container.</p><pre><code class="language-bash">parted /dev/sda -- mkpart primary 0% 100%
</code></pre><p>The partition setup for the primary <code>/dev/sda</code> device is now complete. We will now proceed to partition the <code>/dev/sdb</code> portable device (i.e. the USB, MicroSD card).</p><h3 id="make-boot-partition-on-portable-device-devsdb">Make boot partition on portable device <code>/dev/sdb</code></h3><p>Create a <code>gpt</code> partition table for <code>/dev/sdb</code>.</p><pre><code class="language-bash">parted /dev/sdb -- mklabel gpt</code></pre><p>Create boot partition for <code>/dev/sdb</code>. This is the unencrypted partition that will contain the bootloader for the operating system. It will reside on the portable device, which should be stored securely when not in use.</p><pre><code class="language-bash">parted /dev/sdb -- mkpart ESP fat32 0% 50%
</code></pre><p>We must set some flags to indicate that this partition contains the bootloader.</p><pre><code class="language-bash">parted /dev/sdb -- set 1 boot on</code></pre><p>Now we will use the remaining space on the portable device to create a partition for the LUKS2 detached header. This partition will not actually contain a filesystem, since the LUKS2 detached header will be read as a raw header.</p><pre><code class="language-bash">parted /dev/sdb -- mkpart primary 50% 100%
</code></pre><p>I choose to devote the remaining 50% of the device&apos;s space, simply because the MicroSD card will not be used for any other purpose. However, in practice a smaller partition can be allocated for the LUKS2 detached header. Unlike LUKS1, the detached header <a href="https://security.stackexchange.com/a/227358">does not have a static, fixed size</a>. However in practice a partition of 16MB should be more than enough.</p><h2 id="make-filesystem-for-boot-partition">Make filesystem for boot partition</h2><p>We will make a <code>FAT32</code> filesystem for the boot partition. We are using <code>FAT32</code> instead of <code>ext4</code> for greater compatibility with bootloaders.</p><pre><code class="language-bash">mkfs.fat -F 32 -n boot /dev/sdb1
</code></pre><p>It is not necessary to make a filesystem for the LUKS2 header partition <code>/dev/sdb2</code>. This is because the LUKS2 header will reside as a <em>raw</em> header without any underlying file system. This avoids the complications of the bootloader having to mount a filesystem before reading the header file.</p><h2 id="make-luks2-encryption-container-with-detached-headers">Make LUKS2 Encryption Container with detached headers</h2><p>We will now use the <code>cryptsetup</code> command to create the LUKS2 Encryption container. We will invoke the <code>luksFormat</code> action on <code>/dev/sda1</code> in order to do so. The command comes with a set of sensible and sane cryptographic defaults, however you may choose to explore <a href="https://wiki.archlinux.org/title/Dm-crypt/Device_encryption#Encryption_options_for_LUKS_mode">further configuration options</a> if you desire.</p><p>The location of the LUKS2 wrapper is on the primary hard drive <code>/dev/sda1</code>.</p><p>The LUKS2 header which contains the metadata is on raw partition <code>/dev/sdb2</code></p><p>This command will prompt you to enter a password. This password will be used to unlock the primary device when the computer boots.</p><pre><code class="language-bash">cryptsetup luksFormat /dev/sda1 --type luks2 --header /dev/sdb2</code></pre><h2 id="open-luks2-container">Open LUKS2 Container</h2><p>A LUKS2 container has been created on <code>/dev/sda1</code>, with it&apos;s corresponding header file located at <code>/dev/sdb2</code>.</p><p>In order to use this container, we must unlock it using the following command. You will be asked to input the decryption password from the previous step.</p><pre><code class="language-bash">cryptsetup luksOpen /dev/sda1 crypted --header /dev/sdb2</code></pre><p>Now the container will be available as <code>crypted</code>, at <code>/dev/mapper/crypted</code>.</p><h2 id="create-a-lvm-volume-within-the-luks2-container">Create a LVM Volume within the LUKS2 Container</h2><p>We will now create a set of LVM volumes within the LUKS2 container. This way we may have other multiple logical partitions within the container itself.</p><p>First, we will initialise the physical volume <code>crypted</code>. This step is necessary in order to create logical volumes within it.</p><pre><code class="language-bash">pvcreate /dev/mapper/crypted</code></pre><p>Next, we will create a volume group upon the newly-initialised physical volume. This volume group will be called <code>vg</code>.</p><pre><code class="language-bash">vgcreate vg /dev/mapper/crypted</code></pre><p>Now that the volume group is made, we can make arbitrary logical volumes. These logical volumes correspond to the unencrypted partitions of a traditional Linux installation.</p><p>Although it is possible to make multiple partitions corresponding to different mount points (such as <code>/home</code>, <code>/etc</code>, <code>var</code>, etc), in practice this is not necessary. This is because the main benefit of having a separate <code>/home</code> partition is easier recovery, where the <code>/home</code> partition can be mounted separately in a rescue process.</p><p>Because all partitions reside within the encrypted LUKS2 container, which must be unlocked for access, there is no benefit to having separate partitions. Hence we will only create a root and swap partition.</p><h3 id="create-a-swap-partition-within-the-lvm-volume">Create a swap partition within the LVM volume</h3><p>Create a <a href="https://en.wikipedia.org/wiki/Memory_paging#Unix_and_Unix-like_systems">swap partition</a> with the label <code>swap</code>. Note that we use the option <code>-L</code> which denotes a numerical size. In the following command, a 16 GB swap partition is created.</p><pre><code class="language-bash">lvcreate -L 16G -n swap vg</code></pre><p>There are <a href="https://askubuntu.com/questions/62073/how-to-decide-on-swap-size">differing opinions</a> on the appropriate size for a swap partition on modern Linux. In particular, if you wish to to enable <a href="https://www.kernel.org/doc/html/latest/power/basic-pm-debugging.html">hibernation</a> you must have the same amount of swap as your RAM.</p><h3 id="create-a-root-partition-within-the-lvm-volume">Create a root partition within the LVM volume</h3><p>Create a root partition with the label <code>nixos</code> using the remaining free space. Note that we use the option <code>-l</code> which denotes a percentage.</p><pre><code class="language-bash">lvcreate -l &apos;100%FREE&apos; -n nixos vg</code></pre><p>Now the LVM volumes are created, and ready to be used.</p><h2 id="create-filesystem-and-swap">Create Filesystem and Swap</h2><p>After creating the volumes, we must create filesystems that will reside on them. For this guide, we will use a relatively ordinary <a href="https://en.wikipedia.org/wiki/Ext4"><code>ext4</code> filesystem</a>. You may substitute more exotic filesystems such as <a href="https://en.wikipedia.org/wiki/ZFS"><code>ZFS</code></a> or <a href="https://en.wikipedia.org/wiki/Btrfs"><code>btfs</code></a> if desired.</p><pre><code class="language-bash">mkfs.ext4 -L nixos /dev/vg/nixos
mkswap -L swap /dev/vg/swap</code></pre><p>After creating the filesystems, we will mount them (and activate swap).</p><h2 id="mount-filesystems">Mount filesystems</h2><p>Mount the root partition</p><pre><code class="language-bash">mount /dev/disk/by-label/nixos /mnt
</code></pre><p>Mount the boot partition</p><pre><code class="language-bash">mkdir -p /mnt/boot
mount /dev/sdb1 /mnt/boot</code></pre><p>Activate swap</p><pre><code class="language-bash">swapon /dev/vg/swap</code></pre><p>Now our filesystems are mounted. The future NixOS installation&apos;s root will be accessible at <code>/mnt</code>, and it&apos;s boot partition at <code>/mnt/boot</code>.</p><h2 id="configure-nixos">Configure NixOS</h2><p>We can now instruct NixOS to generate a set of configuration files for our installation. Make sure to pass the <code>--root /mnt</code> flag, in order to indicate where the root filesystem resides.</p><pre><code class="language-bash">nixos-generate-config --root /mnt</code></pre><p>Now the configuration files will be available at <code>/mnt/etc/nixos</code>. We must modify this file in order to add the appropriate settings.</p><h3 id="configure-configurationsnix">Configure configurations.nix</h3><p>We can now specify the configuration of the NixOS installation using <code>configurations.nix</code>. The included example configuration file has various options that can be explored. In particular, you should check out the following:</p><ul><li><a href="https://nixos.org/manual/nixos/stable/index.html#sec-user-management">User configuration</a></li><li><a href="https://nixos.org/manual/nixos/stable/index.html#sec-networkmanager">Enabling NetworkManager for WiFi</a></li></ul><p>Once generic configurations are complete, we must add the specific <code>boot.initrd.luks.devices</code> settings which will allow the system to boot.</p><p>The following section must be added. We are specifying for NixOS that there is a dictionary of <code>boot.initrd.luks.devices</code>, where there exists a device <code>crypted</code> with configuration options enclosed in another dictionary.</p><pre><code class="language-nix"># Configuration options for LUKS Device
boot.initrd.luks.devices = {
  crypted = {
    device = &quot;/dev/disk/by-partuuid/&lt;PARTUUID of /dev/sda1&gt;&quot;;
    header = &quot;/dev/disk/by-partuuid/&lt;PARTUUID of /dev/sdb2&gt;&quot;;
    allowDiscards = true; # Used if primary device is a SSD
    preLVM = true;
  };
};</code></pre><p>We must specify the <code>device</code> directive which is a path that points to the encrypted primary partition <code>/dev/sda1</code>, as well as <code>header</code> which is a path that points to the LUKS2 Header located on <code>/dev/sdb2</code>.</p><p>These paths can be specified in more than one way. You can see the various ways that this path is specified by listing the subdirectories in <code>/dev/disk</code>.</p><p>I recommend specifying the paths using <code>/dev/disk/by-partuuid</code> instead of <code>/dev/disk/by-uuid</code>, because <code>/dev/sda1</code> does not have a <code>uuid</code> assigned to it, since it doesn&apos;t have a filesystem.</p><p>Hence in order to find the UUIDs of /dev/sda1 and /deb/sdb2, run:</p><p><code>blkid /dev/sda1 -s PARTUUID</code></p><p>Or you may simply run <code>ls -l /dev/disk/by-partuuid</code></p><h3 id="installation">Installation</h3><p>After setting the configuration options, it is time to install the device. You will need to have an internet connection, as NixOS will be downloading and compiling sources. Either connect to an ethernet cable, or a WiFi network.</p><p>Now run <code>nixos-install</code>. We will specify the option <code>--cores 0</code> to let NixOS use all CPU cores when compiling binaries.</p><pre><code class="language-bash">nixos-install --root /mnt --cores 0</code></pre><p>This command will take anywhere from 5 to 25 minutes, depending on the configuration of the system and the speed of the internet connection.</p><p>Once the installation is nearly complete, it will prompt you to set a root password for the newly installed system. After setting the password, the installation is complete.</p><pre><code class="language-bash">reboot</code></pre><h2 id="post-installation">Post-Installation</h2><p>When the installation is complete, perform a reboot. Now you must enter the BIOS/UEFI interface of your computer&apos;s firmware, and change it&apos;s boot settings to use the bootloader located on the portable device <code>/dev/sdb1</code> by default.</p><p>Now your NixOS installation is complete!</p>]]></content:encoded></item><item><title><![CDATA[Flashing Coreboot Firmware with TianoCore on a Purism Librem 14 Laptop in order to install NixOS]]></title><description><![CDATA[This tutorial is a guide on flashing (i.e. installing) Coreboot firmware on to a Purism Librem 14 laptop.]]></description><link>https://shen.hong.io/flashing-coreboot-on-purism-librem-14/</link><guid isPermaLink="false">61e5fd7e8d361100013c839c</guid><category><![CDATA[Computing]]></category><dc:creator><![CDATA[Shen Zhou Hong]]></dc:creator><pubDate>Wed, 01 Dec 2021 11:25:00 GMT</pubDate><media:content url="https://shen.hong.io/content/images/2023/12/storm.webp" medium="image"/><content:encoded><![CDATA[<img src="https://shen.hong.io/content/images/2023/12/storm.webp" alt="Flashing Coreboot Firmware with TianoCore on a Purism Librem 14 Laptop in order to install NixOS"><p>This tutorial is a guide on flashing (i.e. installing) <a href="https://www.coreboot.org/">Coreboot</a> firmware on to a <a href="https://puri.sm/products/librem-14/">Purism Librem 14</a> laptop. Coreboot is an <a href="https://en.wikipedia.org/wiki/Open_source">open source</a> firmware platform that supports multiple <em>payloads</em>. We will be flashing the <a href="https://www.tianocore.org/">TianoCore</a> payload, which is an open source implementation of <a href="https://en.wikipedia.org/wiki/Unified_Extensible_Firmware_Interface">UEFI</a>.</p><p>We will do this by first booting into a &apos;Live&apos; Linux operating system that is installed on a USB disk. From this environment, we will download the <a href="https://mrchromebox.tech/#fwscript">ChromeOS Firmware Utility Script</a> from Mr.Chromebox. Using the firmware utility script, we will flash the laptop with Coreboot and TianoCore.</p><h2 id="why-is-flashing-coreboot-with-tianocore-necessary">Why is Flashing Coreboot with TianoCore Necessary?</h2><p>Modern (circa 2017) Purism devices ship with <a href="https://docs.puri.sm/PureBoot.html">Pureboot firmware</a> by default. This firmware is based upon Coreboot, and offers additional tamper-evident features such as <a href="https://docs.puri.sm/PureBoot/Heads.html#heads">Heads</a>. However, Pureboot is a <a href="https://en.wikipedia.org/wiki/BIOS">BIOS firmware</a>, not UEFI. As a result, <a href="https://github.com/NixOS/nixos-hardware/tree/master/purism/librem/13v3">it does not support EFI boot</a>.</p><p>NixOS 21.05 does comes with instructions for <a href="https://nixos.org/manual/nixos/stable/index.html#sec-installation-partitioning-MBR">installing on a BIOS using GRUB</a>. However, I was unable to accomplish this successfully. As a result, I decided to use Coreboot with TianoCore as my firmware instead.</p><h2 id="prerequisites">Prerequisites</h2><p>You must have a USB stick with an &apos;Live&apos; Linux operating system, a stable power supply, as well as an internet connection. I used an USB stick with an installation image of <a href="https://ubuntu.com/download/desktop">Ubuntu 21.10</a>, but other Debian-based operating systems should also work well.</p><p>This tutorial is written for a user that has an intermediate familiarity with Linux operating systems, but who has never flashed their device firmware before. The firmware installation will use a script, hence you do not need any advanced technical knowledge. A familiarity with the Linux command line is beneficial.</p><p><strong>Warning 1/3: modifying your laptop&apos;s firmware is potentially hazardous.</strong> Should the process be interrupted (e.g. due to unexpected power loss), your device may be unable to boot at all. Recovering from such an incident <a href="https://wiki.mrchromebox.tech/Unbricking#Unbricking.2FFLashing_with_a_CH341a_USB_programmer">requires the use of a hardware programmer</a>, and may be technically challenging.</p><p><strong>Warning 2/3: changing from a BIOS-based firmware to UEFI will render your existing operating system unbootable.</strong> A pre-existing installation will expect a BIOS boot process. Changing the underlying firmware will naturally render the bootloader non-functional. This tutorial is meant for a brand-new NixOS installation, and does not contain any information on recovering any pre-existing operating systems.</p><p><strong>Warning 3/3: be extremely careful with disk labels when following this tutorial.</strong> Devices and partitions may appear under different labels on your system. Commands like <code>dd</code> will cause permanent data loss! Use <code>lsblk</code> in order to check disk labels if at all unsure.</p><p>All commands must be run as the <code>root</code> user. This may be done using <code>sudo</code>, or by opening a root shell. In order to open a root shell, simply run:</p><pre><code class="language-bash">sudo su root</code></pre><h2 id="preparing-installation-medium">Preparing Installation Medium</h2><p>First, download a &apos;Live&apos; Linux operating system, and image it on to a spare USB stick. Make sure to verify the disk label of your USB stick using <code>lsblk</code>. I used a desktop installation image of <a href="https://ubuntu.com/download/desktop">Ubuntu 21.10</a>. Once the image is downloaded, you may image it using <code>dd</code>:</p><pre><code class="language-bash">dd if=/~Downloads/ubuntu-21.10.iso of=/dev/sdX status=progress</code></pre><p>After you have imaged the installation iso on the USB, restart the laptop and boot into the &apos;Live&apos; operating system on the USB.</p><h2 id="download-chromeos-firmware-utility-script">Download ChromeOS Firmware Utility Script</h2><p>Once you have booted into the &apos;Live&apos; environment of the USB operating system, start a root shell. We will now download the ChromeOS firmware utility script. The ChromeOS firmware utility scripts are by <a href="https://mrchromebox.tech/">Mr. Chromebox</a>, which offers a convenient method to flash and update the Coreboot-based firmware of ChromeOS-based computers. Mr. Chromebox is a Purism employee and firmware developer, and his firmware utility script is <a href="https://forums.puri.sm/t/mrchromebox-uefi-firmware-for-librem-devices-unofficial-unsupported/9760">unofficially available for Purism devices</a> as well.</p><p>The download page for the firmware utility script is <a href="https://mrchromebox.tech/#fwscript">available on his website</a>, with the source files also available at his <a href="https://github.com/MrChromebox/scripts">Github Repository</a>. We will download the script from his Github repository using the commands below.</p><p>Change to home directory, and download script:</p><pre><code class="language-bash">cd
curl -LO https://raw.githubusercontent.com/MrChromebox/scripts/master/firmware-util.sh</code></pre><p>Next, <a href="https://sysdig.com/blog/friends-dont-let-friends-curl-bash/">make sure to inspect the downloaded script</a>. Scroll through the file carefully. Press <code>q</code> to exit the reader:</p><pre><code class="language-bash">less firmware-util.sh</code></pre><h2 id="running-the-firmware-utility-script">Running the Firmware Utility Script</h2><p>Run the script using the bash shell:</p><pre><code class="language-bash">bash firmware-util.sh</code></pre><p>The script will proceed to download other auxiliary helper scripts. When it is ready, it will present a visual TUI (Text User Interface), with conveniently labeled menu buttons. It will present a menu with a selection of choices.</p><p>Select the one labeled <em>Install/Update UEFI (Full ROM) Firmware</em>. Depending on your version of the script, it should be the option #2.</p><p>Once you have selected the option, follow the on-screen prompts to confirm. The firmware will then proceed to flash Coreboot with Tianocore on your laptop.</p><p>Make sure to not interrupt the process, and that your laptop is connected to power at all times. Once the firmware flashing process is complete, you may restart your computer by pressing <code>r</code> on the prompt.</p><h2 id="verifying-that-the-new-tianocore-uefi-firmware-is-complete">Verifying that the new TianoCore UEFI firmware is complete.</h2><p>Restart your laptop in order to boot into the new firmware. Press <code>Esc</code> while the laptop is booting, in order to enter the UEFI interface. Please note that <strong>it may take up to 5 minutes for your laptop to reboot for the first time</strong>, as the firmware has to complete some initialisation and hardware auto-detection routines. This is perfectly normal, and you do not need to worry. Subsequent boots will take only a few seconds.</p><p>You should be now in the new TianoCore UEFI firmware menu! You have successfully completed this tutorial.</p><h2 id="next-steps-installing-nixos-or-any-other-os">Next Steps: Installing NixOS, or any other OS.</h2><p>Now your Purism Librem 14 laptop is successfully flashed with a high quality, open source UEFI implementation. You can proceed to install any other operating system on top of it.</p><p>This guide was specifically written to install NixOS on the Purism Librem 14 laptop. <a href="https://shen.hong.io/installing-nixos-with-encrypted-root-partition-and-seperate-boot-partition/">The subsequent guide is available here</a>.</p>]]></content:encoded></item></channel></rss>