<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Nathan's Blog</title>
    <link href="http://www.nathantypanski.com/atom.xml" rel="self" />
    <link href="http://www.nathantypanski.com" />
    <id>http://www.nathantypanski.com/atom.xml</id>
    <author>
        <name>Nathan Typanski</name>
        
        <email>ndt@nathantypanski.com</email>
        
    </author>
    <updated>2025-05-01T00:00:00Z</updated>
    <entry>
    <title>Framework 13&#58; Arch, Secure Boot, &amp; Nix</title>
    <link href="http://www.nathantypanski.com/blog/2025-05-01-framework-13.html" />
    <id>http://www.nathantypanski.com/blog/2025-05-01-framework-13.html</id>
    <published>2025-05-01T00:00:00Z</published>
    <updated>2025-05-01T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> May  1, 2025 <i class="fa fa-cut" July 22, 2025
></i> July 22, 2025

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#big-blue" id="toc-big-blue">Big Blue</a></li>
<li><a href="#hardware-overview" id="toc-hardware-overview">Hardware Overview</a>
<ul>
<li><a href="#battery" id="toc-battery">Battery</a></li>
<li><a href="#suspend" id="toc-suspend">Suspend</a></li>
<li><a href="#security-hardware" id="toc-security-hardware">Security Hardware</a></li>
</ul></li>
<li><a href="#installation" id="toc-installation">Installation</a>
<ul>
<li><a href="#disk-layout-and-encryption" id="toc-disk-layout-and-encryption">Disk Layout and Encryption</a></li>
</ul></li>
<li><a href="#security-configuration" id="toc-security-configuration">Security Configuration</a>
<ul>
<li><a href="#secure-boot-setup" id="toc-secure-boot-setup">Secure Boot Setup</a></li>
<li><a href="#tpm-disk-encryption" id="toc-tpm-disk-encryption">TPM Disk Encryption</a></li>
<li><a href="#defense-against-boot-attacks" id="toc-defense-against-boot-attacks">Defense against boot attacks</a></li>
<li><a href="#fingerprint-unlock" id="toc-fingerprint-unlock">Fingerprint Unlock</a></li>
<li><a href="#nvme-powersaving-settings" id="toc-nvme-powersaving-settings">NVMe Powersaving settings</a></li>
</ul></li>
<li><a href="#userspace-management" id="toc-userspace-management">Userspace management</a>
<ul>
<li><a href="#nix-settingsnix-daemon-restart" id="toc-nix-settingsnix-daemon-restart">Nix settings</a></li>
<li><a href="#home-manager-flake" id="toc-home-manager-flake">Home-Manager Flake</a></li>
<li><a href="#local-ai-with-ollama" id="toc-local-ai-with-ollama">Local AI with ollama</a></li>
</ul></li>
</ul> </div>
<p>In early 2025, I finally found my dream Linux laptop: the AMD Framework 13. Combining the modular, repair-first ethos of the original ThinkPads with a gorgeous 3:2 display.</p>
<section id="big-blue" class="level2">
<h2>Big Blue</h2>
<p>A 14” ThinkPad T420—repeatedly upgraded for more RAM, storage, and eventually even a faster CPU—carried me through college.
I fell in love with its pragmatic design, hacker friendliness, and broad plug-and-play Linux kernel support (most kernel developers—at the time, anyway—were ThinkPad users).</p>
<p>But something <em>happened</em> at Lenovo: in 2015–2016, a string of generously-named <q><a href="https://en.wikipedia.org/wiki/Lenovo#Security_and_privacy_incidents">security incidents</a></q> shattered consumer confidence, and each year felt like a step backwards in ThinkPad quality: Lenovo dialed back repairability in favor of sleek design, and capped screen resolutions for flagship models at 1080/1440p.
In 2020, when the soldered-in-place RAM module on my brand new X1 Carbon failed after mere months of use, I elected to replace <em>Lenovo</em> instead of the machine.</p>
<p>The AMD Framework 13 laptop is everything I loved about ThinkPad: repairable, upgradeable, with high-quality displays and mainline kernel support (Tainted kernels aren’t my thing).
It is a wonderful, tiny machine
with a 3:2 display is <em>just</em> large enough for comfortable daily use and a chassis <em>just</em> light enough for carry as a second laptop when I’m on-call at work.</p>
</section>
<section id="hardware-overview" class="level2">
<h2>Hardware Overview</h2>
<p>I <q>built</q> the hardware myself, meaning my Framework 13 required last-mile assembly by the end-user. This gives a better sense of control &amp; repairability and lets you customize the machine, and it only takes a few minutes to assemble. I read complaints online about build quality but haven’t personally noticed anything bothersome besides wishing the bezel were aluminum (a very minor thing).</p>
<p>The AMD Ryzen AI 7 350 with Radeon 860M provides excellent performance and Linux compatibility. The integrated graphics work well with ollama for local AI inference:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>$ cat /proc/cpuinfo | grep &#39;^model name&#39; | head -n1</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>model name      : AMD Ryzen AI 7 350 w/ Radeon 860M</span></code></pre></div>
<p>The trackpad impressed me—it allows full tactile buttonpress over most of the surface (everything except the top half-centimeter). The keyboard is good quality; I chose blank keycaps for style and to encourage memorizing my layout. The power button doubles as a fingerprint reader that works reliably with out-of-box drivers.</p>
<section id="battery" class="level3">
<h3>Battery</h3>
<p>I get excellent battery life for a Linux machine. With the <code>power-saver</code> power governeror (from <code>powerprofilesctl</code>), I get between 6 and 10 hours of battery life depending on usage.</p>
</section>
<section id="suspend" class="level3">
<h3>Suspend</h3>
<p>Suspend didn’t work flawlessly out of the box: sometimes the system would cold boot instead of resume.</p>
<p>I noticed something odd in the <code>smartctl</code> output:</p>
<pre><code># # smartctl -a /dev/nvme0n1  | grep -i shutdown
Unsafe Shutdowns:                   44,053</code></pre>
<p>This is particular to the <code>WD_BLACK SN7100 2TB</code> drive that I ordered. I’ll describe the fixes later in this article.</p>
</section>
<section id="security-hardware" class="level3">
<h3>Security Hardware</h3>
<p>This AMD processor includes the <a href="https://storage.googleapis.com/gweb-uniblog-publish-prod/documents/AMD_GPZ-Technical_Report_FINAL_05_2022.pdf">Pod Security Processor</a>, AMD’s equivalent to Intel Management Engine. The downside is that these secondary processors are often vulnerable and have privileged access to system hardware. The upside is this provides functionality such as Secure Boot.</p>
<p>As far as I know, it is not currently possible to use CoreBoot on the latest AMD Framework 13, but <a href="https://www.phoronix.com/news/Framework-13-AMD-Coreboot-WIP">experimental support</a> is in progress for the previous gen of AMD CPUs, leaving me hopeful that we will see it here eventually. However, the default firmware supports Secure Boot with custom keys, which is good enough for me at the moment.</p>
</section>
</section>
<section id="installation" class="level2">
<h2>Installation</h2>
<p>NixOS is my distro of choice, but didn’t work for me: it failed to detect the Framework’s internal keyboard, using both the minimal <em>and</em> graphical NixOS ISOs.
I tested external keyboards—which worked. As far as NixOS was concerned, the internal Framework keyboard device <em>did not exist</em>.
I was able to successfully plug in external USB keyboards, however, and fell back to what I’m comfortable running: Arch.</p>
<p>To capture some of the benefits of Nix, I took a hybrid approach: Arch Linux as the base system with Nix (<code>pacman -S nix</code>) and home-manager for userspace configuration. This lets me reuse and improve configurations from my NixOS desktop while maintaining hardware compatibility. The <q>dividing line</q> I established: <code>home-manager</code> manages nearly all of my homedir, while <code>pacman</code> handles system packages and interactive configuration.</p>
<p>I plan to eventually migrate the full system NixOS. The nice thing about this is migrations from running systems are <a href="https://github.com/jeaye/nixos-in-place">popular enough to be well-supported</a> (at least by the community), and NixOS’s declarative nature lends itself particularly well to in-place upgrades.</p>
<section id="disk-layout-and-encryption" class="level3">
<h3>Disk Layout and Encryption</h3>
<p>Taking advantage of the built-in TPM, I configured automatic dm-crypt key storage. The layout uses separate <code>/boot</code> and <code>/boot/efi</code> partitions, with the main <code>/dev/nvme0n1p2</code> partition housing LUKS. Within LUKS is LVM: one volume group for encrypted swap, and another divided into <code>/home</code>, <code>/var/</code>, and <code>/</code> btrfs subvolumes.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>$ lsblk</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>NAME          MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>nvme0n1       259:0    0  1.8T  0 disk</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>├─nvme0n1p1   259:1    0    2G  0 part  /boot</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>├─nvme0n1p2   259:2    0  1.8T  0 part</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>│ └─cryptroot 253:0    0  1.8T  0 crypt</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>│   ├─vg-swap 253:1    0   32G  0 lvm   [SWAP]</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>│   └─vg-main 253:2    0  1.8T  0 lvm   /home</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>│                                       /var</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>│                                       /</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>└─nvme0n1p3   259:3    0    2G  0 part  /boot/efi</span></code></pre></div>
</section>
</section>
<section id="security-configuration" class="level2">
<h2>Security Configuration</h2>
<p>With the base system and disk layout in place, next up is hardening the system security posture. The Framework 13 supports modern security functionality like secure boot, TPM-based disk encryption, and biometric authentication.</p>
<section id="secure-boot-setup" class="level3">
<h3>Secure Boot Setup</h3>
<p>I use <code>systemd-ukify</code>, <code>sbsign</code>, and pacman hooks for a fully automated Secure Boot configuration—meaning I don’t have to manually re-sign binaries when I build a new initramfs or pull down a kernel update.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a># sbctl status</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>Installed:      ✓ sbctl is installed</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>Owner GUID:     9df5ea07-e33e-4361-9c03-51d46b0dd9ed</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>Setup Mode:     ✓ Disabled</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>Secure Boot:    ✓ Enabled</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>Vendor Keys:    none</span></code></pre></div>
<p>To get here you need to generate keys:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a># sbctl create-keys</span></code></pre></div>
<p>This creates keys in <code>/var/lib/sbctl/keys/</code>:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a># ls /var/lib/sbctl/keys</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>db  KEK  PK</span></code></pre></div>
<p>Then you reboot, put the secure-boot in setup mode (which allows writing keys), and enroll your keys with <code>sbctl enroll-keys</code>.</p>
<section id="sbctl-and-signing" class="level4">
<h4>sbctl and signing</h4>
<p>When you enable secure boot, your bootloader and kernel images will require signing. I ensure this with a pacman hook:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a># cat /etc/pacman.d/hooks/90-secureboot-systemd-update.hook</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>[Trigger]</span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>Operation = Upgrade</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>Type = Package</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>Target = systemd # systemd-boot is part of the systemd package</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>[Action]</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>Description = Updating and signing systemd-boot on ESP for Secure Boot...</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>When = PostTransaction</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>Exec = /usr/local/bin/update-signed-bootloader</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>NeedsTargets</span></code></pre></div>
<p>Which calls a script <code>/update-signed/bootloader</code><a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a>.</p>
<p>A database at <code>/var/lib/sbctl/files.json</code> tracks which files need to be signed.
To sign a file and add it to the database, simply sign it with <code>-s, --save</code>:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>$ sbctl sign --save /boot/efi/EFI/Linux/arch-troubleshoot.efi</span></code></pre></div>
<p>Now this file is tracked for future updates:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a># sbctl list-files</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>/boot/efi/EFI/systemd/systemd-bootx64.efi</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>Signed:         ✓ Signed</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>...</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>/boot/efi/EFI/Manual/arch.efi</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>Signed:         ✓ Signed</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>/boot/efi/EFI/Manual/arch-troubleshoot.efi</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>Signed:         ✓ Signed</span></code></pre></div>
<p>Before rebooting, be sure to check the status is healthy:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a># sbctl status</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>Installed:      ✓ sbctl is installed</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>Owner GUID:     9df5ea07-e33e-4361-9c03-51d46b0dd9ed</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>Setup Mode:     ✓ Disabled</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>Secure Boot:    ✓ Enabled</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>Vendor Keys:    none</span></code></pre></div>
</section>
<section id="ukify" class="level4">
<h4>ukify</h4>
<p>The traditional Linux boot chain is multi-step and offers multiple opportunities for customization.
In turn, these are multiple opportunities for compromise:</p>
<ol type="1">
<li>Bootloader.</li>
<li>Kernel images.</li>
<li>initramfs.</li>
<li>Command line options.</li>
</ol>
<p>Instead, we can combine each of these steps after the initial bootloader into one image for signing and execution.
I’m using <code>ukify</code> to generate <a href="https://uapi-group.org/specifications/specs/unified_kernel_image/">unified kernel images</a> with an embedded initramfs and kernel image. This is done via a file <code>/etc/pacman.d/hooks/95-sbctl-ukify</code>:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>[Trigger]</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>Operation = Upgrade</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>Type = Package</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>Target = linux</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>Target = amd-ucode # Add other relevant packages like &#39;systemd&#39; if its updates affect initramfs build</span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>[Action]</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>Description = Rebuilding UKIs and signing with sbctl...</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>When = PostTransaction</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>Exec = /usr/local/bin/rebuild-ukis-for-sbctl.sh</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>NeedsTargets</span></code></pre></div>
<p>This calls a script <code>/usr/local/bin/rebuild-ukis-for-sbctl.sh</code>, that handles image signing and configuring root disk unlock:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-euo</span> pipefail</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Configuration</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="va">LUKS_UUID</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">blkid</span> <span class="at">-s</span> UUID <span class="at">-o</span> value /dev/nvme0n1p2<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a><span class="va">SWAP_UUID</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">blkid</span> <span class="at">-s</span> UUID <span class="at">-o</span> value /dev/mapper/vg-swap<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a><span class="va">UKI_PATH</span><span class="op">=</span><span class="st">&quot;/boot/efi/EFI/Manual&quot;</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="co"># Basic kernel command line</span></span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a><span class="va">CMDLINE</span><span class="op">=</span><span class="st">&quot;root=/dev/mapper/vg-main rw rootflags=subvol=@ rd.luks.name=</span><span class="va">${LUKS_UUID}</span><span class="st">=cryptroot rd.luks.options=</span><span class="va">${LUKS_UUID}</span><span class="st">=tpm2-device=auto,tries=3 resume=UUID=</span><span class="va">${SWAP_UUID}</span><span class="st"> nvme_core.default_ps_max_latency_us=25000&quot;</span></span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-12"><a href="#cb13-12" aria-hidden="true" tabindex="-1"></a><span class="co"># Signing configuration</span></span>
<span id="cb13-13"><a href="#cb13-13" aria-hidden="true" tabindex="-1"></a><span class="va">SECUREBOOT_KEY</span><span class="op">=</span><span class="st">&quot;/var/lib/sbctl/keys/db/db.key&quot;</span></span>
<span id="cb13-14"><a href="#cb13-14" aria-hidden="true" tabindex="-1"></a><span class="va">SECUREBOOT_CERT</span><span class="op">=</span><span class="st">&quot;/var/lib/sbctl/keys/db/db.pem&quot;</span></span>
<span id="cb13-15"><a href="#cb13-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-16"><a href="#cb13-16" aria-hidden="true" tabindex="-1"></a><span class="fu">build_uki()</span> <span class="kw">{</span></span>
<span id="cb13-17"><a href="#cb13-17" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">kernel</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb13-18"><a href="#cb13-18" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">initrd</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$2</span><span class="st">&quot;</span></span>
<span id="cb13-19"><a href="#cb13-19" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">output</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$3</span><span class="st">&quot;</span></span>
<span id="cb13-20"><a href="#cb13-20" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">extra_cmdline</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${4</span><span class="op">:-</span><span class="va">}</span><span class="st">&quot;</span></span>
<span id="cb13-21"><a href="#cb13-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-22"><a href="#cb13-22" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;Building </span><span class="va">$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$output</span><span class="st">&quot;</span><span class="va">)</span><span class="st">...&quot;</span></span>
<span id="cb13-23"><a href="#cb13-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-24"><a href="#cb13-24" aria-hidden="true" tabindex="-1"></a>    <span class="ex">ukify</span> build <span class="dt">\</span></span>
<span id="cb13-25"><a href="#cb13-25" aria-hidden="true" tabindex="-1"></a>        <span class="at">--linux</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$kernel</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb13-26"><a href="#cb13-26" aria-hidden="true" tabindex="-1"></a>        <span class="at">--initrd</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$initrd</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb13-27"><a href="#cb13-27" aria-hidden="true" tabindex="-1"></a>        <span class="at">--microcode</span><span class="op">=</span><span class="st">&#39;/boot/amd-ucode.img&#39;</span> <span class="dt">\</span></span>
<span id="cb13-28"><a href="#cb13-28" aria-hidden="true" tabindex="-1"></a>        <span class="at">--os-release</span><span class="op">=</span><span class="st">&#39;@/etc/os-release&#39;</span> <span class="dt">\</span></span>
<span id="cb13-29"><a href="#cb13-29" aria-hidden="true" tabindex="-1"></a>        <span class="at">--cmdline</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${CMDLINE}</span><span class="st"> </span><span class="va">${extra_cmdline}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb13-30"><a href="#cb13-30" aria-hidden="true" tabindex="-1"></a>        <span class="at">--signtool</span><span class="op">=</span><span class="st">&#39;sbsign&#39;</span> <span class="dt">\</span></span>
<span id="cb13-31"><a href="#cb13-31" aria-hidden="true" tabindex="-1"></a>        <span class="at">--output</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$output</span><span class="st">&quot;</span></span>
<span id="cb13-32"><a href="#cb13-32" aria-hidden="true" tabindex="-1"></a>    <span class="ex">sbctl</span> sign <span class="st">&quot;</span><span class="va">${output}</span><span class="st">&quot;</span></span>
<span id="cb13-33"><a href="#cb13-33" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb13-34"><a href="#cb13-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-35"><a href="#cb13-35" aria-hidden="true" tabindex="-1"></a><span class="co"># Build main kernel</span></span>
<span id="cb13-36"><a href="#cb13-36" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb13-37"><a href="#cb13-37" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux&quot;</span> <span class="dt">\</span></span>
<span id="cb13-38"><a href="#cb13-38" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux.img&quot;</span> <span class="dt">\</span></span>
<span id="cb13-39"><a href="#cb13-39" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${UKI_PATH}</span><span class="st">/arch.efi&quot;</span></span>
<span id="cb13-40"><a href="#cb13-40" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-41"><a href="#cb13-41" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;UKIs rebuilt and signed successfully.&quot;</span></span></code></pre></div>
</section>
</section>
<section id="tpm-disk-encryption" class="level3">
<h3>TPM Disk Encryption</h3>
<p>With the standard <code>dm-crypt/LUKS</code> configuration in place, it’s time to setup TPM enrollment.
The goal of our TPM encryption is to support automatic disk decryption only when the entire Secure Boot chain is untampered. If the measured boot state changes, it falls back to password entry.
Mistakes here mean an adversary with physical access can convince the TPM to unlock your OS without even a password! So we need to validate the firmware at boot time, and the kerel image that we’re booting, and the filesystem layout which we are booting against, to ensure an untampered boot chain all the way up to a running system.</p>
<p>Your TPM uses hashes of system state, or <q>measurements</q> stored in Platform Configuration Registers (PCRs), to determine the state of the machine before releasing key material. To see the available PCRs for your system:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a># systemd-analyze pcrs</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>NR NAME                SHA256</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> 0 platform-code       [6aebf...                    sha256                    ...dea32]</span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> 1 platform-config     [...                         sha256                         ...]</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> 2 external-code       [...                         sha256                         ...]</span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> 3 external-config     [...                (same as external-code)                 ...]</span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> 4 boot-loader-code    [...                         sha256                         ...]</span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> 5 boot-loader-config  [...                         sha256                         ...]</span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> 6 host-platform       [...                         sha256                         ...]</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a> 7 secure-boot-policy  [...                         sha256                         ...]</span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a> 8 -                   0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a> 9 kernel-initrd       [...                         sha256                         ...]</span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a>10 ima                 0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>11 kernel-boot         [...                         sha256                         ...]</span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a>12 kernel-config       0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a>13 sysexts             0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a>14 shim-policy         0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a>15 system-identity     [...                         sha256                         ...]</span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a>16 debug               0000000000000000000000000000000000000000000000000000000000000000</span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a>17 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a>18 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a>19 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a>20 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true" tabindex="-1"></a>21 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true" tabindex="-1"></a>22 -                   ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff</span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a>23 application-support 0000000000000000000000000000000000000000000000000000000000000000</span></code></pre></div>
<p>You need to pick which PCRs must match before TPM auto-unlock proceeds.
Here are the most popular:</p>
<table>
<colgroup>
<col style="width: 2%" />
<col style="width: 6%" />
<col style="width: 91%" />
</colgroup>
<thead>
<tr>
<th><strong>PCR</strong></th>
<th><strong>Name</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0</code></td>
<td><code>platform-code</code></td>
<td>Core system firmware and boot code; changes on firmware updates</td>
</tr>
<tr>
<td><code>4</code></td>
<td><code>boot-loader-code</code></td>
<td>Boot loader and additional drivers, PE binaries invoked by the boot loader; changes on boot loader updates. <code>sd-stub(7)</code> measures system extension images read from the ESP here too (see <code>systemd-sysext(8)</code>).</td>
</tr>
<tr>
<td><code>6</code></td>
<td><code>host-platform</code></td>
<td>Host platform configuration and data</td>
</tr>
<tr>
<td><code>7</code></td>
<td><code>secure-boot-policy</code></td>
<td>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes.</td>
</tr>
<tr>
<td><code>15</code></td>
<td><code>system-identity</code></td>
<td><code>systemd-cryptsetup(8)</code> optionally measures the volume key of activated LUKS volumes into this PCR. <code>systemd-pcrmachine.service(8)</code> measures the machine- id(5) into this PCR. <code>systemd-pcrfs@.service(8)</code> measures mount points, file system UUIDs, labels, partition UUIDs of the root and <code>/var/</code> filesystems into this PCR.</td>
</tr>
</tbody>
</table>
<p>I enrolled the encryption key with the TPM:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a># systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=&quot;0+6+7&quot; /dev/nvme0n1p2</span></code></pre></div>
<p>To confirm that worked:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a># cryptsetup luksDump /dev/nvme0n1p2 | grep &quot;hash-pcrs&quot;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>        tpm2-hash-pcrs:   0+6+7</span></code></pre></div>
<p>This enrolls PCRs 0, 6 and 7. If those values have changed, then the TPM will refuse to unlock the disk.</p>
<blockquote>
<p><strong>Warning</strong>: Even with this TPM-based disk unlock technique, <em>attackers can <a href="https://oddlama.org/blog/bypassing-disk-encryption-with-tpm2-unlock/">decrypt your disk</a></em>!
Nearly every disk encryption guide for Linux misses this attack, as it is difficult to defend against.</p>
<p>As mentioned in the <a href="https://wiki.archlinux.org/title/Systemd-cryptenroll#Trusted_Platform_Module">Arch Wiki</a>:</p>
<blockquote>
<p>*Only binding to PCRs measured pre-boot (PCRs 0-7) opens a vulnerability from rogue operating systems. A rogue partition with metadata copied from the real root filesystem (such as partition UUID) can mimic the original partition. Then, initramfs will attempt to mount the rogue partition as the root filesystem (decryption failure will fall back to password entry), leaving pre-boot PCRs unchanged. The rogue root filesystem with files controlled by an attacker is still able to receive the decryption key for the real root partition. See <a href="https://0pointer.net/blog/brave-new-trusted-boot-world.html">Brave New Trusted Boot World</a> and <a href="https://learn.microsoft.com/en-us/windows/security/operating-system-security/data-protection/bitlocker/countermeasures">BitLocker documentation</a> for additional information.</p>
</blockquote>
<p>To resolve this issue, we will configure a post-boot systemd unit to measure PCR 15. For now, let’s just enroll encryption with a PIN.</p>
</blockquote>
<p>Enroll the TPM with <code>systemd-cryptenroll</code>. Later, I’ll implement full-chain root filesystem verification for improved convenience.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a># systemd-cryptenroll /dev/nvme0n1p2 \</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>    --tpm2-device=auto \</span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>    --tpm2-pcrs=0+6+7 \</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    --tpm2-with-pin=yes</span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>🔐 Please enter current passphrase for disk /dev/nvme0n1p2: •••••••••••••••••••••••••••••</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>🔐 Please enter TPM2 PIN: ••••••••••</span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>🔐 Please enter TPM2 PIN (repeat): ••••••••••</span></code></pre></div>
<p>Now let’s update our kernel cmdline to unlock the root device, with 3 tries for the PIN:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>rd.luks.options=${LUKS_UUID}=tpm2-device=auto,tries=3</span></code></pre></div>
<p>Trigger a ukify rebuild and reboot. The TPM PIN prompting should work as expected.</p>
<section id="pcr-signing" class="level4">
<h4>PCR Signing</h4>
<p>The configuration technique provided earlier has a design weakness. The TPM unlocks the LUKS disk if and only if the PCRs supplied in <code>--tpm2-pcrs</code> have specific, exact hash values.
These values either match when we set them, or boot fails. So a more restrictive PCR configuration is going to require more frequent re-enrollment.
If we wanted to bind the kernel image to the TPM unlock, then we would have to re-enroll on every kernel image update.</p>
<p>Instead, we can configure the TPM to unlock when the PCRs are signed by a given signing key.
This will sign PCR11, which changes on every kernel update, initramfs rebuild, and so on.
Luckily <code>ukify</code> can handle this for us.</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>UKIFY(1)                             ukify                             UKIFY(1)</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>NAME</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a>       ukify - Combine components into a signed Unified Kernel Image for UEFI</span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a>       systems</span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>       [...]</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a>       If PCR signing keys are provided via the</span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a>       PCRPrivateKey=/--pcr-private-key= and PCRPublicKey=/--pcr-public-key=</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>       options, PCR values that will be seen after booting with the given</span>
<span id="cb19-12"><a href="#cb19-12" aria-hidden="true" tabindex="-1"></a>       kernel, initrd, and other sections, will be calculated, signed, and</span>
<span id="cb19-13"><a href="#cb19-13" aria-hidden="true" tabindex="-1"></a>       embedded in the UKI.  systemd-measure(1) is used to perform this</span>
<span id="cb19-14"><a href="#cb19-14" aria-hidden="true" tabindex="-1"></a>       calculation and signing.</span></code></pre></div>
<p>First, generate a keypair:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a># ukify genkey \</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>    --pcr-private-key=/var/lib/pcr-keys/pcr-initrd.key.pem \</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>    --pcr-public-key=/var/lib/pcr-keys/pcr-initrd.pub.pem</span></code></pre></div>
<p>Now we update our <code>ukify</code> build pipeline to use the keys:</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a># ukify build \</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>    --linux=&#39;/boot/vmlinuz-linux&#39; \</span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>    --initrd=&#39;/boot/initramfs-linux&#39; \</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a>    --microcode=&#39;/boot/amd-ucode.img&#39; \</span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a>    --os-release=&#39;@/etc/os-release&#39; \</span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>    --cmdline=&quot;${CMDLINE}&quot; \</span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a>    --pcrs=0,6,7 \</span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>    --pcr-private-key=&#39;/var/lib/pcr-keys/pcr-initrd.key.pem&#39; \</span>
<span id="cb21-9"><a href="#cb21-9" aria-hidden="true" tabindex="-1"></a>    --pcr-public-key=&#39;/var/lib/pcr-keys/pcr-initrd.pub.pem&#39; \</span>
<span id="cb21-10"><a href="#cb21-10" aria-hidden="true" tabindex="-1"></a>    --signtool=&#39;sbsign&#39; \</span>
<span id="cb21-11"><a href="#cb21-11" aria-hidden="true" tabindex="-1"></a>    --secureboot-private-key=&#39;/var/lib/sbctl/keys/db/db.key&#39; \</span>
<span id="cb21-12"><a href="#cb21-12" aria-hidden="true" tabindex="-1"></a>    --secureboot-certificate=&#39;/var/lib/sbctl/keys/db/db.pem&#39; \</span>
<span id="cb21-13"><a href="#cb21-13" aria-hidden="true" tabindex="-1"></a>    --output=&#39;/boot/efi/EFI/Manual/arch.efi&#39;</span></code></pre></div>
<p>Now <code>ukify</code> is handling both secureboot and PCR signing.</p>
<p>Wipe and re-enroll the TPM:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a># systemd-cryptenroll --wipe-slot=tpm2 /dev/nvme0n1p2</span></code></pre></div>
<div class="sourceCode" id="cb23"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a># systemd-cryptenroll --tpm2-device=auto \</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>  --tpm2-pcrs=0+6+7 \</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>  --tpm2-public-key=/var/lib/pcr-keys/pcr-initrd.pub.pem \</span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>  /dev/nvme0n1p2</span></code></pre></div>
<p>Now you see a hybrid PCR approach:</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a># cryptsetup luksDump /dev/nvme0n1p2</span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>    ...</span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>0: systemd-tpm2</span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>        tpm2-hash-pcrs:   0+6+7</span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>        tpm2-pcr-bank:    sha256</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>        tpm2-pubkey:</span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>        tpm2-pubkey-pcrs: 11</span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>        tpm2-primary-alg: ecc</span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a>        tpm2-pin:         false</span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a>        tpm2-pcrlock:     false</span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a>        tpm2-salt:        false</span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a>        tpm2-srk:         true</span>
<span id="cb24-13"><a href="#cb24-13" aria-hidden="true" tabindex="-1"></a>        tpm2-pcrlock-nv:  false</span>
<span id="cb24-14"><a href="#cb24-14" aria-hidden="true" tabindex="-1"></a>        Keyslot:    1</span></code></pre></div>
</section>
</section>
<section id="defense-against-boot-attacks" class="level3">
<h3>Defense against boot attacks</h3>
<p>As mentioned earlier, if you only depend on pre-boot PCR measurements, attackers can decrypt your disk by constructing a system with the same measurements, letting it auto-unlock, then stealing your keys.</p>
<p><em>The attack</em>:</p>
<ol type="1">
<li>Attacker creates a malicious USB/external drive.</li>
<li>Copies your <code>/boot</code> partition exactly.</li>
<li>Creates a fake root partition with the <em>same UUID</em> as your real root.</li>
<li>Boots the system from this malicious setup.</li>
</ol>
<p>Now they can configure the system to mount your <em>real</em> root.</p>
<p>Let’s defend against this.</p>
<p>The problem: PCR 15 measures the filesystems using a content-derived identity hash. It gets invalidated as the system boots, runs <code>pivot_root</code>, and mounts the main filesystems. As our defense, we are going to validate that PCR 15 matches its expected value, before <code>pivot_root</code> but after TPM unlock occurs.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<p>We’ll confirm the kernel cmdline and the PCR 15 values.</p>
<p><code>/usr/local/bin/check-pcr15.sh</code>:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a><span class="va">LOG_DIR</span><span class="op">=</span>/run/pcr15</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a><span class="fu">mkdir</span> <span class="at">-p</span> <span class="st">&quot;</span><span class="va">${LOG_DIR}</span><span class="st">&quot;</span></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a><span class="fu">log()</span> <span class="kw">{</span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">message</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${@}</span><span class="st">&quot;</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$(</span><span class="ex">/bin/date</span> <span class="st">&#39;+%Y-%m-%d %H:%M:%S&#39;</span><span class="va">)</span><span class="st"> - </span><span class="va">$@</span><span class="st">&quot;</span> <span class="kw">|</span></span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a>        <span class="fu">tee</span> <span class="at">-a</span> <span class="st">&quot;</span><span class="va">${LOG_DIR}</span><span class="st">/validation.log&quot;</span> <span class="op">&gt;&amp;</span><span class="dv">2</span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a><span class="fu">parse_cmdline()</span> <span class="kw">{</span></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">cmdline</span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a>    <span class="va">cmdline</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">cat</span> /proc/cmdline<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb25-15"><a href="#cb25-15" aria-hidden="true" tabindex="-1"></a>    <span class="bu">read</span> <span class="at">-r</span> <span class="va">cmdline</span> <span class="op">&lt;</span> /proc/cmdline</span>
<span id="cb25-16"><a href="#cb25-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">[[</span> <span class="st">&quot;</span><span class="va">${cmdline}</span><span class="st">&quot;</span> <span class="ot">=~</span> <span class="ss">expected_pcr15=</span><span class="op">([</span><span class="ss">0</span><span class="op">-</span><span class="ss">9a</span><span class="op">-</span><span class="ss">fA</span><span class="op">-</span><span class="ss">F</span><span class="op">]</span><span class="pp">+</span><span class="op">)</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb25-17"><a href="#cb25-17" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">${BASH_REMATCH</span><span class="op">[</span><span class="dv">1</span><span class="op">]</span><span class="va">}</span><span class="st">&quot;</span></span>
<span id="cb25-18"><a href="#cb25-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span></span>
<span id="cb25-19"><a href="#cb25-19" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb25-20"><a href="#cb25-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb25-21"><a href="#cb25-21" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb25-22"><a href="#cb25-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-23"><a href="#cb25-23" aria-hidden="true" tabindex="-1"></a><span class="fu">get_pcr15()</span> <span class="kw">{</span></span>
<span id="cb25-24"><a href="#cb25-24" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">pcr_output</span></span>
<span id="cb25-25"><a href="#cb25-25" aria-hidden="true" tabindex="-1"></a>    <span class="va">pcr_output</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">systemd-analyze</span> pcrs 15 <span class="at">--json</span><span class="op">=</span>short <span class="dv">2</span><span class="op">&gt;</span>/dev/null<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb25-26"><a href="#cb25-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-27"><a href="#cb25-27" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Extract sha256 field using bash string manipulation</span></span>
<span id="cb25-28"><a href="#cb25-28" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">[[</span> <span class="st">&quot;</span><span class="va">${pcr_output}</span><span class="st">&quot;</span> <span class="ot">=~</span> <span class="dt">\&quot;</span><span class="ss">sha256</span><span class="dt">\&quot;</span><span class="ss">:</span><span class="dt">\&quot;</span><span class="op">([</span><span class="ss">0</span><span class="op">-</span><span class="ss">9a</span><span class="op">-</span><span class="ss">fA</span><span class="op">-</span><span class="ss">F</span><span class="op">]</span><span class="pp">+</span><span class="op">)</span><span class="dt">\&quot;</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb25-29"><a href="#cb25-29" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">${BASH_REMATCH</span><span class="op">[</span><span class="dv">1</span><span class="op">]</span><span class="va">}</span><span class="st">&quot;</span></span>
<span id="cb25-30"><a href="#cb25-30" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span></span>
<span id="cb25-31"><a href="#cb25-31" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;&quot;</span></span>
<span id="cb25-32"><a href="#cb25-32" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb25-33"><a href="#cb25-33" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb25-34"><a href="#cb25-34" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-35"><a href="#cb25-35" aria-hidden="true" tabindex="-1"></a><span class="va">expected_pcr15</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">parse_cmdline</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb25-36"><a href="#cb25-36" aria-hidden="true" tabindex="-1"></a><span class="va">actual_pcr15</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">get_pcr15</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb25-37"><a href="#cb25-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-38"><a href="#cb25-38" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="bu">[</span> <span class="ot">-n</span> <span class="st">&quot;</span><span class="va">${expected_pcr15}</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb25-39"><a href="#cb25-39" aria-hidden="true" tabindex="-1"></a>    <span class="ex">log</span> <span class="st">&quot;Checking PCR 15 value&quot;</span></span>
<span id="cb25-40"><a href="#cb25-40" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="bu">[</span> <span class="st">&quot;</span><span class="va">${actual_pcr15}</span><span class="st">&quot;</span> <span class="ot">!=</span> <span class="st">&quot;</span><span class="va">${expected_pcr15}</span><span class="st">&quot;</span> <span class="bu">]</span><span class="kw">;</span> <span class="cf">then</span></span>
<span id="cb25-41"><a href="#cb25-41" aria-hidden="true" tabindex="-1"></a>        <span class="ex">log</span> <span class="st">&quot;PCR 15 check failed. Expected &#39;</span><span class="va">${expected_pcr15}</span><span class="st">&#39;, got &#39;</span><span class="va">${actual_pcr15}</span><span class="st">&#39;&quot;</span></span>
<span id="cb25-42"><a href="#cb25-42" aria-hidden="true" tabindex="-1"></a>        <span class="ex">log</span> <span class="st">&quot;WARNING: PCR 15 check failed - values don&#39;t match&quot;</span></span>
<span id="cb25-43"><a href="#cb25-43" aria-hidden="true" tabindex="-1"></a>        <span class="ex">log</span> <span class="st">&quot;This could indicate filesystem tampering or normal system changes&quot;</span></span>
<span id="cb25-44"><a href="#cb25-44" aria-hidden="true" tabindex="-1"></a>        <span class="ex">log</span> <span class="st">&quot;pcr15 mismatch // expected: &#39;</span><span class="va">${expected_pcr15}</span><span class="st">&#39; // actual: &#39;</span><span class="va">${actual_pcr15}</span><span class="st">&#39;&quot;</span></span>
<span id="cb25-45"><a href="#cb25-45" aria-hidden="true" tabindex="-1"></a>        <span class="ex">systemctl</span> start emergency.target</span>
<span id="cb25-46"><a href="#cb25-46" aria-hidden="true" tabindex="-1"></a>        <span class="bu">exit</span> 1</span>
<span id="cb25-47"><a href="#cb25-47" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span></span>
<span id="cb25-48"><a href="#cb25-48" aria-hidden="true" tabindex="-1"></a>        <span class="ex">log</span> <span class="st">&quot;PCR 15 check succeeded with value </span><span class="va">${actual_pcr15}</span><span class="st">&quot;</span></span>
<span id="cb25-49"><a href="#cb25-49" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb25-50"><a href="#cb25-50" aria-hidden="true" tabindex="-1"></a><span class="cf">else</span></span>
<span id="cb25-51"><a href="#cb25-51" aria-hidden="true" tabindex="-1"></a>    <span class="ex">log</span> <span class="st">&quot;No expected PCR 15 value provided - capture this for future validation!&quot;</span></span>
<span id="cb25-52"><a href="#cb25-52" aria-hidden="true" tabindex="-1"></a>    <span class="ex">log</span> <span class="st">&quot;Add &#39;expected_pcr15=</span><span class="va">${actual_pcr15}</span><span class="st">&#39; to your kernel command line.&quot;</span></span>
<span id="cb25-53"><a href="#cb25-53" aria-hidden="true" tabindex="-1"></a><span class="cf">fi</span></span></code></pre></div>
<p>Add a systemd unit:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode ini"><code class="sourceCode ini"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="kw">[Unit]</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a><span class="dt">Description</span><span class="ot">=</span><span class="st">Measure filesystem integrity into PCR 15</span></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="dt">DefaultDependencies</span><span class="ot">=</span><span class="kw">no</span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a><span class="dt">After</span><span class="ot">=</span><span class="st">cryptsetup.target</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a><span class="dt">After</span><span class="ot">=</span><span class="st">systemd-pcrphase-initrd.service</span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a><span class="dt">Before</span><span class="ot">=</span><span class="st">initrd-switch-root.target</span></span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a><span class="kw">[Service]</span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a><span class="dt">Type</span><span class="ot">=</span><span class="st">oneshot</span></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a><span class="dt">RemainAfterExit</span><span class="ot">=</span><span class="kw">yes</span></span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a><span class="dt">ExecStart</span><span class="ot">=</span><span class="st">/usr/local/bin/check-pcr15.sh</span></span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a><span class="dt">StandardOutput</span><span class="ot">=</span><span class="st">journal+console</span></span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a><span class="dt">StandardError</span><span class="ot">=</span><span class="st">journal+console</span></span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a><span class="kw">[Install]</span></span>
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a><span class="dt">WantedBy</span><span class="ot">=</span><span class="st">initrd.target</span></span>
<span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a><span class="dt">RequiredBy</span><span class="ot">=</span><span class="st">initrd-switch-root.target</span></span></code></pre></div>
<p>Add a stub hook <code>/etc/initcpio/hooks/check-pcr15</code>:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/bash</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>run_hook() {</span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>    true</span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Have the hook install your dependencies (<code>/etc/initcpio/install/check-pcr15</code>).
This is important because it does automatic dependency resolution, and lets us hardcode extra paths if we think they’re being skippe:</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a><span class="fu">build()</span> <span class="kw">{</span></span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Include the TPM2 tools</span></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/bin/tpm2_pcrread</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/bin/systemd-analyze</span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Dependencies for tpm2_pcrread if needed</span></span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/lib/libtss2-tcti-device.so</span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/lib/libtss2-tcti-cmd.so</span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/lib/libtss2-mu.so</span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/lib/libtss2-esys.so</span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a>    <span class="ex">add_binary</span> /usr/lib/libtss2-tctildr.so</span>
<span id="cb28-14"><a href="#cb28-14" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb28-15"><a href="#cb28-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-16"><a href="#cb28-16" aria-hidden="true" tabindex="-1"></a><span class="fu">help()</span> <span class="kw">{</span></span>
<span id="cb28-17"><a href="#cb28-17" aria-hidden="true" tabindex="-1"></a>    <span class="fu">cat</span> <span class="op">&lt;&lt;HELPEOF</span></span>
<span id="cb28-18"><a href="#cb28-18" aria-hidden="true" tabindex="-1"></a><span class="st">This hook includes TPM2 tools needed for PCR validation.</span></span>
<span id="cb28-19"><a href="#cb28-19" aria-hidden="true" tabindex="-1"></a><span class="op">HELPEOF</span></span>
<span id="cb28-20"><a href="#cb28-20" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span></code></pre></div>
<p>Add your hook to <code>/etc/mkinitcpio.conf</code>:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole sd-encrypt block lvm2 resume check-pcr15 filesystems fsck)</span></code></pre></div>
<p>Ensure the files are present:</p>
<div class="sourceCode" id="cb30"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a># BINARIES</span>
<span id="cb30-2"><a href="#cb30-2" aria-hidden="true" tabindex="-1"></a># This setting includes any additional binaries a given user may</span>
<span id="cb30-3"><a href="#cb30-3" aria-hidden="true" tabindex="-1"></a># wish into the CPIO image.  This is run last, so it may be used to</span>
<span id="cb30-4"><a href="#cb30-4" aria-hidden="true" tabindex="-1"></a># override the actual binaries included by a given hook</span>
<span id="cb30-5"><a href="#cb30-5" aria-hidden="true" tabindex="-1"></a># BINARIES are dependency parsed, so you may safely ignore libraries</span>
<span id="cb30-6"><a href="#cb30-6" aria-hidden="true" tabindex="-1"></a>BINARIES=(/bin/bash /usr/bin/systemd-analyze /usr/bin/tpm2_pcrread /bin/date)</span>
<span id="cb30-7"><a href="#cb30-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb30-8"><a href="#cb30-8" aria-hidden="true" tabindex="-1"></a># FILES</span>
<span id="cb30-9"><a href="#cb30-9" aria-hidden="true" tabindex="-1"></a># This setting is similar to BINARIES above, however, files are added</span>
<span id="cb30-10"><a href="#cb30-10" aria-hidden="true" tabindex="-1"></a># as-is and are not parsed in any way.  This is useful for config files.</span>
<span id="cb30-11"><a href="#cb30-11" aria-hidden="true" tabindex="-1"></a>FILES=(/etc/systemd/system/check-pcr15.service /usr/local/bin/check-pcr15.sh /etc/systemd/system/initrd.target.wants/check-pcr15.service)</span></code></pre></div>
<p>Now you must find the measured PCR 15 value. Since we used systemd, we can fetch it from the journal:</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a># systemctl status check-pcr15.service</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>○ check-pcr15.service - Measure filesystem integrity into PCR 15</span>
<span id="cb31-3"><a href="#cb31-3" aria-hidden="true" tabindex="-1"></a>     Loaded: loaded (/etc/systemd/system/check-pcr15.service; enabled; preset: disabled)</span>
<span id="cb31-4"><a href="#cb31-4" aria-hidden="true" tabindex="-1"></a>     Active: inactive (dead) since Mon 2025-07-14 04:08:52 EDT; 17h ago</span>
<span id="cb31-5"><a href="#cb31-5" aria-hidden="true" tabindex="-1"></a>   Duration: 72ms</span>
<span id="cb31-6"><a href="#cb31-6" aria-hidden="true" tabindex="-1"></a> Invocation: 4e768bbf20c147d18521aecd5fd30778</span>
<span id="cb31-7"><a href="#cb31-7" aria-hidden="true" tabindex="-1"></a>   Main PID: 349 (code=exited, status=0/SUCCESS)</span>
<span id="cb31-8"><a href="#cb31-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb31-9"><a href="#cb31-9" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:51 archlinux systemd[1]: Starting Measure filesystem integrity into PCR 15...</span>
<span id="cb31-10"><a href="#cb31-10" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:51 archlinux check-pcr15.sh[367]: 2025-07-14 08:08:51 - Checking PCR 15 value</span>
<span id="cb31-11"><a href="#cb31-11" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:52 archlinux check-pcr15.sh[370]: 2025-07-14 08:08:51 - PCR 15 check succeeded with value [...]</span>
<span id="cb31-12"><a href="#cb31-12" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:52 archlinux systemd[1]: Finished Measure filesystem integrity into PCR 15.</span>
<span id="cb31-13"><a href="#cb31-13" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:52 archlinux systemd[1]: check-pcr15.service: Deactivated successfully.</span>
<span id="cb31-14"><a href="#cb31-14" aria-hidden="true" tabindex="-1"></a>Jul 14 04:08:52 archlinux systemd[1]: Stopped Measure filesystem integrity into PCR 15.</span></code></pre></div>
<p>Now boot the system once without <code>expected_pcr15</code>, read <code>/run/pcr15/validation.log</code> for the measured PCR15 value. This corresponds to your filesystem layout. Add it into your UKI rebuilder script<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> and you’re good to go.</p>
</section>
<section id="fingerprint-unlock" class="level3">
<h3>Fingerprint Unlock</h3>
<p>First install <code>fprintd</code> with pacman. It then needs to be configured with PAM. Modify <code>/etc/pam.d/system-auth</code>:</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a># diff -c10 /etc/pam.d/system-auth.bk /etc/pam.d/system-auth</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a><span class="kw">*** /etc/pam.d/system-auth.bk   2025-05-01 04:34:54.821598714 -0400</span></span>
<span id="cb33-3"><a href="#cb33-3" aria-hidden="true" tabindex="-1"></a><span class="kw">--- /etc/pam.d/system-auth      2025-04-29 23:22:27.302944835 -0400</span></span>
<span id="cb33-4"><a href="#cb33-4" aria-hidden="true" tabindex="-1"></a><span class="dt">***************</span></span>
<span id="cb33-5"><a href="#cb33-5" aria-hidden="true" tabindex="-1"></a><span class="dt">*** 1,16 ****</span></span>
<span id="cb33-6"><a href="#cb33-6" aria-hidden="true" tabindex="-1"></a><span class="dt">--- 1,17 ----</span></span>
<span id="cb33-7"><a href="#cb33-7" aria-hidden="true" tabindex="-1"></a>  #%PAM-1.0</span>
<span id="cb33-8"><a href="#cb33-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb33-9"><a href="#cb33-9" aria-hidden="true" tabindex="-1"></a>  auth       required                    pam_faillock.so      preauth</span>
<span id="cb33-10"><a href="#cb33-10" aria-hidden="true" tabindex="-1"></a>  # Optionally use requisite above if you do not want to prompt for the password</span>
<span id="cb33-11"><a href="#cb33-11" aria-hidden="true" tabindex="-1"></a>  # on locked accounts.</span>
<span id="cb33-12"><a href="#cb33-12" aria-hidden="true" tabindex="-1"></a>  -auth      [success=2 default=ignore]  pam_systemd_home.so</span>
<span id="cb33-13"><a href="#cb33-13" aria-hidden="true" tabindex="-1"></a><span class="va">+ auth       sufficient                  pam_fprintd.so</span></span>
<span id="cb33-14"><a href="#cb33-14" aria-hidden="true" tabindex="-1"></a>  auth       [success=1 default=bad]     pam_unix.so          try_first_pass nullok</span>
<span id="cb33-15"><a href="#cb33-15" aria-hidden="true" tabindex="-1"></a>  auth       [default=die]               pam_faillock.so      authfail</span>
<span id="cb33-16"><a href="#cb33-16" aria-hidden="true" tabindex="-1"></a>  auth       optional                    pam_permit.so</span>
<span id="cb33-17"><a href="#cb33-17" aria-hidden="true" tabindex="-1"></a>  auth       required                    pam_env.so</span>
<span id="cb33-18"><a href="#cb33-18" aria-hidden="true" tabindex="-1"></a>  auth       required                    pam_faillock.so      authsucc</span>
<span id="cb33-19"><a href="#cb33-19" aria-hidden="true" tabindex="-1"></a>  # If you drop the above call to pam_faillock.so the lock will be done also</span>
<span id="cb33-20"><a href="#cb33-20" aria-hidden="true" tabindex="-1"></a>  # on non-consecutive authentication failures.</span>
<span id="cb33-21"><a href="#cb33-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb33-22"><a href="#cb33-22" aria-hidden="true" tabindex="-1"></a>  -account   [success=1 default=ignore]  pam_systemd_home.so</span>
<span id="cb33-23"><a href="#cb33-23" aria-hidden="true" tabindex="-1"></a>  account    required                    pam_unix.so</span></code></pre></div>
<p>To enroll your fingers, use <code>fprintd-enroll</code> (optionally with <code>-f right-middle-finger</code> and so forth). The readings are stored in <code>/var/lib/fprint</code>.</p>
<p>You can now login and <code>sudo</code> with your fingerprint.</p>
</section>
<section id="nvme-powersaving-settings" class="level3">
<h3>NVMe Powersaving settings</h3>
<p>Returning to the drive powersaving issues described earlier, we need to fix suspend by adjusting the NVMe powersaving settings. Suspend issues present as intermittent cold boots from lid-closed suspend state.</p>
<p>This is the same issue currently being hacked on in the <a href="https://community.frame.work/t/smartctl-shows-excessive-power-cycles-and-unsafe-shutdowns/70954/">Framework forums</a> and similar to some described in the <a href="https://askubuntu.com/questions/1018685/having-a-bunch-of-issues-with-ubuntu-16-04-on-wd-black-nvme-drive">Ubuntu StackExchange</a>.</p>
<p>In my case, I had a really high count of <code>Unsafe shutdowns</code> on my NVMe drive:</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a># nvme id-ctrl /dev/nvme0 | grep -E &quot;(fr|mn) &quot;</span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>mn        : WD_BLACK SN7100 2TB</span>
<span id="cb34-3"><a href="#cb34-3" aria-hidden="true" tabindex="-1"></a>fr        : 7612M0WD</span>
<span id="cb34-4"><a href="#cb34-4" aria-hidden="true" tabindex="-1"></a># smartctl -a/dev/nvme0n1  | grep -i shutdown</span>
<span id="cb34-5"><a href="#cb34-5" aria-hidden="true" tabindex="-1"></a>Unsafe Shutdowns:                   44,055</span></code></pre></div>
<p>It turns out broken suspend was a result of the NVMe drive not successfully entering deeper suspend states. The drive has a parameter controlling how aggressively the SSD can enter low-power states. Higher latency values allow deep sleep states that save more power, but take longer to awake. In our case, the firmware has issues with power transitions, so we need to tell the device to be less aggressive with powersaving.</p>
<p>To check its powersaving status:</p>
<div class="sourceCode" id="cb35"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a># nvme id-ctrl /dev/nvme0n1 | grep -A6 &#39;^ps &#39;</span>
<span id="cb35-2"><a href="#cb35-2" aria-hidden="true" tabindex="-1"></a>ps      0 : mp:4.60W operational enlat:0 exlat:0 rrt:0 rrl:0</span>
<span id="cb35-3"><a href="#cb35-3" aria-hidden="true" tabindex="-1"></a>            rwt:0 rwl:0 idle_power:0.2200W active_power:4.60W</span>
<span id="cb35-4"><a href="#cb35-4" aria-hidden="true" tabindex="-1"></a>            active_power_workload:80K 128KiB SW</span>
<span id="cb35-5"><a href="#cb35-5" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-6"><a href="#cb35-6" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span>
<span id="cb35-7"><a href="#cb35-7" aria-hidden="true" tabindex="-1"></a>            emergency power fail vault time: -</span>
<span id="cb35-8"><a href="#cb35-8" aria-hidden="true" tabindex="-1"></a>ps      1 : mp:3.00W operational enlat:0 exlat:0 rrt:0 rrl:0</span>
<span id="cb35-9"><a href="#cb35-9" aria-hidden="true" tabindex="-1"></a>            rwt:0 rwl:0 idle_power:0.2200W active_power:3.00W</span>
<span id="cb35-10"><a href="#cb35-10" aria-hidden="true" tabindex="-1"></a>            active_power_workload:80K 128KiB SW</span>
<span id="cb35-11"><a href="#cb35-11" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-12"><a href="#cb35-12" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span>
<span id="cb35-13"><a href="#cb35-13" aria-hidden="true" tabindex="-1"></a>            emergency power fail vault time: -</span>
<span id="cb35-14"><a href="#cb35-14" aria-hidden="true" tabindex="-1"></a>ps      2 : mp:2.50W operational enlat:0 exlat:0 rrt:0 rrl:0</span>
<span id="cb35-15"><a href="#cb35-15" aria-hidden="true" tabindex="-1"></a>            rwt:0 rwl:0 idle_power:0.2200W active_power:2.50W</span>
<span id="cb35-16"><a href="#cb35-16" aria-hidden="true" tabindex="-1"></a>            active_power_workload:80K 128KiB SW</span>
<span id="cb35-17"><a href="#cb35-17" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-18"><a href="#cb35-18" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span>
<span id="cb35-19"><a href="#cb35-19" aria-hidden="true" tabindex="-1"></a>            emergency power fail vault time: -</span>
<span id="cb35-20"><a href="#cb35-20" aria-hidden="true" tabindex="-1"></a>ps      3 : mp:0.0200W non-operational enlat:2000 exlat:3000 rrt:3 rrl:3</span>
<span id="cb35-21"><a href="#cb35-21" aria-hidden="true" tabindex="-1"></a>            rwt:3 rwl:3 idle_power:0.0200W active_power:-</span>
<span id="cb35-22"><a href="#cb35-22" aria-hidden="true" tabindex="-1"></a>            active_power_workload:-</span>
<span id="cb35-23"><a href="#cb35-23" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-24"><a href="#cb35-24" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span>
<span id="cb35-25"><a href="#cb35-25" aria-hidden="true" tabindex="-1"></a>            emergency power fail vault time: -</span>
<span id="cb35-26"><a href="#cb35-26" aria-hidden="true" tabindex="-1"></a>ps      4 : mp:0.0050W non-operational enlat:4000 exlat:12000 rrt:4 rrl:4</span>
<span id="cb35-27"><a href="#cb35-27" aria-hidden="true" tabindex="-1"></a>            rwt:4 rwl:4 idle_power:0.0050W active_power:-</span>
<span id="cb35-28"><a href="#cb35-28" aria-hidden="true" tabindex="-1"></a>            active_power_workload:-</span>
<span id="cb35-29"><a href="#cb35-29" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-30"><a href="#cb35-30" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span>
<span id="cb35-31"><a href="#cb35-31" aria-hidden="true" tabindex="-1"></a>            emergency power fail vault time: -</span>
<span id="cb35-32"><a href="#cb35-32" aria-hidden="true" tabindex="-1"></a>ps      5 : mp:0.0030W non-operational enlat:176000 exlat:25000 rrt:5 rrl:5</span>
<span id="cb35-33"><a href="#cb35-33" aria-hidden="true" tabindex="-1"></a>            rwt:5 rwl:5 idle_power:0.0030W active_power:-</span>
<span id="cb35-34"><a href="#cb35-34" aria-hidden="true" tabindex="-1"></a>            active_power_workload:-</span>
<span id="cb35-35"><a href="#cb35-35" aria-hidden="true" tabindex="-1"></a>            emergency power fail recovery time: -</span>
<span id="cb35-36"><a href="#cb35-36" aria-hidden="true" tabindex="-1"></a>            forced quiescence vault time: 10 (unit: 1 second)</span></code></pre></div>
<p>This issue appeared far less often after upgrading to the 6.15.7 kernel and disabling <code>ps 5</code>, the deepest powersave state:</p>
<div class="sourceCode" id="cb36"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a># echo 25000 | sudo tee /sys/module/nvme_core/parameters/default_ps_max_latency_us</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a># cat /sys/module/nvme_core/parameters/default_ps_max_latency_us</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a>25000</span></code></pre></div>
<p>To permanently disable NVMe <code>ps 5</code> (deepest sleep), add <code>nvme_core.default_ps_max_latency_us=25000</code> to your kernel <code>cmdline</code>.</p>
</section>
</section>
<section id="userspace-management" class="level2">
<h2>Userspace management</h2>
<p>I now use <code>nix</code> with <code>home-manager</code> to manage my homedir, similar to my NixOS machine.</p>
<p>Install <a href="https://wiki.archlinux.org/title/Nix">Nix on Arch</a>:</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a># pacman -S nix</span></code></pre></div>
<p>The goal is, over time, more of the system will be eaten by Nix configuration. I view this as a good thing.</p>
<section id="nix-settingsnix-daemon-restart" class="level3">
<h3>Nix settings<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></h3>
<p>You may want to consider enabling Nix store auto-optimization. This is disabled by default.</p>
<div class="sourceCode" id="cb39"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a># echo &quot;auto-optimise-store = true&quot; &gt;&gt; /etc/nix/nix.conf</span></code></pre></div>
<p>I also suggest enabling the build sandbox, if it is disabled, and configuring <code>build-dir</code> to point to a <code>tmpfs</code>:<a href="#fn5" class="footnote-ref" id="fnref5" role="doc-noteref"><sup>5</sup></a></p>
<div class="sourceCode" id="cb40"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>$ grep -E &#39;(build|sandbox)&#39; /etc/nix/nix.conf</span>
<span id="cb40-2"><a href="#cb40-2" aria-hidden="true" tabindex="-1"></a>sandbox = true</span>
<span id="cb40-3"><a href="#cb40-3" aria-hidden="true" tabindex="-1"></a># Unix group containing the Nix build user accounts</span>
<span id="cb40-4"><a href="#cb40-4" aria-hidden="true" tabindex="-1"></a>build-users-group = nixbld</span>
<span id="cb40-5"><a href="#cb40-5" aria-hidden="true" tabindex="-1"></a>build-dir = /tmp/nix-build</span></code></pre></div>
<section id="cachix" class="level4">
<h4>Cachix</h4>
<p>I also configure Cachix community caching. This is useful for <a href="https://github.com/nix-community/emacs-overlay">emacs-overlay</a>.
Note that community binary caches are not without their downsides: caches are trusted. If Cachix is compromised, you will be as well.</p>
<p>This can be done by modifying <code>/etc/nix/nix.conf</code>:</p>
<div class="sourceCode" id="cb41"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>substituters = https://cache.nixos.org https://nix-community.cachix.org</span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a>trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=</span></code></pre></div>
<p>Alternatively, use the <a href="https://docs.cachix.org/installation">Cachix installer</a>.<a href="#fn6" class="footnote-ref" id="fnref6" role="doc-noteref"><sup>6</sup></a></p>
</section>
</section>
<section id="home-manager-flake" class="level3">
<h3>Home-Manager Flake</h3>
<p>People say Nix is all about reproducible builds, but Nix derivations aren’t very reproducible to bat: nothing tracks <em>which dependency versions were used</em> when building a derivation!
To address this issue, I formerly used <a href="https://github.com/nmattia/niv"><code>Niv</code></a> to pin dependencies for Nix projects.
Flakes aim to provide a <em>standard</em> way to pin dependencies for Nix projects, which is why despite <a href="https://discourse.nixos.org/t/nix-community-survey-2024-results/55403">85% community adoption</a>, Flakes have yet to be standardized.<a href="#fn7" class="footnote-ref" id="fnref7" role="doc-noteref"><sup>7</sup></a>
That means it’s time use <code>home-manager</code> Flakes!</p>
<p>First, we need to enable Flakes:<a href="#fn8" class="footnote-ref" id="fnref8" role="doc-noteref"><sup>8</sup></a></p>
<div class="sourceCode" id="cb43"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a># echo &quot;experimental-features = nix-command flakes&quot; &gt;&gt; /etc/nix/nix.conf</span></code></pre></div>
<p>Flakes track Nix dependencies using lockfiles and formalize the input/output interfaces of Nix modules. Let’s look at a home-manager configuration flake:</p>
<div class="sourceCode" id="cb44"><pre class="sourceCode nix"><code class="sourceCode nix"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>  <span class="va">description</span> <span class="op">=</span> <span class="st">&quot;My full system config (Arch + Home Manager)&quot;</span><span class="op">;</span></span>
<span id="cb44-3"><a href="#cb44-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb44-4"><a href="#cb44-4" aria-hidden="true" tabindex="-1"></a>  <span class="va">inputs</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb44-5"><a href="#cb44-5" aria-hidden="true" tabindex="-1"></a>    <span class="va">nixpkgs</span>.<span class="va">url</span> <span class="op">=</span> <span class="st">&quot;github:NixOS/nixpkgs/nixpkgs-unstable&quot;</span><span class="op">;</span></span>
<span id="cb44-6"><a href="#cb44-6" aria-hidden="true" tabindex="-1"></a>    <span class="va">home-manager</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb44-7"><a href="#cb44-7" aria-hidden="true" tabindex="-1"></a>      <span class="va">url</span> <span class="op">=</span> <span class="st">&quot;github:nix-community/home-manager&quot;</span><span class="op">;</span></span>
<span id="cb44-8"><a href="#cb44-8" aria-hidden="true" tabindex="-1"></a>      <span class="va">inputs</span>.<span class="va">nixpkgs</span>.<span class="va">follows</span> <span class="op">=</span> <span class="st">&quot;nixpkgs&quot;</span><span class="op">;</span></span>
<span id="cb44-9"><a href="#cb44-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span></span>
<span id="cb44-10"><a href="#cb44-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb44-11"><a href="#cb44-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb44-12"><a href="#cb44-12" aria-hidden="true" tabindex="-1"></a>  <span class="va">outputs</span> <span class="op">=</span> <span class="op">{</span> <span class="va">self</span><span class="op">,</span> <span class="va">nixpkgs</span><span class="op">,</span> <span class="va">home-manager</span><span class="op">,</span> <span class="op">...</span> <span class="op">}</span>: <span class="kw">let</span></span>
<span id="cb44-13"><a href="#cb44-13" aria-hidden="true" tabindex="-1"></a>    <span class="va">system</span> <span class="op">=</span> <span class="st">&quot;x86_64-linux&quot;</span><span class="op">;</span></span>
<span id="cb44-14"><a href="#cb44-14" aria-hidden="true" tabindex="-1"></a>    <span class="va">username</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>getEnv <span class="st">&quot;USER&quot;</span><span class="op">;</span></span>
<span id="cb44-15"><a href="#cb44-15" aria-hidden="true" tabindex="-1"></a>    <span class="va">homeDirectory</span> <span class="op">=</span> <span class="bu">builtins</span><span class="op">.</span>getEnv <span class="st">&quot;HOME&quot;</span><span class="op">;</span></span>
<span id="cb44-16"><a href="#cb44-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb44-17"><a href="#cb44-17" aria-hidden="true" tabindex="-1"></a>    <span class="va">secrets</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb44-18"><a href="#cb44-18" aria-hidden="true" tabindex="-1"></a>      <span class="co"># ...</span></span>
<span id="cb44-19"><a href="#cb44-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span></span>
<span id="cb44-20"><a href="#cb44-20" aria-hidden="true" tabindex="-1"></a>  <span class="kw">in</span> <span class="op">{</span></span>
<span id="cb44-21"><a href="#cb44-21" aria-hidden="true" tabindex="-1"></a>    <span class="va">homeConfigurations</span>.<span class="sc">${</span>username<span class="sc">}</span> <span class="op">=</span> home<span class="op">-</span>manager<span class="op">.</span>lib<span class="op">.</span>homeManagerConfiguration <span class="op">{</span></span>
<span id="cb44-22"><a href="#cb44-22" aria-hidden="true" tabindex="-1"></a>      <span class="va">pkgs</span> <span class="op">=</span> <span class="bu">import</span> nixpkgs <span class="op">{</span></span>
<span id="cb44-23"><a href="#cb44-23" aria-hidden="true" tabindex="-1"></a>        <span class="kw">inherit</span> <span class="va">system</span><span class="op">;</span></span>
<span id="cb44-24"><a href="#cb44-24" aria-hidden="true" tabindex="-1"></a>      <span class="op">};</span></span>
<span id="cb44-25"><a href="#cb44-25" aria-hidden="true" tabindex="-1"></a>      <span class="va">modules</span> <span class="op">=</span> <span class="op">[</span></span>
<span id="cb44-26"><a href="#cb44-26" aria-hidden="true" tabindex="-1"></a>        <span class="ss">../home-manager/arch.nix</span></span>
<span id="cb44-27"><a href="#cb44-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span></span>
<span id="cb44-28"><a href="#cb44-28" aria-hidden="true" tabindex="-1"></a>          <span class="va">programs</span>.<span class="va">home-manager</span>.<span class="va">enable</span> <span class="op">=</span> <span class="cn">true</span><span class="op">;</span></span>
<span id="cb44-29"><a href="#cb44-29" aria-hidden="true" tabindex="-1"></a>          <span class="va">home</span>.<span class="va">stateVersion</span> <span class="op">=</span> <span class="st">&quot;24.11&quot;</span><span class="op">;</span></span>
<span id="cb44-30"><a href="#cb44-30" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb44-31"><a href="#cb44-31" aria-hidden="true" tabindex="-1"></a>      <span class="op">];</span></span>
<span id="cb44-32"><a href="#cb44-32" aria-hidden="true" tabindex="-1"></a>      <span class="va">extraSpecialArgs</span> <span class="op">=</span> <span class="op">{</span></span>
<span id="cb44-33"><a href="#cb44-33" aria-hidden="true" tabindex="-1"></a>        <span class="kw">inherit</span> <span class="va">system</span><span class="op">;</span></span>
<span id="cb44-34"><a href="#cb44-34" aria-hidden="true" tabindex="-1"></a>        <span class="va">secrets</span> <span class="op">=</span> secrets<span class="op">;</span></span>
<span id="cb44-35"><a href="#cb44-35" aria-hidden="true" tabindex="-1"></a>        <span class="va">username</span> <span class="op">=</span> username<span class="op">;</span></span>
<span id="cb44-36"><a href="#cb44-36" aria-hidden="true" tabindex="-1"></a>        <span class="va">homeDirectory</span> <span class="op">=</span> homeDirectory<span class="op">;</span></span>
<span id="cb44-37"><a href="#cb44-37" aria-hidden="true" tabindex="-1"></a>      <span class="op">};</span></span>
<span id="cb44-38"><a href="#cb44-38" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span></span>
<span id="cb44-39"><a href="#cb44-39" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span>
<span id="cb44-40"><a href="#cb44-40" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<section id="building-our-flake" class="level4">
<h4>Building our Flake</h4>
<p>Uh oh!
I’m reading environment variables.
That’s because there are certain values I’d rather keep out of my Git repo, like my user’s Git committer email.
In vanilla Nix, we could import files from our <code>.gitignore</code> for secrets and the like, but with Flakes only committed files can be imported (internally, Flakes copy inputs to the Nix store prior to build).
My workaround for this is to rely upon the environment, but that’s at odds with reproducibility: in general, don’t assume the same environment variables will be set across systems.</p>
<p>But this is a dotfiles configuration. We <em>want</em> a unique committer email across systems. Let’s use the <code>--impure</code> flag to make our environment available to the builder:</p>
<div class="sourceCode" id="cb45"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>$ . ./secrets.sh</span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a>$ nix run github:nix-community/home-manager -- switch --impure --flake .</span></code></pre></div>
<p>This isn’t the best long-term strategy for secrets.
It’s fine if my email adress is discovered, but I wouldn’t want to expose SSH private keys this way
they are available to all subprocesses, regardless of whether or not they require the secret. For example, let’s’ say I have a high-value secret at <code>SECRET_KEY</code>:</p>
<div class="sourceCode" id="cb46"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>$ export SECRET_KEY=&#39;thxbud&#39;</span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>$ bash</span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>(subshell) $ echo $$</span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a>13830</span></code></pre></div>
<p>Now any user with this PID can read its environment:</p>
<div class="sourceCode" id="cb47"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>$ TARGET_PID=13830</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a>$ bash -c &#39;cat &quot;/proc/${TARGET_PID}/environ&quot; | tr &quot;:\0&quot; &quot;\n&quot; | grep -ai secret&#39;</span>
<span id="cb47-3"><a href="#cb47-3" aria-hidden="true" tabindex="-1"></a>SECRET_KEY=thxbud</span></code></pre></div>
<p>Luckily, my home configuration does not require extensive secrets management. If I had to do more, I wuld probaly wire something up with <code>age</code> and/or my Yubikeys.</p>
</section>
</section>
<section id="local-ai-with-ollama" class="level3">
<h3>Local AI with ollama</h3>
<p>With the lates LLM craze, I figure it’s worth showing how to run some LLMs on this machine. I’m currently using <code>nix-shell</code> to run ollama. I had a hell of a time getting this to work with the Radeon 860M, so here are my steps.</p>
<p>First, you need to reboot and dedicate a portion of your RAM to use as VRAM. Cool that this works! But you can’t adapt it dynamically, so go tweak your UEFI settings.</p>
<p>Next, use <code>rocminfo</code> to get some magic variables:</p>
<div class="sourceCode" id="cb48"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>$ nix-shell -p &quot;rocmPackages.rocminfo&quot; --run &quot;rocminfo&quot; | grep &quot;gfx&quot;</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>  Name:                    gfx1152</span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>        Name:                    amdgcn-amd-amdhsa--gfx1152</span></code></pre></div>
<p>Now set the env vars with the magic <q>make rocm work</q> values, the first of which is taken from <code>rocminfo</code>:</p>
<div class="sourceCode" id="cb49"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/env bash</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a>OLLAMA_PACKAGE=ollama-rocm</span>
<span id="cb49-4"><a href="#cb49-4" aria-hidden="true" tabindex="-1"></a>ENV_VARS=&#39;HCC_AMDGPU_DEVICES=gfx1152 HSA_OVERRIDE_GFX_VERSION=&quot;11.0.0&quot; ROCR_VISIBLE_DEVICES=0&#39;</span>
<span id="cb49-5"><a href="#cb49-5" aria-hidden="true" tabindex="-1"></a>COMMAND=&quot;$ENV_VARS ollama serve&quot;</span>
<span id="cb49-6"><a href="#cb49-6" aria-hidden="true" tabindex="-1"></a>echo &quot;running &#39;${COMMAND}&#39;&quot;</span>
<span id="cb49-7"><a href="#cb49-7" aria-hidden="true" tabindex="-1"></a>echo nix-shell -p &quot;${OLLAMA_PACKAGE}&quot; --command &quot;${COMMAND}&quot;</span></code></pre></div>
<p>If it works, you will see output lines like this, indicating <code>rocm</code> is working:</p>
<div class="sourceCode" id="cb50"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>time=2025-05-06T23:21:04.985-04:00 level=INFO source=types.go:130 msg=&quot;inference compute&quot; id=0 library=rocm variant=&quot;&quot; compute=gfx1152 driver=0.0 name=1002:1114 total=&quot;8.0 GiB&quot; available=&quot;7.9 GiB&quot;</span></code></pre></div>
<p>If you get an error, like <code>msg=unsupported Radeon iGPU detected skipping" id=0 total="512.0 MiB"</code> then this means you need to reboot to your bootloader and change the iGPU settings to allocate static vRAM to your GPU. Sorry. I gave 8 GiB to mine, which is stolen permanently from RAM.</p>
</section>
</section>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p><code>/usr/local/bin/update-signed-bootloader</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>#!/usr/bin/env bash</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>set -e</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>ESP_PATH=&quot;/boot/efi&quot;</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>SYSTEMD_BOOT_SRC=&quot;/usr/lib/systemd/boot/efi/systemd-bootx64.efi&quot;</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>SYSTEMD_BOOT_DEST_MAIN=&quot;${ESP_PATH}/EFI/systemd/systemd-bootx64.efi&quot;</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>SYSTEMD_BOOT_DEST_FALLBACK=&quot;${ESP_PATH}/EFI/BOOT/BOOTX64.EFI&quot;</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>echo &quot;Updating systemd-boot on ESP...&quot;</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a># Ensure destination directories exist</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>mkdir -p &quot;${ESP_PATH}/EFI/systemd&quot;</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>mkdir -p &quot;${ESP_PATH}/EFI/BOOT&quot;</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a># Copy the new bootloader files to the ESP</span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>cp &quot;${SYSTEMD_BOOT_SRC}&quot; &quot;${SYSTEMD_BOOT_DEST_MAIN}&quot;</span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>cp &quot;${SYSTEMD_BOOT_SRC}&quot; &quot;${SYSTEMD_BOOT_DEST_FALLBACK}&quot;</span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>echo &quot;Successfully copied systemd-boot to ESP.&quot;</span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>echo &quot;Running sbctl sign-all to update signatures for bootloader...&quot;</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a># sbctl sign-all will detect that the files on the ESP have changed</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a># (because their checksums are now different after the copy)</span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a># and re-sign them if they are tracked in its database.</span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>sbctl sign-all -g</span>
<span id="cb8-25"><a href="#cb8-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-26"><a href="#cb8-26" aria-hidden="true" tabindex="-1"></a># Alternatively, for more explicit signing of just these files:</span>
<span id="cb8-27"><a href="#cb8-27" aria-hidden="true" tabindex="-1"></a># sbctl sign &quot;${SYSTEMD_BOOT_DEST_MAIN}&quot;</span>
<span id="cb8-28"><a href="#cb8-28" aria-hidden="true" tabindex="-1"></a># sbctl sign &quot;${SYSTEMD_BOOT_DEST_FALLBACK}&quot;</span>
<span id="cb8-29"><a href="#cb8-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-30"><a href="#cb8-30" aria-hidden="true" tabindex="-1"></a>echo &quot;Bootloader update and signing process complete.&quot;</span></code></pre></div>
<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></li>
<li id="fn2"><p>Unfortunately, measuring these values after disk unlock is imperfect: it leaves open a timing window where the disk is unlocked, but the post-boot verification has not yet run. We’re already brittle enough as-is, however, and this is essentially unpaved ground in the Linux Secure Boot world, so we’ll leave our solution at PCR15 mid-boot measurement and let others carry the torch.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>The full, final version of my <code>/usr/local/bin/rebuild-ukis-for-sbctl.sh</code> script is below:</p>
<div class="sourceCode" id="cb32"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb32-2"><a href="#cb32-2" aria-hidden="true" tabindex="-1"></a><span class="bu">set</span> <span class="at">-euo</span> pipefail</span>
<span id="cb32-3"><a href="#cb32-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-4"><a href="#cb32-4" aria-hidden="true" tabindex="-1"></a><span class="va">LUKS_UUID</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">blkid</span> <span class="at">-s</span> UUID <span class="at">-o</span> value /dev/nvme0n1p2<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb32-5"><a href="#cb32-5" aria-hidden="true" tabindex="-1"></a><span class="va">SWAP_UUID</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="ex">blkid</span> <span class="at">-s</span> UUID <span class="at">-o</span> value /dev/mapper/vg-swap<span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb32-6"><a href="#cb32-6" aria-hidden="true" tabindex="-1"></a><span class="va">UKI_PATH_ROOT</span><span class="op">=</span><span class="st">&quot;/boot/efi/EFI/Manual&quot;</span></span>
<span id="cb32-7"><a href="#cb32-7" aria-hidden="true" tabindex="-1"></a><span class="va">DEFAULT_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch.efi&quot;</span></span>
<span id="cb32-8"><a href="#cb32-8" aria-hidden="true" tabindex="-1"></a><span class="va">TROUBLESHOOT_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch-troubleshoot.efi&quot;</span></span>
<span id="cb32-9"><a href="#cb32-9" aria-hidden="true" tabindex="-1"></a><span class="va">LTS_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch-lts.efi&quot;</span></span>
<span id="cb32-10"><a href="#cb32-10" aria-hidden="true" tabindex="-1"></a><span class="va">TROUBLESHOOT_LTS_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch-lts-troubleshoot.efi&quot;</span></span>
<span id="cb32-11"><a href="#cb32-11" aria-hidden="true" tabindex="-1"></a><span class="va">HARDENED_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch-hardened.efi&quot;</span></span>
<span id="cb32-12"><a href="#cb32-12" aria-hidden="true" tabindex="-1"></a><span class="va">TROUBLESHOOT_HARDENED_UKI_PATH</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${UKI_PATH_ROOT}</span><span class="st">/arch-hardened-troubleshoot.efi&quot;</span></span>
<span id="cb32-13"><a href="#cb32-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-14"><a href="#cb32-14" aria-hidden="true" tabindex="-1"></a><span class="co"># usbcore quirks - fix Goodix Fingerprint USB Device</span></span>
<span id="cb32-15"><a href="#cb32-15" aria-hidden="true" tabindex="-1"></a><span class="co"># nvme_core.default_ps_max_latency_us=</span></span>
<span id="cb32-16"><a href="#cb32-16" aria-hidden="true" tabindex="-1"></a><span class="va">COMMON_CMDLINE</span><span class="op">=</span><span class="st">&quot;root=/dev/mapper/vg-main rw rootflags=subvol=@ usbcore.quirks=27c6:609c:0x40 rd.luks.name=</span><span class="va">${LUKS_UUID}</span><span class="st">=cryptroot rd.luks.options=</span><span class="va">${LUKS_UUID}</span><span class="st">=tpm2-device=auto,tpm2-measure-pcr=yes,discard,tries=3 lsm=capability,landlock,lockdown,yama,apparmor,bpf mitigations=full nvme_core.default_ps_max_latency_us=25000&quot;</span></span>
<span id="cb32-17"><a href="#cb32-17" aria-hidden="true" tabindex="-1"></a><span class="va">SECURE_CMDLINE</span><span class="op">=</span><span class="st">&quot;audit=1 iommu=pt&quot;</span></span>
<span id="cb32-18"><a href="#cb32-18" aria-hidden="true" tabindex="-1"></a><span class="va">SUSPEND_CMDLINE</span><span class="op">=</span><span class="st">&quot;rtc_cmos.use_acpi_alarm=1&quot;</span></span>
<span id="cb32-19"><a href="#cb32-19" aria-hidden="true" tabindex="-1"></a><span class="va">RESUME_CMDLINE</span><span class="op">=</span><span class="st">&quot;resume=UUID=</span><span class="va">${SWAP_UUID}</span><span class="st">&quot;</span></span>
<span id="cb32-20"><a href="#cb32-20" aria-hidden="true" tabindex="-1"></a><span class="va">TROUBLE_CMDLINE</span><span class="op">=</span><span class="st">&quot;log_level=7 rd.debug&quot;</span></span>
<span id="cb32-21"><a href="#cb32-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-22"><a href="#cb32-22" aria-hidden="true" tabindex="-1"></a><span class="va">SIGN_TOOL</span><span class="op">=</span>sbsign</span>
<span id="cb32-23"><a href="#cb32-23" aria-hidden="true" tabindex="-1"></a><span class="va">SECUREBOOT_PRIVATE_KEY</span><span class="op">=</span><span class="st">&quot;/var/lib/sbctl/keys/db/db.key&quot;</span></span>
<span id="cb32-24"><a href="#cb32-24" aria-hidden="true" tabindex="-1"></a><span class="va">SECUREBOOT_CERTIFICATE</span><span class="op">=</span><span class="st">&quot;/var/lib/sbctl/keys/db/db.pem&quot;</span></span>
<span id="cb32-25"><a href="#cb32-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-26"><a href="#cb32-26" aria-hidden="true" tabindex="-1"></a><span class="va">PCR_PRIVATE_KEY</span><span class="op">=</span><span class="st">&#39;/var/lib/pcr-keys/pcr-initrd.key.pem&#39;</span></span>
<span id="cb32-27"><a href="#cb32-27" aria-hidden="true" tabindex="-1"></a><span class="va">PCR_PUBLIC_KEY</span><span class="op">=</span><span class="st">&#39;/var/lib/pcr-keys/pcr-initrd.pub.pem&#39;</span></span>
<span id="cb32-28"><a href="#cb32-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-29"><a href="#cb32-29" aria-hidden="true" tabindex="-1"></a><span class="va">EXPECTED_PCR15</span><span class="op">=</span><span class="st">&#39;01ba4719...daca546b&#39;</span></span>
<span id="cb32-30"><a href="#cb32-30" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-31"><a href="#cb32-31" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;cmdline:&quot;</span></span>
<span id="cb32-32"><a href="#cb32-32" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;  </span><span class="va">${COMMON_CMDLINE}</span><span class="st">&quot;</span></span>
<span id="cb32-33"><a href="#cb32-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-34"><a href="#cb32-34" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;hardened cmdline:&quot;</span></span>
<span id="cb32-35"><a href="#cb32-35" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;  </span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SECURE_CMDLINE}</span><span class="st">&quot;</span></span>
<span id="cb32-36"><a href="#cb32-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-37"><a href="#cb32-37" aria-hidden="true" tabindex="-1"></a><span class="fu">build_uki()</span> <span class="kw">{</span></span>
<span id="cb32-38"><a href="#cb32-38" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">kernel_path</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$1</span><span class="st">&quot;</span></span>
<span id="cb32-39"><a href="#cb32-39" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">initrd_path</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$2</span><span class="st">&quot;</span></span>
<span id="cb32-40"><a href="#cb32-40" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">cmdline</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$3</span><span class="st">&quot;</span></span>
<span id="cb32-41"><a href="#cb32-41" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">output_path</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$4</span><span class="st">&quot;</span></span>
<span id="cb32-42"><a href="#cb32-42" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">pcr15_value</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${5</span><span class="op">:-</span><span class="va">}</span><span class="st">&quot;</span></span>
<span id="cb32-43"><a href="#cb32-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-44"><a href="#cb32-44" aria-hidden="true" tabindex="-1"></a>    <span class="bu">set</span> <span class="at">-x</span></span>
<span id="cb32-45"><a href="#cb32-45" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Add expected PCR 15 to kernel command line</span></span>
<span id="cb32-46"><a href="#cb32-46" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">[[</span> <span class="ot">-n</span> <span class="st">&quot;</span><span class="va">${pcr15_value}</span><span class="st">&quot;</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb32-47"><a href="#cb32-47" aria-hidden="true" tabindex="-1"></a>        <span class="bu">echo</span> <span class="st">&quot;got pcr15 value: </span><span class="va">${pcr15_value}</span><span class="st">&quot;</span></span>
<span id="cb32-48"><a href="#cb32-48" aria-hidden="true" tabindex="-1"></a>        <span class="va">cmdline</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${cmdline}</span><span class="st"> expected_pcr15=</span><span class="va">${pcr15_value}</span><span class="st">&quot;</span></span>
<span id="cb32-49"><a href="#cb32-49" aria-hidden="true" tabindex="-1"></a>    <span class="cf">fi</span></span>
<span id="cb32-50"><a href="#cb32-50" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-51"><a href="#cb32-51" aria-hidden="true" tabindex="-1"></a>    <span class="bu">local</span> <span class="va">cmdline_file</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="fu">mktemp</span><span class="va">)</span><span class="st">&quot;</span></span>
<span id="cb32-52"><a href="#cb32-52" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;</span><span class="va">$cmdline</span><span class="st">&quot;</span> <span class="op">&gt;</span> <span class="st">&quot;</span><span class="va">${cmdline_file}</span><span class="st">&quot;</span></span>
<span id="cb32-53"><a href="#cb32-53" aria-hidden="true" tabindex="-1"></a>    <span class="bu">trap</span> <span class="st">&quot;rm -f &#39;</span><span class="va">${cmdline_file}</span><span class="st">&#39;; echo &gt;&amp;2 &#39;Cleaned up temp file: </span><span class="va">${cmdline_file}</span><span class="st">&#39;&quot;</span> EXIT</span>
<span id="cb32-54"><a href="#cb32-54" aria-hidden="true" tabindex="-1"></a>        <span class="co"># --pcrs=0,6,7 \</span></span>
<span id="cb32-55"><a href="#cb32-55" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-56"><a href="#cb32-56" aria-hidden="true" tabindex="-1"></a>    <span class="bu">echo</span> <span class="st">&quot;Building </span><span class="va">$(</span><span class="fu">basename</span> <span class="st">&quot;</span><span class="va">$output_path</span><span class="st">&quot;</span><span class="va">)</span><span class="st">...&quot;</span></span>
<span id="cb32-57"><a href="#cb32-57" aria-hidden="true" tabindex="-1"></a>    <span class="ex">ukify</span> build <span class="dt">\</span></span>
<span id="cb32-58"><a href="#cb32-58" aria-hidden="true" tabindex="-1"></a>        <span class="at">--linux</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${kernel_path}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-59"><a href="#cb32-59" aria-hidden="true" tabindex="-1"></a>        <span class="at">--initrd</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${initrd_path}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-60"><a href="#cb32-60" aria-hidden="true" tabindex="-1"></a>        <span class="at">--microcode</span><span class="op">=</span><span class="st">&#39;/boot/amd-ucode.img&#39;</span> <span class="dt">\</span></span>
<span id="cb32-61"><a href="#cb32-61" aria-hidden="true" tabindex="-1"></a>        <span class="at">--os-release</span><span class="op">=</span><span class="st">&quot;@/etc/os-release&quot;</span> <span class="dt">\</span></span>
<span id="cb32-62"><a href="#cb32-62" aria-hidden="true" tabindex="-1"></a>        <span class="at">--cmdline</span><span class="op">=</span><span class="st">&quot;@</span><span class="va">${cmdline_file}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-63"><a href="#cb32-63" aria-hidden="true" tabindex="-1"></a>        <span class="at">--pcr-private-key</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${PCR_PRIVATE_KEY}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-64"><a href="#cb32-64" aria-hidden="true" tabindex="-1"></a>        <span class="at">--pcr-public-key</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${PCR_PUBLIC_KEY}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-65"><a href="#cb32-65" aria-hidden="true" tabindex="-1"></a>        <span class="at">--signtool</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${SIGN_TOOL}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-66"><a href="#cb32-66" aria-hidden="true" tabindex="-1"></a>        <span class="at">--secureboot-private-key</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${SECUREBOOT_PRIVATE_KEY}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-67"><a href="#cb32-67" aria-hidden="true" tabindex="-1"></a>        <span class="at">--secureboot-certificate</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${SECUREBOOT_CERTIFICATE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-68"><a href="#cb32-68" aria-hidden="true" tabindex="-1"></a>        <span class="at">--output</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${output_path}</span><span class="st">&quot;</span></span>
<span id="cb32-69"><a href="#cb32-69" aria-hidden="true" tabindex="-1"></a>    <span class="bu">set</span> +x</span>
<span id="cb32-70"><a href="#cb32-70" aria-hidden="true" tabindex="-1"></a><span class="kw">}</span></span>
<span id="cb32-71"><a href="#cb32-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-72"><a href="#cb32-72" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-73"><a href="#cb32-73" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux&quot;</span> <span class="dt">\</span></span>
<span id="cb32-74"><a href="#cb32-74" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-75"><a href="#cb32-75" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${RESUME_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-76"><a href="#cb32-76" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${DEFAULT_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-77"><a href="#cb32-77" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-78"><a href="#cb32-78" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-79"><a href="#cb32-79" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-80"><a href="#cb32-80" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux&quot;</span> <span class="dt">\</span></span>
<span id="cb32-81"><a href="#cb32-81" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux-fallback.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-82"><a href="#cb32-82" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${TROUBLE_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-83"><a href="#cb32-83" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${TROUBLESHOOT_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-84"><a href="#cb32-84" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-85"><a href="#cb32-85" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-86"><a href="#cb32-86" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-87"><a href="#cb32-87" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux-lts&quot;</span> <span class="dt">\</span></span>
<span id="cb32-88"><a href="#cb32-88" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux-lts.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-89"><a href="#cb32-89" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${RESUME_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-90"><a href="#cb32-90" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${LTS_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-91"><a href="#cb32-91" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-92"><a href="#cb32-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-93"><a href="#cb32-93" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-94"><a href="#cb32-94" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux-lts&quot;</span> <span class="dt">\</span></span>
<span id="cb32-95"><a href="#cb32-95" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux-lts-fallback.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-96"><a href="#cb32-96" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${TROUBLE_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-97"><a href="#cb32-97" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${TROUBLESHOOT_LTS_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-98"><a href="#cb32-98" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-99"><a href="#cb32-99" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-100"><a href="#cb32-100" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-101"><a href="#cb32-101" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux-hardened&quot;</span> <span class="dt">\</span></span>
<span id="cb32-102"><a href="#cb32-102" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux-hardened.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-103"><a href="#cb32-103" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${SECURE_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-104"><a href="#cb32-104" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${HARDENED_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-105"><a href="#cb32-105" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-106"><a href="#cb32-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-107"><a href="#cb32-107" aria-hidden="true" tabindex="-1"></a><span class="ex">build_uki</span> <span class="dt">\</span></span>
<span id="cb32-108"><a href="#cb32-108" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/vmlinuz-linux-hardened&quot;</span> <span class="dt">\</span></span>
<span id="cb32-109"><a href="#cb32-109" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;/boot/initramfs-linux-hardened-fallback.img&quot;</span> <span class="dt">\</span></span>
<span id="cb32-110"><a href="#cb32-110" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${COMMON_CMDLINE}</span><span class="st"> </span><span class="va">${SUSPEND_CMDLINE}</span><span class="st"> </span><span class="va">${TROUBLE_CMDLINE}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-111"><a href="#cb32-111" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${TROUBLESHOOT_HARDENED_UKI_PATH}</span><span class="st">&quot;</span> <span class="dt">\</span></span>
<span id="cb32-112"><a href="#cb32-112" aria-hidden="true" tabindex="-1"></a>    <span class="st">&quot;</span><span class="va">${EXPECTED_PCR15}</span><span class="st">&quot;</span></span>
<span id="cb32-113"><a href="#cb32-113" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb32-114"><a href="#cb32-114" aria-hidden="true" tabindex="-1"></a><span class="bu">echo</span> <span class="st">&quot;UKIs rebuilt and signed.&quot;</span></span></code></pre></div>
<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></li>
<li id="fn4"><p>You will have to restart the Nix daemon for your changes to take effect:</p>
<div class="sourceCode" id="cb38"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a># systemctl daemon-reload &amp;&amp;</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>    systemctl restart nix-daemon &amp;&amp;</span>
<span id="cb38-3"><a href="#cb38-3" aria-hidden="true" tabindex="-1"></a>    systemctl status nix-daemon</span></code></pre></div>
<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></li>
<li id="fn5"><p>On Arch, <code>/tmp</code> is a <code>tmpfs</code> by default. On NixOS, you may need to configure a <code>tmpfs</code>.<a href="#fnref5" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn6"><p>The <a href="https://docs.cachix.org/faq#what-happens-when-i-run-cachix-use-both-immediately-and-any-stateful-effects-for-the-future">FAQ notes</a> that this command simply modifies <code>/etc/nix/nix.conf</code> for you.<a href="#fnref6" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn7"><p>Confused? See <a href="https://nix.dev/concepts/flakes.html">Nix.dev</a>.<a href="#fnref7" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn8"><p>You will have to restart the Nix daemon for your changes to take effect:</p>
<div class="sourceCode" id="cb42"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a># systemctl daemon-reload &amp;&amp;</span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>    systemctl restart nix-daemon &amp;&amp;</span>
<span id="cb42-3"><a href="#cb42-3" aria-hidden="true" tabindex="-1"></a>    systemctl status nix-daemon</span></code></pre></div>
<a href="#fnref8" class="footnote-back" role="doc-backlink">↩︎</a></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>Switching to NixOS</title>
    <link href="http://www.nathantypanski.com/blog/2022-03-29-nixos.html" />
    <id>http://www.nathantypanski.com/blog/2022-03-29-nixos.html</id>
    <published>2022-03-29T00:00:00Z</published>
    <updated>2022-03-29T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> March 29, 2022 <i class="fa fa-cut" June 23, 2022
></i> June 23, 2022

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#what-is-nix" id="toc-what-is-nix">What is Nix?</a>
<ul>
<li><a href="#expression-language" id="toc-expression-language">Expression language</a></li>
<li><a href="#package-manager" id="toc-package-manager">Package manager</a></li>
<li><a href="#operating-system" id="toc-operating-system">Operating system</a></li>
</ul></li>
<li><a href="#installation" id="toc-installation">Installation</a></li>
<li><a href="#opt-in-state" id="toc-opt-in-state">Opt-in state</a></li>
<li><a href="#config-as-code" id="toc-config-as-code">Config-as-code</a></li>
<li><a href="#home-directory-management" id="toc-home-directory-management">Home directory management</a>
<ul>
<li><a href="#example" id="toc-example">Example</a></li>
<li><a href="#why" id="toc-why">Why?</a></li>
</ul></li>
<li><a href="#nixos-generations" id="toc-nixos-generations">NixOS generations</a></li>
<li><a href="#shells" id="toc-shells">Shells</a></li>
<li><a href="#downsides" id="toc-downsides">Downsides</a>
<ul>
<li><a href="#documentation" id="toc-documentation">Documentation</a></li>
<li><a href="#the-language" id="toc-the-language">The language</a></li>
</ul></li>
<li><a href="#conclusion" id="toc-conclusion">Conclusion</a></li>
</ul> </div>
<p>NixOS is a radical reimagining of what a Unix-like operating system can be like. It has a concept of <q>generations</q> that permit you to roll back (or forward) to previous instances of your operating system state. It’s defined entirely by code written in the <a href="https://nixos.wiki/wiki/Nix_Expression_Language">Nix expression language</a>, a functional programming language designed to make reproduicble builds easy. That’s right, NixOS is fully reproducible: if you have a NixOS machine definition, then you can reproduce not only the exact bytes of each package installed on that machine, but the entire state of the machine itself (besides ephemeral machine-specific bits).</p>
<p>The only comparable project I am aware of is <a href="https://guix.gnu.org/">GNU Guix</a>, a relatively newer project that attempts similar goals using Scheme instead of a domain-specific programming language.</p>
<p>It took a <em>long</em> time for me to warm up to the Nix approach to Unix systems. It takes the entire <a href="https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html">FHS</a> and throws it out the door, and by extension requires you to un-learn concepts that have been more-or-less stable for 30 years. While I have learned to appreciate what Nix offers, certain parts of NixOS remain opaque to me behind veils of abstraction. But with a few months of experience behind me, I can at least state that I <em>like</em> NixOS, that it is usable as a daily driver operating system, and that NixOS is (generally speaking) successful in achieving its lofty goals.</p>
<section id="what-is-nix" class="level2">
<h2>What is Nix?</h2>
<p>The term <q>Nix</q> can have three meanings, depending on context. I’ll cover each of them here.</p>
<section id="expression-language" class="level3">
<h3>Expression language</h3>
<p>The <em>Nix expression language</em> is a domain-specific programming language for reproducible configuraiton of packages and systems. It is a lazy, functional, mostly pure language primarily concerned with the manipulation of associative data structures termed <em>sets</em>:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>nix-repl&gt; mul = { left, right }: left * right</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>nix-repl&gt; mul { left = 10; right = 30; }</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>300</span></code></pre></div>
<p>Most Nix functions take sets as arguments and return sets to inform the caller about the result of a computation.</p>
<p>The standard way to learn Nix is to study the <a href="https://nixos.org/guides/nix-pills/index.html">Nix Pills</a>, which start at the interpreter and work their way up to full package definitions.</p>
<p>Alternatively, you can study the <a href="https://nixos.wiki/wiki/Nix_Expression_Language">wiki page</a>, which is a terse reference for the Nix language.</p>
</section>
<section id="package-manager" class="level3">
<h3>Package manager</h3>
<p>The <em>Nix pacakage manager</em> is a tool for installing and managing derivations (<q>packages</q>) written in the Nix expression language. The official <a href="https://github.com/NixOS/nixpkgs/"><code>nixpkgs</code></a> repository contains a whopping 60,000 packages, which puts it near the top distributions in terms of package count (approximately the same number as Debian or Fedora).</p>
</section>
<section id="operating-system" class="level3">
<h3>Operating system</h3>
</section>
</section>
<section id="installation" class="level2">
<h2>Installation</h2>
<p>Installing NixOS is similar to installing Arch Linux in many ways. There is no official installer, just live <code>.iso</code> and a <a href="https://nixos.org/manual/nixos/stable/index.html#sec-installation">giant manual</a>. You boot the live USB, partition a disk, mount the disk, generate hardware configuration, and run the installer, which installs the necessary components of the operating system to the mounted disk(s). Unlike Arch, if you wish to customize this system, you have one file to modify: <code>/etc/nixos/configuration.nix</code>. This file defines the entire machine state.</p>
</section>
<section id="opt-in-state" class="level2">
<h2>Opt-in state</h2>
<p>I used some modifications to this installation from grahamc’s <a href="https://grahamc.com/blog/erase-your-darlings">Erase Your Darlings</a> and mt-caret’s <a href="https://mt-caret.github.io/blog/posts/2020-06-29-optin-state.html">Encypted Btrfs Root with Opt-in State on NixOS</a>. Although NixOS tries to make its systems fully reproducible, this is not always possible.</p>
<p>Consider <code>/etc/resolv.conf</code>. This file is generated by <a href="https://www.man7.org/linux/man-pages/man1/resolvectl.1.html"><code>resolvconf</code></a> on a typical systemd Linux system and expected to change if your device roams between networks. In lieu of persistently keeping (and tracking) all the state from <code>/etc</code>, we treat <code>/etc</code> as a semi-ephemeral filesystem, and link only the <em>required</em> persistent configuration from <code>/persist/etc</code>.</p>
<p>In <code>/etc/nixos/configuration.nix</code>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>  environment.etc = {</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    nixos.source = &quot;/persist/etc/nixos&quot;;</span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    machine-id.source = &quot;/persist/etc/machine-id&quot;;</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    NIXOS.source = &quot;/persist/etc/NIXOS&quot;;</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    adjtime.source = &quot;/persist/etc/adjtime&quot;;</span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>  };</span></code></pre></div>
<p>Then <code>/etc/nixos/configuration.nix</code> is a hardlink to <code>/persist/etc/nixos/configuration.nix</code>:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a># stat /etc/nixos/configuration.nix /persist/etc/nixos/configuration.nix</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  File: /etc/nixos/configuration.nix</span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  Size: 5629            Blocks: 16         IO Block: 4096   regular file</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>Device: 0,55    Inode: 1141        Links: 1</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>Access: 2022-03-23 09:55:00.014521725 -0400</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>Modify: 2022-03-23 09:55:00.014521725 -0400</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>Change: 2022-03-23 09:55:00.015521737 -0400</span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a> Birth: 2022-03-23 09:55:00.014521725 -0400</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>  File: /persist/etc/nixos/configuration.nix</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  Size: 5629            Blocks: 16         IO Block: 4096   regular file</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>Device: 0,55    Inode: 1141        Links: 1</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>Access: 2022-03-23 09:55:00.014521725 -0400</span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>Modify: 2022-03-23 09:55:00.014521725 -0400</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>Change: 2022-03-23 09:55:00.015521737 -0400</span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> Birth: 2022-03-23 09:55:00.014521725 -0400</span></code></pre></div>
<p>If we wanted to keep additional state, we can expand the list of links in the Nix definition for our machine.</p>
</section>
<section id="config-as-code" class="level2">
<h2>Config-as-code</h2>
<blockquote>
<p>A sufficiently complete specification of a system policy leads to the notion of an ideal average state for the system. Over time, the ideal average state of the system degrades. The aim of system administration is to keep the system as close to its ideal state as possible.</p>
<p>-M. Burgess, <a href="https://www.usenix.org/legacy/publications/library/proceedings/lisa2000/full_papers/burgess/burgess_html/index.html"><em>Theoretical System Administration</em></a></p>
</blockquote>
<p>In my professional life, I’ve written a fair amount of Puppet, Ansible, and Chef code defining the state of Linux systems. These tools all share a model of Linux systems as <em>mutable, living systems</em>. Their policy is designed to take a system from an unknown initial state <em>toward</em> an ideal state. After sufficient repeated applications of this process of <em>convergence</em>, the system state should tend towards our ideal state.</p>
<p>Nix works differently. Its policies, like most configuration management tools, model the end-state of a system. Unlike traditional configuration management tools, Nix does not assume an unknown initial state. It relies heavily on sandboxing and build isolation to produce ideal states from known initial states.</p>
<p>In some sense, this is a strictly <q>easier</q> problem to solve than convergent configuration management. Nix takes steps to ensure there is only one possible path from initial state to ideal state, and therefore can ensure repeated applications of a Nix derivaiton always result in the same end-state.</p>
</section>
<section id="home-directory-management" class="level2">
<h2>Home directory management</h2>
<p>Like many users of Unix-like operating systems, I spent a significant amount of time building and tweaking my <a href="https://github.com/nathantypanski/dotfiles">dotfiles</a> collection to configure new users the way I want. I went through repeated iterations of attempts at making this process <em>manageable</em>, but the tools were never quite sufficient for the job. Prior to Nix, the best solution I’d found was to use <a href="https://www.gnu.org/software/stow/">GNU Stow</a> to create and manage symlinks from <code>~/.vimrc</code> to <code>~/src/github.com/nathantypanski/dotfiles/vim/.vimrc</code> and so on. This works well for individual files, but many tools also have plugin directories and other state that is easy to lose track of. Over time, the dotfiles repository diverges from local system state.</p>
<p>The Nix solution to this problem is <a href="https://github.com/nix-community/home-manager"><code>home-manager</code></a>. This is a tool that lets you use the same declarative configuraiton for your home directory (and its many configuration files) as you would for the NixOS operating system.</p>
<section id="example" class="level3">
<h3>Example</h3>
<p>Here’s an example of <code>home-manager</code>, taken from my current configuration:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>{ config, pkgs, lib, ... }:</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>{</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  imports = [ ./zsh.nix ];</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  home.username = &quot;nathan&quot;;</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  home.homeDirectory = &quot;/home/nathan&quot;;</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>  home.stateVersion = &quot;22.05&quot;;</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>  # Let Home Manager install and manage itself.</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  programs.home-manager.enable = true;</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>  services.gpg-agent = {</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>    enable = true;</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>    defaultCacheTtl = 1800;</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>    enableSshSupport = false;</span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>  home.keyboard.options = [&quot;ctrl:nocaps&quot;];</span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a>  # continued below ...</span></code></pre></div>
<p>Installing and configuring a window manager is as simple as including its block:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>  # continued from above ...</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  wayland.windowManager.sway = {</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    enable = true;</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    wrapperFeatures.gtk = true ;</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    config = {</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>      terminal = &quot;alacritty&quot;;</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>      fonts = {</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        names = [&quot;pango:Terminus&quot;];</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>        style = &quot;normal&quot;;</span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>        size = 10.0;</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>      };</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>  # continued below ...</span></code></pre></div>
<p>You can install per-user packages here. This lets you install software for a particular user that won’t be available to the rest of the system. I use users to segment different types of tasks (e.g., gaming), and I wouldn’t want my gaming user to have access to compiler toolchains.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a>  # continued from above ...</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  home.packages = with pkgs; [</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    haskell.compiler.ghc921</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    pass</span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    rustup</span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    go-tools</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    go</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    python39</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    python39Packages.pip</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>    python39Packages.virtualenv</span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>  ];</span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>  # continued below ...</span></code></pre></div>
<p>Most programs you would normally have in dotfiles with per-application configuration are managed natively using the Nix domain-specific language.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>  # continued from above ...</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  programs.git = {</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    enable = true;</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    userName = &quot;ndt&quot;;</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    userEmail = &quot;...&quot;;</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>  # continued below ...</span></code></pre></div>
<p>Even the shell configuration is defined in the Nix language:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a>  # continued from above ...</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  programs.zsh = {</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    enable = true;</span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    enableCompletion = true;</span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    enableSyntaxHighlighting = true;</span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    history = {</span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>      save = 10000;</span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>      size = 10000;</span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>      share = true;</span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>      extended = true;</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>      ignoreSpace = true;</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>      ignorePatterns = [</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>        &quot;rm *&quot;</span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>        &quot;pkill *&quot;</span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>      ];</span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>    shellAliases = {</span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>      x = &quot;tmux&quot;;</span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>    };</span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>  };</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</section>
<section id="why" class="level3">
<h3>Why?</h3>
<p>It’s probably not immediately apparent what the benefit of using Nix to configure Git, or the shell, or a window manager might be. Each of these tools has its own configuration language already, and you might have taken the time to learn each program’s config format. Migrating this configuration can take time, and besides, you’re just expressing the same config in a different format, right?</p>
<section id="generations" class="level4">
<h4>Generations</h4>
<p>One of the most fundamental benefits of Nix management is its concept of <em>generations</em>. Every deployment of changes to your Nix-managed homedir produces a generation. You can enumerate the generations using the <code>home-manager</code> CLI:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>$ home-manager generations | head</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>2022-04-11 09:07 : id 65 -&gt; /nix/store/9j3s2canrz3q2rwh19maywlj0xgm27lr-home-manager-generation</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>2022-04-11 09:05 : id 64 -&gt; /nix/store/gdcf7r29kphnq6mcmkj9zqjknlbh3gp7-home-manager-generation</span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>2022-04-11 09:04 : id 63 -&gt; /nix/store/rsqf8dyhz9lk1j5351r6zracaanwxq34-home-manager-generation</span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>2022-04-05 20:49 : id 62 -&gt; /nix/store/a2c65dx05595smgw58j02ffy2cm4rrq0-home-manager-generation</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>2022-03-12 18:09 : id 61 -&gt; /nix/store/98b16il8x7rzma5pr75njwavwnadl7p4-home-manager-generation</span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>2022-03-12 13:15 : id 60 -&gt; /nix/store/d0rvr0nx3y2rj0ixa71n9q0a9lpc7dp8-home-manager-generation</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>2022-03-12 13:15 : id 59 -&gt; /nix/store/73xqnpzsfm42hjp76kx4bi769ld4gzmm-home-manager-generation</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>2022-03-11 10:49 : id 58 -&gt; /nix/store/waizbp6iqdp6zgxxrbvbn1pagxk7jh4f-home-manager-generation</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>2022-03-11 09:58 : id 57 -&gt; /nix/store/bg169sw55c440f0rsd7pxsh31zsdw43q-home-manager-generation</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>2022-03-11 09:54 : id 56 -&gt; /nix/store/m3bq41252rcrp8xc48982h5kfhgpbvix-home-manager-generation</span></code></pre></div>
<p>If I’d like to roll back to one of these generations, I can simply run the <code>activate</code> script for that generation. For example, to roll back one generation (to <code>64</code>), I would run:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>$ /nix/store/gdcf7r29kphnq6mcmkj9zqjknlbh3gp7-home-manager-generation/activate</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>Starting Home Manager activation</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>Activating checkFilesChanged</span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>Activating checkLinkTargets</span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>Activating writeBoundary</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>Activating installPackages</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>replacing old &#39;home-manager-path&#39;</span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>installing &#39;home-manager-path&#39;</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>Activating linkGeneration</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>Cleaning up orphan links from /home/nathan</span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>Creating profile generation 66</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>Creating home file links in /home/nathan</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>Activating onFilesChange</span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>Activating reloadSystemd</span></code></pre></div>
<p>This is a powerful feature enabling experimentation. Mistakes are trivial to undo, and you get a fully reproducible configuration for your home directory. That means you get baked-in guarantees that if you choose to spin up a new system with the same configuration, the configuration will not only apply successfully—it will produce exactly the same result.</p>
</section>
</section>
</section>
<section id="nixos-generations" class="level2">
<h2>NixOS generations</h2>
<p>The concept of generations doesn’t originate in <code>home-manager</code>. In fact, it’s a first-party feature of NixOS proper. Each version of your system configuration gets recorded in the generations list, and you can restore the system to any of those versions with a single command with <code>nix-env --rollback</code> (for the previous version) or <code>nix-env -G 3</code>. Note that you may have to set the profile to <code>system</code> in order to change the global system instead of per-user configuration.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a># nix-env --list-generations --profile /nix/var/nix/profiles/system | tail</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  91   2022-04-10 16:06:58</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>  92   2022-04-10 16:18:46</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>  93   2022-04-10 16:22:37</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>  94   2022-04-10 16:39:26</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>  95   2022-04-21 10:04:08</span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>  96   2022-04-22 09:25:09</span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>  97   2022-04-24 13:08:53</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>  98   2022-04-24 13:11:39</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>  99   2022-04-24 14:48:43</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a> 100   2022-04-24 14:49:28   (current)</span></code></pre></div>
</section>
<section id="shells" class="level2">
<h2>Shells</h2>
<p>A feature I didn’t think I’d care for, but ended up using <em>all the time</em> was <code>nix-shell -p ${package_name}</code> to spawn a new Bash shell with some requested software available, but (crucially) <em>without</em> making that software available to the system as a whole.</p>
<p>Let’s say I want to use <a href="https://github.com/radio-rogal/swayshot">swayshot</a> to take a screenshot, but I don’t have <code>slurp</code> or <code>grim</code> installed:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>$ nix-shell -p slurp grim</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>these paths will be fetched (0.03 MiB download, 0.10 MiB unpacked):</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>  /nix/store/0silcp1jlicjbjbjhzvmkffj2wck4m5z-grim-1.4.0</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>  /nix/store/yc2sc0k5d3bm9n6wq57qmmv4dsndkzpn-slurp-1.3.2</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>$ ./swayshot.sh</span></code></pre></div>
<p>Being able to experiment with different tools on-the-fly like this, grants a powerful feeling of freedom to try things. If you don’t like a tool you just tried, then don’t add it to <code>/etc/nixos/configuration.nix</code> and it won’t pollute your environment. The next time you run <code>nixos-collect-garbage</code>, it will be removed from the Nix store.</p>
<p>Likewise <code>home-manager</code> can list packages that are available only to a certain user, but not other users or the root user.</p>
</section>
<section id="downsides" class="level2">
<h2>Downsides</h2>
<p>NixOS is <em>not</em> perfect. In exchange for all this functional, immutable, reproducible OS magic, we need to trade a few things (at least today).</p>
<section id="documentation" class="level3">
<h3>Documentation</h3>
<p>This is still the worst part of NixOS. Community efforts have strived to improve the Nix documentation. Today we have <a href="https://nixos.org/guides/nix-pills/index.html">Nix Pills</a> for learning Nix-the-language, the <a href="https://nixos.org/manual/nixos/stable/">NixOS Manual</a> explaining how to install and configure NixOS systems, and the <a href="https://nixos.wiki/">NixOS Wiki</a> which provides succinct howtos on common problems.</p>
<p>That sounds great, right? In theory, those resources are everything you need. In practice, they each seem to land at the wrong level of abstraction, and the symptoms of this are similar to the problems found with monad tutorials in something like Haskell. The people writing monad tutorials understand monads. You do not. The problem is once you learn how monads work, you lose the ability to explain monads to anyone who doesn’t already know how they work.</p>
<p>This kind of <q>pedagogy disruptor field</q> is common when you’re explaining concepts that one day <q>click.</q> The day before, you didn’t understand NixOS. Then you use it for a few days/weeks/months and one day you suddenly have enough of the pieces in place and it clicks and you understand how the system works. Do you understand all the prerequisites for reaching this state of understanding? Not consciously.</p>
<p>The NixOS documentation authors are all plagued by the pedagogy disruptor field. For instance, the Nix store is an extraordinarily fundamental part of NixOS systems. It contains all installed packages, each prefixed by a cryptographic hash of their contents, and is located at <code>/nix/store</code>:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>$ ls /nix/store | head</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>000yp1grcymcfbmncflf2bhbqyzb8p62-hook.drv</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>0018h2fjjq0zijmyknykxvwysaj24qw0-timeit-2.0.tar.gz.drv</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>001gp43bjqzx60cg345n2slzg7131za8-nix-nss-open-files.patch</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>001ybmr7k4bj79nknk7ykzfqa7wqw55h-source.drv</span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>001ynjbfcyzg60w6y1x0hjx106ixydq8-unit-script-prepare-kexec-start</span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>002gbsl500p1b9m4wlinazna58mcmn6z-gnum4-1.4.19.drv</span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>003cl64qdhj7ng8pjmnihhda315q5czg-home-manager-path.drv</span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>0042c0dpzvx4khk34wl8ikikjrsv3fwn-conduit-1.3.4.2.drv</span>
<span id="cb13-10"><a href="#cb13-10" aria-hidden="true" tabindex="-1"></a>004fc2vsbnzsw43ci25hqk08rpvyagy0-catalog-legacy-uris.patch.drv</span>
<span id="cb13-11"><a href="#cb13-11" aria-hidden="true" tabindex="-1"></a>004h9inrdzqj2sfgssfpmlsl8mp9dn23-source.drv</span></code></pre></div>
<p>The first mention of the Nix store in the NixOS manual (not counting the syntax summary) is in the <a href="https://nixos.org/manual/nixos/stable/index.html#sec-wireless">wifi setup section</a>. There are descriptions of how to clean the nix store, sections on preventing storage of secrets in the nix store, and numerous references to the Nix store with the implication that the reader already understands what it is. At no point is the Nix store defined, or descriptively outlined.</p>
<p>Many attributes of Nix or NixOS are treated in this way. Maintenance tasks for obscure subsystems are described in intricate detail, but the purpose or behavior of those subsystems is absent in documentation.</p>
</section>
<section id="the-language" class="level3">
<h3>The language</h3>
<p>The Nix language is syntactically ugly. I have yet to decide whether it is actually <q>bad.</q> There’s a GNU reimplementation of NixOS called <a href="https://guix.gnu.org/">Guix</a> that attempts to address this by replacing Nix with Guile, a dialect of Lisp.</p>
<p>The Nix language is almost entirely side-effect free. Most of the time, all you’re doing with it is templating Bash scripts with increasingly high-level abstractions.</p>
<p>The standard way to learn it is you go read <a href="https://nixos.org/guides/nix-pills/">Nix pills</a> and then you read <a href="https://nixos.wiki/wiki/Nix_Expression_Language">the wiki</a>. When it comes time to author real-world packages you search <a href="https://github.com/NixOS/nixpkgs/">nixpkgs</a> for similar applications to learn patterns, using <a href="https://nixos.org/manual/nixpkgs/stable/">the manual</a> for reference.</p>
</section>
</section>
<section id="conclusion" class="level1">
<h1>Conclusion</h1>
<p>Nix is a radical approach to software packaging, and it makes reproducibility of complex software systems easier than any other tool I know of. At the same time, it’s a complex and largely undocumented beast. Working with Nix reminds me of pre-1.0 Rust: smart ideas, constant changes, and a growing push for stabilization and documentation that gets better each month.</p>
<p>I still have my gripes with it, but the promise of a new and innovative way to manage systems has finally ripped me away from Arch Linux in search of something better than Unix-style userspace organization. The ability to effortlessly experiment and rollback changes to my OS has made hacking on Linux fun again.</p>
</section>
]]></summary>
</entry>
<entry>
    <title>JWS is a nightmare</title>
    <link href="http://www.nathantypanski.com/blog/2021-12-24-jws-nightmare.html" />
    <id>http://www.nathantypanski.com/blog/2021-12-24-jws-nightmare.html</id>
    <published>2021-12-24T00:00:00Z</published>
    <updated>2021-12-24T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> December 24, 2021 <i class="fa fa-cut" July 30, 2025
></i> July 30, 2025

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#cryptographic-doom" id="toc-cryptographic-doom">Cryptographic doom</a></li>
<li><a href="#verifying-a-jws" id="toc-verifying-a-jws">Verifying a JWS</a>
<ul>
<li><a href="#attacker-controlled-algorithms" id="toc-attacker-controlled-algorithms">Attacker-controlled algorithms</a></li>
</ul></li>
<li><a href="#protocols-should-be-defensive-against-implementations" id="toc-protocols-should-be-defensive-against-implementations">Protocols should be defensive against implementations</a></li>
<li><a href="#a-better-way-to-sign-json" id="toc-a-better-way-to-sign-json">A better way to sign JSON</a></li>
</ul> </div>
<p>JWS is the kind of thing where you could be forgiven for thinking <q>hey, everyone’s using this, so it must be good!</q> JWTs are built on JWS, and lots of people use those. JWS is standardized by the IETF in <a href="https://datatracker.ietf.org/doc/html/rfc7515">RFC7515</a>. I’m hoping that by the time you’re done with this post, you’ll do something else. If you just want to know what that <q>something else</q> is, skip to <a href="#a-better-way-to-sign-json">the bottom</a>.</p>
<p>In this post, I will show how the JWS standard encourages implementations to structure their validation logic in an insecure manner. Thus JWTs, being built upon a rotten foundation, are themselves insecure—or at least very difficult to implement securely. Then I will provide suggestions on alternatives to JWS.</p>
<p>Here’s an example JWS from that RFC:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;payload&quot;</span><span class="fu">:</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ&quot;</span><span class="fu">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;protected&quot;</span><span class="fu">:</span><span class="st">&quot;eyJhbGciOiJFUzI1NiJ9&quot;</span><span class="fu">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;header&quot;</span><span class="fu">:</span> <span class="fu">{</span><span class="dt">&quot;kid&quot;</span><span class="fu">:</span><span class="st">&quot;e9bc097a-ce51-4036-9562-d2ade882db0d&quot;</span><span class="fu">},</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;signature&quot;</span><span class="fu">:</span> <span class="st">&quot;DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q&quot;</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>The payload and <code>protected</code> fields are base64-encoded without padding. The <code>payload</code> looks like this:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span><span class="dt">&quot;iss&quot;</span><span class="fu">:</span><span class="st">&quot;joe&quot;</span><span class="fu">,</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;exp&quot;</span><span class="fu">:</span><span class="dv">1300819380</span><span class="fu">,</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;http://example.com/is_root&quot;</span><span class="fu">:</span><span class="kw">true</span><span class="fu">}</span></span></code></pre></div>
<p>And here’s the <code>protected</code> field:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span><span class="dt">&quot;alg&quot;</span><span class="fu">:</span><span class="st">&quot;ES256&quot;</span><span class="fu">}</span></span></code></pre></div>
<p>ES256 is the ECDSA P-256 SHA-256 digital signature algorithm.</p>
<section id="cryptographic-doom" class="level2">
<h2>Cryptographic doom</h2>
<p>Moxie Marlinspike coined the <a href="https://moxie.org/2011/12/13/the-cryptographic-doom-principle.html"><em>cryptographic doom principle</em></a> in 2011 to refer to a pattern he had seen in implementations of message authentication codes (MACs). It generalizes the kinds of flaws found in <a href="https://www.iacr.org/cryptodb/archive/2002/EUROCRYPT/2850/2850.pdf">Vaudenay’s famous 2002 paper</a> and <a href="https://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf">SSH plaintext recovery</a> into a principle:</p>
<blockquote>
<p>If you have to perform any cryptographic operation before verifying the MAC on a message you’ve received, it will somehow inevitably lead to doom.</p>
</blockquote>
<p>An extraordinarily common example of cryptographic doom is to calculate MACs based on plaintext in an encrypted message payload. To check the authenticity of a message, recipients must necessarily decrypt the message. In Vaudenay’s attack, we exploit this construction to recover plaintext from encrypted messages, using only a single bit of information in servers’ responses (padding error or MAC error).</p>
<p>Although Moxie’s article pertains only to MACs and traditional cryptographic operations, the spirit of his principle extends beyond: <em>trusting data before verifying it is dangerous</em>.
When designing cryptographic protocols, we should <strong>strive to
authenticate data as early as possible.</strong> Don’t design a protocol that
requires processing attacker-controlled metadata before cryptographic
verification! Keep that in mind as you read the rest of this post.</p>
</section>
<section id="verifying-a-jws" class="level2">
<h2>Verifying a JWS</h2>
<p>Let’s take another look at the example JWS object from earlier.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode json"><code class="sourceCode json"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;payload&quot;</span><span class="fu">:</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="st">&quot;eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ&quot;</span><span class="fu">,</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;protected&quot;</span><span class="fu">:</span><span class="st">&quot;eyJhbGciOiJFUzI1NiJ9&quot;</span><span class="fu">,</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;header&quot;</span><span class="fu">:</span> <span class="fu">{</span><span class="dt">&quot;kid&quot;</span><span class="fu">:</span><span class="st">&quot;e9bc097a-ce51-4036-9562-d2ade882db0d&quot;</span><span class="fu">},</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;signature&quot;</span><span class="fu">:</span> <span class="st">&quot;DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q&quot;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p>In order to verify this, we need to do the following:</p>
<ol type="1">
<li><p><code>base64urldecode()</code> the <code>protected</code> attribute.</p></li>
<li><p>Deserialize the <code>protected</code> attribute into JSON.</p></li>
<li><p>Calculate the JOSE header as the union of the <code>protected</code> object and the JWS (<q>unprotected</q>) object.</p></li>
<li><p>Verify that the implementation understands and can process the algorithm and any fields in the <code>crit</code> header.</p></li>
<li><p><code>base64urldecode()</code> the <code>payload</code> attribute.</p></li>
<li><p><code>base64urldecode()</code> the <code>signature</code> attribute.</p></li>
<li><p>Determine the verification algorithm to use by extracting it from the decoded <code>protected</code> header.</p></li>
<li><p>Construct the JWS signing input by concatenating encoded <code>protected</code> and <code>payload</code> headers together with a <q>.</q> character.</p>
<p><span class="math display">\[ \begin{aligned} &amp; \texttt{ascii}(\texttt{base64urlencode}(\texttt{utf-8}(\texttt{protected}))) \\
&amp; \qquad || \quad ``.&quot; \\
&amp; \qquad || \quad \texttt{base64urldecode}(\texttt{payload}) \end{aligned} \]</span></p></li>
<li><p>Using the algorithm from (8), verify the JWS.</p></li>
</ol>
<p>Every step in this list prior to (9) is an opportunity for attackers to modify objects that are deserialized into JSON, control the algorithm in use by the system, and generally mess around where we expect contents to be integrity-protected.</p>
<p>Moreover, the fact that we are required to perform so many deserialization, decoding, and conditional algorithm selection operations prior to JWS verification invites implementations to make bad decisions about error handling. It might be tempting, to an implementor of JWS verification, to return <q>helpful</q> error messages about the validity of the JSON objects inside at step (5) before moving on to verification.</p>
<p>We should strive to introduce minimal transformations prior to signature verification. By requiring deserialization into JSON objects, the design invites implementations to perform early deserialization into application objects, potentially exposing themselves to <a href="https://github.com/advisories?query=cwe%3A502">major deserialization bugs</a> with untrusted input data.</p>
<section id="attacker-controlled-algorithms" class="level3">
<h3>Attacker-controlled algorithms</h3>
<p>As <a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/">Auth0 discovered in late 2020</a>, allowing the attacker to control the algorithm used for signing affords them a number of options to bypass signature checks.</p>
<p>In the simplest case, setting the <code>algorithm</code> field to <code>none</code> and using an empty signature value (<code>""</code>) results in successful verification for any key.</p>
<p>The more complex attack requires the victim to use public key encryption algorithms and publish the public key. If the server supports <code>RS256</code> RSA signatures, attackers can encode the public key into PEM and use it as an HMAC key to sign a message with the <code>HS256</code> algorithm. Unless the library (or server) strictly enforces the algorithm to use for each key ID, it will pass validation.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>The solution to this is to distrust the client’s provided algorithm. If we’re verifying a client’s JWS, then we probably already have their public key. From this key, we can derive the algorithm in use for the client (or store it, in our database, along with the key and <code>kid</code>). When we are presented with a new JWS to verify, we should use the algorithm specified for this key.</p>
<p>The final version of the JWS RFC now includes a section on <a href="https://datatracker.ietf.org/doc/html/rfc7515#section-10.7">algorithm protection</a> addressing this issue, but it fails to be presecriptive about mitigations. According to the RFC, implementations can choose to</p>
<ul>
<li>only support algorithms that are not vulnerable to substitution attacks,</li>
<li>require the <code>alg</code> header be carried in the JWS protected header,</li>
<li>or include a field containing the algorithm in the application payload, and match it with the <code>alg</code> header.</li>
</ul>
<p>Unfortunately, none of these mitigations include deriving the algorithm from the <code>kid</code> parameter, by far the most obviously correct mitigation.</p>
<section id="paseto" class="level4">
<h4>PASETO</h4>
<p><a href="https://github.com/paseto-standard/paseto-spec">PASETO</a> is an alternative JWT-like design that attempts to fix the algorithm selection bug. It uses protocol versions to specify algorithms, which is a generally-accepted good practice, and supports sound cryptographic algorithms like Ed25519. Unlike JWS, it does not support extensions by means of a <code>crit</code> parameter. In fact, it only allows two options:</p>
<ol type="1">
<li><code>version</code>, indicating the ciphersuite to use.</li>
<li><code>purpose</code>, where a value of <code>local</code> means symmetric-key encryption and <code>public</code>means public-key signatures.</li>
</ol>
<p>Unfortunately, the PASETO design invites <a href="https://mailarchive.ietf.org/arch/msg/cfrg/Yd85GHaPfkUYvCsWAdikByQsiLQ/">remarkably similar bugs to JWS</a>, since attackers may control the protocol version instead of the algorithm directly. Now the PASETO spec includes <a href="https://github.com/paseto-standard/paseto-spec/blob/master/docs/02-Implementation-Guide/03-Algorithm-Lucidity.md">warnings to strongly type the algorithm in use</a>, just like JWS.</p>
</section>
</section>
</section>
<section id="protocols-should-be-defensive-against-implementations" class="level2">
<h2>Protocols should be defensive against implementations</h2>
<p>When I’m forced to grapple with something involving cryptography, I like it to be so stupid simple that it’s obviously correct. If I’m reviewing code that includes cryptography parts, I want it to be so obviously flawless that there’s no debate—even among relative amateurs—about its correctness.</p>
<p>Primitives should be outsourced to sound, well-regarded libraries where possible. In fact, as much as we can should build upon a battle-tested, fuzzed, expertly-developed implementation.</p>
<p>When designing security protocols, we should be making this kind of dead-simple implementation as easy and obvious as possible for software. Now, I’m not talking about <em>primitives</em> here, although certain modern primitives are <a href="https://ed25519.cr.yp.to/python/ed25519.py">remarkably simple</a>. I mean that the composition of primitives into a functioning security protocol should, to the extent possible, by extremely obvious.</p>
<p>A major mistake we see again and again in the first generation of internet security protocols is kitchen-sink design. SSL/TLS with its configurable ciphersuites, the Eldritch horror of PGP email signatures, and other early security standards are victims of this. SSL/TLS assumes that implementations can correctly handle things like ciphersuite negotiation. PGP is a <a href="https://blog.cryptographyengineering.com/2014/08/13/whats-matter-with-pgp/">graveyard</a> of this type of <a href="https://latacora.micro.blog/2019/07/16/the-pgp-problem.html">complexity</a>. JWS, despite its marketing as <q>just signed JSON objects,</q> is the polar opposite of obvious protocol. It exposes algorithm selection to the client, verification requires multiple steps of deserialization, encoding, and serialization, and it is far too extensible for its own good.</p>
<p>Modern tools are going the other way. <a href="https://jedisct1.github.io/minisign/">Minisign</a>, <a href="https://www.openbsd.org/papers/bsdcan-signify.html">OpenBSD Signify</a> and <a href="https://age-encryption.org/v1">age</a> are relentlessly simple, electing to do one thing well and no more.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a> Facebook <a href="https://eprint.iacr.org/2018/413.pdf">crypto auth tokens</a> use a clever construction of nested MACs to support per-service auth tokens without explicit key sharing, but it’s still simple enough that you can implement it by nesting library calls.</p>
</section>
<section id="a-better-way-to-sign-json" class="level2">
<h2>A better way to sign JSON</h2>
<p>Most mistakes in request signing protocols stem from serializing, parsing, and decoding the object <a href="https://www.daemonology.net/blog/2008-12-18-AWS-signature-version-1-is-insecure.html">prior to verification</a>. Don’t do those steps. Most of the time it doesn’t matter.</p>
<p>If you need unforgeability of messages by the server, then use Ed25519. This is unnecessary for most web APIs, but it is important in some cases, like <a href="https://shufflesharding.com/posts/aws-sigv4-and-sigv4a">AWS multi-region access points</a>. If you trust the server, then use HMAC-SHA256, a symmetric authentication algorithm. <strong>Do not support both of these at once</strong>. Think long and hard about what is appropriate for your application.</p>
<p>Have clients serialize the JSON object they wish to sign into a bytestring <code>json_bytes</code>. Now your signature algorithm becomes just:</p>
<p><span class="math display">\[ \texttt{sign}(\texttt{json_bytes},\ \texttt{key}).\]</span></p>
<p>And verification?</p>
<p><span class="math display">\[ \texttt{verify}(\texttt{json_bytes},\ \texttt{key}).\]</span></p>
<p>This is the method recommended by Latacora Security in <a href="https://latacora.micro.blog/2019/07/24/how-not-to.html">How (not) to sign a JSON object</a>.</p>
<p>Notice that verification does not require deserialization, nor character decoding! If the payload is a JSON object, we can safely deserialize it after verification. If you want, you can use <a href="https://www.openbsd.org/papers/bsdcan-signify.html">OpenBSD Signify</a> so that clients have readily available CLI and library tooling support.</p>
<p>Now, this has the downside that two identical JSON objects may not result in identical signatures. Fortunately, this does not generally matter, and in any case it probably matters less than the correctness of your cryptographic system. If you need to check the equality of two requests, you can generally verify, deserialize, canonicalize and compare them inside your application server logic as necessary. Plus, this way when you find a bug in your canonicalization logic, you can fix it without forcing your clients to update.</p>
</section>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>The following code has two major problems. The first is that we are returning <q>helpful</q> error messages related to the application <a href="#cryptographic-doom">prior to header verification</a>, potentially leaking bits of information an attacker can use to exploit our application. The second is that we allow the <q>algorithm</q> field to be attacker-controlled, leaving us open to <a href="#attacker-controlled-algorithms">algorithm selection attacks</a>.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> verify_jws(jws_string):</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    jws <span class="op">=</span> json.loads(string)</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Decode protected.</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    protected_decoded <span class="op">=</span> base64urldecode(jws[<span class="st">&#39;protected&#39;</span>])</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Deserialize protected into JSON.</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    protected <span class="op">=</span> json.loads(protected_decoded)</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Calculate the JOSE header.</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    jose <span class="op">=</span> jws <span class="op">|</span> protected</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Verify that the implementation understands and can process any fields in</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    <span class="co"># the &quot;crit&quot; header.</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> header <span class="kw">in</span> protected[<span class="st">&#39;crit&#39;</span>]:</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">not</span> header <span class="kw">in</span> supported_critical_headers:</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>            <span class="cf">raise</span> JWSError(<span class="st">&#39;Unsupported Header&#39;</span>)</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Verify expected headers are present</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">not</span> <span class="st">&#39;exp&#39;</span> <span class="kw">in</span> protected[<span class="st">&#39;crit&#39;</span>]:</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> JWSError(<span class="st">&#39;expected &quot;exp&quot; header&#39;</span>)</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">not</span> <span class="st">&#39;nbf&#39;</span> <span class="kw">in</span> protected[<span class="st">&#39;crit&#39;</span>]:</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> JWSError(<span class="st">&#39;expected &quot;exp&quot; header&#39;</span>)</span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Decode the &quot;payload&quot; attribute</span></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>    payload_decoded <span class="op">=</span> base64urldecode(jws[<span class="st">&#39;payload&#39;</span>])</span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a>    payload <span class="op">=</span> json.loads(payload_decoded)</span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Decode the &quot;signature&quot; attribute</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a>    signature <span class="op">=</span> base64urldecode(jws[<span class="st">&#39;signature&#39;</span>])</span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Reject invalid timestamps</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a>    expiration <span class="op">=</span> datetime.utcfromtimestamp(<span class="bu">int</span>(payload[<span class="st">&#39;exp&#39;</span>]))</span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a>    not_before <span class="op">=</span> datetime.utcfromtimestamp(<span class="bu">int</span>(payload[<span class="st">&#39;nbf&#39;</span>]))</span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a>    now <span class="op">=</span> datetime.now()</span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> now <span class="op">&gt;</span> expiration:</span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> JWSError(<span class="st">&quot;JWS is expired&quot;</span>)</span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> not_before <span class="op">&gt;</span> now:</span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> JWSError(<span class="st">&quot;JWS is not valid yet&quot;</span>)</span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Verify the application can handle the supplied request.</span></span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Raises UnsupportedAttributesError if invalid.</span></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a>    validate_attributes(jose)</span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a>    algorithm <span class="op">=</span> protected[<span class="st">&#39;algorithm&#39;</span>]</span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">not</span> algorithm <span class="kw">in</span> supported_algorithms:</span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a>        msg <span class="op">=</span> <span class="st">&quot;</span><span class="sc">{0}</span><span class="st"> is unsupported&quot;</span>.<span class="bu">format</span>(algorithm)</span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a>        <span class="cf">raise</span> JWSUnsupportedAlgorithmError(msg)</span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Calculate the signature payload.</span></span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a>    signature_payload <span class="op">=</span> (base64url(protected_decoded.encode(<span class="st">&#39;utf-8&#39;</span>))</span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a>                        <span class="op">+</span> <span class="st">&#39;.&#39;</span> <span class="op">+</span> base64url(payload_decoded)).encode(<span class="st">&#39;ascii&#39;</span>)</span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a>    <span class="co"># Verify with the provided algorithm.</span></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> verify_payload(algorithm, signature_payload)</span></code></pre></div>
<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></li>
<li id="fn2"><p>Unfortunately, this simplicity can be to a fault. The new <code>age</code> encryption tool does not support public key authentication, meaning constructs such as</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode bash"><code class="sourceCode bash"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="ex">$</span> curl <span class="st">&quot;</span><span class="va">${ENCRYPTED_TARBALL_URL}</span><span class="st">&quot;</span> age <span class="at">--decrypt</span> <span class="kw">|</span> <span class="fu">tar</span> xf</span></code></pre></div>
<p>are <a href="https://www.imperialviolet.org/2014/06/27/streamingencryption.html">dangerous</a>. But this problem can be resolved by using <code>signify</code> to sign the encrypted blobs, and always verifying data before decrypting it.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>Website statistics</title>
    <link href="http://www.nathantypanski.com/blog/2016-12-08-website-statistics.html" />
    <id>http://www.nathantypanski.com/blog/2016-12-08-website-statistics.html</id>
    <published>2016-12-08T00:00:00Z</published>
    <updated>2016-12-08T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> December  8, 2016 <i class="fa fa-cut" December 09, 2016
></i> December 09, 2016

    </div>
    -->
    
</div>
<p>This post is a quick update about my website. I love running my personal site. It is my diary, it is my intellectual backbone, and it has gone largely neglected for most of 2015–2016.
I am working on redirecting attention back to my blog and starting to write publicly again; let us see if I will be successful.</p>
<p>This post is a very quick and rudimentary overview of the current analytics and popularity of my site.</p>
<figure>
<img src="../images/website_2016-12-08_google_analytics.png" alt="My website in 2016." />
<figcaption aria-hidden="true">My website in 2016.</figcaption>
</figure>
<p>I wrote my first post on this blog on June 3, 2012.
<a href="2016-12-03-systems-and-philosophy.html">Just last Saturday</a> I mentioned it, since I’m starting to get back into writing again.
I got somewhere between 1500 and 2000 user sessions a month on my website, and I’ve been running <a href="https://www.google.com/analytics/">Google Analytics</a> on the site for most of that time.</p>
<p>I used to think <a href="2014-08-03-a-vim-like-emacs-config.html">Towards a Vim-like Emacs</a> was the most popular post on my website.
It hit <a href="https://news.ycombinator.com/item?id=8581530">#1 on Hacker News</a> for a brief period of time,<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> and I’ve never been quite sure if I will be able to top that or if it will go down as my magnum opus.</p>
<p>Now my <a href="2014-08-23-ode-solver-py.html">ODE solver</a> turns out to be the most popular, with 10,716 pageviews in 2016, versus 5,457 for the Emacs post.
This is surprising given that the mathjax was broken for some unknown period of time, until I fixed it <a href="https://github.com/nathantypanski/nathantypanski.com/commit/1557c22449cb1c473b4937dedba3595eebcbcf73">5 days ago</a> in a scramble to <a href="https://github.com/nathantypanski/nathantypanski.com/commit/de89bb8de58aacd6690e06b4c87ac14aa0b9c1ee">fix the Haskell that assembles my website</a>.
This is surprising to me, but it’s easy to explain—that is easily my best post that combines mathematics with a generally useful tutorial on Python code.
If I were a scientist working with applied mathematics, that post would certainly be of more interest to me.</p>
<p>Further, now the <a href="http://spacemacs.org/">Spacemacs</a> project has been successful (10,001 stars on GitHub and 2,597 forks), which brings <a href="https://www.emacswiki.org/emacs/Evil">Evil</a> to the general population with considerably less work than following my Emacs post would require.
I’m not sure how much impact my work had in inspiring that project, or whether Spacemacs started first, but they certainly make it easy enough for the everyday person that you don’t need to do <a href="2014-08-03-a-vim-like-emacs-config.html#stealing-ibuffers-keymap">some of the immensely crazy shit I did stealing keymaps on your own</a>.</p>
<p>No big conclusion for this piece. Just a quick update, carrying high <a href="../pages.html#blogging">temporal locality</a>. <a href="http://genius.com/10981254">Onward! and Onward!</a></p>
<figure>
<img src="../images/website_2016-12-08_google_analytics_vimlike.png" alt="When Towards a Vim-like Emacs hit Hacker News, my website spiked to 13,423 hits in a week." />
<figcaption aria-hidden="true">When <a href="2014-08-03-a-vim-like-emacs-config.html">Towards a Vim-like Emacs</a> <a href="https://news.ycombinator.com/item?id=8581530">hit Hacker News</a>, my website spiked to 13,423 hits in a week.</figcaption>
</figure>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>I think I have a screenshot of this floating around somewhere, but I’m not sure where to find it.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>Systems and philosophy</title>
    <link href="http://www.nathantypanski.com/blog/2016-12-03-systems-and-philosophy.html" />
    <id>http://www.nathantypanski.com/blog/2016-12-03-systems-and-philosophy.html</id>
    <published>2016-12-03T00:00:00Z</published>
    <updated>2016-12-03T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> December  3, 2016 <i class="fa fa-cut" December 03, 2016
></i> December 03, 2016

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#free-software-backoff" id="toc-free-software-backoff">Free software backoff</a></li>
<li><a href="#computer-brand" id="toc-computer-brand">Computer brand</a></li>
<li><a href="#programming-languages" id="toc-programming-languages">Programming languages</a></li>
<li><a href="#conclusion" id="toc-conclusion">Conclusion</a></li>
</ul> </div>
<p>When I started computing, I was heavy into customization. I wrote <a href="/blog/2012-06-03-powerusers.html">Powerusers</a>, which was about the intense customizations I had done to personalize my Arch Linux distribution. It felt sort of juvenile to spend that much time <a href="https://fun.irq.dk/funroll-loops.org/">ricing</a> my Linux distro. But it was fun. Linux and open-source were my hobbies; I did them in my free time and studied computer engineering as my main gig.</p>
<p>At the time, I only really managed two Linux systems: my laptop and this webserver. So it was easy to get away with hacking the hell out of my own system, since I knew it well, and refusing to automate tasks like my webserver configuration because I ran it in more of a fire-and-forget style. Put <a href="https://www.nginx.com/resources/wiki/">nginx</a> in a <a href="https://wiki.archlinux.org/index.php/change_root">chroot</a> once and you’re done.</p>
<p>Now I’m getting older, I manage Linux systems professionally, and my philosophies on customizaiton, among many other things, are starting to change. Here’s an example: my zsh config has <a href="https://github.com/nathantypanski/dotfiles/blob/15900ce524a20b117ba950eaf9dbf03d3c263849/zsh/.config/zsh/settings.zsh#L64"><code>setopt vi</code></a> in it. This means that on my local system, I have vi key bindings in my shell. But when I SSH into a remote host, it’s more likely than not that my shell will be Bash. It’s possible that the system maintainer hasn’t even installed zsh on it, or maybe won’t install it. But I can predictably, always, find Bash. Now I’m considering removing the vi bindings, or scrapping zsh entirely and moving back to Bash, just because that’s what I find on every single system that I encounter.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>I can’t context-switch from vi to Emacs-like Bash keybindings very often. It gets quite confusing.</p>
<blockquote>
<ul>
<li>What shell do you use?</li>
<li>zsh.</li>
<li>What? Just use Bash, dude.</li>
<li>Why?</li>
<li>Just use Bash.</li>
</ul>
</blockquote>
<section id="free-software-backoff" class="level2">
<h2>Free software backoff</h2>
<p>What else has changed? Well, for a long time I was a very public advocate of using only <a href="https://www.gnu.org/philosophy/free-sw.en.html">free software</a>, at least to the fullest extent that I could manage. I wrote all my school papers in <a href="https://www.latex-project.org/">LaTeX</a>, which was the subject of some scorn from my professors when I could not submit <code>.doc</code> or <code>.docx</code> files. I still use LaTeX for some of my personal correspondence, but now I’m back to using Google Docs or <a href="https://www.atlassian.com/software/confluence">Confluence</a> for half of my things. It can be handy, sometimes, to have the editor get out of the way, and sometimes the benefit of collaborative work is worth the software philosophical tradeoff.</p>
<p>However, I will add that free software solutions to the problem of collaborative document editing, at least in the case of wikis, exist. You can use <a href="https://github.com/jgm/gitit">Gitit</a>, which gives you a Git-backed wiki with Pandoc for document markup. Sure, it doesn’t have an LDAP connector, but it’s a wonderful system to write in, and something like Markdown is relatively easier to learn than MediaWiki markup.</p>
</section>
<section id="computer-brand" class="level2">
<h2>Computer brand</h2>
<p>Another switch: I use a Macbook. Not for my personal computing, but for my work stuff, ever since around September 2014. Getting used to OSX after spending something like 5 years running almost exclusively Arch Linux was a real change. But nearly every developer who I know in the industry uses OS X<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a>, and sometimes I just have to play by other people’s rules. I still put Arch on half of it, but to use that system I have to compile a <a href="https://aur.archlinux.org/packages/linux-macbook/">custom kernel</a> with Apple-specific patches to work around <a href="https://bugzilla.kernel.org/show_bug.cgi?id=103211">Bug 103211</a>. This takes an annoying amount of effort when I have more important things to be doing than customizing my Linux distro.</p>
<p>Besides: when there’s an emergency, and I happen to be stranded in a coffee shop and need to access the internet immediately, not having to fumble around with <a href="https://wiki.archlinux.org/index.php/netctl">netctl</a> to access WiFi comes in handy. So, in those situations, I use OS X.</p>
</section>
<section id="programming-languages" class="level2">
<h2>Programming languages</h2>
<p>The other thing that has happened is my languages of choice have become a bit less dogmatic. I will probably always write Python, because it is ubiquitous, easy to write and read, and I know it quite well. But I’ve since <a href="/blog/2015-05-17-ruby.html">picked up Ruby</a>, worked on a Rails application, and now I’m a bit less dogmatic about languages in general. I’m no longer out there trying to get everyone to learn Haskell because strong static type systems and functional languages are just <em>so damn cool</em>. I understand when people look at <a href="https://www.rust-lang.org/en-US/">Rust</a> and tell me that high-level abstractions coupled with a serious type system and strong, compile-time guarantees of memory safety are not useful things to put in production or learn about (though I disagree with those people, and they probably enjoy writing Java).</p>
</section>
<section id="conclusion" class="level2">
<h2>Conclusion</h2>
<p>People who knew me in 2014-2015 might be convinced that I’ve sold out. I’ve reneged on my free software idealism, am slowly tending toward anti-customization, bought an iPhone, and am becoming complacent with my gradual absorption into the general tech monoculture. But in some ways, this is OK. I’ve picked up new skills, tried out new things, and I would bet that people now find me a bit easier to work with.</p>
<p>The other bit is that I’ve moved more seriously into the private industry. When you’re just a student and researcher, it’s easy to be dogmatic about Linux and weird programming languages - especially when you’re broke and can’t afford to pay for computer programs. And when your day-to-day activities are math and electronics classes, it’s easy to go home and hack code in Haskell in your free time. But when you’re hacking code all day and working hard with complex Linux systems, sometimes nothing beats just being able to go home at the end of it, chill out, and watch Netflix on a MacBook.</p>
<p>This isn’t some giant announcement that everything I believed in college was wrong and proprietary software has no ethical issues. It’s more of just an acknowledgement that life is fluid, and reality is dynamic and difficult. Sometimes this means making concessions. I am willing to do that, now, just as long as I learn some new things and can get the job done.</p>
</section>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>If you’re really hardcore, you could argue that <a href="https://en.wikipedia.org/wiki/Bourne_shell">Bourne Shell</a> is the most ubiquitous, and therefore I should use that.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>Something I’m not too happy about, but it’s the case.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>First thoughts on Ruby: A quick showdown vs. Python</title>
    <link href="http://www.nathantypanski.com/blog/2015-05-17-ruby.html" />
    <id>http://www.nathantypanski.com/blog/2015-05-17-ruby.html</id>
    <published>2015-05-17T00:00:00Z</published>
    <updated>2015-05-17T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> May 17, 2015 <i class="fa fa-cut" December 03, 2016
></i> December 03, 2016

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#toy-script-comparisons" id="toc-toy-script-comparisons">Toy script comparisons</a>
<ul>
<li><a href="#running-sum" id="toc-running-sum">Running sum</a></li>
<li><a href="#oop" id="toc-oop">OOP</a></li>
<li><a href="#hidden" id="toc-hidden">Hidden</a></li>
</ul></li>
<li><a href="#conclusion" id="toc-conclusion">Conclusion</a></li>
</ul> </div>
<p>Python and Ruby always struck me as somewhat related.
They’re both dynamic languages from the 1990s, still highly popular, and influenced a number of more modern dynamic programming languages like <a href="https://en.wikipedia.org/wiki/Julia_%28programming_language%29">Julia</a>.
They’re similar enough in spirit that one can easily fall into the trap of thinking that if they know one of Python and Ruby, they don’t have to learn the other.
I was primarily a Python programmer for my research work, and Python is popular in academia, so I am definitely guilty of having a closed mind toward Ruby.</p>
<p>Today I made an effort to challenge my assumptions and give Ruby a serious shot.
I finished the <a href="http://rubykoans.com/">Ruby koans</a>, which took me about <a href="https://github.com/nathantypanski/ruby-koans">five hours, and four hours of <q>real work</q></a>.
I’d messed with <a href="http://tryruby.org/levels/1/challenges/0">Try Ruby</a> before that, but that didn’t take me very far, and <a href="https://rubymonk.com/">RubyMonk</a> felt too slow and clunky for my tastes when I tried the primer.</p>
<p>So the natural thing to do for me is to compare Ruby with Python, and see what I like and don’t like about each of them.
I’m not trying to <a href="http://c2.com/cgi/wiki?PythonVsRuby">start a flamewar</a>, it’s more like I’m just trying to translate some of my knowledge and highlight some of the differences that I saw so far.</p>
<section id="toy-script-comparisons" class="level2">
<h2>Toy script comparisons</h2>
<p>I wrote some toy scripts to show the most jarring differences so far.
Yeah, I’m sure I’ll dig further into the language and all of this code will look silly, but as a budding Rubyist this is about as advanced as I can muster.</p>
<section id="running-sum" class="level3">
<h3>Running sum</h3>
<p>In Ruby, the tendency is to iterate over a number of variables with the <code>#each</code> method, and then you pass in a block that gets called on each of the elements.
What’s immediately noticeable here is that Ruby blocks are way more powerful than Python’s anonymous callalbles (<code>lambdas</code>).</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode Ruby"><code class="sourceCode ruby"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="cf">#!/usr/bin/env ruby</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>running_sum <span class="op">=</span> <span class="kw">[]</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>args <span class="op">=</span> <span class="kw">[]</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="kw">[</span><span class="dv">1</span>,<span class="dv">5</span>,<span class="dv">7</span><span class="kw">]</span><span class="at">.each</span> <span class="cf">do</span> <span class="op">|</span>x<span class="op">|</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  args <span class="op">&lt;&lt;</span> x</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> running_sum<span class="at">.size</span> <span class="op">&gt;</span> <span class="dv">0</span> <span class="cf">then</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    running_sum <span class="op">&lt;&lt;</span> running_sum<span class="at">.last</span> <span class="op">+</span> x</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>  <span class="cf">else</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>    running_sum <span class="op">&lt;&lt;</span> x</span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>  <span class="cf">end</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="fu">puts</span> <span class="st">&quot;Running sum: </span><span class="sc">#{</span>running_sum<span class="sc">}</span><span class="st"> from </span><span class="sc">#{</span>args<span class="sc">}</span><span class="st">.&quot;</span></span></code></pre></div>
<div class="sourceCode" id="cb2"><pre class="sourceCode Python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>running_sum <span class="op">=</span> []</span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>args <span class="op">=</span> []</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> n <span class="kw">in</span> [<span class="dv">1</span>, <span class="dv">5</span>, <span class="dv">7</span>]:</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    args.append(args)</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> running_sum:</span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        running_sum.append(running_sum[<span class="op">-</span><span class="dv">1</span>] <span class="op">+</span> n)</span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span>:</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>        running_sum.append(n)</span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Running sum: </span><span class="sc">{}</span><span class="st"> from </span><span class="sc">{}</span><span class="st">.&quot;</span>.<span class="bu">format</span>(running_sum, args))</span></code></pre></div>
<p>At face value, the two aren’t very different, but these examples illustrate a fundamental difference between the attitudes toward iteration: Python uses <code>for</code> loops for everything, but Rubyists will do everything in their power to avoid using them.</p>
</section>
<section id="oop" class="level3">
<h3>OOP</h3>
<p>Ruby classes seem to be both less verbose and syntactically simpler than Python classes.
Both languages have a <q>message passing</q> way of calling instance methods: in Python, with <code>getattr(object, name)</code> and in Ruby via <code>#send()</code>.
Mixins, via <code>modules</code>, are the coolest advantage Ruby seems to have over Python, where the closest equivalent is boring old implementation inheritance.</p>
<p>Both languages have an equivalent for getters and setters.
In Python, this is done via a decorator, but in Ruby it seems to be a language builtin.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode Ruby"><code class="sourceCode ruby"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="cf">#!/usr/bin/env ruby</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="cf">module</span> <span class="dt">Nameable</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">def</span> name<span class="op">=</span>(name)</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">puts</span> <span class="st">&quot;changed name to </span><span class="sc">#{</span>name<span class="sc">}</span><span class="st">&quot;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="ot">@name</span> <span class="op">=</span> name</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="cf">end</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="cf">def</span> name</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>    <span class="ot">@name</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  <span class="cf">end</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="cf">class</span> <span class="dt">Dog</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>  <span class="fu">include</span> <span class="dt">Nameable</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>  <span class="cf">def</span> initialize(name)</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>    <span class="ot">@name</span> <span class="op">=</span> name</span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a>  <span class="cf">end</span></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a>  <span class="cf">def</span> bark</span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a>    <span class="fu">puts</span> <span class="vs">&#39;Woof!&#39;</span></span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a>  <span class="cf">end</span></span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a>fido <span class="op">=</span> <span class="dt">Dog</span><span class="at">.new</span>(<span class="vs">&#39;Fido&#39;</span>)</span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a>fido<span class="at">.send</span>(<span class="vs">&#39;bark&#39;</span>)</span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a>fido<span class="at">.name</span> <span class="op">=</span> <span class="vs">&#39;Cheese&#39;</span></span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a><span class="fu">puts</span> fido<span class="at">.name</span></span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode Python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Nameable(<span class="bu">object</span>):</span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="at">@property</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> name(<span class="va">self</span>):</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="va">self</span>._name</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="at">@name.setter</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> name(<span class="va">self</span>, value):</span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&#39;Changed name to </span><span class="sc">{}</span><span class="st">&#39;</span>.<span class="bu">format</span>(value))</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>._name <span class="op">=</span> value</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="kw">class</span> Dog(Nameable):</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> <span class="fu">__init__</span>(<span class="va">self</span>, name):</span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>        <span class="va">self</span>._name <span class="op">=</span> name</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>    <span class="kw">def</span> bark(<span class="va">self</span>):</span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>        <span class="bu">print</span>(<span class="st">&quot;Woof!&quot;</span>)</span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a>fido <span class="op">=</span> Dog(<span class="st">&#39;Fido&#39;</span>)</span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a>fido.bark()</span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a>fido.name <span class="op">=</span> <span class="st">&#39;Cheese&#39;</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(fido.name)</span></code></pre></div>
</section>
<section id="hidden" class="level3">
<h3>Hidden</h3>
<p><em>Finally, a serious scripting task!</em></p>
<p>I’ve been playing with <a href="https://github.com/venam/2bwm">2bwm</a> as my window manager lately.
One of its major defects is that it lacks an easy way to list all hidden windows and show them.
It comes with a tool, <code>hidden</code>, that will list hidden windows and print <code>xdotool</code> output for raising them, but the output looks like this:</p>
<pre class="sourceCode"><code>&#39;hidden.rb&#39;:&#39;xdotool windowactivate 0xc003ec windowraise 0xc003ec&#39;
&#39;[nathan@dionysus][~/dotfiles]%&#39;:&#39;xdotool windowactivate 0xc0085c windowraise 0xc0085c&#39;</code></pre>
<p>Which obviously isn’t sufficient for easily raising the windows.
A potential fix to this is building a wrapper for <a href="http://tools.suckless.org/dmenu/">dmenu</a> that lets you easily raise hidden windows.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode Ruby"><code class="sourceCode ruby"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="cf">#!/usr/bin/env ruby</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>hidden_windows <span class="op">=</span> <span class="in">`hidden -c`</span><span class="at">.split</span>(<span class="ss">/</span><span class="sc">\n</span><span class="ss">/</span>)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>map <span class="op">=</span> <span class="op">{}</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>hidden_windows<span class="at">.each</span> <span class="cf">do</span> <span class="op">|</span>line<span class="op">|</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    match <span class="op">=</span> <span class="ss">/&#39;(.*)&#39;:&#39;(.*)&#39;/</span><span class="at">.match</span>(line)</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    key <span class="op">=</span> match<span class="kw">[</span><span class="dv">1</span><span class="kw">]</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    val <span class="op">=</span> match<span class="kw">[</span><span class="dv">2</span><span class="kw">]</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> map<span class="at">.has_key?</span> key</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>        key <span class="op">=</span> key <span class="op">+</span> <span class="ch">&#39;*&#39;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">end</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>    map<span class="kw">[</span>key<span class="kw">]</span> <span class="op">=</span> val</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>choice <span class="op">=</span> <span class="dv">nil</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a><span class="cn">IO</span><span class="at">.popen</span>(<span class="vs">&#39;dmenu&#39;</span>, <span class="vs">&#39;r+&#39;</span>) <span class="cf">do</span> <span class="op">|</span>dmenu<span class="op">|</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>    map<span class="at">.keys.each</span> <span class="cf">do</span> <span class="op">|</span>name<span class="op">|</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>        dmenu<span class="at">.write</span>(name)</span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a>        dmenu<span class="at">.write</span>(<span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span>)</span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a>    <span class="cf">end</span></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a>    dmenu<span class="at">.close_write</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>    choice <span class="op">=</span> dmenu<span class="at">.gets.rstrip</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a><span class="cf">end</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a><span class="cn">IO</span><span class="at">.popen</span>(map<span class="kw">[</span>choice<span class="kw">]</span>)</span></code></pre></div>
<p>In Python:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode Python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> subprocess</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> re</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> main():</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    hidden_windows <span class="op">=</span> subprocess.check_output([<span class="st">&#39;hidden&#39;</span>, <span class="st">&#39;-c&#39;</span>])</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>    hidden_windows <span class="op">=</span> hidden_windows.splitlines()</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>    hidden_map <span class="op">=</span> {}</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>    search <span class="op">=</span> re.<span class="bu">compile</span>(<span class="st">&quot;^&#39;(.*)&#39;:&#39;(.*)&#39;$&quot;</span>)</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> window <span class="kw">in</span> hidden_windows:</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>        window <span class="op">=</span> window.decode(<span class="st">&#39;utf-8&#39;</span>)</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>        match <span class="op">=</span> search.match(<span class="bu">str</span>(window))</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>        name, tool <span class="op">=</span> match.group(<span class="dv">1</span>), match.group(<span class="dv">2</span>)</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>        <span class="cf">while</span> name <span class="kw">in</span> hidden_map:</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>            name <span class="op">+=</span> <span class="st">&#39;*&#39;</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>        hidden_map[name] <span class="op">=</span> tool</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>    dmenu <span class="op">=</span> subprocess.check_output([<span class="st">&#39;dmenu&#39;</span>],</span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>            <span class="bu">input</span><span class="op">=</span><span class="bu">bytes</span>(<span class="st">&#39;</span><span class="ch">\n</span><span class="st">&#39;</span>.join(hidden_map.keys()), encoding<span class="op">=</span><span class="st">&#39;utf-8&#39;</span>))</span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>    subprocess.call(hidden_map[dmenu.rstrip().decode(<span class="st">&#39;utf-8&#39;</span>)], shell<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&#39;__main__&#39;</span>:</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a>    main()</span></code></pre></div>
<p>The advantages Ruby has here are its simpler means of spawning and interacting with subprocesses, and the far prettier Regex syntax, both of which are inspired by Perl.
Python, by contrast, has a clunkier regex engine that requires an import, a call to <code>re.compile</code>, and another call to <code>regex.match(...)</code> in order to use it.</p>
<p>The other thing you notice here is the <code>bytes</code> handling. Subprocess output returns <code>bytes</code> objects in Python, and they have to be decoded into <code>utf-8</code> before I can do much with them.</p>
<p>This results in a far more verbose Python solution.</p>
<p>It’s also worth mentioning that the subprocess interface in Ruby is handled through an RAII-style block. In Python’s case, it looks like <a href="https://bugs.python.org/issue10554"><code>Popen</code> objects can be used in <code>with</code> statements</a>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode Python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="cf">with</span> Popen([<span class="st">&quot;ifconfig&quot;</span>], stdout<span class="op">=</span>PIPE) <span class="im">as</span> proc:</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    log.write(proc.stdout.read())</span></code></pre></div>
<p>but that leaves the clunky <code>Popen</code> interface to worry about. Personally I can never remember what <code>stdout=PIPE</code> actually does, or which of those given modes I should be using for my process.
In Ruby, I get to use <a href="http://man7.org/linux/man-pages/man3/fopen.3.html"><code>fopen(3)</code> arguments</a> to describe how I’d like to handle fds on the subprocess. That’s an interface I’m already familiar with, and it’s intutitive.</p>
</section>
</section>
<section id="conclusion" class="level2">
<h2>Conclusion</h2>
<p>My initial impression of Ruby is quite positive.
It’s a strong candidate for replacing my <q>one off Python scripts</q> that often felt a bit too verbose for the tasks they were achieving.
While I’m not comfortable enough to do serious development in it yet, I do get the feeling that Ruby will be quite readable over the equivalent Bash/Awk/Sed mashup that I might otherwise use to avoid writing a wordy Python script that uses <code>subprocess</code>.</p>
<p>There’s a distinct impression that Ruby is more <q>fun</q> than Python.
There are many ways to achieve certain tasks, and the general goal is to find the most beautiful way.
In Python, it seems like there’s a heavier focus on readability.
I was skimming through some of the Rails codebase, and Rubyists seem to have no qualms about using the obscure <code>||=</code> operator, in spite of the fact that it <a href="https://stackoverflow.com/questions/995593/what-does-or-equals-mean-in-ruby">really doesn’t mean what you think it means</a>.
I think the most obscure syntactic construct in Python is just the <a href="https://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator">wacky ternary operator</a>.</p>
<p>I do not believe that Python and Ruby are really playing in the same niche. While Python dominates in academia and scientific programming, Ruby seems to win in the Perl-ish systems scripting and web development areas (though <a href="https://www.djangoproject.com/">Django</a> and <a href="http://www.tornadoweb.org/en/stable/">Tornado</a> put out some serious competition with <a href="http://rubyonrails.org/">Rails</a> and <a href="http://www.sinatrarb.com/">Sinatra</a>, and Python’s <a href="http://blog.getpelican.com/">Pelican</a> looks like a fine alternative to <a href="http://jekyllrb.com/">Jekyll</a>).</p>
</section>
]]></summary>
</entry>
<entry>
    <title>Heads down for the finish line</title>
    <link href="http://www.nathantypanski.com/blog/2015-04-15-heads-down-for-the-finish-line.html" />
    <id>http://www.nathantypanski.com/blog/2015-04-15-heads-down-for-the-finish-line.html</id>
    <published>2015-04-15T00:00:00Z</published>
    <updated>2015-04-15T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> April 15, 2015 <i class="fa fa-cut" April 15, 2015
></i> April 15, 2015

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#red-hat" id="toc-red-hat">Red Hat</a></li>
<li><a href="#pcse-coding-challenge" id="toc-pcse-coding-challenge">PCSE Coding Challenge</a></li>
<li><a href="#mike-bland-cnu-research-google-and-the-us-government" id="toc-mike-bland-cnu-research-google-and-the-us-government">Mike Bland, CNU research, Google, and the US Government</a></li>
<li><a href="#microcontrollers-are-fun" id="toc-microcontrollers-are-fun">Microcontrollers are fun</a></li>
<li><a href="#leaving-college-joining-palantir" id="toc-leaving-college-joining-palantir">Leaving college, joining Palantir</a></li>
</ul> </div>
<p>I haven’t blogged since January, and I really wish I had a better reason for it but the truth is I simply have not applied my characteristic productive laziness to writing.</p>
<p>I’ve been reading a lot of jwz lately.
I think I read <a href="http://www.jwz.org/gruntle/nscpdorm.html">the netscape dorm</a> three times in the last few weeks.
That was a bad sign, and I should have known better, but I didn’t quite pick up on it.</p>
<p>So let’s do a breakdown of the past few months; maybe somehow I can justify the silence.</p>
<section id="red-hat" class="level2">
<h2>Red Hat</h2>
<p><a href="https://fedoraproject.org/wiki/User:Spot">Tom Callaway</a> from Red Hat <a href="http://blog.pcs.cnu.edu/?p=254">came to speak on March 24</a> for CNU’s PCSE<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> department and the <a href="http://cnulug.org/">LUG I founded</a>.
That was awesome.
I don’t think anything could validate a newly-founded Linux user group more than having industry leaders come joke with you about tiling window managers. Quoting Tom:</p>
<blockquote>
<p>The venn diagram between people who use tiling window managers and people who know Haskell is just a circle.</p>
</blockquote>
<p>In complete good spirit, it’s worth mentioning <a href="https://i3wm.org/">i3</a> was written largely <em>because</em> the developers thought XMonad was cool, but didn’t want to learn Haskell.</p>
<p>After the presentation I was told by one of the LUG members that Tom’s talk was really inspiring for them.
The student said <q>It’s really great seeing industry leaders and finding out they’re just like us - only maybe a bit older or more knowledgeable - and feeling like I can be just like that, too, if I just work hard and dedicate myself.</q></p>
<p>It’s that kind of thing that makes me so glad I stuck with CNULUG.
Even through all the lows, the awful turnout for the first year or so,
we’ve pulled through - and now we matter so much that we’re making a real difference
in students’ lives.
Those sort of results, to me, almost makes my whole four years at CNU worth it on their own.</p>
</section>
<section id="pcse-coding-challenge" class="level2">
<h2>PCSE Coding Challenge</h2>
<p>A team I was on (with <a href="https://www.linkedin.com/pub/wilson-ho/82/33/b23">Wilson Ho</a> and <a href="https://www.linkedin.com/pub/ian-miller/b1/ba9/513">Ian Miller</a>) won my university’s <q>PCSE Coding Challenge</q> on April 3.
That was fun.
I totally embarrassed myself during the big-wig ACM programming competition last semester, so it’s relieving to see some sort of quantifiable progress.</p>
<p>Something struck me about the <a href="/files/2015Spring-PCSE-coding-challenge-Koehl.pdf">problems</a>, though.
We had three language options: Java, C (<code>gcc</code> with default flags), and C++ (<code>g++</code> with default flags).
At least two of these would be more well-suited to scripting languages, though.</p>
<p>Problem 4 is simple text substitution.
The input two lines forming a substitution table (preceded by a less-than-helpful count), followed by a list of plaintext/cyphertext blocks:</p>
<pre class="sourceCode"><code>3
a b c
B C D
2
PT: abc
CT: BCDDCB</code></pre>
<p>You’re supposed to encrypt the plaintext blocks, and decrypt the cyphertext blocks.
Normally I’d solve this in Bash:<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></p>
<div class="sourceCode" id="cb4"><pre class="sourceCode Bash"><code class="sourceCode bash"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/bin/bash</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="bu">read</span> <span class="at">-r</span> <span class="va">cypher_count</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">[[</span> <span class="va">${cypher_count}</span> <span class="ot">-eq</span> 0 <span class="kw">]]</span> <span class="kw">&amp;&amp;</span> <span class="cf">break</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>    <span class="bu">read</span> <span class="at">-r</span> <span class="va">plaintext</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="bu">read</span> <span class="at">-r</span> <span class="va">cyphertext</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>    <span class="bu">read</span> <span class="at">-r</span> <span class="va">count</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> _ <span class="kw">in</span> <span class="va">$(</span><span class="fu">seq</span> <span class="va">${count})</span><span class="kw">;</span> <span class="cf">do</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>        <span class="bu">read</span> <span class="at">-r</span> <span class="va">line</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="kw">[[</span> <span class="va">${line</span><span class="op">:</span><span class="dv">0</span><span class="op">:</span><span class="dv">2</span><span class="va">}</span> <span class="ot">==</span> <span class="st">&#39;PT&#39;</span> <span class="kw">]];</span> <span class="cf">then</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>            <span class="bu">echo</span> <span class="at">-n</span> <span class="st">&quot;CT: &quot;</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>            <span class="bu">echo</span> <span class="va">${line</span><span class="op">:</span><span class="dv">4</span><span class="va">}</span> <span class="kw">|</span> <span class="fu">tr</span> <span class="st">&quot;</span><span class="va">$plaintext</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$cyphertext</span><span class="st">&quot;</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>        <span class="cf">else</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>            <span class="bu">echo</span> <span class="at">-n</span> <span class="st">&quot;PT: &quot;</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>            <span class="bu">echo</span> <span class="va">${line</span><span class="op">:</span><span class="dv">4</span><span class="va">}</span> <span class="kw">|</span> <span class="fu">tr</span> <span class="st">&quot;</span><span class="va">$cyphertext</span><span class="st">&quot;</span> <span class="st">&quot;</span><span class="va">$plaintext</span><span class="st">&quot;</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>        <span class="cf">fi</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>    <span class="cf">done</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a><span class="cf">done</span></span></code></pre></div>
<p>Problems 1 and 5 were semi-interesting graph problems, but why bother with those when there are nice and easy text substitution problems to play with?</p>
<p>Anyway, I’m actually pretty embarrassed by the win.
My team solved the above text translation problem and the Sudoku problem (problem 3).
Neither of those are particularly difficult, or require any sort of serious algorithms knowledge to solve. They’re more like run-of-the-mill programming challenges.
We were about 90% of the way through problem 2, another simple text processing/sorting problem, when the timer rang.</p>
<p>Main lesson learned: brush up on I/O in whatever language I’m choosing before the next algorithms competition.
We could have performed a <em>lot</em> better if we spent less time fumbling around with a <code>BufferedReader</code> and more time thinking about algorithms.</p>
</section>
<section id="mike-bland-cnu-research-google-and-the-us-government" class="level2">
<h2>Mike Bland, CNU research, Google, and the US Government</h2>
<p><a href="http://mike-bland.com/">Mike Bland</a> keynoted <a href="http://cnu.edu/research/about/paideia.asp">Paideia</a>, CNU’s annual <q>research conference</q> on April 11.
He gave a primer of his talk, itself a sort of toned-down variation of his slideshow behemoth
<a href="https://18f.gsa.gov/2014/12/11/large-scale-development-culture-change/">Large Scale Development Culture Change: Google and the US Government</a>
to the PCSE department on Friday, April 10.
Apparently no one told him that the April 10 event would be a <q>talk</q>, and not a Q&amp;A, so Mike just ran through the same slides he’d be covering at Paideia.</p>
<p>Mike really did a great job relating to the audience at the keynote.
Even though he covered somewhat technical material, I think everyone who was there (mostly non-programmers) managed to relate to his talk and took him seriously.
He made <a href="https://18f.gsa.gov/">18F</a> seem like <em>the real deal</em>,
where Google was just like the training grounds for work that will have real,
lasting impact on the United States.</p>
<p>One unfortunate consequence of Friday’s talk was that PCSE folks were notably absent on Saturday.
In some sense that’s OK because they already saw most of it on Friday, but in another sense it’s not since Mike was quite obviously more well-prepared on Saturday.</p>
<p>Mike is locally famous for organizing student demonstrations to <q>save</q>, from bureaucratic shutdown, CNU’s
<a href="http://cnu.edu/academics/graduate/">graduate program</a>,
which basically makes him the reason I can write <q>Christopher Newport University</q> on my resumé instead of <q>Christopher Newport College</q>.
But he was actually <em>confused</em> why he was relevant when
<a href="http://www.pcs.cnu.edu/~David.Gore/">Dr. Gore</a> asked him to keynote CNU’s undergraduate/graduate research conference. Go figure.</p>
<p>One thing I noticed is that he put a lot of effort into engaging the audience during
his presentations.
Sometimes this was successful, and sometimes this wasn’t.
The difference I noticed between successively captivating a live audience and <em>not</em> doing so is when he asked people in the audience to express themselves.
I can recall this same dynamic from when I gave my <a href="http://nathantypanski.github.io/git-talk/">talk advocating Git</a> at NASA Langley: people were engaged when it seemed like I valued their opinions on things.
Asking questions like <q>How many have you have used version control? How many of you <em>liked</em> using it?</q> engaged viewers way more than my 15-minute usage demo.</p>
<p>Similarly, when an audience members got to ask questions and be part of a conversation
instead of just recipients of a speech, they seemed more alive and interested themselves,
and the presentation seemed to have bigger impact.</p>
<p>This kind of thing reminds me not to focus too hard on slides when I do a presentation.
What I should be doing is motivating the audience, and then letting them tell me what they’re interested in - not the other way around.
Unless I have something truly original to say, people are probably going to be more interested in how what I’m saying applies to <em>them</em> than whatever it is that I’m saying.
Maybe that’s a sad reality, and people should care more about the generalized intellectual pursuit than individual relevance, but feeling that way isn’t going to make people listen to you.</p>
<p>Making people relate to you will make them listen. Inspiring the hell out of people will make them listen. You don’t need to win everywhere to captivate an audience, but you better damn well captivate them or you’re just wasting a bunch of people’s time.</p>
<p><a href="2014-08-22-openssl-makefiles.html">Previously</a>.</p>
</section>
<section id="microcontrollers-are-fun" class="level2">
<h2>Microcontrollers are fun</h2>
<p>I’m in a microprocessors class this semester, and it’s awesome.
It’s definitely among the hardest classes I’ve taken at CNU, but also one of the most rewarding.
Among most programmers I meet, both hardware programming and C are seen as some crazy sort of <q>black magic</q>.
I understand we all pine for the good ol’ days when C was taught to every budding programmer instead of <a href="http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html">Java</a>,
when Linus Torvalds’ <a href="http://grisha.org/blog/2013/04/02/linus-on-understanding-pointers/">comments on linked lists</a>
actually made sense to people with CS degrees,
but modern CS departments apparently don’t throw students into the deep end with nothing but a C compiler and an assembly-language manual anymore.</p>
<p>Yeah, most people in the class weren’t ready for that.
They freaked out at first.
But, you know what?
The students learned the material.
Myself included.
<a href="http://cnu.edu/pcs/faculty_staff/wang.asp">Dr. Wang</a> didn’t spoonfeed us; he just gave us hardware manuals and told us <q>when you get out in the industry, you’re not gonna have textbooks</q> and let everyone sink or swim.</p>
<p>I spent this evening solving example problems from an exercise booklet on a <a href="https://www.microchip.com/wwwproducts/Devices.aspx?product=PIC16F877A">PIC16F877A microcontroller</a>.
It was fun.
I feel like I haven’t had fun programming in a while, but when you sit down and program hardware in C it can really reinvigorate love of the craft.
The work wasn’t very interesting: make different LEDs light up depending on the voltage reading from a dial.
Set a cutoff threshold whenever a button is pressed and light different LEDs depending on whether you’re above or below that threshold.
Simple things, but the experience is rewarding.
It reminds me of being in high school, writing my first Java classes to simulate the Monty Hall problem or whatever.
There’s a sense of joy and wonder that programming can produce, and it’s when I find it that I do my best work.</p>
</section>
<section id="leaving-college-joining-palantir" class="level1">
<h1>Leaving college, joining Palantir</h1>
<p>I graduate in May, and I’m absolutely terrified.
I took an internship with Palantir this summer, on their Mission Operations team, instead of taking the <q>safe</q> route taking a full-time offer (like I might have gotten with <a href="https://www.ssaihq.com/">SSAI</a>, who have given me arguably the most rewarding work experience of my lifetime applying bleeding-edge industry practices to the US government).
As apprehensive as this leaves me, I’m really excited about joining Palantir.</p>
<p>Why? I really can’t overstate the influence of the mission ops team lead in inspiring me to do this.
Paraphrasing, this guy told me upfront:</p>
<blockquote>
<p>You seem good, but you’re kind of green.
There are specific things about Linux that you didn’t know - out of genuine ignorance - and
our goal is gonna be to teach you those things, so you’re capable enough
to do your day-to-day tasks with us and not need support from anyone else.
You’re not on the level we need <em>quite</em> yet, and that’s why we’re offering
you an internship, but my goal is going to be to <em>get</em> you there and <em>make</em>
you that good so you can succeed with us.</p>
</blockquote>
<p>Now, to understand why that kind of talk inspires me, you’ve gotta understand something about
who I am.
I’m naturally a bit arrogant.
I founded the Linux user group at my school, and I built my first Arch system seven years ago
with nothing but a paper printout of the <a href="https://wiki.archlinux.org/index.php/Beginners%27_guide">beginner’s guide</a> and a single desktop computer.
I’ve been administering my own webservers for three years, and running other people’s
Linux servers for money for two.
At least among my peers at CNU, I’m simply <em>not used</em> to people knowing more than me about this operating system.</p>
<p>But the people at Palantir are better than me.
They showed me up in the interview process, but it’s not just that
- being smart isn’t the only requirement to win my respect - they’re willing to teach me.
And that kind of attitude is what makes me fall in love with a company.</p>
<p>I’ll quote <a href="http://dcurt.is/the-compounding-returns-of-intelligence">one of their cofounders, Stephen Cohen</a>, to drive this point home:</p>
<blockquote>
<p>We tend to massively underestimate the compounding returns of intelligence. As humans, we need to solve big problems. If you graduate Stanford at 22 and Google recruits you, you’ll work a 9-to-5. It’s probably more like an 11-to-3 in terms of hard work. They’ll pay well. It’s relaxing. But what they are actually doing is paying you to accept a much lower intellectual growth rate. When you recognize that intelligence is compounding, the cost of that missing long-term compounding is enormous. They’re not giving you the best opportunity of your life. Then a scary thing can happen: You might realize one day that you’ve lost your competitive edge. You won’t be the best anymore. You won’t be able to fall in love with new stuff. Things are cushy where you are. You get complacent and stall. So, run your prospective engineering hires through that narrative. Then show them the alternative: working at your startup.</p>
</blockquote>
</section>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Physics, computer science, and engineering.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>To be fair, the C solution isn’t much worse:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode C"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdio.h&gt;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;string.h&gt;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="pp">#include </span><span class="im">&lt;stdlib.h&gt;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="pp">#define MAX_LINE </span><span class="dv">512</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>        <span class="dt">char</span> <span class="op">*</span>line <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="dt">char</span><span class="op">)</span> <span class="op">*</span> MAX_LINE<span class="op">);</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>        <span class="dt">int</span> count <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">do</span> <span class="op">{</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>                scanf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> <span class="op">&amp;</span>count<span class="op">);</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>                <span class="dt">char</span> <span class="op">*</span>pt <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="dt">char</span><span class="op">)</span> <span class="op">*</span> count <span class="op">+</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>                <span class="dt">char</span> <span class="op">*</span>ct <span class="op">=</span> malloc<span class="op">(</span><span class="kw">sizeof</span><span class="op">(</span><span class="dt">char</span><span class="op">)</span> <span class="op">*</span> count <span class="op">+</span> <span class="dv">1</span><span class="op">);</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>                <span class="dt">int</span> i<span class="op">;</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>                <span class="cf">for</span><span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> count<span class="op">;</span> i<span class="op">++)</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>                        scanf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%c</span><span class="st"> &quot;</span><span class="op">,</span> pt <span class="op">+</span> i<span class="op">);</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>                <span class="cf">for</span><span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> count<span class="op">;</span> i<span class="op">++)</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>                        scanf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%c</span><span class="st"> &quot;</span><span class="op">,</span> ct <span class="op">+</span> i<span class="op">);</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>                scanf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%d</span><span class="st"> &quot;</span><span class="op">,</span> <span class="op">&amp;</span>count<span class="op">);</span></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>                <span class="cf">for</span><span class="op">(</span>i <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> i <span class="op">&lt;</span> count<span class="op">;</span> i<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a>                        <span class="dt">char</span> <span class="op">*</span>left<span class="op">;</span></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>                        <span class="dt">char</span> <span class="op">*</span>right<span class="op">;</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>                        fgets<span class="op">(</span>line<span class="op">,</span> MAX_LINE<span class="op">,</span> stdin<span class="op">);</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">if</span> <span class="op">(</span>line<span class="op">[</span><span class="dv">0</span><span class="op">]</span> <span class="op">==</span> <span class="ch">&#39;P&#39;</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a>                                left <span class="op">=</span> pt<span class="op">;</span></span>
<span id="cb2-29"><a href="#cb2-29" aria-hidden="true" tabindex="-1"></a>                                right <span class="op">=</span> ct<span class="op">;</span></span>
<span id="cb2-30"><a href="#cb2-30" aria-hidden="true" tabindex="-1"></a>                                printf<span class="op">(</span><span class="st">&quot;CT: &quot;</span><span class="op">);</span></span>
<span id="cb2-31"><a href="#cb2-31" aria-hidden="true" tabindex="-1"></a>                        <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb2-32"><a href="#cb2-32" aria-hidden="true" tabindex="-1"></a>                                left <span class="op">=</span> ct<span class="op">;</span></span>
<span id="cb2-33"><a href="#cb2-33" aria-hidden="true" tabindex="-1"></a>                                right <span class="op">=</span> pt<span class="op">;</span></span>
<span id="cb2-34"><a href="#cb2-34" aria-hidden="true" tabindex="-1"></a>                                printf<span class="op">(</span><span class="st">&quot;PT: &quot;</span><span class="op">);</span></span>
<span id="cb2-35"><a href="#cb2-35" aria-hidden="true" tabindex="-1"></a>                        <span class="op">}</span></span>
<span id="cb2-36"><a href="#cb2-36" aria-hidden="true" tabindex="-1"></a>                        <span class="dt">int</span> j<span class="op">;</span></span>
<span id="cb2-37"><a href="#cb2-37" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">for</span><span class="op">(</span>j <span class="op">=</span> <span class="dv">4</span><span class="op">;</span> line<span class="op">[</span>j<span class="op">]</span> <span class="op">!=</span> <span class="ch">&#39;</span><span class="sc">\n</span><span class="ch">&#39;</span><span class="op">;</span> j<span class="op">++)</span> <span class="op">{</span></span>
<span id="cb2-38"><a href="#cb2-38" aria-hidden="true" tabindex="-1"></a>                                <span class="dt">int</span> k<span class="op">;</span></span>
<span id="cb2-39"><a href="#cb2-39" aria-hidden="true" tabindex="-1"></a>                                <span class="cf">for</span><span class="op">(</span>k <span class="op">=</span> <span class="dv">0</span><span class="op">;</span> left<span class="op">[</span>k<span class="op">]</span> <span class="op">!=</span> line<span class="op">[</span>j<span class="op">];</span> k<span class="op">++);</span></span>
<span id="cb2-40"><a href="#cb2-40" aria-hidden="true" tabindex="-1"></a>                                printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">%c</span><span class="st">&quot;</span><span class="op">,</span> right<span class="op">[</span>k<span class="op">]);</span></span>
<span id="cb2-41"><a href="#cb2-41" aria-hidden="true" tabindex="-1"></a>                        <span class="op">}</span></span>
<span id="cb2-42"><a href="#cb2-42" aria-hidden="true" tabindex="-1"></a>                        printf<span class="op">(</span><span class="st">&quot;</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb2-43"><a href="#cb2-43" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb2-44"><a href="#cb2-44" aria-hidden="true" tabindex="-1"></a>                free<span class="op">(</span>pt<span class="op">);</span></span>
<span id="cb2-45"><a href="#cb2-45" aria-hidden="true" tabindex="-1"></a>                free<span class="op">(</span>ct<span class="op">);</span></span>
<span id="cb2-46"><a href="#cb2-46" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span> <span class="cf">while</span> <span class="op">(</span>count <span class="op">!=</span> <span class="dv">0</span><span class="op">);</span></span>
<span id="cb2-47"><a href="#cb2-47" aria-hidden="true" tabindex="-1"></a>        free<span class="op">(</span>line<span class="op">);</span></span>
<span id="cb2-48"><a href="#cb2-48" aria-hidden="true" tabindex="-1"></a>        <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-49"><a href="#cb2-49" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>But there’s a bug in the official test data, and that code won’t work unless
the following is fixed:</p>
<pre class="sourceCode"><code>-5
+7
 d e h l o r w
 H J L O Q T V</code></pre>
<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>You don't need a great idea</title>
    <link href="http://www.nathantypanski.com/blog/2015-01-17-great-implementation.html" />
    <id>http://www.nathantypanski.com/blog/2015-01-17-great-implementation.html</id>
    <published>2015-01-17T00:00:00Z</published>
    <updated>2015-01-17T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> January 17, 2015 <i class="fa fa-cut" March 22, 2015
></i> March 22, 2015

    </div>
    -->
    
</div>
<p>This world has enough great ideas. You need a great implementation.</p>
<p>A tremendous amount of my more introspective/thoughtful brainpower is spent considering great ideas. I think about the ones that are successful; I try to come up with ideas of my own; I repeatedly fail at the latter.</p>
<p>One of my goals as a software developer is to someday design and implement a reasonably useful and well-used open-source API. It doesn’t even matter what for. I want to leave my mark by building something useful that other people can build useful things on top of. Maybe that goal is somewhat daft, but it motivates me and keeps me working to improve my abilities. The skills involved are not something I’m very good at, in part since I’m still in college and short of experience on the matter.<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<p>One of the factors that holds me back is the feeling that I don’t have any great new ideas. Why bother using some API I’ve written, when there are dozens (or even perhaps one or two) out there that do the job already? I can’t supply anything new on the ideas front, so where’s the room for my work?</p>
<p>Let’s draw some parallels to real-life products. Google didn’t invent search; they simply did it better than anyone else. <a href="http://www.soylent.me/">Soylent</a> didn’t invent liquid food, they just made it more nutritious than <a href="https://en.wikipedia.org/wiki/Feeding_tube">the crap they feed you in hospitals</a>. <a href="https://pypi.python.org/pypi/blessings/">Blessings</a> was far from the first terminal GUI library, but it made things significantly easier to use.</p>
<p>Usually, when I’m working with a sub-par piece of software, API, or <q>real-world</q> product, I’m not bothered by the concept of it. I usually like the concept. It’s the implementations that <a href="http://www.paulgraham.com/organic.html">are broken</a>, and that’s where you find room for improvement.</p>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>This isn’t my <em>only</em> goal, or even necessarily one of my <em>main</em> ones, but it’s something I feel will be a milestone in my career as a dev. It’s an important stepping-stone in the transitional period from <q>someone who writes code</q> to <q>someone who builds solutions.</q><a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>Git: rebasing apart history</title>
    <link href="http://www.nathantypanski.com/blog/2014-12-13-git-rebase-away-files.html" />
    <id>http://www.nathantypanski.com/blog/2014-12-13-git-rebase-away-files.html</id>
    <published>2014-12-13T00:00:00Z</published>
    <updated>2014-12-13T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> December 13, 2014 <i class="fa fa-cut" December 03, 2016
></i> December 03, 2016

    </div>
    -->
    
</div>
<div id="toc"><h2>Contents</h2>
<ul>
<li><a href="#picking-a-rebase-target" id="toc-picking-a-rebase-target">Picking a rebase target</a></li>
<li><a href="#cleaning-your-worktree" id="toc-cleaning-your-worktree">Cleaning your worktree</a></li>
<li><a href="#blasting-apart-history" id="toc-blasting-apart-history">Blasting apart history</a>
<ul>
<li><a href="#removing-unrelated-files" id="toc-removing-unrelated-files">Removing unrelated files</a></li>
<li><a href="#making-new-commits" id="toc-making-new-commits">Making new commits</a></li>
</ul></li>
<li><a href="#conclusion" id="toc-conclusion">Conclusion</a></li>
</ul> </div>
<p>I always appreciate it when people share some of the quirks of their development workflow,
particularly with Git, since the version control monster is so powerful that it can be
difficult to stumble your way through its features.</p>
<p>Here’s something I do every so often: I’m working too quickly, and I add extra
unrelated files to an otherwise granular commit. Then I have to fix it, but I
usually don’t want to rewrite the commit message. What’s the solution?</p>
<p>Here’s an example from my Emacs config:</p>
<pre class="sourceCode"><code>Refs: [master]

    eyecandy: just use zenburn theme

---
 .travis.yml                    | 11 +++++++++++
 config/eyecandy/my-eyecandy.el | 22 +++-------------------
 2 files changed, 14 insertions(+), 19 deletions(-)</code></pre>
<p>I was partway through an attempt at adding simple <a href="https://travis-ci.org/">travis</a> testing
to the repo, but accidentally included that <code>.travis.yml</code> along with a batch of
theme config changes.</p>
<p>Obviously my personal text editor config files can’t get away with <a href="https://github.com/nathantypanski/emacs.d/commit/3597bfd5d1c1c561f019f7fce7ad6119b5edb07e">bad commits</a>!<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a> I’ll have to fix that.</p>
<p>There are a handful of ways I could do this, but the method I inevitably choose
is <a href="http://git-scm.com/docs/git-rebase"><code>git rebase</code></a>. Called with the <code>--interactive</code>
switch, <code>git rebase</code> gives you a list of commit messages since some point in the
tree, and lets you modify and rewrite them as you see fit.</p>
<p>What we’re going to do here is this:</p>
<ul>
<li>start an interactive rebase,</li>
<li>remove <code>.travis.yml</code> from the commit shown above,</li>
<li>amend the old commit,</li>
<li>create a new commit adding <code>.travis.yml</code> to the repository.</li>
</ul>
<p>The reason I prefer using an interactive rebase is</p>
<ol type="1">
<li>I know how to do it,</li>
<li>it differs less from my everyday git usage than the alternatives.<a href="#fn2" class="footnote-ref" id="fnref2" role="doc-noteref"><sup>2</sup></a></li>
</ol>
<section id="picking-a-rebase-target" class="level2">
<h2>Picking a rebase target</h2>
<p>One of the most important choices before you start any interactive rebase is <q>how far back
am I rebasing.</q> It’s only safe to rewrite the parts of history that haven’t been pushed to
a remote yet,<a href="#fn3" class="footnote-ref" id="fnref3" role="doc-noteref"><sup>3</sup></a> so the natural choice is to rebase against a remote.</p>
<p>I’ll pretend that I’m working on a codebase with multiple people, purely for didactic
purposes, and do a fetch before I get started:</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git fetch origin
~/dotfiles/emacs/.emacs.d/ »</code></pre>
<p>Since there was no output, that means I have the latest version of <code>origin</code>.
Great! Let’s get started:</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git rebase -i origin/master
Cannot rebase: You have unstaged changes.
Please commit or stash them.</code></pre>
<p>Woops.</p>
</section>
<section id="cleaning-your-worktree" class="level2">
<h2>Cleaning your worktree</h2>
<p>When you start an interactive rebase, you need to have a clean worktree.
To get there, I used to run <code>git stash</code> before the rebase, and <code>git stash apply</code>
afterwards to reapply my old changes after modifying the log.
As it turns out, there’s a better way (from <code>man git-rebase</code>):</p>
<pre class="sourceCode"><code>--[no-]autostash
    Automatically create a temporary stash before the operation begins, and apply it
    after the operation ends. This means that you can run rebase on a dirty worktree.
    However, use with care: the final stash application after a successful rebase might
    result in non-trivial conflicts.</code></pre>
<p>As mentioned, stashes don’t necessarily apply cleanly after a rebase. This works best
if you know that your unstaged changes won’t conflict with anything you’re rebasing.
In my case, I only start rebases if I think my stash (if any) will apply cleanly, so
I’m going to make this a default in my config:<a href="#fn4" class="footnote-ref" id="fnref4" role="doc-noteref"><sup>4</sup></a></p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git config --get rebase.autostash
~/dotfiles/emacs/.emacs.d/ » git config --global rebase.autostash true
~/dotfiles/emacs/.emacs.d/ » git config --get rebase.autostash
true</code></pre>
</section>
<section id="blasting-apart-history" class="level2">
<h2>Blasting apart history</h2>
<p>Now we can get started, and Git will <code>stash</code> unstaged changes automatically before
interactive rebases.</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git rebase -i origin/master</code></pre>
<p>We’re dropped into the interactive rebase window:</p>
<pre class="sourceCode"><code>pick 712edab eyecandy: just use zenburn theme

# Rebase 8edc222..712edab onto 8edc222
#
# Commands:
#  , pick = use commit
#  , reword = use commit, but edit the commit message
#  , edit = use commit, but stop for amending
#  , squash = use commit, but meld into previous commit
#  , fixup = like &quot;squash&quot;, but discard this commit&#39;s log message
#  , exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out</code></pre>
<section id="removing-unrelated-files" class="level3">
<h3>Removing unrelated files</h3>
<p>I want to remove a file from <code>712edab</code>, so I change that commit to <code>edit</code>:</p>
<pre class="sourceCode"><code>edit 712edab eyecandy: just use zenburn theme</code></pre>
<p>and save the file. Now we’re back at the terminal:</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git rebase -i origin/master
Created autostash: ce6634c
HEAD is now at 712edab eyecandy: just use zenburn theme
Stopped at 712edab4e039cf2bd784031f04a202eec5f2195a... eyecandy: just use zenburn theme
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue</code></pre>
<p>This has dropped us into the history at a point in time <q>just after</q> <code>712edab</code> was committed.
We can modify the files that were changed in that commit, and then use <code>git commit --amend</code>
to replace the old commit with our new changes.</p>
<p>In my case, I still want to keep my <code>.travis.yml</code> changes, but wish to move them
into a separate commit. I’ll remove the file from Git’s index, but not my worktree,
using <code>git rm --cached</code>.</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git rm --cached .travis.yml
rm &#39;.travis.yml&#39;
~/dotfiles/emacs/.emacs.d/ » git commit --amend</code></pre>
</section>
<section id="making-new-commits" class="level3">
<h3>Making new commits</h3>
<p>Note how the addition of <code>.travis.yml</code> is gone now:</p>
<pre class="sourceCode"><code>eyecandy: just use zenburn theme

# Changes to be committed:
#       modified:   config/eyecandy/my-eyecandy.el
#</code></pre>
<p>But the file is still present on my machine:</p>
<pre class="sourceCode"><code>[detached HEAD cc71d68] eyecandy: just use zenburn theme
 Date: Sat Dec 13 20:45:54 2014 -0500
 1 file changed, 3 insertions(+), 19 deletions(-)
~/dotfiles/emacs/.emacs.d/ » ls .travis.yml
.travis.yml</code></pre>
<p>Great! We’ve rewritten the commit to remove the unrelated changes. It’s worth
noting that <code>edit</code> actions in a rebase still let you make new commits, unlike
most <q>detached head</q> states in Git.</p>
<p>So we can just add the file back in right here, and finish the rebase:</p>
<pre class="sourceCode"><code>~/dotfiles/emacs/.emacs.d/ » git add .travis.yml
~/dotfiles/emacs/.emacs.d/ » git commit -m &#39;test out travis CI for emacs&#39;
[detached HEAD dab1f85] test out travis CI for emacs
 1 file changed, 11 insertions(+)
 create mode 100644 .travis.yml
~/dotfiles/emacs/.emacs.d/ » git rebase --continue
Successfully rebased and updated refs/heads/master.
Applied autostash.</code></pre>
</section>
</section>
<section id="conclusion" class="level2">
<h2>Conclusion</h2>
<p>Now the repo is how I wanted it:</p>
<pre class="sourceCode"><code>2014-12-14 03:46 Unknown         o Unstaged changes
2014-12-13 22:42 Nathan Typanski o [master] test out travis CI for emacs
2014-12-13 20:45 Nathan Typanski o eyecandy: just use zenburn theme
2014-12-13 19:09 Nathan Typanski o {origin/master} evil: fancy smart indent behavior</code></pre>
<p>This is just a little snapshot of my Git workflow.
I wrote this post because I find myself constantly wondering if I’m using Git <q>the right way</q>,
and find myself curious how other people solve problems like this one.
Perhaps there’s a way to do this using only a handful of one-liners, and I’m
going overkill by using <code>git rebase</code> for this kind of thing (it certainly feels that way).</p>
<p>But then I realize there’s no One True Workflow with Git.
Git usage is a <a href="https://en.wikipedia.org/wiki/State_function">state function</a>:
we went from our initial state to our desired result state, and nobody cares
how we got there. That’s the whole point.</p>
</section>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Or I might not tolerate it in a few weeks. Ho, ho.<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn2"><p>If I have to hack apart a tree in Git, the easiest way for me to do it is
usually inside of an interactive rebase. Unlike memorizing a bunch of esoteric
one-liners, this lets me work on a repository like I’m always <q>at its tip</q> -
which is the normal state of a Git repo when you’re working on new changes.<a href="#fnref2" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn3"><p>You can get away with rewriting commits that are on a remote, but then you’re
doomed to <code>git push -f</code>’ing all over said remote. Without good reason, this is a
pretty bad idea. If you have coworkers, it’s a really bad idea.<a href="#fnref3" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
<li id="fn4"><p>From the man page for <code>git rebase</code>:</p>
<pre class="sourceCode"><code>CONFIGURATION
       rebase.autostash
                  If set to true enable --autostash option by default.</code></pre>
<a href="#fnref4" class="footnote-back" role="doc-backlink">↩︎</a></li>
</ol>
</section>
]]></summary>
</entry>
<entry>
    <title>Evil-mode configuration demo</title>
    <link href="http://www.nathantypanski.com/blog/2014-12-08-demo-a-vim-like-emacs.html" />
    <id>http://www.nathantypanski.com/blog/2014-12-08-demo-a-vim-like-emacs.html</id>
    <published>2014-12-08T00:00:00Z</published>
    <updated>2014-12-08T00:00:00Z</updated>
    <summary type="html"><![CDATA[<div class="info">
    <!--
    <div class="snippets">
        <i class="fa fa-calendar"></i> December  8, 2014 <i class="fa fa-cut" March 22, 2015
></i> March 22, 2015

    </div>
    -->
    
</div>
<p>In an effort to help out one of my readers, I made a quick Asciicast demo of the first few steps in my <a href="/blog/2014-08-03-a-vim-like-emacs-config.html">Towards a Vim-like Emacs</a> blog post.
Since I figure at least a few others might be interested, I’ve reproduced it here:<a href="#fn1" class="footnote-ref" id="fnref1" role="doc-noteref"><sup>1</sup></a></p>
<script type="text/javascript" src="https://asciinema.org/a/14201.js" id="asciicast-14201" async></script>
<section id="footnotes" class="footnotes footnotes-end-of-document" role="doc-endnotes">
<hr />
<ol>
<li id="fn1"><p>Does anyone know how to download from Asciinema? If so, <a href="/contact.html">drop me a line</a>. I’d love to keep a backup copy of the stuff I upload there.</p>
<p><strong>Update 2014/01/13:</strong> reader Gabriel Lazar informed me that the data is <a href="https://asciinema-bb-eu.s3.amazonaws.com/uploads/asciicast/stdout_frames/14201/stdout.json">available in JSON format</a> and playable, in theory, using a portion of the open-source <a href="https://github.com/asciinema/asciinema.org">asciinema.org code</a>. As an alternate backup solution, he suggests recording the playback of a screencast as a video. Thanks, Gabriel!<a href="#fnref1" class="footnote-back" role="doc-backlink">↩︎</a></p></li>
</ol>
</section>
]]></summary>
</entry>

</feed>
