<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<atom:link href="https://www.anthes.is/rss.xml" rel="self" type="application/rss+xml" />
<title>My Unix blog: scripts, software, /etc - anthesis</title>
<description>Hey, welcome to my blog! I typically write about various forms of
technology, including software, programming, and Unix-like operating
systems. I often discuss topics related to
OpenBSD.</description>
<link>https://www.anthes.is/</link>
<lastBuildDate>Sun, 29 Mar 2026 00:00:00 +0100</lastBuildDate>

<item>
<guid>https://www.anthes.is/font-comparison-review-atkinson-hyperlegible-mono.html</guid>
<link>https://www.anthes.is/font-comparison-review-atkinson-hyperlegible-mono.html</link>
<pubDate>Tue, 22 Jul 2025 00:00:00 +0200</pubDate>
<title>Font comparison and review: Atkinson Hyperlegible Mono</title>
<description><![CDATA[

<h1 id="font-comparison-and-review-atkinson-hyperlegible-mono">Font comparison and review: Atkinson Hyperlegible Mono</h1>
<p>Published: July 22nd, 2025</p>
<p>Updated: July 25th, 2025</p>
<p>Recently, I modified <code>anthes.is</code> to use <a href="https://www.anthes.is/#a-note-on-licensing">my own subsetted
versions</a> of Atkinson Hyperlegible Next and
Atkinson Hyperlegible Mono. Following the principle of <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food">eating your own
dog food</a>, I
also switched to Atkinson Hyperlegible Mono in my terminal. After a
month of daily use, I can now assess this font&#8217;s practical advantages
and compare it to established programming fonts like JetBrains Mono and
Fira Code.</p>
<p>Download links:</p>
<ul>
<li><a href="https://github.com/googlefonts/atkinson-hyperlegible-next-mono">Google Fonts GitHub repository (recommended)</a></li>
<li><a href="https://www.brailleinstitute.org/freefont/">Official download link (requires email and EULA)</a></li>
<li><a href="https://github.com/ryanoasis/nerd-fonts/releases/tag/v3.4.0">Nerd Fonts release that added Atkinson Hyperlegible Mono</a></li>
</ul>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#on-character-distinction-and-readability">On character distinction and readability</a>
<ul>
<li><a href="https://www.anthes.is/#multi-character-homoglyphs">Multi-character homoglyphs</a></li>
<li><a href="https://www.anthes.is/#single-character-homoglyphs">Single character homoglyphs</a></li>
<li><a href="https://www.anthes.is/#mirror-image-glyphs">Mirror image glyphs</a></li>
<li><a href="https://www.anthes.is/#scenarios-where-character-distinction-matters">Scenarios where character distinction matters</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#about-the-atkinson-hyperlegible-font-family">About the Atkinson Hyperlegible font family</a></li>
<li><a href="https://www.anthes.is/#the-atkinson-hyperlegible-familys-unique-design-features">The Atkinson Hyperlegible family&#8217;s unique design features</a>
<ul>
<li><a href="https://www.anthes.is/#distinct-silhouettes">Distinct silhouettes</a></li>
<li><a href="https://www.anthes.is/#enhanced-letterforms">Enhanced letterforms</a></li>
<li><a href="https://www.anthes.is/#asymmetrical-spurs-and-exaggerated-descenders">Asymmetrical spurs and exaggerated descenders</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#comparison-to-jetbrains-mono-and-fira-code">Comparison to JetBrains Mono and Fira Code</a>
<ul>
<li><a href="https://www.anthes.is/#single-homoglyphs-comparison">Single homoglyphs comparison</a></li>
<li><a href="https://www.anthes.is/#mirror-glyphs-comparison">Mirror glyphs comparison</a></li>
<li><a href="https://www.anthes.is/#programming-symbols-comparison">Programming symbols comparison</a></li>
<li><a href="https://www.anthes.is/#alphabet-comparison">Alphabet comparison</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#installation-and-configuration">Installation and configuration</a></li>
<li><a href="https://www.anthes.is/#caveats">Caveats</a></li>
<li><a href="https://www.anthes.is/#other-resources">Other resources</a></li>
<li><a href="https://www.anthes.is/#a-note-on-licensing">A note on licensing</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="on-character-distinction-and-readability">On character distinction and readability</h2>
<p>Understanding Atkinson Hyperlegible Mono&#8217;s strengths requires examining
the readability challenges programming fonts must address. While many
fonts distinguish between <code>0</code> and <code>O</code>, or <code>1</code>, <code>l</code>, and <code>I</code>, these
represent only two of many cases where character distinction matters.</p>
<p>Typographers call lookalike characters &#8220;homoglyphs.&#8221; The examples below
showcase both homoglyphs and &#8220;mirror image&#8221; glyphs. Screenshots in this
section come from <a href="https://www.researchgate.net/publication/338149302_Evaluating_Fonts_Font_Family_Selection_for_Accessibility_Display_Readability">Evaluating Fonts: Font Family Selection for
Accessibility &#38; Display
Readability</a>.</p>
<h3 id="multi-character-homoglyphs">Multi-character homoglyphs</h3>
<p>Multi-character homoglyphs occur when a sequence of glyphs appear to
form a single character. For example, <code>cl</code> can resemble <code>d</code>. Monospace
fonts reduce this problem, since each character occupies equal
horizontal space.</p>
<p><img src="https://www.anthes.is/images/worse_multi_homoglyphs.9ba5e76ebd864cbdaac843e51f7a52470bdc502fe1934213bb18d1af2a560366.png" alt="Typography comparison titled 'WORSE' in black text. Shows two fonts in
red text: 'Arial Narrow: rn m, cl d, cj g, vv w, vy w' and 'Josefin: rn
m, cl d, cj g, vv
w'." /></p>
<p><img src="https://www.anthes.is/images/better_multi_homoglyphs.83ce20dfc661b5a4395cfb9e2ec43e7a31a7778d1271d9b2bcbc037749a5a453.png" alt="Typography comparison titled 'BETTER' in black text. Shows two fonts
in green text: 'Convergence: rn m, cl d, cj g, vv w, vy w' and 'Quando:
rn m, cl d, cj g, vv
w'." /></p>
<p>In the &#8220;worse&#8221; example, letters blend together to resemble different
characters. The &#8220;better&#8221; examples use subtle details to prevent
this&#8212;like Convergence&#8217;s curly <code>y</code> and Quando&#8217;s serifs.</p>
<h3 id="single-character-homoglyphs">Single character homoglyphs</h3>
<p>Single character homoglyphs occur when one glyph resembles
another&#8212;such as <code>8</code> and <code>B</code>.</p>
<p><img src="https://www.anthes.is/images/worse_single_homoglyphs.0e425bfc07bbdf740fe1e37443de701726908c112fa5998885207be37429d928.png" alt="Typography comparison titled 'WORSE' in black text. Shows four fonts
with character sequences in red text: Arial Narrow displays '5S8B 7Z
QOCG 1lI qgy ce', Fugaz One shows '8B 7Z DO0CG6 1lI aqgy ce yuvw', Gill
Sans displays '5S8B 7Z QO 1lI ce vyw', and Autour One shows 'gq gy yq
aqc'." /></p>
<p><img src="https://www.anthes.is/images/better_single_homoglyphs.fcdfa02963df88aeb92cd93dfd47b9ca110e2bbda022eee79557121df64cd1cb.png" alt="Typography comparison titled 'BETTER' in black text. Shows four fonts
with character sequences in green text: DM Serif displays '5S8B 7Z QOCG
1lI qgy ce', SecularOne shows '8B 7Z DO0CG6 1lI aqgy ce yuvw',
Convergence displays '5S8B 7Z QO 1lI ce vyw', and Artifika shows 'gq gy
yq
aqc'." /></p>
<p>Several problems emerge in the &#8220;worse&#8221; examples.</p>
<ul>
<li>In Fugaz One, the <code>O</code> and <code>0</code> appear indistinguishable. Nearly the
same applies to <code>u</code>, <code>v</code>, and <code>w</code>.</li>
<li>The <code>1</code>, <code>l</code>, and <code>I</code> look identical in Gill Sans.</li>
</ul>
<p>The &#8220;better&#8221; examples address these concerns through deliberate design
choices. SecularOne uses a circle for <code>O</code> and an oval for <code>0</code>, while the
<code>u</code>, <code>v</code>, and <code>w</code> maintain distinct shapes.</p>
<h3 id="mirror-image-glyphs">Mirror image glyphs</h3>
<p>Mirror image glyphs occur when flipping one character creates
another&#8212;like <code>d</code> and <code>b</code>. Serif fonts address this by adding
distinguishing serifs, but sans-serif fonts must find other solutions.</p>
<p><img src="https://www.anthes.is/images/worse_mirror_glyphs.8730dc20a1819f8824507e06ca929dff365d8bebb597a2ce201587242871837d.png" alt="Typography comparison titled 'WORSE' in black text. Shows 'Montserrat:
db qp gp ae' in red
text." /></p>
<p><img src="https://www.anthes.is/images/better_mirror_glyphs.726c2e611f5105f7b5033d5f291f34cbb9ca6e5d336be596c82655a272cd0229.png" alt="Typography comparison titled 'BETTER' in black text with explanatory
note: '(notice the b is a different shape, not a mirror of d)'. Shows
'Convergence: db qp gp ae' in green
text." /></p>
<p>Montserrat achieves visual harmony, but creates mirror images.
Convergence solves this with a curly <code>q</code> tail and asymmetrical spurs
that distinguish <code>d</code> from <code>b</code>.</p>
<h3 id="scenarios-where-character-distinction-matters">Scenarios where character distinction matters</h3>
<p>Character distinction proves important in several scenarios:</p>
<ul>
<li>Debugging the results of a Structured Query Language (SQL) query, like
<code>SELECT * FROM users WHERE id = &#39;B510&#39;</code>, only to realize you meant
<code>SELECT * FROM users WHERE id = &#39;8510&#39;</code>.</li>
<li>Copying hexadecimal output by hand from an airgapped machine. Or
writing down GNU Privacy Guard (GPG) key fingerprints down on paper.</li>
<li>Distinguishing between similar commit hashes when cherry-picking or
reverting in git, such as <code>a1c4e8b</code> versus <code>a1c4e8d</code>.</li>
<li>Command-line flags, like <code>-1</code> and <code>-l</code>.</li>
</ul>
<p>These examples illustrate that visual distinctiveness proves important
in many situations. Still, some questions remain: what informed Atkinson
Hyperlegible&#8217;s design process, and what makes Atkinson Hyperlegible Mono
special compared to other programming fonts?</p>
<h2 id="about-the-atkinson-hyperlegible-font-family">About the Atkinson Hyperlegible font family</h2>
<p>The <a href="https://www.brailleinstitute.org/freefont">Atkinson Hyperlegible family comes from the Braille
Institute</a>. Approaching their
centennial in 2019, <a href="https://helloapplied.com/atkinson-hyperlegible">the institute hired Applied Design
Works</a> to develop a new
brand identity. Applied Design Works needed a font that balanced
character differentiation with visual harmony. When existing fonts
failed to meet their specific accessibility and branding requirements,
they designed their own, naming it after the institute&#8217;s founder: J.
Robert Atkinson.</p>
<p><a href="https://www.fastcompany.com/90395836/this-typeface-hides-a-secret-in-plain-sight-and-thats-the-point">Atkinson Hyperlegible earned Fast Company&#8217;s 2019 Innovation By Design
award</a>.
By 2025, Atkinson Hyperlegible generated over 43 million weekly
impressions via Google Fonts. The <a href="https://www.brailleinstitute.org/about-us/news/braille-institute-launches-enhanced-atkinson-hyperlegible-font-to-make-reading-easier/">Braille Institute then released
enhanced
versions</a>:
Atkinson Hyperlegible Next and Atkinson Hyperlegible Mono.</p>
<p>The <em>Next</em> variant of Atkinson Hyperlegible expands from 2 to 7 weights
and extends language support from 27 to 150+ languages. The <em>Mono</em>
version addresses what the Braille Institute called &#8220;one of the most
requested additions&#8221;&#8212;a variant for developers.</p>
<h2 id="the-atkinson-hyperlegible-familys-unique-design-features">The Atkinson Hyperlegible family&#8217;s unique design features</h2>
<p>The Braille Institute, Applied Design Works, and Material Design all
detail these design features. Since visual examples showcase typography
better than descriptions, this article includes images from <a href="https://m3.material.io/blog/atkinson-hyperlegible-design">Material
Design&#8217;s blog
post</a>.</p>
<p>Annotated images show the proportional version. Where the monospace
version differs significantly, non-annotated comparison images follow.</p>
<h3 id="distinct-silhouettes">Distinct silhouettes</h3>
<p><img src="https://www.anthes.is/images/atkinson-hyperlegible-b8-distinct-silhouettes.e28d28c9bf82537bf63b9dc3d9031355e0e99734a417d8d7e87ba28452c3dc5b.png" alt="Atkinson Hyperlegible showing 'B' and '8', with a label below that
says, &quot;Distinct
Silhouettes.&quot;" /></p>
<p>The <code>B</code> features two bowls of different sizes while the <code>8</code> combines a
small circle atop a larger oval.</p>
<h3 id="enhanced-letterforms">Enhanced letterforms</h3>
<p><img src="https://www.anthes.is/images/atkinson-hyperlegible-jIil-enhanced-letterforms.d6c13c94926a986aafbe79e9e554515cc19745a298d873f21da2563fbad151e7.png" alt="Atkinson Hyperlegible showing 'jIil!' with three accessibility
features: 1) exaggerated forms 2) selective serifs and 3) increased
inter-letter spacing. Blue annotation circles highlight each legibility
enhancement." /></p>
<p>This image highlights several enhancements: the <code>j</code> features an
exaggerated tail, the <code>I</code> gains horizontal top and bottom bars, the <code>i</code>
and <code>l</code> get serifs, and the <code>!</code> increases spacing between its dot and
vertical stroke.</p>
<p>Since these specific glyphs differ significantly between versions,
here&#8217;s the monospace variant for comparison:</p>
<p><img src="https://www.anthes.is/images/atkinson-hyperlegible-mono-jIil.cc96702deb6e2a5ef373f53b2ce2dbb8e157870c1e77edb327100a4090e3cc82.png" alt="Atkinson Hyperlegible Mono showing 'jIil!'. Unlike the previous image,
this one doesn't have labels or
annotations." /></p>
<p>Key differences include:</p>
<ul>
<li>The <code>j</code> and <code>l</code> gain longer feet and leftward serifs.</li>
<li>The <code>I</code> extends its horizontal bars.</li>
<li>The <code>i</code> adds a horizontal bottom and longer leftward serif.</li>
</ul>
<h3 id="asymmetrical-spurs-and-exaggerated-descenders">Asymmetrical spurs and exaggerated descenders</h3>
<p><img src="https://www.anthes.is/images/atkinson-hyperlegible-bdpq-asymmetrical-features.be5d9224083a4b62f1b4d08d96bd62fbe61d8d6251938ae1e0428a3e23fbc366.png" alt="Atkinson Hyperlegible showing 'bdpq' letterform differentiation using
two key techniques: 1) asymmetrical spurs and 2) exaggerated
descenders." /></p>
<p>The designers used two techniques to distinguish mirror image glyphs:</p>
<ol>
<li>Asymmetrical spurs distinguish <code>b</code> and <code>d</code>&#8212;the <code>d</code> features a
thicker spur that juts out more.</li>
<li>Exaggerated descenders separate <code>p</code> and <code>q</code>&#8212;the <code>q</code> extends out
into a longer, sweeping tail.</li>
</ol>
<h2 id="comparison-to-jetbrains-mono-and-fira-code">Comparison to JetBrains Mono and Fira Code</h2>
<p>How does Atkinson Hyperlegible Mono compare to established programming
fonts like JetBrains Mono and Fira Code? While many monospace fonts
target readability, direct comparison reveals Atkinson Hyperlegible
Mono&#8217;s specific advantages.</p>
<p>This comparison focuses on legibility features rather than stylistic
preferences or minor aesthetic differences.</p>
<h3 id="single-homoglyphs-comparison">Single homoglyphs comparison</h3>
<p><img src="https://www.anthes.is/images/single_homoglyphs_comparison_5S8B.2dec241e59cd770d50a29d42b0d7f0318a907c350f714b30940caf3fa68a76cf.png" alt="Font comparison displaying character sequences '5S8B7Z jIil1!' across
JetBrains Mono, Fira Code, and Atkinson Hyperlegible
Mono." /></p>
<p>These fonts appear nearly identical at first glance, requiring closer
examination to spot the differences.</p>
<ul>
<li>JetBrains Mono adds a serif to the <code>7</code> to distinguish it from the
<code>Z</code>.</li>
<li>Fira Code uses a curved hook on the <code>j</code> and smaller curved serif on
the <code>l</code>, distinguishing them within the <code>jIil1!</code> group.</li>
<li>Atkinson Hyperlegible Mono provides the strongest distinction between
<code>8</code>, <code>B</code>, <code>5</code>, and <code>S</code>. The <code>j</code> and <code>l</code> feature asymmetrical serifs,
while the <code>5</code> uses a diagonal rather than vertical downstroke.</li>
</ul>
<p><img src="https://www.anthes.is/images/single_homoglyphs_comparison_CD0O.988af4f47fa32b126104687b07c7d62e7195319593e8b54512e5d7e0d93f11e1.png" alt="Font comparison displaying 'CD0OQG6 yuvw' across JetBrains Mono, Fira
Code, and Atkinson Hyperlegible
Mono." /></p>
<p>Here the differences become more apparent.</p>
<ul>
<li>JetBrains Mono struggles with <code>0</code>, <code>O</code>, and <code>Q</code> similarity, though <code>G</code>
and <code>6</code> maintain good distinction. <code>y</code> and <code>v</code> appear more similar than
in other fonts.</li>
<li>Fira Code&#8217;s <code>0</code> and <code>O</code> distinguish themselves through a slash and
width variation, but <code>G</code> and <code>6</code> appear similar.</li>
<li>Atkinson Hyperlegible Mono excels in this comparison. The <code>Q</code> features
a distinctive middle line, while the <code>0</code> uses a reverse slash to avoid
confusion with the null sign in math and the slashed <code>O</code> in
Danish&#47;Norwegian.</li>
</ul>
<h3 id="mirror-glyphs-comparison">Mirror glyphs comparison</h3>
<p><img src="https://www.anthes.is/images/mirror_glyphs_comparison_dbqp.d2a81b693a1faebb609fe096d841d4a5acac74982ff0133737e4055acbf5ffb0.png" alt="Font comparison showing mirror image glyphs 'db qp gp ae' across three
programming fonts: JetBrains Mono, Fira Code, and Atkinson Hyperlegible
Mono" /></p>
<ul>
<li>JetBrains Mono provides the least distinction, with <code>d</code>, <code>b</code>, <code>q</code>, and
<code>p</code> appearing as true mirrors. The <code>a</code> and <code>e</code> also show similarity.</li>
<li>Fira Code provides subtle distinction between <code>d</code>, <code>b</code>, <code>q</code>, and <code>p</code>,
though noticing the differences requires close examination. The <code>g</code> and
<code>p</code> face the same way, and the <code>g</code> appears more ornate. <code>a</code> and <code>e</code>
remain distinct.</li>
<li>Atkinson Hyperlegible Mono achieves the strongest distinction between
<code>d</code>, <code>b</code>, <code>q</code>, and <code>p</code> through its asymmetrical design features. The <code>a</code>
and <code>e</code> also maintain clear differentiation.</li>
</ul>
<h3 id="programming-symbols-comparison">Programming symbols comparison</h3>
<p><img src="https://www.anthes.is/images/programming_symbols_comparison_brackets.6e80af20e769a438d1d180152361408ef1f88cd42dad2157d69d1ac7990c9b17.png" alt="Font comparison showing programming symbols '\(\)\[\]\{\}\`\'&quot;:;.,'
across JetBrains Mono, Fira Code, and Atkinson Hyperlegible
Mono." /></p>
<p>The programming symbols comparison reveals interesting trade-offs for
Atkinson Hyperlegible Mono.</p>
<ul>
<li>JetBrains Mono excels at distinguishing <code>.</code> from <code>,</code> and <code>:</code> from <code>;</code>.
Ditto for <code>()</code>, <code>[]</code>, and <code>{}</code>.</li>
<li>Fira Code also handles <code>()</code>, <code>[]</code>, and <code>{}</code> well.</li>
<li>Atkinson Hyperlegible Mono shows weaker <code>[]</code> and <code>{}</code> distinction.</li>
</ul>
<p><img src="https://www.anthes.is/images/programming_symbols_comparison_operators.d4a3140fc7b95a005a9ffe6a1a2bf28e187d9fcd1b2719040fec6ea98aa0bce4.png" alt="Font comparison showing programming symbols '=+-_\~* /\ <>' across
JetBrains Mono, Fira Code, and Atkinson Hyperlegible
Mono." /></p>
<ul>
<li>JetBrains Mono maintains consistent horizontal length for <code>+</code> and <code>=</code>,
but shortens <code>-</code> relative to those operators.</li>
<li>Fira Code uses uniform length for <code>+</code>, <code>=</code>, and <code>-</code>. <code>-</code> and <code>_</code> share
similar length. The <code>&#47;\</code> characters join together and render smaller
compared to the other fonts.</li>
<li>Atkinson Hyperlegible Mono varies all operator lengths for
distinction. The <code>-</code> and <code>_</code> show great differentiation, while <code>*</code>
reduces in size and ascends above <code>+</code> for clarity. The <code>&#60;&#62;</code> characters
remain separate rather than joining.</li>
</ul>
<h3 id="alphabet-comparison">Alphabet comparison</h3>
<p><img src="https://www.anthes.is/images/boxing_wizards.703dfe1edc4ea255f3273f93cf3dcb3198cc9964636bae038b98a7c20786d522.png" alt="Font comparison showing &quot;The five boxing wizards jump quickly.&quot; across
JetBrains Mono, Fira Code, and Atkinson Hyperlegible
Mono." /></p>
<h2 id="installation-and-configuration">Installation and configuration</h2>
<p>These instructions apply to Unix-like operating systems. In other words:
Linux, Berkeley Software Distribution (BSD) derivatives, and other
similar operating systems.</p>
<p>While the Braille Institute offers direct downloads, they require email
registration and End User License Agreement (EULA) acceptance for an
open source font. Open source repositories provide a better alternative.</p>
<p>Make sure you have git installed and available, then clone the
<code>googlefonts&#47;atkinson-hyperlegible-next-mono</code> repository on Github.</p>
<pre><code class="language-shell">$ git clone https:&#47;&#47;github.com&#47;googlefonts&#47;atkinson-hyperlegible-next-mono
</code></pre>
<p>Create the <code>~&#47;.local&#47;share&#47;fonts</code> directory.</p>
<pre><code class="language-shell">$ mkdir -p ~&#47;.local&#47;share&#47;fonts
</code></pre>
<p>Install Atkinson Hyperlegible Mono in <code>~&#47;.local&#47;share&#47;fonts</code>.</p>
<pre><code class="language-shell">$ cp .&#47;atkinson-hyperlegible-next-mono&#47;fonts&#47;ttf&#47;*.ttf ~&#47;.local&#47;share&#47;fonts&#47;
</code></pre>
<p>Build font information cache files.</p>
<pre><code class="language-shell">$ fc-cache -fv
</code></pre>
<p>Configure Atkinson Hyperlegible Mono as your default monospace font
system-wide and per application. The <a href="https://wiki.archlinux.org/title/Font_configuration">Arch Wiki&#8217;s font configuration
guide</a> covers
system-wide setup. Terminal emulators and code editors typically set
font through settings menus or configuration files.</p>
<h2 id="caveats">Caveats</h2>
<ul>
<li><a href="https://github.com/googlefonts/atkinson-hyperlegible-next-mono/issues/3">Some versions of Atkinson Hyperlegible Mono don&#8217;t include the backtick&#47;grave
symbol</a>.</li>
<li>Applied Design Works specializes in branding rather than typeface
design.</li>
<li><a href="https://www.maxkohler.com/notes/2021-02-16-atkinson-hyperreadable/">Max Kohler&#8217;s development
notes</a>
indicate Applied Design Works focused primarily on readers with vision
difficulties rather than readers with dyslexia, though they expect the
accessibility features to benefit both groups.</li>
<li>The font&#8217;s commercial origins and the creators&#8217; branding incentives
may influence legibility claims.</li>
<li>Atkinson Hyperlegible Mono lacks programming ligature support.</li>
<li>Legibility claims lack peer-reviewed research support. While <a href="https://github.com/thibaudcolas/readability-group-survey">Atkinson
Hyperlegible performed well in the Readability Group
Survey</a>, this
measured preference rather than objective performance. Internal
testing used vision simulation and reading metrics, but independent
scientific validation remains absent.</li>
</ul>
<h2 id="other-resources">Other resources</h2>
<ul>
<li>Pimp My Type offers reviews of both the <a href="https://pimpmytype.com/font/atkinson-hyperlegible/">original Atkinson
Hyperlegible</a> and
<a href="https://pimpmytype.com/font/atkinson-hyperlegible-next/">Atkinson Hyperlegible
Next</a>, as well
as <a href="https://pimpmytype.com/atkinson-hyperlegible-font-pairs/">font pairing
guidance</a>.</li>
<li><a href="https://www.anthes.is/bookmarks.html">My bookmarks page</a> contains links
related to typography, legibility, and fonts.</li>
<li><a href="https://www.codingfont.com/AtkinsonHyperlegibleMono">Coding Font hosts a dedicated page for Atkinson Hyperlegible
Mono</a>. You can
compare fonts side-by-side there.</li>
</ul>
<h2 id="a-note-on-licensing">A note on licensing</h2>
<p>The Braille Institute and the designers they collaborated with released
the Atkinson Hyperlegible fonts under the <a href="https://openfontlicense.org/">SIL Open Font License,
version 1.1</a>. The website links to a
license that reserves &#8220;ATKINSON&#8221; and &#8220;HYPERLEGIBLE&#8221; as Reserved Font
Names. Anyone that modifies the font <a href="https://openfontlicense.org/how-to-modify-ofl-fonts/#what-do-i-need-to-do-to-redistribute-a-modified-font">must remove those words from the
name and
metadata</a>.
<a href="https://openfontlicense.org/webfonts-and-reserved-font-names/#subsetting">Subsetting counts as modification in many
cases</a>.</p>
<p>Since I subset these fonts for performance reasons, I named the
derivative versions &#8220;Anthesis Legible Sans&#8221; and &#8220;Anthesis Legible Mono&#8221;
to honor the license.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/gentoo-one-liners.html</guid>
<link>https://www.anthes.is/gentoo-one-liners.html</link>
<pubDate>Wed, 04 Jun 2025 00:00:00 +0200</pubDate>
<title>Gentoo one-liners for a rainy day</title>
<description><![CDATA[

<h1 id="gentoo-one-liners-for-a-rainy-day">Gentoo one-liners for a rainy day</h1>
<p>Published: June 4th, 2025</p>
<p>Updated: July 3rd, 2025</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#prerequisites">Prerequisites</a></li>
<li><a href="https://www.anthes.is/#finding-packages-with-the-most-dependencies">Finding packages with the most dependencies</a></li>
<li><a href="https://www.anthes.is/#finding-the-largest-packages">Finding the largest packages</a></li>
<li><a href="https://www.anthes.is/#combining-package-size-and-dependency-count">Combining package size and dependency count</a></li>
<li><a href="https://www.anthes.is/#print-packages-without-a-packageenv-entry">Print packages without a package.env entry</a></li>
<li><a href="https://www.anthes.is/#as-above-but-only-larger-packages">As above, but only larger packages</a></li>
<li><a href="https://www.anthes.is/#analyzing-upgradeable-packages">Analyzing upgradeable packages</a></li>
<li><a href="https://www.anthes.is/#multi-column-sorting">Multi-column sorting</a></li>
<li><a href="https://www.anthes.is/#practical-applications">Practical applications</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="prerequisites">Prerequisites</h2>
<p>Install the necessary Portage utilities and GNU Parallel:</p>
<pre><code class="language-sh">emerge -av app-portage&#47;portage-utils app-portage&#47;gentoolkit sys-process&#47;parallel
</code></pre>
<h2 id="finding-packages-with-the-most-dependencies">Finding packages with the most dependencies</h2>
<p>Show which packages pull in the greatest number of dependencies:</p>
<pre><code class="language-sh">qlist -CI | sort -u | parallel &#39;echo $(qdepends -CqqQ {} | wc -l) {}&#39; | sort -n &#62; pkgs_with_most_deps.txt
</code></pre>
<p>What each part does:</p>
<ul>
<li><code>qlist -CI</code>: lists all installed packages without color output in
<code>category&#47;package</code> format.</li>
<li><code>sort -u</code>: sorts output and removes duplicate entries.</li>
<li><code>parallel</code>: executes dependency counting through GNU Parallel for better performance.</li>
<li><code>qdepends -CqqQ {}</code>: queries dependencies without color output, quietly.</li>
<li><code>wc -l</code>: counts the dependency lines.</li>
<li><code>sort -n</code>: sorts numerically by dependency count.</li>
</ul>
<p>The output shows dependency count followed by package name, from
packages with the least dependencies to greatest.</p>
<h2 id="finding-the-largest-packages">Finding the largest packages</h2>
<p>Find which packages consume the most storage:</p>
<pre><code class="language-sh">qlist -CI | sort -u | parallel qsize -C | awk &#39;{print $NF,$1}&#39; | cut -d &#39;:&#39; -f 1 | sort -h &#62; biggest_pkgs.txt
</code></pre>
<p>Breaking this down:</p>
<ul>
<li><code>qsize -C</code>: reports package sizes in human-readable format without color output.</li>
<li><code>awk &#39;{print $NF,$1}&#39;</code>: reorders output to put size first, then package name.</li>
<li><code>cut -d &#39;:&#39; -f 1</code>: removes trailing colon.</li>
<li><code>sort -h</code>: sorts by human-readable numbers (handles K, M, G suffixes).</li>
</ul>
<p>This arranges packages from smallest to largest.</p>
<h2 id="combining-package-size-and-dependency-count">Combining package size and dependency count</h2>
<p>Merge dependency and size data in one file for analysis:</p>
<pre><code class="language-sh">join -j 2 -o 1.1,2.1,0 &#60;(sort -k2 pkgs_with_most_deps.txt) &#60;(sort -k2 biggest_pkgs.txt) | sort -n | tr &#39; &#39; &#39;	&#39; &#62; combined.txt
</code></pre>
<p>The <code>join</code> command joins lines of two files on a common field:</p>
<ul>
<li><code>-j 2</code>: join on field 2 (package names).</li>
<li><code>-o 1.1,2.1,0</code>: output format (dependencies, size, package name).</li>
<li><code>&#60;(sort -k2 ...)</code>: process substitution with secondary key sorting.</li>
<li><code>tr &#39; &#39; &#39;	&#39;</code>: converts spaces to tabs for cleaner formatting.</li>
</ul>
<h2 id="print-packages-without-a-package.env-entry">Print packages without a package.env entry</h2>
<p>Print packages not yet customized in <code>&#47;etc&#47;portage&#47;package.env</code>:</p>
<pre><code class="language-sh">comm -13 &#60;(awk &#39;match($1, &#47;^[a-zA-Z0-9_+-]+\&#47;[a-zA-Z0-9_+-]+$&#47;) {print $1}&#39; &#47;etc&#47;portage&#47;package.env&#47;* | sort -u) &#60;(qlist -CI | sort -u)
</code></pre>
<p>This <code>comm</code> usage compares sorted lists:</p>
<ul>
<li><code>-13</code>: shows only lines unique to installation.</li>
<li><code>awk &#39;match($1, &#47;^[a-zA-Z0-9_+-]+\&#47;[a-zA-Z0-9_+-]+$&#47;)&#39;</code>: extracts package
in <code>category&#47;package</code> format.</li>
</ul>
<h2 id="as-above-but-only-larger-packages">As above, but only larger packages</h2>
<p>Focus on packages large enough to warrant optimization attention:</p>
<pre><code class="language-sh">comm -13 &#60;(awk &#39;match($1, &#47;^[a-zA-Z0-9_+-]+\&#47;[a-zA-Z0-9_+-]+$&#47;) {print $1}&#39; &#47;etc&#47;portage&#47;package.env&#47;* | sort -u) &#60;(qlist -CI | sort -u) | grep -f - combined.txt | awk &#39;match($2, &#47;^([0-9]{4}\.[0-9]K)|([0-9]+\.[0-9][MG])$&#47;)&#39; | sort -n
</code></pre>
<p>This adds size filtering:</p>
<ul>
<li><code>grep -f -</code>: uses the uncustomized package list as search patterns.</li>
<li><code>awk &#39;match($2, &#47;^([0-9]{4}\.[0-9]K)|([0-9]+\.[0-9][MG])$&#47;)&#39;</code>: matches
packages &#62;=1000K.</li>
</ul>
<h2 id="analyzing-upgradeable-packages">Analyzing upgradeable packages</h2>
<p>Find packages not yet customized in <code>&#47;etc&#47;portage&#47;package.env</code> that
already need an upgrade:</p>
<pre><code class="language-sh">comm -13 &#60;(awk &#39;match($1, &#47;^[a-zA-Z0-9_+-]+\&#47;[a-zA-Z0-9_+-]+$&#47;) {print $1}&#39; &#47;etc&#47;portage&#47;package.env&#47;* | sort -u) &#60;(EIX_LIMIT=0 eix -u -# | sort -u)
</code></pre>
<ul>
<li><code>EIX_LIMIT=0</code>: removes eix output limits.</li>
<li><code>eix -u -#</code>: lists upgradeable packages in <code>category&#47;package</code> format.</li>
</ul>
<h2 id="multi-column-sorting">Multi-column sorting</h2>
<p>Use multiple sort keys for different analysis priorities:</p>
<pre><code class="language-sh">sort -k1,1n -k2,2h combined.txt
sort -k2,2h -k1,1n combined.txt
</code></pre>
<p>The first command prioritizes dependency count, using size as a
tiebreaker. The second makes size the primary criterion. The <code>h</code> and <code>n</code>
refer to human-readable and numeric sorting.</p>
<h2 id="practical-applications">Practical applications</h2>
<p>These commands identify large packages that can be replaced with
lightweight alternatives, which proves useful for several reasons:</p>
<ul>
<li>Freeing up storage space.</li>
<li>Creating a more minimal system.</li>
</ul>
<p>I used these commands to guide CFLAGS tweaking to better optimize Gentoo
in a Virtual Machine (VM), since VMs have limited resources. I use a
setup where video playback doesn&#8217;t benefit from hardware acceleration at
the time of writing (Qubes OS). I wanted to see if this approach could
make a difference.</p>
<p>I think I managed to gain a few Frames Per Second (FPS) from this, but I
didn&#8217;t benchmark it beyond comparing frame timings in mpv. The gain was
relatively minor (&#60;=10fps).</p>
<p>These days I believe strongly in benchmarking and profiling, as they
make a real difference. The gains from those techniques typically exceed
the gains from CFLAGS tweaking.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/gpg-transition-20250406.html</guid>
<link>https://www.anthes.is/gpg-transition-20250406.html</link>
<pubDate>Sun, 06 Apr 2025 00:00:00 +0200</pubDate>
<title>OpenPGP Key Transition Statement</title>
<description><![CDATA[

<h1 id="openpgp-key-transition-statement">OpenPGP Key Transition Statement</h1>
<p>Date: April 6, 2025</p>
<p>I am transitioning from my old GPG key to a new one. The old key will
continue to be valid until its expiration date (August 8th, 2025).
However, I&#8217;d prefer for all encrypted email correspondence to be
encrypted to the new key, and will be making all signatures going
forward with the new key.</p>
<p>This transition document is signed by both keys to validate the
transition.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#key-details">Key Details</a>
<ul>
<li><a href="https://www.anthes.is/#old-key">Old Key</a></li>
<li><a href="https://www.anthes.is/#new-key">New Key</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#verification-instructions">Verification Instructions</a>
<ul>
<li><a href="https://www.anthes.is/#keys">Keys</a></li>
<li><a href="https://www.anthes.is/#transition-document">Transition Document</a></li>
<li><a href="https://www.anthes.is/#verification-steps">Verification Steps</a></li>
</ul></li>
</ul>
<!-- mtoc-end -->
<h2 id="key-details">Key Details</h2>
<h3 id="old-key">Old Key</h3>
<pre><code class="language-text">pub   ed25519&#47;0x53670DEBCF375780 2021-07-14 [SC] [expires: 2025-08-08]
      Key fingerprint = 9096 5AE1 20F8 E848 979D  EA48 5367 0DEB CF37 5780
</code></pre>
<h3 id="new-key">New Key</h3>
<pre><code class="language-text">pub   ed25519&#47;0xF1B59F496EE704B0 2025-04-02 [SC] [expires: 2026-04-04]
      Key fingerprint = 4A21 44F7 48E4 F5DC 17C5  E707 F1B5 9F49 6EE7 04B0
</code></pre>
<h2 id="verification-instructions">Verification Instructions</h2>
<p>To fetch both my keys and the clear-signed transition document for
verification:</p>
<h3 id="keys">Keys</h3>
<pre><code class="language-shell"># Get old key
$ curl -O https:&#47;&#47;www.anthes.is&#47;pubkeys&#47;eurydice.asc
$ gpg --import eurydice.asc

# Get new key
$ curl -O https:&#47;&#47;www.anthes.is&#47;pubkeys&#47;F1B59F496EE704B0.asc
$ gpg --import F1B59F496EE704B0.asc
</code></pre>
<h3 id="transition-document">Transition Document</h3>
<pre><code class="language-shell"># Get the transition statement document
$ curl -O https:&#47;&#47;www.anthes.is&#47;gpg-transition-20250406.txt
</code></pre>
<h3 id="verification-steps">Verification Steps</h3>
<p>Check the full fingerprints against those listed in this document:</p>
<pre><code class="language-shell">$ gpg --fingerprint 0x53670DEBCF375780
$ gpg --fingerprint 0xF1B59F496EE704B0
</code></pre>
<p>Verify that my old key is signed by my new key, and that the new key is
signed by my old key:</p>
<pre><code class="language-shell">$ gpg --check-sigs 0x53670DEBCF375780
$ gpg --check-sigs 0xF1B59F496EE704B0
</code></pre>
<p>Verify the transition document has been signed by both keys:</p>
<pre><code class="language-shell">$ gpg --verify gpg-transition-20250406.txt
</code></pre>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/nix-internals-cflags.html</guid>
<link>https://www.anthes.is/nix-internals-cflags.html</link>
<pubDate>Sun, 29 Dec 2024 00:00:00 +0100</pubDate>
<title>Linux&#47;Unix internals: x86_64 CFLAGS&#47;CXXFLAGS</title>
<description><![CDATA[

<h1 id="linuxunix-internals-x86_64-cflagscxxflags">Linux&#47;Unix internals: x86_64 CFLAGS&#47;CXXFLAGS</h1>
<p>Published: December 29th, 2024</p>
<p>Updated: July 3rd, 2025</p>
<p>As I&#8217;ve used Gentoo, I&#8217;ve become curious about what kinds of interesting
build flags other systems use to compile their binaries on a global
level, as well as what configure-time flags they set on GNU Compiler
Collection (GCC) and Clang. This article collects these flags for some
distributions to make them easier for me to track down and compare in
the future.</p>
<p>Because this is a rabbit hole, there are some things I won&#8217;t be doing in
the interest of time. They&#8217;re listed in the <a href="https://www.anthes.is/#caveats">Caveats section</a>.</p>
<p>Some relevant documentation can be found in these places:</p>
<ul>
<li><a href="https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html">GCC flag docs</a></li>
<li><a href="https://clang.llvm.org/docs/ClangCommandLineReference.html">Clang flag docs</a></li>
<li><a href="https://www.man7.org/linux/man-pages/man1/ld.1.html">ld(1)</a>
(documents RELocation Read-Only (RELRO), BIND_NOW, etc)</li>
</ul>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#arch">Arch</a></li>
<li><a href="https://www.anthes.is/#alpine">Alpine</a></li>
<li><a href="https://www.anthes.is/#clear-linux">Clear Linux</a></li>
<li><a href="https://www.anthes.is/#debian">Debian</a></li>
<li><a href="https://www.anthes.is/#fedora">Fedora</a></li>
<li><a href="https://www.anthes.is/#gentoo">Gentoo</a></li>
<li><a href="https://www.anthes.is/#nixos">NixOS</a></li>
<li><a href="https://www.anthes.is/#openbsd">OpenBSD</a></li>
<li><a href="https://www.anthes.is/#opensuse">OpenSUSE</a></li>
<li><a href="https://www.anthes.is/#solus">Solus</a></li>
<li><a href="https://www.anthes.is/#ubuntu">Ubuntu</a></li>
<li><a href="https://www.anthes.is/#void">Void</a></li>
<li><a href="https://www.anthes.is/#caveats">Caveats</a></li>
<li><a href="https://www.anthes.is/#areas-for-improvement">Areas for improvement</a></li>
<li><a href="https://www.anthes.is/#other-resources">Other resources</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="arch">Arch</h2>
<p><a href="https://bbs.archlinux.org/viewtopic.php?pid=2189095#p2189095">According to this
poster</a>,
the build flags can be found in <a href="https://gitlab.archlinux.org/archlinux/devtools/-/blob/5c0f8d37d5b1f3c380b49c60f3d2e5e8ab586f62/config/makepkg/x86_64.conf#L43">the x86_64.conf file in the devtools
package</a>.</p>
<p>CFLAGS:</p>
<pre><code class="language-shell">-march=x86-64
-mtune=generic
-O2
-pipe
-fno-plt
-fexceptions
-Wp,-D_FORTIFY_SOURCE=3
-Wformat
-Werror=format-security
-fstack-clash-protection
-fcf-protection
-fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer
</code></pre>
<p>CXXFLAGS:</p>
<pre><code class="language-shell">$CFLAGS
-Wp,-D_GLIBCXX_ASSERTIONS
</code></pre>
<p>LDFLAGS:</p>
<pre><code class="language-shell">-Wl,-O1
-Wl,--sort-common
-Wl,--as-needed
-Wl,-z,relro
-Wl,-z,now
-Wl,-z,pack-relative-relocs
</code></pre>
<p>LTOFLAGS:</p>
<pre><code class="language-shell">-flto=auto
</code></pre>
<p>Here are the <a href="https://gitlab.archlinux.org/archlinux/packaging/packages/gcc/-/blob/5d3fc68a98b548026f01eec74707445e8b16413a/PKGBUILD#L76">configure-time GCC flags for
Arch</a>.</p>
<p>Here are the <a href="https://gitlab.archlinux.org/archlinux/packaging/packages/clang/-/blob/e8801aceb5f717ae9c8477618e7b0485414eee32/PKGBUILD#L79">configure-time Clang flags for
Arch</a>.</p>
<p>Here are the <a href="https://gitlab.archlinux.org/archlinux/packaging/packages/llvm/-/blob/809803c2eaf4d2c3b526ae77820167d1fab99bb0/PKGBUILD#L81">configure-time Low Level Virtual Machine (LLVM) flags for
Arch</a>.</p>
<h2 id="alpine">Alpine</h2>
<p>Alpine compiles packages using abuild. The
<a href="https://git.alpinelinux.org/abuild/tree/default.conf?id=8c47dfcfb16619a4943af8f10628d5042d667ec4">default.conf</a>
file contains the compiler flags used for the distribution.</p>
<p>CFLAGS:</p>
<pre><code class="language-shell">-Os
-fstack-clash-protection
-Wformat
-Werror=format-security
</code></pre>
<p>CXXFLAGS:</p>
<pre><code class="language-shell">-Os
-fstack-clash-protection
-Wformat
-Werror=format-security
-D_GLIBCXX_ASSERTIONS=1
-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS=1
-D_LIBCPP_ENABLE_HARDENED_MODE=1
</code></pre>
<p>LDFLAGS:</p>
<pre><code class="language-shell">-Wl,--as-needed,-O1,--sort-common
</code></pre>
<p><a href="https://gitlab.alpinelinux.org/alpine/aports/-/blob/b6ad59f82c35cd47d5200d5b5919f7ba20c871e3/main/gcc/APKBUILD#L348">GCC configuration for
Alpine</a>.</p>
<p><a href="https://gitlab.alpinelinux.org/alpine/aports/-/blob/b6ad59f82c35cd47d5200d5b5919f7ba20c871e3/main/clang19/APKBUILD#L85">Clang configuration for
Alpine</a>.</p>
<p><a href="https://gitlab.alpinelinux.org/alpine/aports/-/blob/b6ad59f82c35cd47d5200d5b5919f7ba20c871e3/main/llvm19/APKBUILD#L141">LLVM configuration for
Alpine</a>.</p>
<p><a href="https://gitlab.alpinelinux.org/alpine/tsc/-/issues/64">This discussion has some interesting
details</a>.</p>
<h2 id="clear-linux">Clear Linux</h2>
<p>The <a href="https://www.clearlinux.org/clear-linux-documentation/guides/clear/performance.html">performance section in the
documentation</a>
lays everything out nicely.</p>
<ul>
<li><a href="https://github.com/clearlinux/clr-rpm-config/blob/fdefdfa05363304f341150cf532137ebd5079a71/macros#L538">__global_cflags in
macros</a></li>
<li><a href="https://github.com/clearlinux/clr-rpm-config/blob/fdefdfa05363304f341150cf532137ebd5079a71/rpmrc#L13">optflags in
rpmrc</a></li>
</ul>
<p>For x86_64, here is what that looks like:</p>
<pre><code class="language-shell">-O2
-g
-feliminate-unused-debug-types
-pipe
-Wall
-Wp,-D_FORTIFY_SOURCE=2
-fexceptions
-fstack-protector
--param=ssp-buffer-size=64
-Wformat
-Wformat-security
-Wl,-z,now,-z,relro,-z,max-page-size=0x4000,-z,separate-code
-Wno-error
-ftree-vectorize
-ftree-slp-vectorize
-Wl,--enable-new-dtags
-Wl,--build-id=sha1
-ftrivial-auto-var-init=zero
-mrelax-cmpxchg-loop
-m64
-march=westmere
-mtune=skylake-avx512
-fasynchronous-unwind-tables
-Wp,-D_REENTRANT
</code></pre>
<p>The configuration contains some interesting flags. Two flags in
particular I find noteworthy: <code>-ftrivial-auto-var-init=zero</code> and
<code>-mrelax-cmpxchg-loop</code>.</p>
<p><code>-ftrivial-auto-var-init</code> is a security-related flag. I&#8217;ve heard that
both ChromiumOS and Android use that flag in some form. Gentoo may add
it to their hardened builds, at least <a href="https://bugs.gentoo.org/913339">this open bug suggests
so</a>.</p>
<p><code>-mrelax-cmpxchg-loop</code> relaxes spin loops in certain conditions,
benefiting thread synchronization. <a href="https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103069">Here is the GCC bug where it&#8217;s
discussed</a>. <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/building-innovation-and-performance-with-gcc12.html">Intel
discusses it
here</a>.</p>
<p>The question of compiler configure-time flags for Clear Linux gets
complicated, because they have separate GCC builds for Advanced Vector
Extensions 2 (AVX2) and Advanced Vector Extensions 512 (AVX512) Here are
the <a href="https://github.com/clearlinux-pkgs/gcc/blob/fb236130c0cfccd0ab3e3bf7e8e4d3cc6f3bbc70/gcc.spec#L289">configure-time flags for the main GCC
build</a>.</p>
<p>Similar situation for Clang. Here are the <a href="https://github.com/clearlinux-pkgs/llvm/blob/3fade2633755c8ddd4b4ce7c91ec37eb6c219880/llvm.spec#L311">Clear Linux
configuration-time flags for
Clang&#47;LLVM</a>.</p>
<p>I&#8217;m breaking a rule I made for myself a little bit (no per-package
CFLAGS&#47;CXXFLAGS reviewing) to discuss some interesting tweaks Clear
Linux does. <a href="https://github.com/clearlinux/autospec">autospec</a> is at the
heart of this. A couple of interesting things about autospec and the
packages in <a href="https://github.com/clearlinux-pkgs">clearlinux-pkgs</a>:</p>
<ul>
<li>On x86_64, it <a href="https://github.com/clearlinux/autospec/blob/54240261104357e79455574d5b821860e27de60a/autospec/specfiles.py#L616">enables <code>-fzero-call-used-regs=used</code> on
security-sensitive
packages</a>.
I found this due to <a href="https://seirdy.one/notes/2023/04/17/clang-supports-wiping-call-used-registers">Seirdy&#8217;s note on the
subject</a>.</li>
<li><a href="https://github.com/clearlinux/autospec/blob/54240261104357e79455574d5b821860e27de60a/autospec/specfiles.py#L634">On packages where the <code>funroll-loops</code> option is
set</a>
(no doubt <a href="https://shlomifish.org/humour/by-others/funroll-loops/Gentoo-is-Rice.html">a reference&#47;inside
joke</a>),
<code>-O3</code> gets added if <code>use_clang</code> was also set. Otherwise,
<code>-fno-semantic-interposition</code> and <code>-falign-functions=32</code> get added.</li>
</ul>
<p>There&#8217;s a lot to learn from this distribution.</p>
<h2 id="debian">Debian</h2>
<p><a href="https://manpages.debian.org/bookworm/dpkg-dev/dpkg-buildflags.1.en.html">dpkg-buildflags</a>
is a Perl script, provided by the <code>dpkg-dev</code> package. A lot of the heavy
lifting gets done by the libraries sourced by the script, which live in
<code>&#47;usr&#47;share&#47;perl5&#47;Dpkg</code>. Those libraries come from the <code>libdpkg-perl</code>
package (a dependency of <code>dpkg-dev</code>).</p>
<p>Debian 12 (Bookworm).</p>
<pre><code class="language-shell">$ dpkg-buildflags --query
Vendor: Debian
Environment:

Area: future
Features:
 lfs=no
Builtins:

Area: hardening
Features:
 bindnow=no
 format=yes
 fortify=yes
 pie=yes
 relro=yes
 stackprotector=yes
 stackprotectorstrong=yes
Builtins:
 pie=yes

Area: optimize
Features:
 lto=no
Builtins:

Area: qa
Features:
 bug=no
 canary=no
Builtins:

Area: reproducible
Features:
 fixdebugpath=yes
 fixfilepath=yes
 timeless=yes
Builtins:

Area: sanitize
Features:
 address=no
 leak=no
 thread=no
 undefined=no
Builtins:

Flag: ASFLAGS
Value:
Origin: vendor

Flag: CFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: CPPFLAGS
Value: -Wdate-time -D_FORTIFY_SOURCE=2
Origin: vendor

Flag: CXXFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: DFLAGS
Value: -frelease
Origin: vendor

Flag: FCFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong
Origin: vendor

Flag: FFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong
Origin: vendor

Flag: GCJFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong
Origin: vendor

Flag: LDFLAGS
Value: -Wl,-z,relro
Origin: vendor

Flag: OBJCFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor

Flag: OBJCXXFLAGS
Value: -g -O2 -ffile-prefix-map=&#47;home&#47;user=. -fstack-protector-strong -Wformat -Werror=format-security
Origin: vendor
</code></pre>
<p><a href="https://salsa.debian.org/toolchain-team/gcc/-/blob/80e847249c0b608ef570071c27feddd47d7ad74b/debian/rules2#L211">Debian&#8217;s GCC configure-time flags can be found in this
file</a>,
stored in the CONFARGS variable.</p>
<p><a href="https://salsa.debian.org/pkg-llvm-team/llvm-toolchain/-/blob/d8936ab77b62cf373bf6d3295a4ac4ecb5a56c70/debian/rules">Debian&#8217;s Clang&#47;LLVM configure-time
flags</a>.</p>
<h2 id="fedora">Fedora</h2>
<p>Obtained via <a href="https://docs.fedoraproject.org/en-US/packaging-guidelines/RPMMacros/">Red Hat Package Manager (RPM)
macros</a>.</p>
<p><a href="https://src.fedoraproject.org/rpms/redhat-rpm-config/blob/93063bb396395b9a208a2448fdcf55eccf16219e/f/buildflags.md">Build flags docs
here</a>.</p>
<p>These commands were run in a Fedora 40 virtual machine.</p>
<pre><code class="language-shell">$ rpm --eval "%{optflags}" | tr &#39; &#39; &#39;
&#39; | grep -v &#39;^$&#39;
-O2
-flto=auto
-ffat-lto-objects
-fexceptions
-g
-grecord-gcc-switches
-pipe
-Wall
-Wno-complain-wrong-lang
-Werror=format-security
-Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3
-Wp,-D_GLIBCXX_ASSERTIONS
-specs=&#47;usr&#47;lib&#47;rpm&#47;redhat&#47;redhat-hardened-cc1
-fstack-protector-strong
-specs=&#47;usr&#47;lib&#47;rpm&#47;redhat&#47;redhat-annobin-cc1
-m64
-march=x86-64
-mtune=generic
-fasynchronous-unwind-tables
-fstack-clash-protection
-fcf-protection
-fno-omit-frame-pointer
-mno-omit-leaf-frame-pointer
</code></pre>
<pre><code class="language-shell">$ rpm --eval "%{build_ldflags}" | tr &#39; &#39; &#39;
&#39; | grep -v &#39;^$&#39;
-Wl,-z,relro
-Wl,--as-needed
-Wl,-z,pack-relative-relocs
-Wl,-z,now
-specs=&#47;usr&#47;lib&#47;rpm&#47;redhat&#47;redhat-hardened-ld
-specs=&#47;usr&#47;lib&#47;rpm&#47;redhat&#47;redhat-annobin-cc1
-Wl,--build-id=sha1
</code></pre>
<p><a href="https://src.fedoraproject.org/rpms/gcc/blob/dc203d1b77666d6375f6e5e73dfa9d49a33392a3/f/gcc.spec#_1285">Fedora&#8217;s GCC configure-time
flags</a>.</p>
<p><a href="https://src.fedoraproject.org/rpms/llvm/blob/2d83cf0031e0bb176dcb488a6985d26112e8d0b0/f/llvm.spec#_891">Fedora&#8217;s Clang configure-time
flags</a>.</p>
<h2 id="gentoo">Gentoo</h2>
<p><code>&#47;etc&#47;portage&#47;make.conf</code>. But additional flags get set implicitly as
well. The settings depend on things like what USE flags get set, as an
example.</p>
<p><a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/eclass/toolchain.eclass?id=8b7d8b342b1379cd768c2d9458bd37ca0540baf3#n768">The build flags mainly live in
toolchain.eclass</a>.
<a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/eclass/toolchain.eclass?id=8b7d8b342b1379cd768c2d9458bd37ca0540baf3#n1166">Gentoo&#8217;s configure-time flags for GCC live in
toolchain.eclass</a>
as well.</p>
<p>The <a href="https://gitweb.gentoo.org/proj/gcc-patches.git/tree/14.2.0/gentoo">referenced patches can be found
here</a>.</p>
<p><a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/sys-devel/gcc/gcc-14.2.1_p20241116.ebuild?id=8b7d8b342b1379cd768c2d9458bd37ca0540baf3">Current
GCC</a>.</p>
<p><a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/llvm-core/clang/clang-19.1.4.ebuild?id=8b7d8b342b1379cd768c2d9458bd37ca0540baf3">Current
Clang</a>.</p>
<p><a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/llvm-core/llvm/llvm-19.1.6.ebuild?id=8bce482033f18b19b2c57c964e7fb738f2e69985#n371">Current
LLVM</a>.</p>
<p>The <a href="https://wiki.gentoo.org/wiki/Hardened/Toolchain#Changes">hardened toolchain changes table in
Project:Toolchain</a>
is worth mentioning as well.</p>
<p>While on the subject of Gentoo, ChromiumOS is interesting to think
about. ChromiumOS is the open source base for ChromeOS on Chromebooks.
It uses Portage, Gentoo&#8217;s package management system. I haven&#8217;t looked
into it enough to offer insightful commentary on it&#8212;I&#8217;d have to sift
through a lot of code to talk about it halfway intelligently.</p>
<h2 id="nixos">NixOS</h2>
<p>I&#8217;m presuming something here: that NixOS doesn&#8217;t add CFLAGS&#47;CXXFLAGS
beyond what nixpkgs does. I don&#8217;t see why it would, after all, but I
felt it was important to note that all the same.</p>
<p><a href="https://github.com/NixOS/nixpkgs/blob/0f9814b0086f39e6ffdf17ccb9e2de06875a89a5/doc/stdenv/stdenv.chapter.md#hardening-in-nixpkgs-sec-hardening-in-nixpkgs">Hardening flags get mentioned
here</a>.</p>
<p>Here are the ones used by default at the time of writing (I verified
this by <a href="https://nix-tutorial.gitlabpages.inria.fr/nix-tutorial/first-package.html">copying the .nix file from this
guide</a>
and setting <code>NIX_DEBUG = 2;</code> in the <code>pkgs.stdenv.mkDerivation</code> section,
then built the package):</p>
<pre><code class="language-shell">-fPIC
-Wformat
-Wformat-security
-Werror=format-security
-fstack-protector-strong
--param ssp-buffer-size=4
-O2
-D_FORTIFY_SOURCE=2
-fno-strict-overflow
-Wl,-z,relro
-Wl,-z,now
</code></pre>
<p>If <code>-fzero-call-used-regs=used-gpr</code> gets used, it wasn&#8217;t printed during
the test build.</p>
<p><a href="https://github.com/NixOS/nixpkgs/blob/47a040766c7da47a24f7abfae4472866188a9e91/pkgs/development/compilers/gcc/default.nix">GCC
nixpkg</a>.</p>
<p><a href="https://github.com/NixOS/nixpkgs/blob/47a040766c7da47a24f7abfae4472866188a9e91/pkgs/development/compilers/llvm/common/clang/default.nix">Clang
nixpkg</a>.</p>
<p><a href="https://github.com/NixOS/nixpkgs/blob/47a040766c7da47a24f7abfae4472866188a9e91/pkgs/development/compilers/llvm/common/llvm/default.nix">LLVM
nixpkg</a>.</p>
<h2 id="openbsd">OpenBSD</h2>
<p>Local changes to their integrated (and old) version of GCC get described
in <a href="https://man.openbsd.org/gcc-local">gcc-local</a>. The same applies to
<a href="https://man.openbsd.org/clang-local">clang-local</a>, although it&#8217;s a
current version.</p>
<p><a href="https://cvsweb.openbsd.org/ports/lang/gcc/">OpenBSD&#8217;s GCC port</a> and
<a href="https://cvsweb.openbsd.org/ports/devel/llvm/">OpenBSD&#8217;s LLVM port</a>.</p>
<p><a href="https://cvsweb.openbsd.org/src/gnu/llvm/">Here is the source code for OpenBSD&#8217;s version of
LLVM&#47;Clang</a>. <a href="https://cvsweb.openbsd.org/src/gnu/gcc/">Same for
GCC</a>.</p>
<h2 id="opensuse">OpenSUSE</h2>
<p><a href="https://web.archive.org/web/20241209164113/https://build.opensuse.org/projects/openSUSE:Factory/prjconf">Line 3581
here</a>
should list the options. OpenSUSE:Factory is what they use to build
Tumbleweed packages.</p>
<pre><code class="language-shell">-O2
-Wall
-U_FORTIFY_SOURCE
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-funwind-tables
-fasynchronous-unwind-tables
-fstack-clash-protection
-Werror=return-type
%%{?_lto_cflags}
</code></pre>
<p>There is a <a href="https://build.opensuse.org/projects/openSUSE:Factory/packages/gcc/files/gcc.spec?expand=1&amp;rev=78">dummy package for GCC
here</a>,
and <a href="https://build.opensuse.org/projects/openSUSE:Factory/packages/llvm/files/llvm.spec?expand=1&amp;rev=146">ditto for
clang&#47;llvm</a>.
At the time of writing, the relevant versions are 14 and 19,
respectively.</p>
<p>So, <a href="https://build.opensuse.org/projects/devel:gcc/packages/gcc14/files/gcc14.spec?expand=1&amp;rev=51">OpenSUSE&#8217;s GCC configure-time flags can be found
here</a>.
Starts on line 2510.</p>
<p><a href="https://build.opensuse.org/projects/openSUSE:Factory/packages/llvm19/files/llvm19.spec?expand=1&amp;rev=6">OpenSUSE&#8217;s Clang configure-time flags can be found
here</a>.
Starts on line 1092.</p>
<h2 id="solus">Solus</h2>
<p>These flags come from <a href="https://github.com/getsolus/packages/issues/124">this GitHub
issue</a>. From what I can
tell, the flags get set by <a href="https://github.com/getsolus/ypkg">ypkg</a>.
Specifically,
<a href="https://github.com/getsolus/ypkg/blob/f6cc1aab08b5b446a61a9d927b3c2887d15e9928/ypkg2/ypkgcontext.py">ypkgcontext.py</a>.</p>
<p>To get the latest flags, I guess one would have to figure out how to get
ypkg (or some other piece of the Solus build system) to output them
somehow. I haven&#8217;t done so, but it certainly seems possible.</p>
<p>CFLAGS:</p>
<pre><code class="language-shell">-mtune=generic
-march=x86-64
-g2
-O2
-pipe
-fno-plt
-fPIC
-Wformat
-Wformat-security
-D_FORTIFY_SOURCE=2
-fstack-protector-strong
--param=ssp-buffer-size=32
-fasynchronous-unwind-tables
-ftree-vectorize
-feliminate-unused-debug-types
-Wall
-Wno-error
-Wp,-D_REENTRANT
</code></pre>
<p>CXXFLAGS:</p>
<pre><code class="language-shell">-mtune=generic
-march=x86-64
-g2
-O2
-pipe
-fno-plt
-fPIC
-D_FORTIFY_SOURCE=2
-fstack-protector-strong
--param=ssp-buffer-size=32
-fasynchronous-unwind-tables
-ftree-vectorize
-feliminate-unused-debug-types
-Wall
-Wno-error
-Wp,-D_REENTRANT
</code></pre>
<p>LDFLAGS:</p>
<pre><code class="language-shell">-Wl,--copy-dt-needed-entries
-Wl,-O1
-Wl,-z,relro
-Wl,-z,now
-Wl,-z,max-page-size=0x1000
-Wl,-Bsymbolic-functions
-Wl,--sort-common
</code></pre>
<p><a href="https://github.com/getsolus/packages/blob/c79150396860fbf21e420ce6bfff9d90d50e6436/packages/g/gcc/package.yml#L133">Solus&#8217; GCC configure-time flags can be found
here</a>.</p>
<p><a href="https://github.com/getsolus/packages/blob/c79150396860fbf21e420ce6bfff9d90d50e6436/packages/l/llvm/package.yml#L493">Solus&#8217; Clang&#47;LLVM configure-time flags can be found
here</a>.</p>
<h2 id="ubuntu">Ubuntu</h2>
<p>Retrieved the same way as Debian. Ubuntu 24.10 (oracular).</p>
<pre><code class="language-shell">$ dpkg-buildflags --query
Vendor: Ubuntu
Environment:

Area: abi
Features:
 lfs=no
 time64=yes
Builtins:
 lfs=yes
 time64=yes

Area: future
Features:
 lfs=no
Builtins:

Area: hardening
Features:
 bindnow=no
 branch=yes
 format=yes
 fortify=yes
 pie=yes
 relro=yes
 stackclash=yes
 stackprotector=yes
 stackprotectorstrong=yes
Builtins:
 pie=yes

Area: optimize
Features:
 lto=yes
Builtins:

Area: qa
Features:
 bug=no
 bug-implicit-func=yes
 canary=no
 elfpackagemetadata=no
 framepointer=yes
Builtins:

Area: reproducible
Features:
 fixdebugpath=yes
 fixfilepath=yes
 timeless=yes
Builtins:

Area: sanitize
Features:
 address=no
 leak=no
 thread=no
 undefined=no
Builtins:

Flag: ASFLAGS
Value:
Origin: vendor

Flag: ASFLAGS_FOR_BUILD
Value:
Origin: vendor

Flag: CFLAGS
Value: -g -O2 -Werror=implicit-function-declaration -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CFLAGS_FOR_BUILD
Value: -g -O2 -Werror=implicit-function-declaration -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CPPFLAGS
Value: -Wdate-time -D_FORTIFY_SOURCE=3
Origin: vendor

Flag: CPPFLAGS_FOR_BUILD
Value: -Wdate-time -D_FORTIFY_SOURCE=3
Origin: vendor

Flag: CXXFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: CXXFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: DFLAGS
Value: -frelease
Origin: vendor

Flag: DFLAGS_FOR_BUILD
Value: -frelease
Origin: vendor

Flag: FCFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FCFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: FFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -fcf-protection
Origin: vendor

Flag: LDFLAGS
Value: -Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -Wl,-z,relro
Origin: vendor

Flag: LDFLAGS_FOR_BUILD
Value: -flto=auto -ffat-lto-objects -Wl,-z,relro
Origin: vendor

Flag: OBJCFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCXXFLAGS
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: OBJCXXFLAGS_FOR_BUILD
Value: -g -O2 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -ffile-prefix-map=&#47;home&#47;ubuntu=. -flto=auto -ffat-lto-objects -fstack-protector-strong -fstack-clash-protection -Wformat -Werror=format-security -fcf-protection
Origin: vendor

Flag: RUSTFLAGS
Value: -Cforce-frame-pointers=yes
Origin: vendor

Flag: RUSTFLAGS_FOR_BUILD
Value:
Origin: vendor
</code></pre>
<p>Ubuntu Oracular GCC version appears to be 14. <a href="https://git.launchpad.net/ubuntu/+source/gcc-defaults/tree/debian/rules?h=ubuntu/oracular&amp;id=00df0a295f4a4436ec08ba729711016ac68c0588#n236">Here is the relevant
Ubuntu GCC
file</a>
that I figured that out from. <a href="https://git.launchpad.net/ubuntu/+source/llvm-defaults/tree/debian/rules?h=ubuntu/oracular&amp;id=6914a22e703cf147e4515a4927312fc1dc57346a#n104">Ditto for
Clang&#47;LLVM</a>,
which appears to be 19.</p>
<p><a href="https://git.launchpad.net/ubuntu/+source/gcc-14/tree/debian/rules2?h=ubuntu/oracular&amp;id=196135dbf5be9bda2e279f90063bd5a6b0a9a9a1#n263">Ubuntu&#8217;s GCC configure-time flags begin
here</a>.</p>
<p><a href="https://git.launchpad.net/ubuntu/+source/llvm-toolchain-19/tree/debian/rules?h=ubuntu/oracular&amp;id=0c82c471ddfc3a1357374ff67564160a9187145d#n847">Ubuntu&#8217;s LLVM configure-time flags can be found
here</a>.</p>
<h2 id="void">Void</h2>
<p>I found these by reasoning out what these files in the <a href="https://github.com/void-linux/void-packages">void-packages
repo</a> set:</p>
<ul>
<li><a href="https://github.com/void-linux/void-packages/blob/53b88e33a063b45611f5e3cec12aa6c4b0ab17e2/etc/defaults.conf#L36">defaults.conf</a></li>
<li><a href="https://github.com/void-linux/void-packages/blob/53b88e33a063b45611f5e3cec12aa6c4b0ab17e2/common/build-profiles/x86_64.sh">x86_64.sh</a></li>
<li><a href="https://github.com/void-linux/void-packages/blob/53b88e33a063b45611f5e3cec12aa6c4b0ab17e2/common/build-profiles/bootstrap.sh">bootstrap.sh</a></li>
<li><a href="https://github.com/void-linux/void-packages/blob/53b88e33a063b45611f5e3cec12aa6c4b0ab17e2/common/environment/configure/hardening.sh">hardening.sh</a></li>
</ul>
<p>CFLAGS:</p>
<pre><code class="language-shell">-mtune=generic
-O2
-pipe
-fstack-clash-protection
-D_FORTIFY_SOURCE=2
</code></pre>
<p>CXXFLAGS:</p>
<pre><code class="language-shell">-mtune=generic
-O2
-pipe
-fstack-clash-protection
-D_FORTIFY_SOURCE=2
</code></pre>
<p>LDFLAGS:</p>
<pre><code class="language-shell">-Wl,--as-needed
-Wl,-z,relro
-Wl,-z,now
</code></pre>
<p><a href="https://github.com/void-linux/void-packages/blob/4dcad43166c40b08800bad3c6db1deff3ac4105c/srcpkgs/gcc/template#L205">Void&#8217;s GCC configure-time flags can be found
here</a>.</p>
<p><a href="https://github.com/void-linux/void-packages/blob/4dcad43166c40b08800bad3c6db1deff3ac4105c/srcpkgs/llvm17/template#L7">Void&#8217;s Clang configure-time flags can be found
here</a>.
The main version of clang used by the distro can be <a href="https://github.com/void-linux/void-packages/blob/4dcad43166c40b08800bad3c6db1deff3ac4105c/srcpkgs/llvm/template#L3">found in the llvm
package
template</a>.</p>
<h2 id="caveats">Caveats</h2>
<p>Here are things I haven&#8217;t done, and why I haven&#8217;t done them.</p>
<ul>
<li>Reviewing each patch for gcc&#47;clang for compilation flags. I had a look
at their configure-time flags, but reviewing patches takes more time
than I want to invest at the moment.</li>
<li>Reviewing the configuration and patches of things other than the
compiler that influence binary execution, such as binutils, libc, etc.
These aren&#8217;t really compilation flags, although still interesting. I
guess I do make an exception for LDFLAGS.</li>
<li>Reviewing the configuration of every supported compiler. I&#8217;m only
doing GCC and Clang. I won&#8217;t include their configure-time flags inline,
but I&#8217;ll link to where they can be found (since the configuration of the
compiler often ties closely to build flags).</li>
<li>Comparing each architecture per distribution to find architecture
specific flags. I&#8217;m only doing x86_64 for now.</li>
<li>Covering every single distribution. Generally speaking, outside of
large forks like Ubuntu, I&#8217;m interested in independent distributions
rather than forks. Mainly because I have a feeling this is where the
most difference is likely to be observed.</li>
<li>Covering build flags for every compiled language (Rust, Go, etc).
These are indeed interesting, but my focus is on C&#47;C++, specifically
CFLAGS and CXXFLAGS.</li>
</ul>
<p>My approach favored coverage, not extreme precision. I gathered
compilation flags that I could find in a configuration file (the
equivalent of <code>&#47;etc&#47;portage&#47;make.conf</code> for other distributions), through
some light source code reading, or by executing some utility. I had to
limit myself in some ways because otherwise it was unlikely that I&#8217;d
release this article anytime soon.</p>
<p>Please don&#8217;t take what I&#8217;ve written here as gospel. Something being
absent doesn&#8217;t necessarily prove anything, as the distribution may have
enabled it in a place that I didn&#8217;t look. However, something being
present and reflected in the linked source code <em>does</em> prove that a
distribution uses it in that context, or at least used it at the time of
writing.</p>
<h2 id="areas-for-improvement">Areas for improvement</h2>
<ul>
<li>Including more distributions and systems. There are a lot of them to
consider and some of them would be a pain to gather flags for (lots of
source code review).</li>
<li>Investigating ChromiumOS and Android in particular.</li>
<li>Looking more into areas mentioned in the <a href="https://www.anthes.is/#caveats">Caveats section</a>
(reviewing patches, other toolchain components, etc).</li>
<li>Many others I haven&#8217;t thought of.</li>
</ul>
<h2 id="other-resources">Other resources</h2>
<ul>
<li><a href="https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++.html">Compiler Options Hardening Guide for C and
C++</a></li>
<li><a href="https://github.com/jvoisin/compiler-flags-distro">compiler-flags-distro</a>:
usage of enabled-by-default hardening-related compiler flags across
Linux distributions.</li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/encrypted-headless-raspberry-pi-gentoo.html</guid>
<link>https://www.anthes.is/encrypted-headless-raspberry-pi-gentoo.html</link>
<pubDate>Sat, 06 Jul 2024 00:00:00 +0200</pubDate>
<title>Encrypted headless Raspberry Pi running Gentoo</title>
<description><![CDATA[

<h1 id="encrypted-headless-raspberry-pi-running-gentoo">Encrypted headless Raspberry Pi running Gentoo</h1>
<p>Published: July 6th, 2024</p>
<p>Last updated: July 3rd, 2025</p>
<p>Setting up Gentoo on a Raspberry Pi 4B with full disk encryption took me
about a week, but I&#8217;m happy with the result and learned several things
along the way. I want to share my findings so others don&#8217;t have to spend
as much time figuring this out.</p>
<p>Note that this provides a rough outline, not a step-by-step guide for
installing Gentoo.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#what-my-setup-involves">What my setup involves</a></li>
<li><a href="https://www.anthes.is/#covering-the-microsd-card-installation">Covering the microSD card installation</a>
<ul>
<li><a href="https://www.anthes.is/#wireless-networking">Wireless networking</a></li>
<li><a href="https://www.anthes.is/#serial-console">Serial console</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#summarizing-the-rest">Summarizing the rest</a>
<ul>
<li><a href="https://www.anthes.is/#preparing-the-disk">Preparing the disk</a></li>
<li><a href="https://www.anthes.is/#ensuring-kernel-support">Ensuring kernel support</a></li>
<li><a href="https://www.anthes.is/#compiling-the-raspberry-pi-foundation-kernel-and-installing-it">Compiling the Raspberry Pi foundation kernel and installing it</a></li>
<li><a href="https://www.anthes.is/#editing-configuration-files">Editing configuration files</a></li>
<li><a href="https://www.anthes.is/#copying-the-filesystem-contents-over">Copying the filesystem contents over</a></li>
<li><a href="https://www.anthes.is/#switching-to-gentoo-sources">Switching to gentoo-sources</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#conclusion-and-thoughts">Conclusion and thoughts</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="what-my-setup-involves">What my setup involves</h2>
<p>Here are the components my setup uses:</p>
<ul>
<li>Raspberry Pi 4 Model B, Revision 1.2</li>
<li>Universal Serial Bus (USB) to Universal Asynchronous
Receiver&#47;Transmitter (UART) serial cable</li>
<li>Argon One M.2 case</li>
<li>Non-Volatile Memory Express (NVMe) Solid State Drive (SSD)</li>
<li>Micro Secure Digital (microSD) card</li>
</ul>
<p>I could have used a monitor and keyboard instead of a serial connection.
This would have been easier overall. Since I planned to use the
Raspberry Pi headlessly, I wanted to learn how to set it up without a
keyboard and monitor.</p>
<h2 id="covering-the-microsd-card-installation">Covering the microSD card installation</h2>
<p>I mainly followed <a href="https://wiki.gentoo.org/wiki/Raspberry_Pi_Install_Guide">Gentoo&#8217;s official Raspberry Pi Install
Guide</a> and
<a href="https://wiki.gentoo.org/wiki/Raspberry_Pi4_64_Bit_Install">Raspberry Pi4 64 Bit
Install</a> to
get an initial Gentoo installation on a microSD card. I want to discuss
some difficulties I encountered during this process.</p>
<p>The most challenging parts of the installation were getting wireless
networking and the serial console working. I&#8217;ll cover each separately.</p>
<h3 id="wireless-networking">Wireless networking</h3>
<p>Getting wireless networking functional required handling a couple of
tasks correctly:</p>
<ol>
<li><p>Download the firmware, place it in the appropriate locations, and
create symbolic links. <a href="https://wiki.gentoo.org/wiki/Raspberry_Pi_Install_Guide#Installing_the_Raspberry_Pi_Foundation_files">The Gentoo wiki covers this
process</a>.
I found that referencing the Raspberry Pi Operating System (OS)
directory listing for <code>&#47;usr&#47;lib&#47;firmware&#47;brcm&#47;</code> helped me understand the
symbolic link naming scheme.</p>
<p>Here&#8217;s what a working <code>&#47;usr&#47;lib&#47;firmware&#47;brcm&#47;</code> looks like for me
(though note that I haven&#8217;t tested bluetooth):</p>
<pre><code>$ ls -lh &#47;usr&#47;lib&#47;firmware&#47;brcm&#47;
total 704K
-rw-r--r-- 1 root root  63K Jun 24 12:07 BCM4345C0.hcd
lrwxrwxrwx 1 root root   13 Jun 24 12:07 BCM4345C0.raspberrypi,4-model-b.hcd -&#62; BCM4345C0.hcd
-rw-r--r-- 1 root root 629K Jun 24 12:04 brcmfmac43455-sdio.bin
-rw-r--r-- 1 root root 2.7K Jun 24 12:05 brcmfmac43455-sdio.clm_blob
lrwxrwxrwx 1 root root   22 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.bin -&#62; brcmfmac43455-sdio.bin
lrwxrwxrwx 1 root root   27 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.clm_blob -&#62; brcmfmac43455-sdio.clm_blob
lrwxrwxrwx 1 root root   22 Jun 24 12:06 brcmfmac43455-sdio.raspberrypi,4-model-b.txt -&#62; brcmfmac43455-sdio.txt
-rw-r--r-- 1 root root 2.1K Jun 24 12:05 brcmfmac43455-sdio.txt
</code></pre></li>
<li><p>Install networking software like <code>net-misc&#47;dhcpcd</code> and
<code>net-misc&#47;wpa_supplicant</code>, then configure the software as needed. The
<a href="https://wiki.gentoo.org/wiki/Handbook:Main_Page">Gentoo handbook</a>
documents these steps.</p></li>
</ol>
<p>Despite the second step appearing straightforward, I encountered an
issue. How do I install networking tools when I need those same tools to
configure networking?</p>
<p>For many people, the easiest solution would be plugging in an Ethernet
cable and installing packages that way. I also read that downloading the
distfiles and copying them over manually works, since in theory,
<code>emerge</code> won&#8217;t need to download anything.</p>
<p>I already knew that I wouldn&#8217;t have Ethernet access, so I tried the
distfiles method and received this error:</p>
<pre><code>root@nasberry &#47;etc&#47;portage # emerge --ask net-misc&#47;dhcpcd net-wireless&#47;wpa_supplicant

!!! &#47;etc&#47;portage&#47;make.profile is not a symlink and will probably prevent most merges.
!!! It should point into a profile within &#47;var&#47;db&#47;repos&#47;gentoo&#47;profiles&#47;
!!! (You can safely ignore this message when syncing. It&#39;s harmless.)


!!! Your current profile is invalid. If you have just changed your profile
!!! configuration, you should revert back to the previous configuration.
!!! Allowed actions are limited to --help, --info, --search, --sync, and
!!! --version.
</code></pre>
<p>After investigating further, I realized that <code>&#47;var&#47;db&#47;repos&#47;gentoo</code>
didn&#8217;t exist. I would need to run <code>emerge-webrsync</code> to get it, which
requires networking.</p>
<p>What worked for me was to plug the microSD card into a USB adapter,
mount the root filesystem on my laptop, create a <a href="https://wiki.gentoo.org/wiki/Embedded_Handbook/General/Compiling_with_QEMU_user_chroot">Quick Emulator (QEMU)
chroot</a>,
and then compile the software I needed inside the chroot using my
laptop&#8217;s networking.</p>
<h3 id="serial-console">Serial console</h3>
<p>To get the serial console working, I completed these steps:</p>
<ol>
<li><p>I ensured that <code>enable_uart=1</code> appeared in <code>&#47;boot&#47;config.txt</code>. I
found this in <a href="https://www.raspberrypi.com/documentation/computers/config_txt.html">the Raspberry Pi documentation for
<code>config.txt</code></a>.</p></li>
<li><p>I added <code>console=tty console=serial0,115200</code> to <code>&#47;boot&#47;cmdline.txt</code>.
<a href="https://www.raspberrypi.com/documentation/computers/configuration.html#kernel-command-line-cmdline-txt"><code>cmdline.txt</code> documentation is available
here</a>.</p>
<p>Note that the order matters because whichever <code>console=</code> entry
appears last becomes the destination for <code>&#47;dev&#47;console</code>. I discovered
this because mine were in the wrong order and the Linux Unified Key
Setup (LUKS) decryption prompt, as well as OpenRC output, never
appeared on my serial console.</p>
<p>More information is available at the <a href="https://www.kernel.org/doc/html/latest/admin-guide/serial-console.html">kernel.org documentation for
the serial
console</a>.</p></li>
<li><p>For <a href="https://www.raspberrypi.com/documentation/computers/configuration.html#enabling-early-console-for-linux">early console
support</a>,
I added <code>earlycon=uart8250,mmio32,0xfe215040</code> to <code>&#47;boot&#47;cmdline.txt</code>.</p></li>
<li><p>To get a tty, I uncommented the line in <code>&#47;etc&#47;inittab</code> that points to
the <code>&#47;dev&#47;ttyS0</code> device.</p>
<pre><code># SERIAL CONSOLES
s0:12345:respawn:&#47;sbin&#47;agetty -L 115200 ttyS0 vt100
#s1:12345:respawn:&#47;sbin&#47;agetty -L 115200 ttyS1 vt100
</code></pre></li>
</ol>
<h2 id="summarizing-the-rest">Summarizing the rest</h2>
<p>At this point, I had a fully functioning Gentoo installation with
wireless networking and serial console access on the microSD card. The
next challenge was transferring the root filesystem to an encrypted SSD
and getting it working.</p>
<p>The approach I used involves having the Raspberry Pi 4B read firmware
files and everything else needed to boot from the unencrypted Extensible
Firmware Interface (EFI) partition on the microSD card.
<code>&#47;boot&#47;config.txt</code> and <code>&#47;boot&#47;cmdline.txt</code> contain configuration entries
and parameters relevant to the boot process.</p>
<h3 id="preparing-the-disk">Preparing the disk</h3>
<p>I <a href="https://wiki.archlinux.org/title/Securely_wipe_disk">securely wiped the
disk</a> by
overwriting the SSD with random data to hinder cryptanalysis and
performed a <a href="https://wiki.archlinux.org/title/SSD_Memory_Cell_Clearing">memory cell
clearing</a>
(also known as &#8220;secure erase&#8221;).</p>
<p>After that, I created a <a href="https://wiki.archlinux.org/title/Full_disk_encryption#LVM_on_LUKS">LUKS volume with Logical Volume Manager (LVM)
inside
it</a>
on the SSD. Then I <a href="https://wiki.gentoo.org/wiki/Handbook:AMD64/Installation/Disks">created
filesystems</a>
on the logical volumes.</p>
<h3 id="ensuring-kernel-support">Ensuring kernel support</h3>
<p>I ensured that the kernel included support for relevant technologies. In
my case, this included <a href="https://wiki.gentoo.org/wiki/LVM">LVM</a>,
<a href="https://wiki.gentoo.org/wiki/Dm-crypt">dm-crypt</a>, and <a href="https://wiki.gentoo.org/wiki/Xfs">X File System
(XFS)</a>, as the system needs these
during the boot process.</p>
<p>You can choose between <code>sys-kernel&#47;raspberrypi-sources</code> and
<code>sys-kernel&#47;gentoo-sources</code>. I can confirm that both work, although I
started with <code>sys-kernel&#47;raspberrypi-sources</code> first.
<code>sys-kernel&#47;gentoo-sources</code> lacks some features like device tree overlay
support, but it works for my use case.</p>
<p>Other kernels like the distribution kernel could work, but I haven&#8217;t
tested them.</p>
<h3 id="compiling-the-raspberry-pi-foundation-kernel-and-installing-it">Compiling the Raspberry Pi foundation kernel and installing it</h3>
<p>I followed instructions from the <a href="https://www.raspberrypi.com/documentation/computers/linux_kernel.html">Linux kernel section of the Raspberry
Pi
docs</a>,
adjusting where necessary:</p>
<p>Create a backup of the previous kernel image if present.</p>
<pre><code># cp &#47;boot&#47;kernel8.img &#47;boot&#47;kernel8.img.bak
</code></pre>
<p>Change directory to kernel sources.</p>
<pre><code># cd &#47;usr&#47;src&#47;linux
</code></pre>
<p>Build the kernel. The Raspberry Pi docs recommend setting make jobs to
1.5x the number of processors.</p>
<pre><code># make -j6 Image.gz modules dtbs
</code></pre>
<p>Install the kernel modules. Complete this step before generating the
initramfs, since the initramfs uses the modules created from this step.</p>
<pre><code># make -j6 modules_install
</code></pre>
<p>Run <code>make install</code> to generate the initramfs since you&#8217;ll need it to
decrypt and mount the root filesystem. <code>-j6</code> doesn&#8217;t seem to provide any
benefit here.</p>
<pre><code># make install
</code></pre>
<p>Copy necessary files to the correct locations.</p>
<pre><code># cp arch&#47;arm64&#47;boot&#47;Image.gz &#47;boot&#47;kernel8.img
# cp arch&#47;arm64&#47;boot&#47;dts&#47;broadcom&#47;*.dtb &#47;boot&#47;
# cp arch&#47;arm64&#47;boot&#47;dts&#47;overlays&#47;*.dtb* &#47;boot&#47;overlays&#47;
# cp arch&#47;arm64&#47;boot&#47;dts&#47;overlays&#47;README &#47;boot&#47;overlays&#47;
</code></pre>
<h3 id="editing-configuration-files">Editing configuration files</h3>
<p>Update <code>&#47;etc&#47;fstab</code> to include the NVMe disk instead of the SD card.</p>
<pre><code>#&#47;dev&#47;mmcblk0p3 &#47;   ext4    defaults    0   0
&#47;dev&#47;gentoolvm&#47;root &#47;   xfs defaults    0   1
</code></pre>
<p>Update <code>&#47;boot&#47;config.txt</code>. The system will automatically select
<code>kernel8.img</code> as the default kernel, but you need to add the initramfs.</p>
<pre><code>initramfs initramfs-6.6.31_p20240529-raspberrypi-v8.img followkernel
</code></pre>
<p>Update <code>&#47;boot&#47;cmdline.txt</code> so it provides information about the root
filesystem location (<code>root=UUID=...</code>), which LUKS volume to decrypt
(<code>rd.luks.uuid=...</code>), and which LVM volume group to activate
(<code>rd.lvm.vg=gentoolvm</code>). You can find UUIDs with <code>blkid</code>.</p>
<pre><code>root=UUID=... rd.luks.uuid=... rd.lvm.vg=gentoolvm
</code></pre>
<h3 id="copying-the-filesystem-contents-over">Copying the filesystem contents over</h3>
<p>I created a full system backup of the Raspberry Pi&#8217;s microSD card with
bsdtar.</p>
<pre><code># bsdtar \
    --acls \
    --xattrs \
    --zstd \
    --exclude=&#39;&#47;dev&#47;*&#39; \
    --exclude=&#39;&#47;proc&#47;*&#39; \
    --exclude=&#39;&#47;sys&#47;*&#39; \
    --exclude=&#39;&#47;tmp&#47;*&#39; \
    --exclude=&#39;&#47;run&#47;*&#39; \
    --exclude=&#39;&#47;mnt&#47;*&#39; \
    --exclude=&#39;&#47;media&#47;*&#39; \
    --exclude=&#39;&#47;lost+found&#47;&#39; \
    -cvaf &#47;home&#47;user&#47;backup.tar.zst \
    &#47;
</code></pre>
<p>Then I unpacked it onto the SSD. I had decrypted the LUKS partition and
mounted the filesystems at <code>&#47;mnt</code>.</p>
<pre><code># cd &#47;mnt
# bsdtar --acls --xattrs -xvpf &#47;home&#47;user&#47;backup.tar.zst
</code></pre>
<p>After rebooting, the system presented the decryption prompt and I could
boot successfully.</p>
<h3 id="switching-to-gentoo-sources">Switching to gentoo-sources</h3>
<p>The Raspberry Pi foundation kernel lacks security support from Gentoo,
so I wanted to switch to <code>sys-kernel&#47;gentoo-sources</code>. This process is
straightforward.</p>
<p>Change directory to the new kernel sources.</p>
<pre><code>$ cd &#47;usr&#47;src&#47;linux-6.6.30-gentoo
</code></pre>
<p>Copy the working config over.</p>
<pre><code># cp &#47;usr&#47;src&#47;linux-6.6.31_p20240529-raspberrypi&#47;.config .
</code></pre>
<p>Update the current kernel configuration so it works with this kernel.</p>
<pre><code># make oldconfig
</code></pre>
<p>Follow the previous process to build the kernel, except skip copying the
overlay files since <code>sys-kernel&#47;gentoo-sources</code> doesn&#8217;t create them.
Once complete, update <code>&#47;boot&#47;config.txt</code> so it points to the correct
initramfs file.</p>
<p>Another step I had to complete was updating <code>&#47;boot&#47;cmdline.txt</code> to
include <code>8250.nr_uarts=1</code>, otherwise the <a href="https://forums.raspberrypi.com/viewtopic.php?t=246215">serial console fails to
initialize</a>.</p>
<h2 id="conclusion-and-thoughts">Conclusion and thoughts</h2>
<p>This setup won&#8217;t prevent a determined adversary from removing the
microSD card and tampering with anything on the boot partition (the
kernel, the initramfs, or the firmware). It also doesn&#8217;t prevent
hardware-level attacks. If an attacker leverages these, they can obtain
the encryption password entered on boot.</p>
<p>Still, the encryption protects the data when the Raspberry Pi is powered
off. This setup raises the difficulty level for data recovery, but it
doesn&#8217;t account for sophisticated attacks like this.</p>
<p>If those types of attacks ever became part of my threat model, I would
look into Secure Boot on the Raspberry Pi.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/how-to-find-the-best-software-for-you.html</guid>
<link>https://www.anthes.is/how-to-find-the-best-software-for-you.html</link>
<pubDate>Wed, 14 Feb 2024 00:00:00 +0100</pubDate>
<title>How to find the best software for you</title>
<description><![CDATA[

<h1 id="how-to-find-the-best-software-for-you">How to find the best software for you</h1>
<p>Published: February 14th, 2024</p>
<p>Last updated: July 4th, 2025</p>
<p>With software, there can be so many choices that figuring out where to
start is less easy than it could be, let alone deciding which project
fits your needs best. In my experience, finding good quality software
usually involves taking a moment to ask the right questions and look in
the right places.</p>
<p>One important thing to remember: don&#8217;t let perfect get in the way of
good enough. The best software for you is whatever helps you achieve
your goals while meeting your requirements.</p>
<p>Also, note that I rarely go through this formal of a process. Think of
it as a collection of ideas that I wanted to organize.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#whats-your-use-case-for-the-software">What&#8217;s your use case for the software?</a>
<ul>
<li><a href="https://www.anthes.is/#questions-to-determine-your-software-use-case">Questions to determine your software use case</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#what-are-some-methods-to-find-software">What are some methods to find software?</a></li>
<li><a href="https://www.anthes.is/#what-are-some-ways-to-compare-software-projects">What are some ways to compare software projects?</a>
<ul>
<li><a href="https://www.anthes.is/#how-to-review-open-source-repositories">How to review open source repositories</a></li>
<li><a href="https://www.anthes.is/#other-things-to-review-and-ask">Other things to review and ask</a></li>
</ul></li>
</ul>
<!-- mtoc-end -->
<h2 id="whats-your-use-case-for-the-software">What&#8217;s your use case for the software?</h2>
<p>Plenty of software exists. But whether that software is useful to you
depends on if it matches your specific use case. Here&#8217;s a list of
questions that can help determine exactly what you need.</p>
<h3 id="questions-to-determine-your-software-use-case">Questions to determine your software use case</h3>
<ul>
<li><p>What problem does the software solve? If it replaces a prior solution,
what about the previous approach didn&#8217;t work and what needs to be
different this time? Understanding these things provides the foundation
for everything else. At its core, choosing software involves finding
what solves a problem you have.</p></li>
<li><p>Can you break that problem into smaller ones so that specialized tools
can handle each part? Sometimes you may not need another application and
can handle it with programs you already have, especially if you&#8217;re good
at shell scripting.</p></li>
<li><p>Who are the target users and what do they require? Consider their
accessibility requirements and whether the program fulfills those. User
Interface (UI) and User Experience (UX) considerations most likely fall
under this category as well.</p></li>
<li><p>What capabilities do you need? Let&#8217;s say you have a list of web pages
and need to take a screenshot of each one. A utility that only checks if
those websites remain online may prove useful for other reasons, but it
won&#8217;t handle everything you need on its own. You might also need
features related to tool integration, like the ability to work with
certain data formats, databases, Application Programming Interfaces
(APIs), or other common data exchange systems.</p></li>
<li><p>What capabilities <em>aren&#8217;t</em> needed? The simpler a piece of software,
the fewer places exist in the code for bugs. Perhaps you&#8217;re thinking of
creating a blog written in Markdown, one that doesn&#8217;t need dynamic
elements like JavaScript or PHP. With this in mind, you could rule out
content management systems and start looking at static site generators
or Markdown to HyperText Markup Language (HTML) converters instead.</p></li>
<li><p>What operating systems and&#47;or environments must the software support?
Software proves more useful when you can actually install and run it.
On that note, you can often deploy programs in a container or virtual
machine if needed. Or you can try porting it yourself.</p></li>
<li><p>What are your privacy and security requirements? To give a meaningful
answer, you need to know your threat model first. There&#8217;s little sense
in assuming that something provides privacy or security without a threat
model, since it remains unknown what it protects from or what stays
private from whom. Thinking about these things helps you protect your
assets and could save you some headaches.</p></li>
<li><p>Do you have any preferences or requirements when it comes to software
licensing, source code availability, and similar factors? Here are some
examples where the differences between open source licenses in
particular matter:</p>
<ul>
<li>Perhaps you need to incorporate some open source code into
proprietary software, or maybe you favor ease of adoption. In this
case, something with a permissive license could work well.
Massachusetts Institute of Technology (MIT), Berkeley Software
Distribution (BSD), and Internet Systems Consortium (ISC) all fall
under the permissive license category.</li>
<li>If you feel strongly that source code, including any derivative
works, should always remain open to all, then you may want to consider
a copyleft license. GNU General Public License (GPL), Affero General
Public License (AGPL), and Mozilla Public License (MPL) all fall under
the copyleft license category.</li>
</ul></li>
<li><p>What other constraints do you have? These can include factors like
performance, scalability, user support, compliance with standards and
regulations, and so on. Knowing these things helps you focus on the
options that will most likely work for you.</p></li>
</ul>
<h2 id="what-are-some-methods-to-find-software">What are some methods to find software?</h2>
<p>Now that you have a better idea of what you need, you can start
searching for software. Remember to verify that a given resource
deserves your trust before following any instructions or taking any
advice.</p>
<ul>
<li><p>Searching websites or applications that provide or index software.
This offers the most direct option. However, sometimes it can take
some digging to figure out how to find things. Browsing topics or
&#8220;awesome lists&#8221; on a place like GitHub can provide some insight into
what&#8217;s available.</p></li>
<li><p>Reading discussions (think forums, chat rooms, and mailing lists, to
name a few), wikis, blogs, or other written resources. You can get some
good ideas by visiting these places and skimming through them.</p></li>
<li><p>Consulting other forms of media such as videos. These tend to take
more time, or at least they do for me. However, they remain valuable and
can lead to some good finds.</p></li>
<li><p>Prompting Large Language Models (LLMs) to brainstorm ideas with you,
make suggestions, and explain concepts. Remember to stay somewhat
skeptical and fact-check the output to verify legitimacy. Still,
Artificial Intelligence (AI) shows promise despite its current
limitations.</p></li>
<li><p>Asking knowledgeable people what they use, how they found it, and why
they chose it over the alternatives. You&#8217;ll likely get a better response
from people if you ask good questions and show them that you value their
time; in other words, it should be evident that you&#8217;ve already done some
research, such as using a search engine and consulting other publicly
available resources.</p></li>
</ul>
<h2 id="what-are-some-ways-to-compare-software-projects">What are some ways to compare software projects?</h2>
<p>Once you&#8217;ve found some software through trustworthy means that fits your
use case, another question arises: how do you decide between them?</p>
<p>Certain positive signals can indicate that a project deserves
consideration. The signals themselves don&#8217;t offer any guarantee of code
quality; only examining the code itself can provide that. Even so, these
have been good heuristics (rules of thumb) to follow for me because many
of them correlate with code quality.</p>
<p>Though if you have time, motivation, and skill, reading
through the source code remains a good idea.</p>
<h3 id="how-to-review-open-source-repositories">How to review open source repositories</h3>
<p>Here are some things to examine when reviewing open source projects.
Some of these will depend on the frontend (GitHub, GitLab, etc).</p>
<ul>
<li><p>Check the license and README, ensuring that everything there fits with
your goals and use case.</p></li>
<li><p>Check what programming languages the project uses. Can you fix or
extend software written in those languages? Do you prefer something
about the programming language used for one project more than the
language used by another?</p></li>
<li><p>Does a security policy exist? If so, what does it say? Vulnerability
disclosure programs, bug bounties, and regular audits are good
signals.</p></li>
<li><p>Do they incorporate best practices and standards in the industry?</p>
<ul>
<li>Tests</li>
<li>Linters</li>
<li>Static analysis tools</li>
<li>Continuous Integration and Continuous Delivery (CI&#47;CD)</li>
<li>Conventional commits</li>
<li>Semantic versioning</li>
</ul></li>
<li><p>When did the project first start? Repositories that have remained
active for a while may be more likely to stick around.</p></li>
<li><p>What different types of activity occur and how recently have they
happened? These show whether a project receives active maintenance,
which often means quicker resolution of bugs and vulnerabilities.
Remember that the simpler the code, the less maintenance it probably
needs.</p>
<ul>
<li>When did the last commit occur?</li>
<li>How frequently does this repository get new commits?</li>
<li>When did the last release happen?</li>
<li>How frequently do new releases appear?</li>
<li>What other kinds of activity happened in the past month?</li>
</ul></li>
<li><p>Indicators of popularity, such as the number of stars and forks. But
exercise caution with these. People have been able to buy fake stars and
forks in various places for a while now.</p>
<ul>
<li>One idea: glance at the forks and see if any of them show
significant activity. Look at the accounts responsible for the more
popular forks and check for things that are more difficult to fake,
like merged pull requests in reputable repositories that they don&#8217;t
own.</li>
</ul></li>
<li><p>The number of people that contribute regularly. Bus factor deserves
consideration—how many people would have to get hit by a bus for the
entire thing to stall indefinitely? Does that represent an acceptable
level of risk for something you may use consistently? Are you willing to
maintain it yourself if the project goes dormant?</p></li>
<li><p>Open and closed issues. Observe the ratio between them and apply
various filters, combining them if it seems interesting. The manner in
which maintainers resolve problems can speak volumes about the level of
care they put into the software.</p>
<ul>
<li>Sort by number of comments, from greatest to least. These are
significant to the community in some way. The same goes for sorting by
reactions.</li>
<li>Sort by update time, from most recent to least, as a way of gleaning
where people&#8217;s attention goes.</li>
<li>Any issues with interesting labels, especially those related to
security. Closed issues with a label like &#8220;wontfix&#8221; or &#8220;invalid&#8221; can
also be illuminating.</li>
<li>Issues opened by important people, like the repository author.</li>
<li>Pinned issues.</li>
</ul></li>
<li><p>Pull requests. Ideally, some degree of scrutiny exists, and yet
also a willingness to accept good contributions. You can use many of the
same sorting and filtering options as before.</p>
<ul>
<li>Closed pull requests, both merged and unmerged.</li>
<li>Open pull requests that link to issues (you can do this the other
way around, too).</li>
<li>Do procedures exist that contributors need to follow? Do multiple
people review pull requests before merge? Both are good signals.</li>
</ul></li>
<li><p>The documentation. READMEs, manuals, official wikis and blogs, release
notes, etc.</p>
<ul>
<li>How much effort went into the documentation? You could look at
commits and pull requests related to this, but you can also determine
this by feel.</li>
<li>Does the documentation stay up to date? Does it make sense to you?</li>
</ul></li>
<li><p>How many external dependencies does the project pull in? These
represent unknowns that you implicitly trust when you use the
software. Dependencies aren&#8217;t inherently good or bad. They just provide
another data point to consider.</p></li>
</ul>
<h3 id="other-things-to-review-and-ask">Other things to review and ask</h3>
<ul>
<li><p>See if you can find the project on a website that lists publicly
disclosed vulnerabilities. What vulnerabilities have appeared before and
do any patterns exist? For instance: does the same class of
vulnerability keep cropping up? Do vulnerabilities typically prove
severe with low exploit complexity?</p></li>
<li><p>Have security researchers performed audits, and do they happen on a
consistent basis? What were the results? What did the security
researchers think of the product&#8217;s design?</p></li>
<li><p>How have the developers handled vulnerabilities in the past? Have they
maintained transparency with their users when it comes to security
issues?</p></li>
<li><p>Do the developers cryptographically sign their releases and&#47;or commits?</p></li>
<li><p>Can you install the software through your system&#8217;s package manager?</p></li>
<li><p>Do some research and find other places that mention the software,
especially community-oriented ones that the creators don&#8217;t own or
moderate. What do other people say about it?</p></li>
<li><p>If there&#8217;s a company behind the software, what are the public&#8217;s
impressions of that company? Do signs exist that the company cares
about its users and the quality of its product? Where does their money
come from and where does it go?</p></li>
<li><p>Look through any important documents, like terms of service and
privacy policy. Does anything interesting or out of place appear there?</p></li>
<li><p>If there&#8217;s a dedicated website, did they put it together in a logical,
effective fashion?</p></li>
<li><p>Does the software use a memory-safe programming language?</p></li>
<li><p>You can run the software in a virtual machine you don&#8217;t use for
anything else and study its behavior.</p></li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/domain-sift.html</guid>
<link>https://www.anthes.is/domain-sift.html</link>
<pubDate>Mon, 04 Sep 2023 00:00:00 +0200</pubDate>
<title>domain-sift: extract and format domains</title>
<description><![CDATA[

<h1 id="domain-sift-extract-and-format-domains">domain-sift: extract and format domains</h1>
<p>Last updated: July 8th, 2025</p>
<p><code>domain-sift</code> is a Perl script that extracts unique domains from at
least one provided file and prints them to standard output in a given
format. If you don&#8217;t provide a file, <code>domain-sift</code> reads from Standard
Input (STDIN) instead.</p>
<p>One use of this utility: extract domains from blocklists that contain
known malicious or otherwise undesirable domains, then format them so
that a Domain Name System (DNS) resolver can block those domains.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#project-structure">Project structure</a></li>
<li><a href="https://www.anthes.is/#installation">Installation</a></li>
<li><a href="https://www.anthes.is/#documentation">Documentation</a></li>
<li><a href="https://www.anthes.is/#domain-sift-and-unwind">domain-sift and unwind</a></li>
<li><a href="https://www.anthes.is/#domain-sift-and-unbound">domain-sift and Unbound</a></li>
<li><a href="https://www.anthes.is/#domain-sift-and-unbound-rpz">domain-sift and Unbound (RPZ)</a></li>
<li><a href="https://www.anthes.is/#about-blocklist-sources">About blocklist sources</a></li>
<li><a href="https://www.anthes.is/#caveats">Caveats</a></li>
<li><a href="https://www.anthes.is/#license">License</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="project-structure">Project structure</h2>
<pre><code>|-- Changes
|-- LICENSE
|-- MANIFEST
|-- Makefile.PL
|-- README.md
|-- bin
|   `-- domain-sift
|-- lib
|   `-- Domain
|       |-- Sift
|       |   |-- Manipulate.pm
|       |   `-- Match.pm
|       `-- Sift.pm
`-- t
    |-- 00-load.t
    |-- Domain-Sift-Manipulate.t
    |-- Domain-Sift-Match.t
    |-- manifest.t
    |-- pod-coverage.t
    `-- pod.t
</code></pre>
<h2 id="installation">Installation</h2>
<p>To install <code>domain-sift</code>, <a href="https://github.com/maybebyte/domain-sift/releases">download the most recent
release</a> and run the
following commands inside the source directory. Note that <code>domain-sift</code>
requires Perl 5.36 or later, since subroutine signatures are no longer
experimental in that release.</p>
<pre><code>$ perl Makefile.PL
$ make
$ make test
# make install
</code></pre>
<h2 id="documentation">Documentation</h2>
<p>After installation, you can read the documentation with <code>perldoc</code>. <code>man</code>
often works as well.</p>
<pre><code>$ perldoc Domain::Sift
$ perldoc Domain::Sift::Match
$ perldoc Domain::Sift::Manipulate
$ perldoc domain-sift
</code></pre>
<h2 id="domain-sift-and-unwind">domain-sift and unwind</h2>
<p>Here&#8217;s how to use <code>domain-sift</code> with
<a href="https://man.openbsd.org/unwind"><code>unwind(8)</code></a> on OpenBSD.</p>
<ol>
<li> Get domains from your blocklist source:</li>
</ol>
<pre><code>$ domain-sift &#47;path&#47;to&#47;blocklist_source &#62; blocklist
</code></pre>
<ol start="2">
<li> Move your blocklist to <code>&#47;etc&#47;blocklist</code>:</li>
</ol>
<pre><code># mv blocklist &#47;etc&#47;blocklist
</code></pre>
<ol start="3">
<li> Edit your <code>unwind.conf</code> to include your new blocklist:</li>
</ol>
<pre><code>block list "&#47;etc&#47;blocklist"
</code></pre>
<ol start="4">
<li> Restart <code>unwind</code>:</li>
</ol>
<pre><code># rcctl restart unwind
</code></pre>
<h2 id="domain-sift-and-unbound">domain-sift and Unbound</h2>
<p>Here&#8217;s how to use <code>domain-sift</code> with <a href="https://man.openbsd.org/unbound"><code>unbound(8)</code></a> on OpenBSD.</p>
<ol>
<li> Get domains from your blocklist source:</li>
</ol>
<pre><code>$ domain-sift -f unbound &#47;path&#47;to&#47;blocklist_source &#62; blocklist
</code></pre>
<ol start="2">
<li> Move the blocklist to <code>&#47;var&#47;unbound&#47;etc</code>.</li>
</ol>
<pre><code># mv blocklist &#47;var&#47;unbound&#47;etc&#47;blocklist
</code></pre>
<ol start="3">
<li> Edit your <code>unbound.conf</code> to include your new blocklist:</li>
</ol>
<pre><code class="language-yaml">include: "&#47;var&#47;unbound&#47;etc&#47;blocklist"
</code></pre>
<ol start="4">
<li> Restart Unbound.</li>
</ol>
<pre><code># rcctl restart unbound
</code></pre>
<h2 id="domain-sift-and-unbound-rpz">domain-sift and Unbound (RPZ)</h2>
<p><code>domain-sift</code> also supports the Response Policy Zone (RPZ) format. <a href="https://datatracker.ietf.org/doc/draft-vixie-dnsop-dns-rpz/">This
Internet Draft defines the RPZ
format</a>.</p>
<p>With RPZ, you can create DNS blocking policies in a standardized way.
You can even block wildcarded domains (<code>*.example.com</code> also blocks
<code>subdomain.example.com</code>, <code>subdomain.subdomain.example.com</code>, and so on).</p>
<p>Here&#8217;s how to use <code>domain-sift</code> with Unbound and RPZ on OpenBSD.</p>
<ol>
<li> Get domains from your blocklist source:</li>
</ol>
<pre><code>$ domain-sift -f rpz &#47;path&#47;to&#47;blocklist_source &#62; blocklist
</code></pre>
<ol start="2">
<li> Edit your <code>unbound.conf</code>:</li>
</ol>
<pre><code class="language-yaml">rpz:
  name: rpz.home.arpa
  zonefile: &#47;var&#47;unbound&#47;etc&#47;rpz-block.zone
  #rpz-log: yes
  rpz-signal-nxdomain-ra: yes
</code></pre>
<p>NOTE: <code>rpz.home.arpa</code> serves as an example. The name entry may be
different in your case. In a Local Area Network (LAN) where Unbound runs
on the gateway&#47;router, make sure that a <code>local-data</code> entry exists
somewhere so that the name you chose resolves. Something like this
should work:</p>
<pre><code class="language-yaml">local-data: "rpz.home.arpa. IN A x.x.x.x"
</code></pre>
<p>You&#8217;ll need to replace <code>x.x.x.x</code> with the machine&#8217;s actual IP address.</p>
<ol start="3">
<li> Create <code>&#47;var&#47;unbound&#47;etc&#47;rpz-block.zone</code>:</li>
</ol>
<pre><code class="language-DNS">$ORIGIN rpz.home.arpa.
$INCLUDE &#47;var&#47;unbound&#47;etc&#47;blocklist
</code></pre>
<ol start="4">
<li> Make sure that you move <code>blocklist</code> to the correct location:</li>
</ol>
<pre><code># mv &#47;path&#47;to&#47;blocklist &#47;var&#47;unbound&#47;etc&#47;blocklist
</code></pre>
<ol start="5">
<li> Restart Unbound:</li>
</ol>
<pre><code># rcctl restart unbound
</code></pre>
<h2 id="about-blocklist-sources">About blocklist sources</h2>
<p><code>domain-sift</code> only deals with extracting domains from text files and
formatting them. It doesn&#8217;t fetch blocklists or provide them.</p>
<p>The design explicitly includes this limitation for a few reasons:</p>
<ol>
<li><p>It follows the Unix philosophy: do one thing well; read from a file
or STDIN; print to Standard Output (STDOUT).</p></li>
<li><p>It lets <code>domain-sift</code> use minimal
<a href="https://man.openbsd.org/pledge"><code>pledge(2)</code></a> promises through
<a href="https://man.openbsd.org/OpenBSD%3A%3APledge"><code>OpenBSD::Pledge(3p)</code></a>.</p></li>
<li><p>The simple design makes it much more flexible and portable.</p></li>
</ol>
<p>Here&#8217;s roughly what I use to fetch blocklists:</p>
<pre><code>$ grep -Ev &#39;^#&#39; blocklist_urls | xargs -- ftp -o - | domain-sift &#62; blocklist
</code></pre>
<p>You can find blocklist sources in many places, such as
<a href="https://firebog.net/">firebog.net</a>.</p>
<h2 id="caveats">Caveats</h2>
<p>If you&#8217;ve pulled in a lot of domains, Unbound may fail to start on
OpenBSD because it doesn&#8217;t have enough time to process them all. You
can fix this by increasing Unbound&#8217;s timeout value.</p>
<pre><code>$ rcctl get unbound timeout
30
# rcctl set unbound timeout 120
$ rcctl get unbound timeout
120
</code></pre>
<h2 id="license">License</h2>
<p>This software is Copyright © 2023 by Ashlen.</p>
<p>This software uses the Internet Systems Consortium License (ISC
License). For more details, see the <code>LICENSE</code> file in the project root.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/domain-name-meanings.html</guid>
<link>https://www.anthes.is/domain-name-meanings.html</link>
<pubDate>Mon, 28 Nov 2022 00:00:00 +0100</pubDate>
<title>Domain name meanings</title>
<description><![CDATA[

<h1 id="domain-name-meanings">Domain name meanings</h1>
<p>This article is about the personal meaning of <code>amissing.link</code> and
<code>anthes.is</code>.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#what-was-the-meaning-of-a-missing-link">What was the meaning of &#8220;a missing link?&#8221;</a></li>
<li><a href="https://www.anthes.is/#whats-the-meaning-of-anthesis">What&#8217;s the meaning of &#8220;anthesis?&#8221;</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="what-was-the-meaning-of-a-missing-link">What was the meaning of &#8220;a missing link?&#8221;</h2>
<p>I think of it this way: when something goes missing, you find it in the
last place you look. I figured &#8220;a missing link&#8221; could represent a
website someone hasn&#8217;t visited yet. There&#8217;s a certain feeling you can
experience when you stumble upon a link to something, select it, and end
up appreciating what you found. I hoped people would experience that
feeling when they discovered this website, and I still do.</p>
<h2 id="whats-the-meaning-of-anthesis">What&#8217;s the meaning of &#8220;anthesis?&#8221;</h2>
<p>Anthesis refers to the time when a flower opens fully and functions, or
the beginning of that time. Another way to express this concept is
&#8220;blossoming&#8221; or &#8220;blooming.&#8221;</p>
<p>As a metaphor, anthesis holds significance for me because I&#8217;ve searched
for my path for a long time. In the past, I felt my life lacked
brightness. Fortunately, these days I have more of what&#8217;s necessary to
grow.</p>
<p>I don&#8217;t think blossoming means life&#8217;s difficult aspects disappear
permanently. Sometimes it still seems like there isn&#8217;t enough light, and
storms still rage from time to time. These things arrive, stay for a
while, and eventually fade away.</p>
<p>Instead, to flourish means understanding that nothing stays the same and
embracing these challenges for what they truly represent, rather than
avoiding or trying to control them. If we only had clear, sunny days,
there would be no rain. Without rain, there would be no growth&#8212;no
anthesis.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/dns-sinkhole.html</guid>
<link>https://www.anthes.is/dns-sinkhole.html</link>
<pubDate>Thu, 14 Apr 2022 00:00:00 +0200</pubDate>
<title>Creating a DNS sinkhole with Perl and unbound(8)</title>
<description><![CDATA[

<h1 id="creating-a-dns-sinkhole-with-perl-and-unbound8">Creating a DNS sinkhole with Perl and unbound(8)</h1>
<p><em>Tested on OpenBSD 7.0 and OpenBSD 7.1-current</em></p>
<p><strong>NOTE (2023-09-10):</strong> I&#8217;m deprecating genblock, please use
<a href="https://www.anthes.is/domain-sift.html">domain-sift</a> instead. domain-sift has a proper test
suite and packaging, unlike genblock. This page and genblock remain
available for historical purposes, at least for now.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#what-is-a-dns-sinkhole">What is a DNS sinkhole?</a></li>
<li><a href="https://www.anthes.is/#why-not-just-use-an-adblocker">Why not just use an adblocker?</a></li>
<li><a href="https://www.anthes.is/#where-to-get-the-domains-to-block">Where to get the domains to block?</a></li>
<li><a href="https://www.anthes.is/#processing-domains-with-genblock">Processing domains with genblock</a>
<ul>
<li><a href="https://www.anthes.is/#the-basics">The basics</a></li>
<li><a href="https://www.anthes.is/#making-genblock-more-useful">Making genblock more useful</a></li>
<li><a href="https://www.anthes.is/#getting-more-help">Getting more help</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#configuring-a-dns-resolver-to-use-the-blocklist">Configuring a DNS resolver to use the blocklist</a>
<ul>
<li><a href="https://www.anthes.is/#unbound">Unbound</a></li>
<li><a href="https://www.anthes.is/#unwind">Unwind</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#automation">Automation</a></li>
<li><a href="https://www.anthes.is/#source-code">Source code</a></li>
<li><a href="https://www.anthes.is/#addendum-i-enforcing-dns-provider-in-pfconf">Addendum I: Enforcing DNS provider in pf.conf</a></li>
<li><a href="https://www.anthes.is/#addendum-ii-getting-around-it">Addendum II: Getting around it</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="what-is-a-dns-sinkhole">What is a DNS sinkhole?</h2>
<p>A Domain Name System (DNS) sinkhole is a domain name server that doesn&#8217;t
perform domain name resolution for certain domains (typically those that
contain undesired content, such as trackers, malware, and ads).</p>
<p>Ordinarily, a client asks for the Internet Protocol (IP) address
associated with a domain and the configured DNS server provides it.
Here, the <a href="https://support.mozilla.org/en-US/kb/canary-domain-use-application-dnsnet">canary domain
&#8220;use-application-dns.net&#8221;</a>
serves as an example.</p>
<pre><code>$ host use-application-dns.net
use-application-dns.net has address 44.236.72.93
use-application-dns.net has address 44.235.246.155
use-application-dns.net has address 44.236.48.31
</code></pre>
<p>Domains provide a human-readable way to represent one or more IP
addresses, and DNS forms one part of a larger system for communications.
On a website, for instance, TCP&#47;IP (Transmission Control Protocol&#47;
Internet Protocol) enables a client to access the resources on the web
server. DNS merely provides the road map that shows how to get there.</p>
<p>A DNS sinkhole refuses to provide answers for certain domains. Since the
client trying to connect doesn&#8217;t know the IP address of the server to
connect to, it won&#8217;t make the connection.<sup id="fnref1"><a href="https://www.anthes.is/#fn1" rel="footnote">1</a></sup></p>
<pre><code>$ host use-application-dns.net
Host use-application-dns.net not found: 5(REFUSED)
</code></pre>
<h2 id="why-not-just-use-an-adblocker">Why not just use an adblocker?</h2>
<p>Nothing prevents you from using a browser extension to block ads,
provided it&#8217;s open source and trustworthy (I recommend <a href="https://ublockorigin.com/">uBlock
Origin</a>).</p>
<p>DNS sinkholes have unique strengths that an adblocker alone cannot
provide. You can apply domain filtering to the entire network, meaning
individual configuration for every device isn&#8217;t necessary. You can block
domains on devices that aren&#8217;t easily configurable otherwise (think
Internet of Things, smart TVs, mobile devices, guest devices, and so
forth).</p>
<h2 id="where-to-get-the-domains-to-block">Where to get the domains to block?</h2>
<p>Many different sources exist. The <a href="https://v.firebog.net/hosts/lists.php?type=tick">ticked
lists</a> from
<a href="https://firebog.net/">firebog.net</a> are worth considering. Here&#8217;s how
ticked lists work:</p>
<blockquote>
<p>Sites with preferably no falsely blocked sites, for Pi-hole installs
that require minimal maintenance</p>
</blockquote>
<p><a href="https://pi-hole.net/">Pi-hole</a> is a popular DNS sinkhole with a web
interface.</p>
<p>Searching for <a href="https://man.openbsd.org/hosts">hosts(5)</a> files also
works.</p>
<h2 id="processing-domains-with-genblock">Processing domains with genblock</h2>
<p><code>genblock</code> is a tool I wrote in Perl to extract domains from a given
input. Previously a shell script handled this, but I realized that shell
wasn&#8217;t ideal for this task and scrapped it in favor of something more
structured and maintainable. Here&#8217;s how to use <code>genblock</code>.</p>
<h3 id="the-basics">The basics</h3>
<p>Pretend a file named <code>examplefile</code> exists in the current directory with
these contents.</p>
<pre><code>$ cat examplefile
# Look at how awesome this blocklist is. Isn&#39;t it cool?
# It only uses 100% verified and real domains.
# Don&#39;t worry, bing.com is still accessible.
google.com
microsoft.com
3.1415926
www.apple.com
FACEBOOK.com
foobar
127.0.0.1 instagram.com
127.0.0.1 amazon.com
192.168.1.1
GOOGLE.COM
</code></pre>
<p>The simplest way to process it with <code>genblock</code> looks like this.</p>
<pre><code>$ .&#47;genblock examplefile
amazon.com
facebook.com
google.com
instagram.com
microsoft.com
www.apple.com
</code></pre>
<p>Notice several things:</p>
<ul>
<li>Uppercase converts to lowercase. DNS is case insensitive.</li>
<li>Duplicate entries are removed.</li>
<li>Bogus entries don&#8217;t appear.</li>
<li>Lines that start with a comment are ignored completely (otherwise
<code>bing.com</code> would appear in the list).</li>
<li>Lines are sorted (easier to <a href="https://man.openbsd.org/diff"><code>diff(1)</code></a>
that way)</li>
</ul>
<h3 id="making-genblock-more-useful">Making genblock more useful</h3>
<p>Here&#8217;s a more realistic example that fetches a blocklist and processes
it with <code>genblock</code>, then writes the output to a file in a format
accepted by <a href="https://man.openbsd.org/unbound">unbound(8)</a>.</p>
<pre><code>$ ftp -o - https:&#47;&#47;adaway.org&#47;hosts.txt |
&#62; .&#47;genblock -t unbound &#62; blocklist.txt
$ cat blocklist.txt
local-zone: "0ce3c-1fd43.api.pushwoosh.com" always_refuse
local-zone: "100016075.collect.igodigital.com" always_refuse
local-zone: "10148.engine.mobileapptracking.com" always_refuse
local-zone: "10870841.collect.igodigital.com" always_refuse
local-zone: "1170.api.swrve.com" always_refuse
local-zone: "1170.content.swrve.com" always_refuse
local-zone: "1188.api.swrve.com" always_refuse
[many more lines like this]
</code></pre>
<p>Note that <code>always_nxdomain</code> isn&#8217;t used here. Giving the impression that
these domains don&#8217;t exist when we aren&#8217;t checking for existence
complicates troubleshooting. A clear response back shows that the
blocking is part of a policy, and that the DNS isn&#8217;t broken.</p>
<h3 id="getting-more-help">Getting more help</h3>
<p>Access the help output like this.</p>
<pre><code>$ .&#47;genblock -h
</code></pre>
<h2 id="configuring-a-dns-resolver-to-use-the-blocklist">Configuring a DNS resolver to use the blocklist</h2>
<p>Remember to check after setup that domain filtering works properly with
something like <a href="https://man.openbsd.org/host"><code>host(1)</code></a> or
<a href="https://man.openbsd.org/host"><code>dig(1)</code></a>.</p>
<h3 id="unbound">Unbound</h3>
<p>Move the file somewhere that indicates it&#8217;s a system configuration file
(so <code>&#47;etc</code>, or alternatively <code>&#47;var&#47;unbound&#47;etc</code> on OpenBSD) and give it
world readable permissions, but nothing else.</p>
<pre><code># mv blocklist.txt &#47;etc&#47;blocklist.txt
# chmod 0444 &#47;etc&#47;blocklist.txt
</code></pre>
<p>For <code>unbound</code>, add this somewhere in
<a href="https://man.openbsd.org/unbound.conf">unbound.conf(5)</a>.</p>
<pre><code>include: &#47;etc&#47;blocklist.txt
</code></pre>
<p>Check the config for validity.</p>
<pre><code># unbound-checkconf &#47;var&#47;unbound&#47;etc&#47;unbound.conf
unbound-checkconf: no errors in &#47;var&#47;unbound&#47;etc&#47;unbound.conf
</code></pre>
<p>Restart the service, assuming it&#8217;s enabled and already running.</p>
<pre><code># rcctl restart unbound
unbound(ok)
unbound(ok)
</code></pre>
<h3 id="unwind">Unwind</h3>
<p><a href="https://man.openbsd.org/unwind"><code>unwind(8)</code></a> is a validating DNS
resolver that&#8217;s part of the OpenBSD base system. It&#8217;s meant for
workstations or laptops and only listens on <code>localhost</code>.</p>
<p>One domain per line is the format <code>unwind</code> accepts per
<a href="https://man.openbsd.org/unwind.conf"><code>unwind.conf(5)</code></a>, so no need to
do anything special there. Move the file and modify permissions as
before.</p>
<pre><code># mv blocklist.txt &#47;etc&#47;blocklist.txt
# chmod 0444 &#47;etc&#47;blocklist.txt
</code></pre>
<p>To include the file in <code>unwind.conf</code>, add this (optionally, with <code>log</code>
at the end as shown here to log blocked queries).</p>
<pre><code>block list &#47;etc&#47;blocklist.txt log
</code></pre>
<p>Check the config for validity.</p>
<pre><code># unwind -n
configuration OK
</code></pre>
<p>Restart the service, assuming it&#8217;s enabled and already running.</p>
<pre><code># rcctl restart unwind
unwind(ok)
unwind(ok)
</code></pre>
<h2 id="automation">Automation</h2>
<p>Create <code>&#47;etc&#47;sysadm</code> and move <code>genblock</code> and a list of blocklist URLs
there (a different directory works fine, too. This is just what I use).</p>
<pre><code># mkdir -m 700 &#47;etc&#47;sysadm
# mv genblock &#47;etc&#47;sysadm&#47;
# mv blocklist_urls &#47;etc&#47;sysadm&#47;
</code></pre>
<p><a href="https://man.openbsd.org/cron"><code>cron(8)</code></a> can handle the automation. To
generate a new blocklist weekly for <code>unbound</code> and check for validity
before restarting DNS, add this to <code>&#47;etc&#47;weekly.local</code>.</p>
<pre><code># For genblock. Does blocklist handling so the script doesn&#39;t have
# to. Generates blocklist from content of URLs and reloads daemons.
&#47;bin&#47;test -e &#47;etc&#47;blocklist.txt \
    &#38;&#38; &#47;bin&#47;mv &#47;etc&#47;blocklist.txt &#47;etc&#47;blocklist.txt.bak

&#47;usr&#47;bin&#47;xargs -- &#47;usr&#47;bin&#47;ftp -o - -- &#60; &#47;etc&#47;sysadm&#47;blocklist_urls \
    | &#47;etc&#47;sysadm&#47;genblock -t unbound &#62; &#47;etc&#47;blocklist.txt

# https:&#47;&#47;support.mozilla.org&#47;en-US&#47;kb&#47;canary-domain-use-application-dnsnet
echo &#39;local-zone: "use-application-dns.net" always_refuse&#39; &#62;&#62; &#47;etc&#47;blocklist.txt

if &#47;usr&#47;sbin&#47;unbound-checkconf; then
    &#47;bin&#47;chmod 0444 &#47;etc&#47;blocklist.txt
    &#47;usr&#47;sbin&#47;rcctl restart unbound
else
    &#47;bin&#47;mv &#47;etc&#47;blocklist.txt.bak &#47;etc&#47;blocklist.txt
fi
</code></pre>
<p>I keep these portions outside of <code>genblock</code> for portability and to
reduce code complexity. That way, there&#8217;s no need to worry about all the
different init systems, DNS resolvers, methods of fetching, websites to
fetch from, and so forth.</p>
<h2 id="source-code">Source code</h2>
<p>You can find the source code for <code>genblock</code> in my <code>sysadm</code> repo,
accessible <a href="https://www.anthes.is/src/sysadm/">on this website</a> or <a href="https://github.com/maybebyte/sysadm">on
GitHub</a>.</p>
<h2 id="addendum-i-enforcing-dns-provider-in-pf.conf">Addendum I: Enforcing DNS provider in pf.conf</h2>
<p>None of this prevents queries to a different server.</p>
<pre><code># host use-application-dns.net
Host use-application-dns.net not found: 5(REFUSED)
# host use-application-dns.net 1.1.1.1
Using domain server:
Name: 1.1.1.1
Address: 1.1.1.1#53
Aliases:

use-application-dns.net has address 44.236.72.93
use-application-dns.net has address 44.235.246.155
use-application-dns.net has address 44.236.48.31
</code></pre>
<p>If the DNS sinkhole runs on a router, place this in
<a href="https://man.openbsd.org/pf.conf"><code>pf.conf(5)</code></a> to block queries to an
external DNS server.</p>
<pre><code>block return in log quick proto { tcp udp } to !($int_if) port { domain domain-s }
</code></pre>
<p>The correct value for <code>$int_if</code> may vary. It&#8217;s <code>vport0</code> in my case,
which is the same interface that hands out Dynamic Host Configuration
Protocol (DHCP) leases. Either way, this step matters because many
devices come with a fallback DNS entry that renders these efforts
useless.</p>
<h2 id="addendum-ii-getting-around-it">Addendum II: Getting around it</h2>
<p>You can still circumvent these measures even with firewall rules in
place. Encrypting outgoing traffic to be later decrypted with something
like a Virtual Private Network (VPN), Secure Shell (SSH) tunnel, or Tor
accomplishes this.</p>
<div class="footnotes">
<hr/>
<ol>

<li id="fn1">
<p>Please note that theoretically nothing prevents the client from
connecting to the IP address itself, if it&#8217;s known or can be
discovered some other way.&#160;<a href="https://www.anthes.is/#fnref1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/overwriting-disks.html</guid>
<link>https://www.anthes.is/overwriting-disks.html</link>
<pubDate>Wed, 02 Mar 2022 00:00:00 +0100</pubDate>
<title>On overwriting disks</title>
<description><![CDATA[

<h1 id="on-overwriting-disks">On overwriting disks</h1>
<p><em>Tested on OpenBSD 7.0-current</em></p>
<p><strong>NOTE</strong>: data loss may occur if you run commands in this article
without care. Ensure you target the correct disk before executing
anything.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#the-usual-method">The usual method</a></li>
<li><a href="https://www.anthes.is/#overwriting-disks-with-a-progress-bar">Overwriting disks with a progress bar</a></li>
<li><a href="https://www.anthes.is/#writing-image-files-to-disk-with-a-progress-bar">Writing image files to disk with a progress bar</a></li>
<li><a href="https://www.anthes.is/#why-use-dd-at-all-then">Why use dd at all then?</a></li>
<li><a href="https://www.anthes.is/#conclusion">Conclusion</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="the-usual-method">The usual method</h2>
<p>It&#8217;s common (and good) advice to first overwrite a disk with random data
if you plan to create an encrypted volume. The typical suggestion
involves using <a href="https://man.openbsd.org/dd"><code>dd(1)</code></a> in some fashion
like this:</p>
<pre><code># dd if=&#47;dev&#47;urandom of=&#47;dev&#47;rsd0c bs=1m
</code></pre>
<p><code>dd</code> handles this task just fine. Certain versions of <code>dd</code> support
<code>status=progress</code>. This isn&#8217;t the case for OpenBSD&#8217;s <code>dd</code>, but sending
<code>SIGINFO</code> will display the current status.</p>
<p>That said, nothing particularly distinguishes how <code>dd</code> handles
overwriting disks. As far as I can tell, <code>dd</code> leverages the power of
input and output streams in a *nix environment. Something that a tool as
simple as <a href="https://man.openbsd.org/cat"><code>cat(1)</code></a> can also do.</p>
<pre><code># cat &#47;dev&#47;urandom &#62; &#47;dev&#47;rsd0c
</code></pre>
<p>Don&#8217;t mistake that as an endorsement of <code>cat</code> for this job. Better tools
exist.</p>
<h2 id="overwriting-disks-with-a-progress-bar">Overwriting disks with a progress bar</h2>
<p>When overwriting disks with little storage capacity, the lack of
progress bar isn&#8217;t pressing because the operation doesn&#8217;t take long. For
larger disks, an Estimated Time of Arrival (ETA) proves invaluable. I
like <a href="http://ivarch.com/programs/pv.shtml"><code>pv</code></a> for this task.</p>
<p>Install <code>pv</code> as a package.</p>
<pre><code># pkg_add pv
</code></pre>
<p>Here, I&#8217;m plugging in a flash drive for testing purposes and running
<a href="https://man.openbsd.org/dmesg"><code>dmesg(8)</code></a> for the device name.</p>
<pre><code>$ dmesg
[...]
sd5 at scsibus6 targ 1 lun 0: &#60;, USB DISK 3.0, PMAP&#62; removable serial.655716319B52EBB03391
sd5: 15120MB, 512 bytes&#47;sector, 30965760 sectors
</code></pre>
<p>Incidentally, this already gives us the info we need to move forward
(number of sectors and number of bytes per sector). But, let&#8217;s check
disk details with <a href="https://man.openbsd.org/disklabel"><code>disklabel(8)</code></a>
anyway for more information.</p>
<pre><code># disklabel sd5
# &#47;dev&#47;rsd5c:
type: SCSI
disk: VOID_LIVE
label:
duid: 0000000000000000
flags:
bytes&#47;sector: 512
sectors&#47;track: 63
tracks&#47;cylinder: 255
sectors&#47;cylinder: 16065
cylinders: 1927
total sectors: 30965760
boundstart: 0
boundend: 30965760
drivedata: 0

16 partitions:
#                size           offset  fstype [fsize bsize   cpg]
  a:         30965760                0 ISO9660
  c:         30965760                0 ISO9660
</code></pre>
<p>One last step before overwriting: determine how many bytes total are on
this disk by multiplying the bytes&#47;sector value by total sectors.</p>
<pre><code>$ echo $((512 * 30965760))
15854469120
</code></pre>
<p>Now we can overwrite the disk. We need <code>-s</code> for the total amount of data
to transfer, and <code>-S</code> to stop transferring once that amount has been
transferred. Without these, <code>pv</code> will display the rate of data transfer,
but not an ETA since it doesn&#8217;t know how much data it will process. This
makes sense because <code>&#47;dev&#47;urandom</code> will never send an &#8220;end of file,&#8221; so
we must tell <code>pv</code> when to stop.</p>
<pre><code># pv -s 15854469120 -S &#47;dev&#47;urandom &#62; &#47;dev&#47;rsd5c
47.5MiB 0:00:02 [23.4MiB&#47;s] [&#62;                                                                                                                                              ]  0% ETA 0:10:34
</code></pre>
<h2 id="writing-image-files-to-disk-with-a-progress-bar">Writing image files to disk with a progress bar</h2>
<p>Let&#8217;s say for the sake of demonstration that I made a terrible mistake
and I really needed that live disk. No problem, we can write the data
back. I&#8217;ve already downloaded and cryptographically verified the image
file I&#8217;m using here. The usage is simpler here, since <code>pv</code> will detect
the size of the image file and will receive an &#8220;end of file."</p>
<pre><code># pv void-live-x86_64-20210930.iso &#62; &#47;dev&#47;rsd5c
26.5MiB 0:00:01 [26.4MiB&#47;s] [====&#62;                                                                                                                                          ]  4% ETA 0:00:19
</code></pre>
<h2 id="why-use-dd-at-all-then">Why use dd at all then?</h2>
<p><code>dd</code> offers a level of control beyond these other tools, and proves
helpful in other contexts besides writing arbitrary data to disk.
<code>seek=</code> and <code>skip=</code> are two options that come to mind for carving out
data, along with <code>count=</code> for terminating the process at a specific
point.</p>
<p><code>dd</code> also eliminates the need to find the total number of bytes, since
it knows when to stop writing. This happens mainly because <code>dd</code> doesn&#8217;t
redirect STDOUT with the shell to perform writing, but rather uses the
<code>of=</code> feature built into <code>dd</code>. <code>dd</code> can learn things about the nature of
the output file that <code>pv</code> won&#8217;t know due to design&#47;implementation
differences.</p>
<p>As an aside, it&#8217;s straightforward to pipe <code>pv</code> into <code>dd</code> or vice versa,
but I haven&#8217;t encountered a situation where that was necessary.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I realize none of this is exactly groundbreaking&#8212;this demonstrates
pretty basic stuff, and only shows one use case for <code>pv</code>. Wacky things
are possible, like creating tarballs with a progress bar, for instance.</p>
<p>Nothing is wrong with keeping it traditional and using <code>dd</code>. I found
<code>pv</code> useful when writing random data to large disks so I had some idea
of when the process would end.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/local-authoritative-dns.html</guid>
<link>https://www.anthes.is/local-authoritative-dns.html</link>
<pubDate>Fri, 07 Jan 2022 00:00:00 +0100</pubDate>
<title>Local authoritative DNS on OpenBSD using dhcpd(8) and unbound(8)</title>
<description><![CDATA[

<h1 id="local-authoritative-dns-on-openbsd-using-dhcpd8-and-unbound8">Local authoritative DNS on OpenBSD using dhcpd(8) and unbound(8)</h1>
<p><em>Tested on OpenBSD 7.0</em></p>
<p>One meaningful addition to home networks is the ability to refer to
devices using domain names instead of IP addresses. Domain names are
more memorable and human readable. Local authoritative Domain Name
System (DNS) enables things like this to work:</p>
<pre><code>$ host peterepeat
peterepeat.home.arpa has address 192.168.1.241

$ ping -c 1 peterepeat
PING peterepeat.home.arpa (192.168.1.241): 56 data bytes
64 bytes from 192.168.1.241: icmp_seq=0 ttl=255 time=0.395 ms

--- peterepeat.home.arpa ping statistics ---
1 packets transmitted, 1 packets received, 0.0% packet loss
round-trip min&#47;avg&#47;max&#47;std-dev = 0.395&#47;0.395&#47;0.395&#47;0.000 ms
</code></pre>
<p>This document makes some assumptions. Primarily, that there&#8217;s <a href="https://www.anthes.is/openbsd-router.html">a router
running OpenBSD</a> that serves Dynamic Host
Configuration Protocol (DHCP) and DNS with
<a href="https://man.openbsd.org/dhcpd"><code>dhcpd(8)</code></a> and
<a href="https://man.openbsd.org/unbound"><code>unbound(8)</code></a>. Local authoritative DNS
extends this setup.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#rfc8375-why-we-use-homearpa">RFC8375 (why we use home.arpa.)</a></li>
<li><a href="https://www.anthes.is/#configuring-unbound8">Configuring unbound(8)</a></li>
<li><a href="https://www.anthes.is/#configuring-dhcpd8">Configuring dhcpd(8)</a></li>
<li><a href="https://www.anthes.is/#testing-dns-resolution">Testing DNS resolution</a></li>
<li><a href="https://www.anthes.is/#querying-hosts-without-a-fqdn">Querying hosts without a FQDN</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="rfc8375-why-we-use-home.arpa.">RFC8375 (why we use home.arpa.)</h2>
<p>Often people choose a domain name for their home network on a whim,
something like <code>localdomain</code> or <code>lan</code>. I used <code>lan</code> for a while. It
turns out there&#8217;s a specific-use domain name explicitly reserved for
this purpose: <code>home.arpa.</code> (<a href="https://datatracker.ietf.org/doc/html/rfc8375">Check out RFC8375 for more
information</a>).</p>
<p>Now that we&#8217;ve chosen a domain name, let&#8217;s configure it.</p>
<h2 id="configuring-unbound8">Configuring unbound(8)</h2>
<p>Unbound functions primarily as a caching recursive resolver. But it can
also serve zones authoritatively,<sup id="fnref1"><a href="https://www.anthes.is/#fn1" rel="footnote">1</a></sup> as shown by this commented out
section in the default configuration file.</p>
<pre><code># Serve zones authoritatively from Unbound to resolver clients.
# Not for external service.
#
#local-zone: "local." static
#local-data: "mycomputer.local. IN A 192.0.2.51"
#local-zone: "2.0.192.in-addr.arpa." static
#local-data-ptr: "192.0.2.51 mycomputer.local"
</code></pre>
<p>I prefer to include a separate file in
<a href="https://man.openbsd.org/unbound.conf"><code>unbound.conf(5)</code></a> so that this
part of the configuration remains distinct. Edit
<code>&#47;var&#47;unbound&#47;etc&#47;unbound.conf</code> and place the desired filename in there
somewhere.</p>
<pre><code>include: &#47;var&#47;unbound&#47;etc&#47;unbound.conf.lan
</code></pre>
<p>After writing those changes, create the included file and add these
contents. Adjust things as needed. Unbound already includes RFC8375
support, so you only need to add <code>local-data</code> and <code>local-data-ptr</code>.</p>
<pre><code># Define individual hosts here. Both an A record and a PTR
# record are needed. Note that local-data-ptr reverses local-data.
local-data: "peterepeat.home.arpa. IN A 192.168.1.241"
local-data-ptr: 192.168.1.241 peterepeat.home.arpa"
</code></pre>
<p>Save the file. Check that the syntax is valid (unbound checks the syntax
of the included file, too).</p>
<pre><code># unbound-checkconf
unbound-checkconf: no errors in &#47;var&#47;unbound&#47;etc&#47;unbound.conf
</code></pre>
<h2 id="configuring-dhcpd8">Configuring dhcpd(8)</h2>
<p>A viable <a href="https://man.openbsd.org/dhcpd.conf">dhcpd.conf(5)</a> needs
to declare a domain name and at least one host, along with mandatory
parameters. A working configuration looks like this (note that
<code>fixed-address</code> takes a domain name, not an IP address).</p>
<pre><code>subnet 192.168.1.0 netmask 255.255.255.0 {
    option domain-name "home.arpa";
    option domain-name-servers 192.168.1.1;
    option routers 192.168.1.1;
    range 192.168.1.10 192.168.1.200;

    host peterepeat {
        fixed-address peterepeat.home.arpa;
        hardware ethernet 34:cb:02:02:2c:0a;
        option host-name "peterepeat";
    }
}
</code></pre>
<p>I prefer to add <code>use-host-decl-names</code> to assign the hostname
automatically based on the host declaration like so.</p>
<pre><code>subnet 192.168.1.0 netmask 255.255.255.0 {
    option domain-name "home.arpa";
    option domain-name-servers 192.168.1.1;
    option routers 192.168.1.1;
    range 192.168.1.10 192.168.1.200;

    group {
        use-host-decl-names on;

        host peterepeat {
            fixed-address peterepeat.home.arpa;
            hardware ethernet 34:cb:02:02:2c:0a;
        }
    }
}
</code></pre>
<p>Check that dhcpd accepts the configuration. If there are no
complaints, restart both daemons.</p>
<pre><code># dhcpd -n
# rcctl restart dhcpd unbound
</code></pre>
<h2 id="testing-dns-resolution">Testing DNS resolution</h2>
<p>Get a new DHCP lease on the client side (as of OpenBSD 6.9, you can
do this with <a href="https://man.openbsd.org/dhcpleasectl.8">dhcpleasectl(8)</a>.
The correct interface varies).</p>
<pre><code># dhcpleasectl re0
</code></pre>
<p>Then, try to resolve the new hostname.</p>
<pre><code>$ host peterepeat.home.arpa
peterepeat.home.arpa has address 192.168.1.241
$ host 192.168.1.241
241.1.168.192.in-addr.arpa domain name pointer peterepeat.home.arpa.
</code></pre>
<h2 id="querying-hosts-without-a-fqdn">Querying hosts without a FQDN</h2>
<p>This setup works well enough as-is, but you might not be able to query
hosts without a Fully-Qualified Domain Name (FQDN) out of the box. Check
to see if <a href="https://man.openbsd.org/host"><code>host(1)</code></a> fails with a partial
hostname.</p>
<pre><code>$ host peterepeat
Host peterepeat not found: 3(NXDOMAIN)
</code></pre>
<p>This happens because the system doesn&#8217;t append <code>.home.arpa</code> to
<code>peterepeat</code> before the lookup. The machine trying to perform the lookup
needs to have this line added to
<a href="https://man.openbsd.org/resolv.conf"><code>resolv.conf(5)</code></a>.</p>
<pre><code>domain home.arpa
</code></pre>
<p>Now things work as expected, saving a few keystrokes.</p>
<pre><code>$ host peterepeat
peterepeat.home.arpa has address 192.168.1.241
</code></pre>
<div class="footnotes">
<hr/>
<ol>

<li id="fn1">
<p><a href="https://man.openbsd.org/nsd"><code>nsd(8)</code></a> can also fulfill this
function if you forward lookups to <code>home.arpa.</code> to it with unbound, but
that&#8217;s a more involved setup. RFC8375 states that combining the
recursive resolver function for general DNS lookups with an
authoritative resolver for <code>home.arpa.</code> is permissible.&#160;<a href="https://www.anthes.is/#fnref1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/freebsd-entertainment-center.html</guid>
<link>https://www.anthes.is/freebsd-entertainment-center.html</link>
<pubDate>Sun, 13 Jun 2021 00:00:00 +0200</pubDate>
<title>Setting up a 4K Kodi box with sndio on FreeBSD</title>
<description><![CDATA[

<h1 id="setting-up-a-4k-kodi-box-with-sndio-on-freebsd">Setting up a 4K Kodi box with sndio on FreeBSD</h1>
<p><em>Tested on FreeBSD 13.0-RELEASE</em></p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#installation">Installation</a></li>
<li><a href="https://www.anthes.is/#after-installation">After installation</a>
<ul>
<li><a href="https://www.anthes.is/#setting-up-power-management">Setting up power management</a></li>
<li><a href="https://www.anthes.is/#creating-makeconf">Creating make.conf</a></li>
<li><a href="https://www.anthes.is/#checking-out-source-code">Checking out source code</a></li>
<li><a href="https://www.anthes.is/#checking-out-the-ports-tree">Checking out the ports tree</a></li>
<li><a href="https://www.anthes.is/#installing-portmaster">Installing portmaster</a></li>
<li><a href="https://www.anthes.is/#checking-out-freebsds-source-code">Checking out FreeBSD&#8217;s source code</a></li>
<li><a href="https://www.anthes.is/#odds-and-ends-before-compiling-kodi">Odds and ends before compiling Kodi</a></li>
<li><a href="https://www.anthes.is/#installing-needed-tools">Installing needed tools</a></li>
<li><a href="https://www.anthes.is/#setting-up-doasconf">Setting up doas.conf</a></li>
<li><a href="https://www.anthes.is/#entering-a-tmux-session">Entering a tmux session</a></li>
<li><a href="https://www.anthes.is/#compiling-kodi">Compiling Kodi</a></li>
<li><a href="https://www.anthes.is/#using-portmaster-to-compile-ports">Using portmaster to compile ports</a></li>
<li><a href="https://www.anthes.is/#reviewing-installation-messages">Reviewing installation messages</a></li>
<li><a href="https://www.anthes.is/#setting-up-a-user-environment-for-kodi">Setting up a user environment for Kodi</a></li>
<li><a href="https://www.anthes.is/#creating-the-kodi-user">Creating the kodi user</a></li>
<li><a href="https://www.anthes.is/#creating-xinitrc">Creating .xinitrc</a></li>
<li><a href="https://www.anthes.is/#starting-x">Starting X</a></li>
<li><a href="https://www.anthes.is/#starting-kodi-automatically">Starting Kodi automatically</a></li>
<li><a href="https://www.anthes.is/#setting-up-gettytab">Setting up gettytab</a></li>
<li><a href="https://www.anthes.is/#editing-ttys">Editing ttys</a></li>
<li><a href="https://www.anthes.is/#ensuring-x-only-starts-in-the-correct-terminal">Ensuring X only starts in the correct terminal</a></li>
<li><a href="https://www.anthes.is/#configuring-the-sound-system">Configuring the sound system</a></li>
<li><a href="https://www.anthes.is/#bit-perfect">Bit-perfect</a></li>
<li><a href="https://www.anthes.is/#not-bit-perfect">Not bit-perfect</a></li>
<li><a href="https://www.anthes.is/#staying-up-to-date">Staying up to date</a></li>
<li><a href="https://www.anthes.is/#setting-up-cron-to-fetch-updates-nightly">Setting up cron to fetch updates nightly</a></li>
<li><a href="https://www.anthes.is/#updating-the-system-and-ports">Updating the system and ports</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#parting-words">Parting words</a></li>
<li><a href="https://www.anthes.is/#resources">Resources</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="installation">Installation</h2>
<p>For brevity, this guide doesn&#8217;t cover the FreeBSD installation process
in depth. The FreeBSD handbook <a href="https://docs.freebsd.org/en/books/handbook/bsdinstall/">documents this
process</a>.</p>
<p>The short version:</p>
<ol>
<li><a href="https://www.freebsd.org/where/">Download an installation image for
amd64</a>.</li>
<li>Verify the image&#8217;s checksum and GNU Privacy Guard (GPG) signature.
Without this verification, you can&#8217;t confirm the installation image&#8217;s
authenticity or detect tampering.</li>
<li>Flash the image to a Universal Serial Bus (USB) drive.</li>
<li>Boot from the USB drive and complete the installation procedure.</li>
</ol>
<h2 id="after-installation">After installation</h2>
<p>The post-installation setup becomes more specific here. This guide uses
ports instead of packages because Kodi lacks built-in
<a href="https://sndio.org/">sndio</a> support by default. This choice makes the
post-installation phase more involved.</p>
<p>Log in to the freshly installed FreeBSD system and follow these steps.</p>
<h3 id="setting-up-power-management">Setting up power management</h3>
<p>Enable and start
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=powerd"><code>powerd(8)</code></a>
for power management. This reduces power consumption when the
entertainment center sits idle.</p>
<pre><code># sysrc powerd_enable="YES"
# service powerd start
</code></pre>
<h3 id="creating-make.conf">Creating make.conf</h3>
<p>To make changes take effect immediately, set up
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=make.conf"><code>make.conf(5)</code></a>
before compiling anything.</p>
<p>To stay current, <a href="https://www.anthes.is/src/sysadm/file/examples/freebsd/poudriere/make.conf.html">my make.conf lives in
git</a>, but
don&#8217;t copy it without understanding it first. The configuration includes
everything needed to compile with sndio, LibreSSL, and Intel Quick Sync
Video support.</p>
<h3 id="checking-out-source-code">Checking out source code</h3>
<h4 id="checking-out-the-ports-tree">Checking out the ports tree</h4>
<p><a href="https://docs.freebsd.org/en/books/handbook/ports/#ports-using">See the FreeBSD handbook entry on ports for more
details</a>.</p>
<pre><code># portsnap fetch extract
</code></pre>
<h4 id="installing-portmaster">Installing portmaster</h4>
<p>Install a ports management tool. For simplicity, this guide uses
<code>ports-mgmt&#47;portmaster</code>. While <code>ports-mgmt&#47;poudriere</code> also works well,
teaching poudriere exceeds this guide&#8217;s scope. Portmaster gets the job
done.</p>
<pre><code># make -C &#47;usr&#47;ports&#47;ports-mgmt&#47;portmaster install clean
</code></pre>
<h4 id="checking-out-freebsds-source-code">Checking out FreeBSD&#8217;s source code</h4>
<p>Install <code>git</code>, as the source tree checkout requires it. The <code>git-tiny</code>
flavor works well.</p>
<pre><code># portmaster devel&#47;git@tiny
</code></pre>
<p><a href="https://docs.freebsd.org/en/books/handbook/cutting-edge/#updating-src-obtaining-src">Check out the source
tree</a>.
Building <code>graphics&#47;drm-kmod</code> requires the source tree.</p>
<pre><code># git clone https:&#47;&#47;git.freebsd.org&#47;src.git -b releng&#47;13.0 &#47;usr&#47;src
</code></pre>
<h3 id="odds-and-ends-before-compiling-kodi">Odds and ends before compiling Kodi</h3>
<h4 id="installing-needed-tools">Installing needed tools</h4>
<p>Install a few tools to make the build process more manageable before
building Kodi. <code>sysutils&#47;tmux</code> proves essential because you can detach
from a tmux session and log out while portmaster builds, then later log
in and reattach to check the build progress.</p>
<p><code>security&#47;doas</code> provides a more minimalist method of privilege
elevation.</p>
<p>Consider installing a text editor and shell if desired. I like
<code>editors&#47;neovim</code> and <code>shells&#47;oksh</code>.</p>
<pre><code># portmaster sysutils&#47;tmux security&#47;doas
</code></pre>
<h4 id="setting-up-doas.conf">Setting up doas.conf</h4>
<p>If you installed <code>security&#47;doas</code>, set up
<a href="https://man.openbsd.org/doas.conf"><code>doas.conf(5)</code></a>. Since FreeBSD
currently lacks persistence support, adding <code>nopass</code> improves usability.</p>
<pre><code># echo &#39;permit nopass :wheel&#39; &#62;&#47;usr&#47;local&#47;etc&#47;doas.conf
</code></pre>
<h4 id="entering-a-tmux-session">Entering a tmux session</h4>
<p>This step matters for the next section. The one-liner below checks
whether the current user has a tmux session open. If not, it first tries
to attach to an existing session, and creates a new session if that
fails.</p>
<pre><code>$ [ -z "${TMUX}" ] &#38;&#38; { tmux attach || tmux; }
</code></pre>
<h3 id="compiling-kodi">Compiling Kodi</h3>
<h4 id="using-portmaster-to-compile-ports">Using portmaster to compile ports</h4>
<p>Now compile Kodi.</p>
<p>Note: if you don&#8217;t want ports configuration beyond what make.conf
specifies, prepend <code>BATCH=1</code> to the below command. I recommend at least
reviewing the options available for <code>multimedia&#47;ffmpeg</code> and
<code>multimedia&#47;kodi</code> with <code>make config</code>.</p>
<p>Remember to install packages needed for <a href="https://wiki.archlinux.org/title/Hardware_video_acceleration">Hardware video
acceleration</a>.
For the Latte Panda Delta, these packages include
<code>multimedia&#47;libva-intel-media-driver</code> and <code>multimedia&#47;libvdpau-va-gl</code>.</p>
<pre><code># portmaster audio&#47;sndio graphics&#47;drm-kmod multimedia&#47;libva-intel-media-driver \
&#62; multimedia&#47;libvdpau-va-gl x11&#47;xorg misc&#47;unclutter-xfixes multimedia&#47;kodi \
&#62; multimedia&#47;kodi-addon-inputstream.adaptive
</code></pre>
<p>After giving portmaster permission to build everything, detach from the
tmux session (<code>CTRL-b d</code> by default. Or create a second window with
<code>CTRL-b c</code> and issue <code>tmux detach</code>). Then, close the SSH connection and
take a fifteen-minute break.</p>
<p>After the initial break, SSH back in and <code>tmux attach</code> to check for
early errors that need attention. Waiting hours only to discover that
portmaster encountered an early error and stopped building is
frustrating.</p>
<p>Now, relax&#8212;compiling Kodi and Xorg on a single board computer takes
time.</p>
<h4 id="reviewing-installation-messages">Reviewing installation messages</h4>
<p>Review installation messages to check for needed interventions.</p>
<pre><code>$ pkg query &#39;%M&#39; | less
</code></pre>
<h3 id="setting-up-a-user-environment-for-kodi">Setting up a user environment for Kodi</h3>
<h4 id="creating-the-kodi-user">Creating the kodi user</h4>
<p>Create a separate user for Kodi (named <code>kodi</code> here). Add the <code>kodi</code> user
to the <code>video</code> group.</p>
<pre><code># adduser
</code></pre>
<p>If you didn&#8217;t add <code>kodi</code> to the <code>video</code> group during user creation, go
ahead and fix it now:</p>
<pre><code># pw groupmod video -m kodi
</code></pre>
<p>After that, log in as the <code>kodi</code> user.</p>
<h4 id="creating-.xinitrc">Creating .xinitrc</h4>
<p>Create <code>&#47;home&#47;kodi&#47;.xinitrc</code> to configure part of the startup process
for the graphical environment.</p>
<p>The <code>xset</code> commands prevent interference with Kodi&#8217;s screen blanking
mechanisms. <code>unclutter</code> hides the cursor when idle. By default, the
cursor stays invisible during ordinary usage, but if you bump a pointer
device, it sticks around. <code>unclutter</code> helpfully hides it again after a
short period of inactivity.</p>
<pre><code>$ cat &#60;&#60;EOF &#62;~&#47;.xinitrc
&#62; . "${HOME}&#47;.profile"
&#62; xset s noblank
&#62; xset s off
&#62; xset -dpms
&#62; unclutter &#38;
&#62;
&#62; exec kodi
&#62; EOF
</code></pre>
<h4 id="starting-x">Starting X</h4>
<p>From a console (not SSH), start X.</p>
<pre><code>$ startx
</code></pre>
<p>If X starts successfully, log out of the <code>kodi</code> user and back in to the
user with root access.</p>
<h3 id="starting-kodi-automatically">Starting Kodi automatically</h3>
<h4 id="setting-up-gettytab">Setting up gettytab</h4>
<p>Having Kodi start automatically on boot can help. This requires the
<code>kodi</code> user to log in automatically. To enable this, append some
configuration to
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=gettytab"><code>gettytab(5)</code></a>.</p>
<pre><code># cat &#60;&#60;EOF &#62;&#62;&#47;etc&#47;gettytab
&#62; # autologin kodi
&#62; A|Al|Autologin console:\
&#62;   :ht:np:sp#115200:al=kodi
&#62; EOF
</code></pre>
<h4 id="editing-ttys">Editing ttys</h4>
<p>Edit
<a href="https://www.freebsd.org/cgi/man.cgi?query=ttys&amp;apropos=0&amp;sektion=0&amp;manpath=FreeBSD+13.0-RELEASE&amp;arch=default&amp;format=html"><code>ttys(5)</code></a>
to match the following configuration.</p>
<pre><code># Virtual terminals
#ttyv1  "&#47;usr&#47;libexec&#47;getty Pc" xterm   onifexists  secure
ttyv1   "&#47;usr&#47;libexec&#47;getty Al" xterm   onifexists  secure
</code></pre>
<h4 id="ensuring-x-only-starts-in-the-correct-terminal">Ensuring X only starts in the correct terminal</h4>
<p>As the <code>kodi</code> user, append this check to <code>&#47;home&#47;kodi&#47;.profile</code>. The
check makes X start only in the correct terminal and prevents X from
starting if already running.</p>
<pre><code>$ cat &#60;&#60;EOF &#62;&#62;~&#47;.profile
&#62; if [ -z "${DISPLAY}" ] &#38;&#38; [ "$(tty)" = &#39;&#47;dev&#47;ttyv1&#39; ]; then
&#62;   exec startx
&#62; fi
&#62; EOF
</code></pre>
<p>Finally, reboot the system.</p>
<pre><code># reboot
</code></pre>
<h3 id="configuring-the-sound-system">Configuring the sound system</h3>
<p>If everything works correctly, configure the sound system. Set the
default device with
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=sysctl"><code>sysctl(8)</code></a>
if needed (check <code>&#47;dev&#47;sndstat</code> for available devices).</p>
<pre><code># sysctl hw.snd.default.unit=1 # needed for HDMI in this case
</code></pre>
<p>Now, decide whether to use bit-perfect mode and consult the relevant
section below. Note that the <a href="https://kodi.wiki/view/Settings/Player/Videos#Sync_playback_to_display">&#8220;Sync playback to
display&#8221;</a>
option in Kodi conflicts with bit-perfect audio. It resamples both video
and audio to match the display&#8217;s refresh rate.</p>
<h4 id="bit-perfect">Bit-perfect</h4>
<p>To use bit-perfect mode, apply two <code>sysctl</code> tweaks. This example uses
the first device, but check which device needs tweaking.</p>
<pre><code># sysctl dev.pcm.1.bitperfect=1
# sysctl hw.snd.maxautovchans=0
</code></pre>
<h4 id="not-bit-perfect">Not bit-perfect</h4>
<p>Change <code>hw.snd.feeder_rate_quality</code> from its default value of <code>1</code>.
According to
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=sound"><code>sound(4)</code></a>,
the default linear interpolation doesn&#8217;t provide antialiasing filtering.
I prefer to start with the highest resampling quality possible and lower
the value if needed.</p>
<pre><code># sysctl hw.snd.feeder_rate_quality=4
</code></pre>
<p>Remember that regardless of which mode you select, to make sysctl tweaks
permanent, you must edit
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=sysctl.conf"><code>sysctl.conf(5)</code></a>
accordingly.</p>
<p><a href="https://forums.freebsd.org/threads/sndiod-enable.62892/#post-363265">Note that sndiod isn&#8217;t needed in either
case</a>.</p>
<h3 id="staying-up-to-date">Staying up to date</h3>
<h4 id="setting-up-cron-to-fetch-updates-nightly">Setting up cron to fetch updates nightly</h4>
<p>Pull in updates every night at 03:00 using
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=portsnap"><code>portsnap(8)</code></a>
and
<a href="https://www.freebsd.org/cgi/man.cgi?sektion=0&amp;manpath=FreeBSD%2013.0-RELEASE&amp;arch=default&amp;format=html&amp;query=freebsd-update"><code>freebsd-update(8)</code></a>.</p>
<p>Note that neither <code>portsnap cron</code> nor <code>freebsd-update cron</code> applies
updates. They only download updates.</p>
<pre><code># cat &#60;&#60;EOF | crontab -
&#62; 0 3 * * * root &#47;usr&#47;sbin&#47;portsnap cron
&#62; 0 3 * * * root &#47;usr&#47;sbin&#47;freebsd-update cron
&#62; EOF
</code></pre>
<h4 id="updating-the-system-and-ports">Updating the system and ports</h4>
<p>When ready to update, run these commands to apply changes to the ports
and source tree. They also apply binary updates to the base system.</p>
<pre><code># portsnap fetch update
# freebsd-update fetch install
# git -C &#47;usr&#47;src pull
</code></pre>
<p>If <code>freebsd-update fetch install</code> installs any updates, rebuild
<code>graphics&#47;drm-kmod</code> <a href="https://docs.freebsd.org/en/books/handbook/x11/#x-config-video-cards">per the FreeBSD
handbook</a>.</p>
<pre><code># portmaster -f graphics&#47;drm-kmod
</code></pre>
<p>Always check <code>&#47;usr&#47;ports&#47;UPDATING</code> before upgrading any ports. Then,
upgrade.</p>
<pre><code>$ less &#47;usr&#47;ports&#47;UPDATING
# portmaster -a
</code></pre>
<p>Reboot the system.</p>
<pre><code># reboot
</code></pre>
<h2 id="parting-words">Parting words</h2>
<p>The entertainment center works well once configured. Setup proves
involved initially (due to my specific requirements and preferences),
but maintenance stays manageable.</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="https://kodi.wiki/view/Video_levels_and_color_space">Video levels and color
space</a>. Helpful
entry from the Kodi Wiki.</li>
<li><a href="https://w6rz.net/">Calibration media for TV brightness, contrast, and other
settings</a>, as mentioned in <a href="https://www.avsforum.com/forum/139-display-calibration/948496-avs-hd-709-blu-ray-mp4-calibration.html">this
thread</a>.
The original poster provides this as an alternate download link (other
links point to Google Docs).</li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/fdroid.html</guid>
<link>https://www.anthes.is/fdroid.html</link>
<pubDate>Mon, 12 Apr 2021 00:00:00 +0200</pubDate>
<title>Recommended F-Droid apps</title>
<description><![CDATA[

<h1 id="recommended-f-droid-apps">Recommended F-Droid apps</h1>
<p><strong>NOTE:</strong> Though I care about open source software, I can no longer
recommend F-Droid as an app store as of 2023-04-18. Please see <a href="https://privsec.dev/posts/android/f-droid-security-issues/">F-Droid
Security
Issues</a>.
These days I prefer to <a href="https://www.privacyguides.org/en/android/#manually-with-rss-notifications">update apps via Really Simple Syndication
(RSS)</a>
instead.</p>
<p>I may rewrite this post or delete it later, but I felt I should at least
mention this concern first.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#what-is-f-droid">What is F-Droid?</a></li>
<li><a href="https://www.anthes.is/#f-droid-apps">F-Droid apps</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="what-is-f-droid">What is F-Droid?</h2>
<p>From the <a href="https://www.f-droid.org/">F-Droid website</a>:</p>
<blockquote>
<p>&#8220;F-Droid is an installable catalogue of FOSS (Free and Open Source
Software) applications for the Android platform. The client makes it
easy to browse, install, and keep track of updates on your device.&#8221;</p>
</blockquote>
<h2 id="f-droid-apps">F-Droid apps</h2>
<ul>
<li><a href="https://f-droid.org/en/packages/de.kaffeemitkoffein.imagepipe/">ImagePipe</a>
or <a href="https://f-droid.org/en/packages/com.jarsilio.android.scrambledeggsif/">Scrambled
Exif</a>.
Both apps remove Exchangeable Image File Format (EXIF) data, metadata in
photos that can reveal personal information, before you send pictures.
Despite serving a similar purpose, the two apps differ in implementation
and features. Notably, ImagePipe also reduces image size (though it
keeps the original unaltered and stores the changed copy in a separate
folder), which may or may not be desirable depending on your use case.</li>
<li><a href="https://f-droid.org/en/packages/fr.neamar.kiss/">Keep It Simple, Stupid (KISS)
Launcher</a>. I find that
pinning a few choice apps and searching for the rest with KISS Launcher
provides a more pleasant experience relative to the default
<a href="https://grapheneos.org/">GrapheneOS</a> launcher. Also, launchers let you
use custom icons (I use <a href="https://f-droid.org/en/packages/com.donnnno.arcticons/">Arcticons
Dark</a>).</li>
<li><a href="https://f-droid.org/en/packages/sh.ftp.rocketninelabs.meditationassistant.opensource/">Meditation
Assistant</a>.
Practice agnostic and doesn&#8217;t do more than I need it to&#8211;in other words,
perfect for my use case.</li>
<li><a href="https://f-droid.org/en/packages/is.xyz.mpv/">mpv-android</a>. For those
that love <a href="https://mpv.io/">mpv</a> and want it on their phone, too.</li>
<li><a href="https://netguard.me/">Netguard</a>. Block internet access per app.
Unfortunately, I haven&#8217;t found a way to combine it with Orbot yet.</li>
<li><a href="https://newpipe.net/">NewPipe</a>. NewPipe lets you watch YouTube
privately on your phone; you can also keep up with your favorite content
creators without signing up for an account.</li>
<li><a href="https://guardianproject.info/apps/org.torproject.android/">Orbot</a>. An
invaluable tool for privacy, Orbot tunnels the traffic of selected apps
through Tor.</li>
<li><a href="https://f-droid.org/packages/org.quantumbadger.redreader/">RedReader</a>,
an unofficial open source client for Reddit. It has a clean UI with no
nonsense. I found it through word of mouth, as I&#8217;ve never liked the
redesign and wanted to find a less feature-rich (and less buggy) client
than Slide. This is exactly what RedReader delivers.</li>
<li><a href="https://termux.com/">Termux</a>. Termux provides a terminal emulator for
Android and offers an incredible amount of power. I use Termux to
securely transfer files between my phone and computer using SSH.</li>
<li><a href="https://f-droid.org/en/packages/dummydomain.yetanothercallblocker/">Yet Another Call Blocker
(YACB)</a>.
A useful app. You can add problematic area codes to YACB and it filters
them out automatically. You can exempt contacts from being treated as
unknown callers, and YACB even has an option to stop the call before the
phone starts ringing on Android 7+.</li>
<li><a href="https://f-droid.org/en/packages/app.fedilab.nitterizeme/">UntrackMe</a>.
UntrackMe transforms certain links, such as YouTube, Twitter, and
Instagram, to point at a Free&#47;Libre and Open Source Software (FLOSS)
privacy respecting alternative. In the case of Twitter, it changes the
domain to a Nitter instance.</li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/why-openbsd.html</guid>
<link>https://www.anthes.is/why-openbsd.html</link>
<pubDate>Thu, 25 Mar 2021 00:00:00 +0100</pubDate>
<title>Why OpenBSD?</title>
<description><![CDATA[

<h1 id="why-openbsd">Why OpenBSD?</h1>
<p>Previously, I scattered my thoughts on
<a href="https://www.openbsd.org/">OpenBSD</a> around my website, alluding to its
strengths when needed. However, that approach made my argumentation feel
disjointed as a result. It seems more sensible to have a central place
to discuss these things.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#why-not-openbsd">Why not OpenBSD?</a></li>
<li><a href="https://www.anthes.is/#simplicity">Simplicity</a></li>
<li><a href="https://www.anthes.is/#less-decision-paralysis">Less decision paralysis</a></li>
<li><a href="https://www.anthes.is/#great-documentation">Great documentation</a></li>
<li><a href="https://www.anthes.is/#security">Security</a></li>
<li><a href="https://www.anthes.is/#privacy">Privacy</a></li>
<li><a href="https://www.anthes.is/#stability">Stability</a></li>
<li><a href="https://www.anthes.is/#sane-defaults">Sane defaults</a></li>
<li><a href="https://www.anthes.is/#versatile">Versatile</a></li>
<li><a href="https://www.anthes.is/#other-resources">Other resources</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="why-not-openbsd">Why not OpenBSD?</h2>
<p>First, I&#8217;d like to bring up the &#8216;dealbreakers.&#8217; I wouldn&#8217;t recommend
OpenBSD to those that:</p>
<ul>
<li>Dislike reading&#47;are unwilling to learn.</li>
<li>Prioritize performance above all else.</li>
<li>Desire a powerful filesystem like Z File System (ZFS).</li>
<li>Want all the latest features.</li>
<li>Need every task or detail to be abstracted away graphically.</li>
<li>Want it to work like <code>$PREVIOUS_OS</code>.</li>
</ul>
<h2 id="simplicity">Simplicity</h2>
<p>When I say simplicity, I mean simplicity from a software
design&#47;development perspective. OpenBSD follows the <a href="https://web.mit.edu/6.055/old/S2009/notes/unix.pdf">Unix
philosophy</a> and
consciously avoids feature creep. There aren&#8217;t as many bells and
whistles compared to other operating systems and that&#8217;s good. That means
there&#8217;s less to sift through if something breaks.</p>
<h2 id="less-decision-paralysis">Less decision paralysis</h2>
<p>One of Linux&#8217;s strengths is also a grave weakness: the abundance of
choice. Deciding what implementation to use for a mail, web, Domain Name
System (DNS), or Network Time Protocol (NTP) server is a task in itself,
as there are many out there.</p>
<p>With OpenBSD, one already has a sane, powerful, and secure suite of
software to choose from, also known as the <a href="https://why-openbsd.rocks/fact/base-system-concept/">base
system</a>. For
instance, a web server with HyperText Transfer Protocol Secure (HTTPS)
and automated certificate renewal can be had with
<a href="https://man.openbsd.org/httpd"><code>httpd(8)</code></a>,
<a href="https://man.openbsd.org/acme-client"><code>acme-client(1)</code></a>, and
<a href="https://man.openbsd.org/cron"><code>cron(8)</code></a>, all without installing any
additional software.</p>
<p>See <a href="https://www.openbsd.org/innovations.html">OpenBSD&#8217;s &#8220;innovations&#8221;
page</a> for more cool software
and ideas developed by the OpenBSD project. Did you know that
<a href="https://www.openssh.com/">OpenSSH</a> is an OpenBSD project?</p>
<h2 id="great-documentation">Great documentation</h2>
<p>OpenBSD feels transparent and comprehensible. Between the Frequently
Asked Questions (FAQ), man pages, and mailing lists, as well as other
resources like <code>&#47;etc&#47;examples</code> and <code>&#47;usr&#47;local&#47;share&#47;doc&#47;pkg-readmes</code>,
the user isn&#8217;t lacking in ways to understand how the system works under
the hood. An OpenBSD installation is a didactic environment well-suited
to anyone with a Do It Yourself (DIY) attitude.</p>
<h2 id="security">Security</h2>
<p>No discussion of OpenBSD&#8217;s strengths would be complete without mention
of <a href="https://www.openbsd.org/security.html">its focus on security</a>.</p>
<p>One example I like, albeit one not strictly focused on the base system,
is how the OpenBSD project packages Firefox and Tor Browser with
<a href="https://man.openbsd.org/pledge"><code>pledge(2)</code></a> and
<a href="https://man.openbsd.org/unveil"><code>unveil(2)</code></a> in mind. Speaking to the
latter system call, there&#8217;s no reason these browsers should be able to
read <code>~&#47;.ssh</code> or <code>~&#47;.gnupg</code>, so they can&#8217;t. They can only interact with
allowlisted paths (with permission to read, write, execute, create, or
any combination thereof, depending on what&#8217;s stipulated). As a result,
the amount of damage a malicious extension or browser exploit could
wreak is much less than usual.</p>
<h2 id="privacy">Privacy</h2>
<p><code>kern.video.record</code> and <code>kern.audio.record</code> are both set to <code>0</code> by
default, meaning that no video or audio recording can occur without
permission.</p>
<h2 id="stability">Stability</h2>
<p>I mean this both in terms of system stability and how fast things
change. A constantly changing system is a nightmare to maintain for
system administrators.</p>
<p>Users can depend on the project making a new release available about
once every 6 months. Every new release comes with documentation on
changes made and how to upgrade, which is a painless process with the
<a href="https://man.openbsd.org/sysupgrade"><code>sysupgrade(8)</code></a> tool.</p>
<h2 id="sane-defaults">Sane defaults</h2>
<p>OpenBSD is certainly configurable, but the project&#8217;s attitudes toward
knobs is different from some others. OpenBSD tries to set good defaults
that work for everyone in the hopes that people won&#8217;t need to fiddle
around much (the more someone who doesn&#8217;t totally understand what
they&#8217;re doing changes a system, the more likely they are to break
something).</p>
<p>Unlike many minimal Linux distributions, these services are all enabled
by default on a new OpenBSD installation:</p>
<ul>
<li>time synchronization</li>
<li>cron</li>
<li>log rotation</li>
<li>a stateful packet filter&#47;firewall</li>
<li>local mail delivery</li>
</ul>
<h2 id="versatile">Versatile</h2>
<p>OpenBSD makes a good OS for both the desktop and the server, at least
for my needs.</p>
<h2 id="other-resources">Other resources</h2>
<ul>
<li><a href="https://why-openbsd.rocks/fact/">Why OpenBSD rocks</a></li>
<li><a href="https://github.com/ligurio/awesome-openbsd">awesome-openbsd</a></li>
<li><a href="https://rgz.ee/">Roman Zolotarev&#8217;s website</a></li>
<li><a href="https://dataswamp.org/~solene/">Solene&#8217;s website</a></li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/beets.html</guid>
<link>https://www.anthes.is/beets.html</link>
<pubDate>Thu, 05 Nov 2020 00:00:00 +0100</pubDate>
<title>Tagging and managing music with beets</title>
<description><![CDATA[

<h1 id="tagging-and-managing-music-with-beets">Tagging and managing music with beets</h1>
<p>Last updated: July 8th, 2025</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#why-metadata-matters">Why metadata matters</a></li>
<li><a href="https://www.anthes.is/#beets-as-a-music-manager">Beets as a music manager</a></li>
<li><a href="https://www.anthes.is/#how-to-install-beets">How to install beets</a></li>
<li><a href="https://www.anthes.is/#how-to-configure-beets">How to configure beets</a></li>
<li><a href="https://www.anthes.is/#how-to-import-music">How to import music</a></li>
<li><a href="https://www.anthes.is/#how-to-query-music">How to query music</a></li>
<li><a href="https://www.anthes.is/#what-plugins-does-beets-have">What plugins does beets have?</a>
<ul>
<li><a href="https://www.anthes.is/#album-art-with-fetchart">Album art with fetchart</a></li>
<li><a href="https://www.anthes.is/#genre-metadata-with-lastgenre">Genre metadata with lastgenre</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#cue-sheets-and-how-to-use-them">Cue sheets and how to use them</a>
<ul>
<li><a href="https://www.anthes.is/#scripting-cue-splitting">Scripting cue splitting</a></li>
</ul></li>
</ul>
<!-- mtoc-end -->
<h2 id="why-metadata-matters">Why metadata matters</h2>
<p>Tagged music files come with significant benefits. To name just a few:</p>
<ul>
<li><p>You know what to expect from your music without relying on long
filenames or navigating through a forest of directories.</p></li>
<li><p>You can find music of a specific type by querying the metadata. For
instance, you can list all music from a specific genre or year.</p></li>
<li><p>ReplayGain normalizes loudness so that your hand can rest somewhere
other than the volume knob.</p></li>
</ul>
<h2 id="beets-as-a-music-manager">Beets as a music manager</h2>
<p>If you&#8217;ve never tagged music before or want to change your workflow,
maybe you can <a href="https://beets.io/">use beets to manage your library</a>. You
can gain many of the benefits mentioned before through fetching and
applying metadata from sources like
<a href="https://musicbrainz.org/">MusicBrainz</a>. Plugins fill in the gaps.</p>
<p>You may like to keep <a href="https://beets.readthedocs.io">the documentation for
beets</a> in a separate tab while you read
this. This blog post provides an overview, but the docs provide more
exhaustive detail.</p>
<h2 id="how-to-install-beets">How to install beets</h2>
<p>You can install beets with a package manager, either the one your
operating system provides or a Python package manager like <code>pip</code>.</p>
<p>If you use <a href="https://www.openbsd.org">OpenBSD</a>, you can install beets
with <code>pkg_add</code>:</p>
<pre><code># pkg_add beets
</code></pre>
<h2 id="how-to-configure-beets">How to configure beets</h2>
<p>By default, beets places imported music in <code>~&#47;Music</code>. If you need to
change that, add the desired path to the <code>config.yaml</code> file like so:</p>
<pre><code>directory: &#47;path&#47;to&#47;music&#47;library
</code></pre>
<p>Often you can go straight to importing music without any further
modifications after this. Otherwise, the documentation on configuration
can help you if you run into any issues.</p>
<h2 id="how-to-import-music">How to import music</h2>
<p>Beets needs a music library to work with, which means you need to import
some music.</p>
<pre><code>$ beet import &#47;path&#47;to&#47;album
</code></pre>
<p>If the similarity score meets the threshold, beets tags the music
automatically and moves on. Otherwise, beets asks for more details.</p>
<h2 id="how-to-query-music">How to query music</h2>
<p>To list your music, use <code>beet ls</code>. But beware: this lists <em>all</em> music
currently managed by beets.</p>
<p>To narrow down your query, you can use the available metadata fields.
You can list those metadata fields with <code>beet fields</code>. The output
includes fields like <code>album</code>, <code>artist</code>, <code>year</code>, <code>country</code>, and others.</p>
<p>Some plugins provide metadata fields as well. For instance, with the
<a href="https://www.anthes.is/#genre-metadata-with-lastgenre">lastgenre plugin</a> installed, you can
query by genre like this.</p>
<pre><code>$ beet ls genre:&#39;Progressive Rock&#39;
</code></pre>
<h2 id="what-plugins-does-beets-have">What plugins does beets have?</h2>
<p>Beets includes many high quality plugins that extend its capabilities. I
show only a fraction of them here, so make sure to read through the
documentation on plugins afterward.</p>
<h3 id="album-art-with-fetchart">Album art with fetchart</h3>
<p>If you use a minimal music player without album art display
capabilities, then this may not matter to you. Still, with more
full-featured media applications, missing artwork bothers some people.</p>
<p>To fetch album art with the fetchart plugin, add the following to your
<code>config.yaml</code>:</p>
<pre><code>plugins: fetchart
</code></pre>
<p>Then, update the library.</p>
<pre><code>$ beet fetchart
</code></pre>
<h3 id="genre-metadata-with-lastgenre">Genre metadata with lastgenre</h3>
<p>Out of the box, beets doesn&#8217;t deal with genres at all because
MusicBrainz doesn&#8217;t have that information.</p>
<p>To add genre information to your collection by pulling it from
<a href="https://www.last.fm/">Last.fm</a>, put this inside your <code>config.yaml</code>:</p>
<pre><code>plugins: lastgenre
</code></pre>
<p>Then, update the library.</p>
<pre><code>$ beet lastgenre
</code></pre>
<h2 id="cue-sheets-and-how-to-use-them">Cue sheets and how to use them</h2>
<p>Beets needs a separate file for each track to tag music. Yet sometimes
only one Free Lossless Audio Codec (FLAC) file exists for an entire
album. Although many other formats besides FLAC exist, pretend for now
that your album came with one FLAC file that contains all the tracks.</p>
<p>In this case, look for a text file that describes the album&#8217;s track
layout with timestamps. People refer to a text file like this as a &#8220;cue
sheet.&#8221; With a cue sheet, you can split that single FLAC file into
separate files by track, through a process known as &#8220;cue splitting.&#8221;</p>
<p>Given a cue sheet and a FLAC file with many tracks, you can perform cue
splitting like this.</p>
<ol>
<li><p>Install <code>shntool</code> for cue splitting, and <code>cuetools</code> to tag the
resulting files.</p>
<pre><code># pkg_add shntool cuetools
</code></pre></li>
<li><p>Navigate to the album in question.</p>
<pre><code>$ cd &#47;path&#47;to&#47;album
</code></pre></li>
<li><p>Split the FLAC file.</p>
<pre><code>$ shnsplit -f example.cue -o flac example.flac
</code></pre>
<p><code>-f</code> points to the cue sheet. <code>-o</code> specifies the encoder, which
defaults to Waveform Audio File Format (WAV).</p>
<p>By default, <code>shnsplit</code> creates files with this format:
<code>split-track01.flac</code>. <code>beet import</code> renames files according to their
metadata, so the filenames don&#8217;t matter.</p></li>
<li><p>Rename the FLAC file so that it ends in <code>.bak</code>.</p>
<pre><code>$ mv example.flac{,.bak}
</code></pre>
<p>You need to perform this step so that cuetag won&#8217;t target the
original FLAC file later on.</p></li>
<li><p>Tag the split files with the original metadata.</p>
<pre><code>$ cuetag example.cue .&#47;*.flac
</code></pre>
<p><code>.&#47;*.flac</code> targets all FLAC files in the current directory. See
<a href="https://github.com/koalaman/shellcheck/wiki/SC2035">shellcheck&#8217;s wiki entry for
SC2035</a> for an
explanation of why I use <code>.&#47;*.flac</code> instead of <code>*.flac</code>.</p></li>
<li><p>If satisfied, delete the original FLAC file.</p>
<pre><code>$ rm example.flac.bak
</code></pre></li>
<li><p>Now that you performed cue splitting, you can import the music.</p>
<pre><code>$ beet import .
</code></pre></li>
</ol>
<h3 id="scripting-cue-splitting">Scripting cue splitting</h3>
<p>Given that this process can feel tedious, <a href="https://github.com/maybebyte/dotfiles/blob/e3b9752f26812326e194d2d7db5c273a13ee5759/.local/bin/splitflac">I wrote a small shell
script</a>
to automate cue splitting. <code>splitflac</code> works like so:</p>
<pre><code>$ splitflac example.cue example.flac
</code></pre>
<p>By default, <code>splitflac</code> doesn&#8217;t delete the original FLAC file. To do so
on a successful split, pass <code>-d</code>.</p>
<pre><code>$ splitflac -d example.cue example.flac
</code></pre>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/openbsd-router.html</guid>
<link>https://www.anthes.is/openbsd-router.html</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0100</pubDate>
<title>Building an OpenBSD router using an APU4D4</title>
<description><![CDATA[

<h1 id="building-an-openbsd-router-using-an-apu4d4">Building an OpenBSD router using an APU4D4</h1>
<p><em>Tested on OpenBSD 6.8</em></p>
<p><strong>NOTE</strong> (2023-07-22): the <a href="https://pcengines.ch/eol.htm">Accelerated Processing Unit (APU) platform
is end-of-life</a>. This article may need
updating, though many principles still apply. Proceed with caution.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#foreword">Foreword</a></li>
<li><a href="https://www.anthes.is/#hardware">Hardware</a></li>
<li><a href="https://www.anthes.is/#download-openbsd">Download OpenBSD</a></li>
<li><a href="https://www.anthes.is/#install-openbsd">Install OpenBSD</a></li>
<li><a href="https://www.anthes.is/#after-installation">After installation</a></li>
<li><a href="https://www.anthes.is/#wireguard">WireGuard</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="foreword">Foreword</h2>
<p>Consumer routers are riddled with security problems. Between 1999 and
2017, researchers disclosed 600 Common Vulnerabilities and Exposures
(CVEs) for router software&#8212;and those represent only the public
vulnerabilities. The paper <a href="https://repository.stcloudstate.edu/cgi/viewcontent.cgi?article=1067&amp;context=msia_etds">&#8220;So You Think Your Router Is
Safe?&#8221;</a>
addresses this subject well.</p>
<p>Faced with this reality, and as someone who&#8217;s caught the OpenBSD bug (or
pufferfish, same difference), I decided to build my own router. Here&#8217;s
how I did it.</p>
<h2 id="hardware">Hardware</h2>
<p>To buy an APU4D4 or similar, visit <a href="https://pcengines.ch/">PC Engines</a>.
Their boards come with <a href="https://www.coreboot.org/">coreboot</a>
preinstalled and the experience has been great.</p>
<p><img src="https://www.anthes.is/images/apu4b4_1_thumb.c18528fca0cba9ef4c3848c0f10fc6430be53fd2305f83197d02448a1b9f305e.2.png" alt="APU4B4 board on a wooden surface (front
side)." /></p>
<p><img src="https://www.anthes.is/images/apu4b4_2_thumb.4847a17a1552db16c59c2e6f5063d3250c4d2e188f35ea0de4d999b8589029bd.2.png" alt="APU4B4 board on a wooden surface (back
side)." /></p>
<p>Include a <a href="https://www.pcengines.ch/usbcom1a.htm">USB to DB9F serial
adapter</a> in your order, as you&#8217;ll
need it for installation.</p>
<p><img src="https://www.anthes.is/images/usbcom1a_thumb.ed1530b66afc03466606d66be7c67b728f4648652f57df7b269195ec731b6088.2.png" alt="USB to DB9F serial adapter on a wooden
surface." /></p>
<p><a href="https://pcengines.ch/pdf/apu4.pdf">Consult the manual for assembly
instructions</a>.</p>
<h2 id="download-openbsd">Download OpenBSD</h2>
<p>Download, verify, and flash the amd64 image that includes the file sets
<code>(installXX.img)</code> to a USB drive. <a href="https://www.openbsd.org/faq/faq4.html">OpenBSD&#8217;s FAQ covers
this</a>.</p>
<h2 id="install-openbsd">Install OpenBSD</h2>
<p>Connect to the serial port. I run OpenBSD on my laptop, so I use
<a href="https://man.openbsd.org/cu"><code>cu(1)</code></a> for serial connections. The user
must belong to the <code>dialer</code> group to use
<a href="https://man.openbsd.org/cua"><code>cua(4)</code></a> devices.</p>
<p>Display the current user and their groups with
<a href="https://man.openbsd.org/id"><code>id(1)</code></a>.</p>
<pre><code>$ id
</code></pre>
<p>Add the user to the <code>dialer</code> group if necessary with
<a href="https://man.openbsd.org/usermod"><code>usermod(8)</code></a>.</p>
<pre><code># usermod -G dialer [user]
</code></pre>
<p>Finally, connect to the serial port. This specifies the line to use
<code>(-l)</code> and the baud rate <code>(-s)</code>. The APU4D4 requires a baud rate of
<code>115200</code>.</p>
<pre><code>$ cu -l cuaU0 -s 115200
</code></pre>
<p>Remember to enter this at the boot prompt afterward to configure the
serial connection. The installer sets these later.</p>
<pre><code>boot&#62; stty com0 115200
boot&#62; set tty com0
boot&#62; boot
</code></pre>
<p>From here, the FAQ provides enough information to complete the
installation. Check <a href="https://www.openbsd.org/plat.html">the documentation for the relevant
architecture</a>. In this case, consult
<a href="https://www.openbsd.org/amd64.html">the notes on amd64</a>.</p>
<h2 id="after-installation">After installation</h2>
<p>Complete the usual tasks (read
<a href="https://man.openbsd.org/afterboot"><code>afterboot(8)</code></a>, check system mail,
etc.). After that, you need to implement several components:</p>
<ul>
<li>A <a href="https://www.openbsd.org/faq/pf/example1.html">firewall, Dynamic Host Configuration Protocol (DHCP) server, and
Domain Name System (DNS)
server</a>. See my
<a href="https://www.anthes.is/src/sysadm/file/examples/openbsd/pf.conf.html">pf.conf</a>.</li>
<li>A way to connect to the Internet, which varies between providers. For
some, you may need a Point-To-Point Protocol over Ethernet (PPPoE)
connection. See <a href="https://man.openbsd.org/pppoe"><code>pppoe(4)</code></a>.</li>
</ul>
<p>You can also add these components:</p>
<ul>
<li>Many use cases require a <a href="https://www.openbsd.org/faq/faq6.html#Bridge">network bridge for Ethernet
interfaces</a>. As of 6.9, I
use <a href="https://man.openbsd.org/veb"><code>veb(4)</code></a> with good results.</li>
<li><a href="https://lipidity.com/openbsd/router/">IPv6</a>, depending on your use
case and whether your Internet Service Provider (ISP) supports it.</li>
<li>DNS sinkhole with <a href="https://man.openbsd.org/unbound"><code>unbound(8)</code></a>, see
<a href="https://www.anthes.is/dns-sinkhole.html">Creating a DNS sinkhole with Perl and unbound(8)</a>.</li>
<li>Authoritative DNS for devices on the home network with the special-use
domain <code>home.arpa</code>, see <a href="https://www.anthes.is/local-authoritative-dns.html">Local authoritative DNS on OpenBSD using
dhcpd(8) and unbound(8)</a>.</li>
</ul>
<p>Always give official OpenBSD documentation preferential treatment and
cross-reference it when using unofficial documentation. Keep it simple
and avoid changing settings unless you understand what they do.</p>
<h2 id="wireguard">WireGuard</h2>
<p><a href="https://dataswamp.org/~solene/2021-10-09-openbsd-wireguard-exit.html">Solene has a great article on
this</a>.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/firefox-keyword-search.html</guid>
<link>https://www.anthes.is/firefox-keyword-search.html</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0100</pubDate>
<title>Firefox keyword search: bookmark method</title>
<description><![CDATA[

<h1 id="firefox-keyword-search-bookmark-method">Firefox keyword search: bookmark method</h1>
<p>Last updated: July 8th, 2025</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#introduction">Introduction</a></li>
<li><a href="https://www.anthes.is/#why-use-bookmark-keywords">Why use bookmark keywords?</a></li>
<li><a href="https://www.anthes.is/#defining-a-keyword-for-wiktionary">Defining a keyword for Wiktionary</a>
<ul>
<li><a href="https://www.anthes.is/#test-it-out-search-wiktionary-for-definitions">Test it out: search Wiktionary for definitions</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#what-else-can-you-do">What else can you do?</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="introduction">Introduction</h2>
<p>Many people use a search engine to navigate the Internet, but not
everyone knows about Firefox keyword search. This feature lets you
assign keywords to bookmarks so that you can query virtually any site
with ease.<sup id="fnref1"><a href="https://www.anthes.is/#fn1" rel="footnote">1</a></sup> Read on to learn how to set it up.</p>
<h2 id="why-use-bookmark-keywords">Why use bookmark keywords?</h2>
<p>Smart keywords (Mozilla&#8217;s term for this feature) can be a useful tool
for several reasons.</p>
<ul>
<li><em>Better online privacy</em>. Only query and send data to the site you want
answers from, right?</li>
<li><em>They save time</em>. Once you set them up, you can effortlessly search
your favorite websites from the address bar.</li>
</ul>
<h2 id="defining-a-keyword-for-wiktionary">Defining a keyword for Wiktionary</h2>
<p>Let&#8217;s use <a href="https://www.wiktionary.org/">Wiktionary, the multilingual
dictionary</a> as an example.</p>
<p>Open up Firefox and navigate to www.wiktionary.org. Right click
Wiktionary&#8217;s search bar to pull up a context menu with several entries.
Left click the &#8220;Add a Keyword for this Search…&#8221; entry.</p>
<p><img src="https://www.anthes.is/images/add-keyword-1.eaf5c6be649c4675eb34e426d68373beb9310bbccd8cf3a8547717c1f9d1e99e.2.png" alt="Inside a context menu, the &quot;Add a Keyword for this Search…&quot; entry
shows as
highlighted." /></p>
<p>Firefox now presents a dialog box with three options.</p>
<ol>
<li> The <em>name</em> of the bookmark.</li>
<li> The <em>location</em> to save the bookmark in.</li>
<li> The <em>keyword</em> to use for the bookmark.</li>
</ol>
<p>Choose a name and location if you like. Then, type in your desired
keyword and click &#8220;Save&#8221; to add it. I chose <code>wkt</code>.</p>
<p><img src="https://www.anthes.is/images/add-keyword-2.1ec0e0e718ca7a14def4adfe3ef4fe322fc8ecb74abc5b7819cff5a221632a40.2.png" alt="A dialog box that asks for the name of the bookmark, the folder to
save it in, and the
keyword." /></p>
<h3 id="test-it-out-search-wiktionary-for-definitions">Test it out: search Wiktionary for definitions</h3>
<p>Now you can use keyword search to find the definition of a word. Type
<code>wkt</code> (or whatever you chose) into Firefox&#8217;s address bar, followed by
the word you want to know the meaning of.</p>
<p>Here&#8217;s an exercise: what&#8217;s the difference between &#8220;somnambulism&#8221; and
&#8220;funambulism?&#8221;</p>
<h2 id="what-else-can-you-do">What else can you do?</h2>
<p>You can edit your regular bookmarks in the same way to navigate to any
site in a couple of keystrokes. Note that because there&#8217;s no query to
make, you only type in the keyword itself.</p>
<div class="footnotes">
<hr/>
<ol>

<li id="fn1">
<p>Pretty much anything based on Firefox can add keywords to bookmarks
as well. This includes Tor Browser.&#160;<a href="https://www.anthes.is/#fnref1" rev="footnote">&#8617;</a></p>
</li>

</ol>
</div>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/nixos-pros-cons.html</guid>
<link>https://www.anthes.is/nixos-pros-cons.html</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0100</pubDate>
<title>NixOS review: 8 important pros and cons</title>
<description><![CDATA[

<h1 id="nixos-review-8-important-pros-and-cons">NixOS review: 8 important pros and cons</h1>
<p>Last updated: July 4th, 2025</p>
<p>Before you continue on with my review of NixOS, here&#8217;s a short list of
the advantages and disadvantages I talk about for reference.</p>
<dl>
<dt>Strengths</dt>
<dd>
Abstraction
</dd>
<dd>
Reproducible builds
</dd>
<dd>
Atomic upgrades
</dd>
<dd>
Rollbacks
</dd>
<dd>
Immutability
</dd>
<dt>Weaknesses</dt>
<dd>
The learning curve
</dd>
<dd>
Some security concerns
</dd>
<dd>
Requires systemd
</dd>
</dl>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#whats-nixos-and-why-use-it">What&#8217;s NixOS, and why use it?</a></li>
<li><a href="https://www.anthes.is/#what-do-imperative-and-declarative-mean">What do imperative and declarative mean?</a>
<ul>
<li><a href="https://www.anthes.is/#enable-ssh-imperatively">Enable SSH imperatively</a></li>
<li><a href="https://www.anthes.is/#enable-ssh-declaratively">Enable SSH declaratively</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#nixos-advantages">NixOS advantages</a>
<ul>
<li><a href="https://www.anthes.is/#pro-1-abstraction">Pro #1: Abstraction</a></li>
<li><a href="https://www.anthes.is/#pro-2-reproducible-builds">Pro #2: Reproducible builds</a></li>
<li><a href="https://www.anthes.is/#pro-3-atomic-upgrades">Pro #3: Atomic upgrades</a></li>
<li><a href="https://www.anthes.is/#pro-4-rollbacks">Pro #4: Rollbacks</a></li>
<li><a href="https://www.anthes.is/#pro-5-immutability">Pro #5: Immutability</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#nixos-disadvantages">NixOS disadvantages</a>
<ul>
<li><a href="https://www.anthes.is/#con-1-the-learning-curve">Con #1: The learning curve</a></li>
<li><a href="https://www.anthes.is/#con-2-some-security-concerns">Con #2: Some security concerns</a></li>
<li><a href="https://www.anthes.is/#con-3-requires-systemd">Con #3: Requires systemd</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#concluding-my-nixos-review">Concluding my NixOS review</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="whats-nixos-and-why-use-it">What&#8217;s NixOS, and why use it?</h2>
<p><a href="https://nixos.org/">NixOS</a> is a unique Linux distribution. The main
thing that makes NixOS special is the ability to describe your desired
system layout with the Nix language. To do this, you edit a file named
<code>&#47;etc&#47;nixos&#47;configuration.nix</code> and then rebuild the system.</p>
<p>Declarative package management and system configuration have some
benefits over the imperative approach used by more traditional operating
systems. But to meaningfully review the pros and cons of NixOS, we must
first understand these terms and how they relate to one another.</p>
<p>If you already know the differences between them, <a href="https://www.anthes.is/#nixos-advantages">feel free to skip
ahead</a>.</p>
<h2 id="what-do-imperative-and-declarative-mean">What do imperative and declarative mean?</h2>
<p>The easiest way for me to explain these two concepts is to talk about
them in the context of software development.</p>
<p><em>Imperative</em> programming languages are things like Python and C. To make
languages like this useful, you provide step-by-step instructions that
lead to your end goal. In other words, <strong>imperative means you write out
how to do something</strong>.</p>
<p>Meanwhile, Haskell and Nix are examples of <em>declarative</em> programming
languages. Their design allows them to perform the necessary steps on
their own when given a proper description. In other words, <strong>declarative
means you describe what the end result should be</strong>.</p>
<p>Let&#8217;s compare the process of activating a Secure Shell (SSH) service on
Arch Linux and NixOS to demonstrate the differences between these two
paradigms.</p>
<h3 id="enable-ssh-imperatively">Enable SSH imperatively</h3>
<ol>
<li> Install the <code>openssh</code> package.</li>
</ol>
<pre><code># pacman -S openssh
</code></pre>
<ol start="2">
<li> Enable the service.</li>
</ol>
<pre><code># systemctl enable ssh
</code></pre>
<h3 id="enable-ssh-declaratively">Enable SSH declaratively</h3>
<ol>
<li> Edit the <code>&#47;etc&#47;nixos&#47;configuration.nix</code> file.</li>
</ol>
<pre><code>services.sshd.enable = true;
</code></pre>
<ol start="2">
<li> Rebuild and switch to the new configuration. During the build, NixOS
detects that the <code>sshd</code> service depends on the <code>openssh</code> package, so
it installs it.</li>
</ol>
<pre><code># nixos-rebuild switch
</code></pre>
<h2 id="nixos-advantages">NixOS advantages</h2>
<h3 id="pro-1-abstraction">Pro #1: Abstraction</h3>
<p>The nice thing about NixOS is that different software packages can use
the same syntax for configuration. Compare the way that default fonts are
set in Extensible Markup Language (XML) to the Nix expression.</p>
<p>You may notice that the XML sample only defines serif. Yet right below
it, Nix declares default serif, sans-serif, and monospace
fonts in less space.</p>
<pre><code>&#60;?xml version=&#39;1.0&#39;?&#62;
&#60;!DOCTYPE fontconfig SYSTEM &#39;fonts.dtd&#39;&#62;
&#60;fontconfig&#62;
    &#60;alias&#62;
        &#60;family&#62;serif&#60;&#47;family&#62;
        &#60;prefer&#62;
        &#60;family&#62;Liberation Serif&#60;&#47;family&#62;
        &#60;&#47;prefer&#62;
    &#60;&#47;alias&#62;
&#60;&#47;fontconfig&#62;
</code></pre>
<pre><code>fonts.fontconfig.defaultFonts = {
    serif = [ "Liberation Serif" ];
    sansSerif = [ "Liberation Sans" ];
    monospace = [ "Liberation Mono" ];
};
</code></pre>
<p>Note that this will <em>not</em> install font packages for you. The system
provides a separate <code>fonts.fonts</code> option where you list each package
out.</p>
<h3 id="pro-2-reproducible-builds">Pro #2: Reproducible builds</h3>
<p>Reproducibility and deterministic behavior are dense topics. When it
comes to NixOS, the idea is that recreating a given system
configuration is straightforward. You can copy <code>&#47;etc&#47;nixos&#47;configuration.nix</code> over to a
different machine and build from it. Assuming that file contains valid
Nix expressions, it should yield the same system state.</p>
<p>NixOS excels as a Linux distribution for cloud servers, as
reliable system deployment is straightforward and built into the operating system
itself. Additionally, Nix itself is a powerful collaborative tool
because creating a development environment with the same version of
important libraries is relatively straightforward.</p>
<h3 id="pro-3-atomic-upgrades">Pro #3: Atomic upgrades</h3>
<p>Another helpful feature Nix developers included is the avoidance
of partial states. When software follows this principle,
either everything takes effect or nothing does. This principle is also known as
<em>atomicity</em>.</p>
<p>NixOS treats upgrading as an atomic transaction. Here&#8217;s a practical
example of how that can be useful: if a power outage happens during a
rebuild, the packages remain in a consistent state. The system uses
either the entire working set of packages from before or after.</p>
<h3 id="pro-4-rollbacks">Pro #4: Rollbacks</h3>
<p>&#8220;Generations&#8221; are a key feature of NixOS. If you mess something up, you
can roll back to a previous working configuration. The boot loader
includes a list of generations to select from as well.</p>
<h3 id="pro-5-immutability">Pro #5: Immutability</h3>
<p>The system installs packages in unique locations within the Nix store
(<code>&#47;nix&#47;store</code>), and they always remain the same once built. The subdirectory
for each package comes from a cryptographic hash of its build
dependency graph.</p>
<p>Setting the jargon aside, this design means you can use multiple
versions of the same software&#8212;this even applies to identical
versions with different build dependencies or flags.</p>
<h2 id="nixos-disadvantages">NixOS disadvantages</h2>
<h3 id="con-1-the-learning-curve">Con #1: The learning curve</h3>
<p>To manage your NixOS system, you need to invest some time and effort
into learning Nix and related tools. After all, most of the system
configuration you would perform by hand with another Linux distribution
gets handled through a programming language instead.</p>
<p>Here&#8217;s my recommendation: experiment with Nix and see how you feel about
it before installing NixOS on bare metal. Check the <a href="https://nixos.org/guides/nix-language.html">Nix
language guide</a> and follow
along to get a sense of how the language works.</p>
<h3 id="con-2-some-security-concerns">Con #2: Some security concerns</h3>
<p>Reviewing what open issues a software project has before using it is a
good idea&#8212;especially those relating to security. Here are a few issues
in the <a href="https://github.com/NixOS/nixpkgs">nixpkgs repository</a> to
consider before using NixOS.</p>
<ul>
<li><a href="https://github.com/NixOS/nixpkgs/issues/24288">World-readable secrets inside the Nix store</a></li>
<li><a href="https://github.com/NixOS/nixpkgs/issues/11908">Many NixOS services needlessly run as root</a></li>
<li><a href="https://github.com/NixOS/nixpkgs/issues/121293">chmod leaves opportunity to leak secrets</a></li>
<li><a href="https://github.com/NixOS/nixpkgs/issues/156400">Secrets provided in arguments are exposed to unprivileged users</a></li>
<li><a href="https://github.com/NixOS/nixpkgs/issues/55370">nobody&#47;nogroup shouldn&#8217;t be used</a></li>
</ul>
<p>Most software projects of notable size and scope have some security
issues. Decide for yourself what an acceptable threshold is. You might
also consult the <a href="https://nixos.org/community/teams/security.html">NixOS security
page</a>.</p>
<h3 id="con-3-requires-systemd">Con #3: Requires systemd</h3>
<p><a href="https://github.com/NixOS/nixpkgs/issues/126797">NixOS depends on
systemd</a>. No option
exists to use something different like OpenRC or runit. This will
probably remain the case for the foreseeable future.</p>
<p>If you&#8217;re fine with using a Linux distribution that has systemd, then
this isn&#8217;t a concern for you. All the same, one drawback of NixOS is
that it doesn&#8217;t enjoy the level of freedom that something like Gentoo
has in this regard.</p>
<h2 id="concluding-my-nixos-review">Concluding my NixOS review</h2>
<p>Every system has its strengths and weaknesses, whether it&#8217;s a Linux
distribution or otherwise. Software is a tool: to select the right tool for
the job, you need to first understand the problem you&#8217;re looking to
solve.</p>
<p>Hopefully my NixOS review has given you some reasons to explore the Nix
ecosystem, as well as some knowledge to arm yourself with if you do so.
Assuming the benefits were compelling to you and the drawbacks are
things you can live with, you may as well give it a try. Experience is
one of the best ways to learn.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/why-self-host.html</guid>
<link>https://www.anthes.is/why-self-host.html</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0100</pubDate>
<title>Benefits and drawbacks of self-hosting</title>
<description><![CDATA[

<h1 id="benefits-and-drawbacks-of-self-hosting">Benefits and drawbacks of self-hosting</h1>
<p>Last updated: July 7th, 2025</p>
<p>As someone who has run many services for a while now, I feel I can speak
to the advantages and disadvantages of self-hosting. In my case, the
benefits have more than made up for the drawbacks. Still, each person or
organization must decide for themselves whether the pros outweigh the
cons.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#benefits">Benefits</a></li>
<li><a href="https://www.anthes.is/#drawbacks">Drawbacks</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="benefits">Benefits</h2>
<ul>
<li><strong>Learning more about system administration and software stacks</strong> by
building it yourself. Few teachers beat hands-on experience. When I
first started, I didn&#8217;t even know what the term &#8220;A record&#8221; meant (for
those who don&#8217;t already know, an A record refers to a Domain Name System
(DNS) record that maps a domain to an Internet Protocol (IP) address).</li>
<li><strong>Control and choice.</strong> For instance, you can decide what operating
system to use. Often many software choices exist for a particular
use case, and you can control the configuration details.</li>
<li><strong>Transparency.</strong> Self-hostable software typically uses open source
code. Because you assembled the pieces, you can more readily understand
how everything fits together. That means when something goes wrong, you
have some idea of where to start looking.</li>
<li><strong>Repairability.</strong> When something breaks, you can fix it yourself
without waiting for someone else to get to it.</li>
<li><strong>Privacy.</strong> This relates to the other points&#8212;because you have
control and transparency, you can know how the system processes
sensitive data and what parties can gain access to it.</li>
</ul>
<h2 id="drawbacks">Drawbacks</h2>
<ul>
<li><strong>Time investment.</strong> Learning the necessary skills and setting
everything up takes patience, and the time investment doesn&#8217;t end there.
Managing your own server requires regular system maintenance. This
involves making sure everything still functions correctly and performing
upgrades, among other tasks.</li>
<li><strong>Full responsibility.</strong> You bear responsibility for everything that
happens on your server. Not only <em>can</em> you fix it yourself, you <em>must</em>
fix it yourself. Sometimes this creates real inconvenience because
things often break at inopportune times.</li>
<li><strong>Ongoing costs.</strong> Self-hosting typically costs money. Recurring costs
for a virtual private server and domain name can add up over time. But
these may cost less than what you would otherwise pay for cloud
computing, so context matters here.</li>
<li><strong>Security risk.</strong> You can create vulnerabilities without
even realizing it. This is why taking your time and acquiring the
knowledge needed to do things right matters. Starting small also helps.
For example, hosting a static website carries little risk.</li>
</ul>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/os.html</guid>
<link>https://www.anthes.is/os.html</link>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0100</pubDate>
<title>Open source operating system recommendations</title>
<description><![CDATA[

<h1 id="open-source-operating-system-recommendations">Open source operating system recommendations</h1>
<p><a href="https://www.anthes.is/images/xkcd-standards.6975f55c09cec9a24ccb0185707d56892b881f9b1157c3e6d7ff61554d91ba94.2.png"><img src="https://www.anthes.is/images/xkcd-standards.6975f55c09cec9a24ccb0185707d56892b881f9b1157c3e6d7ff61554d91ba94.2.png" alt="A three panel comic strip from xkcd. It's titled &quot;How Standards
Proliferate&quot; with a subheading of &quot;See: A/C chargers, character
encodings, instant messaging, etc.&quot; The first panel says &quot;Situation:
there are 14 competing standards.&quot; The second panel features two stick
figures talking to one another, a man on the left and a woman on the
right. The man says to the woman, &quot;14?! Ridiculous! We need to develop
one universal standard that covers everyone's use cases.&quot; The woman
replies, &quot;Yeah!&quot; The third panel has a box in the top left hand corner
that says, &quot;Soon:&quot;, and the main text says, &quot;Situation: There are 15
competing standards.&quot;
" /></a></p>
<p>The <a href="https://xkcd.com/927/">xkcd comic</a> above illustrates the
difficulties of creating a universal standard, and why it often creates
yet another competing standard instead. This broadly applicable lesson
explains the wide array of open source operating systems available
today.</p>
<p>One of the most common experiences that someone exploring alternative
operating systems on their own may encounter is feeling overwhelmed by
the sheer amount of choice available. While I have no real solution for
this feeling, I hope that my own &#8220;best of kind&#8221; list can be useful
regardless.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#resources">Resources</a></li>
<li><a href="https://www.anthes.is/#user-friendly-and-just-works">User friendly and just works</a></li>
<li><a href="https://www.anthes.is/#innovative-and-for-power-users">Innovative and for power users</a></li>
<li><a href="https://www.anthes.is/#reasonably-secure-and-paranoid">Reasonably secure and paranoid</a></li>
<li><a href="https://www.anthes.is/#run-the-latest-software-and-do-it-your-way">Run the latest software and do it your way</a></li>
<li><a href="https://www.anthes.is/#customize-everything-and-learn-a-lot-about-linux">Customize everything and learn a lot about Linux</a></li>
<li><a href="https://www.anthes.is/#the-minimal-unix-like-cousin-of-arch">The minimal Unix-like cousin of Arch</a></li>
<li><a href="https://www.anthes.is/#simple-stable-and-follows-the-unix-philosophy">Simple, stable, and follows the Unix philosophy</a></li>
<li><a href="https://www.anthes.is/#a-secure-mobile-operating-system">A secure mobile operating system</a></li>
<li><a href="https://www.anthes.is/#reproducible-declaratively-built-operating-system">Reproducible, declaratively built operating system</a></li>
</ul>
<!-- mtoc-end -->
<h2 id="resources">Resources</h2>
<p>Before my recommendations, here are some resources that I find helpful:</p>
<ul>
<li><a href="https://librehunt.org/">Librehunt</a> first explains in clear terms the
concepts that anyone would need to know to make an informed decision
about Linux. After that, the second part involves a quiz so that
Librehunt can suggest relevant Linux distributions.</li>
<li><a href="https://distrochooser.de/">Distrochooser</a> resembles Librehunt in that
it uses the answers to a quiz to recommend Linux distributions, except
I&#8217;d say the target audience is more technically inclined.</li>
<li><a href="https://distrowatch.com/">Distrowatch</a> presents a more visually busy
and dense website, to say the least. But it&#8217;s a decent tool to search
operating systems by category, discover new ones, or read a little about
what&#8217;s happening in the Linux world. <strong>Note the way they measure
popularity</strong>: the numbers by each distribution reflect how many times
visitors accessed that page on the Distrowatch website in the last 6
months.</li>
<li><a href="https://repology.org/">Repology</a> offers a way to search the software
repositories of many different open source operating systems at once.</li>
</ul>
<h2 id="user-friendly-and-just-works">User friendly and just works</h2>
<p>Many options exist in this space, but a great all-around pick that I
always fall back to is <a href="https://linuxmint.com/">Linux Mint</a>. The
Cinnamon edition stands out in particular, as it feels user friendly and
polished, yet it also empowers the user. The large, helpful community
provides exactly what someone new to Linux will appreciate. I feel
confident pointing to Linux Mint for this use case, as it showcases the
unique strengths of Linux in an approachable way to new users.</p>
<h2 id="innovative-and-for-power-users">Innovative and for power users</h2>
<p>Red Hat, the largest Linux company in the world, backs
<a href="https://fedoraproject.org/">Fedora</a>. It offers many compelling features
out of the box, such as the SELinux (Security-Enhanced Linux) mandatory
access control system and the copy-on-write filesystem known as btrfs.
If taking advantage of new Linux features and keeping a finger on its
pulse matters to you, Fedora is a sensible choice.</p>
<h2 id="reasonably-secure-and-paranoid">Reasonably secure and paranoid</h2>
<p><a href="https://www.qubes-os.org">Qubes OS</a> focuses on security as an operating
system designed to separate different aspects of your digital life into
virtual machines, also called qubes. The idea is to compartmentalize
everything so that if one qube gets compromised, the rest of the system
remains unaffected. Qubes OS integrates
<a href="https://www.whonix.org/">Whonix</a> which is a huge privacy gain. I highly
recommend it to anyone that prioritizes the security of their machine
over all else.</p>
<h2 id="run-the-latest-software-and-do-it-your-way">Run the latest software and do it your way</h2>
<p><a href="https://archlinux.org/">Arch</a> often serves as the first advanced Linux
distribution that people try. A distribution you install from the
command-line, Arch aims to provide the newest releases of software in
its repositories. The Arch wiki provides an excellent source of
information, and a massive selection of software is available through
the Arch User Repository (AUR). Arch offers a middle ground between
customization and practicality that many people appreciate.</p>
<h2 id="customize-everything-and-learn-a-lot-about-linux">Customize everything and learn a lot about Linux</h2>
<p><a href="https://www.gentoo.org/">Gentoo</a> prioritizes extensive customization
and choice. Portage (Gentoo&#8217;s package management system) exposes a
wealth of options to the user, allowing them to adjust the compile time
options of software they install through something called &#8220;USE flags.&#8221;
Additionally, you choose components like the system logger and init
system during the installation process, which also takes place at the
command-line. Gentoo&#8217;s wiki and its knowledgeable yet friendly community
make it one of the best ways to learn about the deep inner workings of
Linux.</p>
<h2 id="the-minimal-unix-like-cousin-of-arch">The minimal Unix-like cousin of Arch</h2>
<p><a href="https://voidlinux.org/">Void</a> falls somewhere between Arch and Gentoo
in my eyes. It feels more Unix-like than Arch, yet it doesn&#8217;t lean as
heavily into customization as Gentoo. Void&#8217;s package manager (xbps),
init system (runit), and alternative C library support (musl) serve as
major selling points of the distribution. I can see the logic behind
many of the decisions and design choices that the project makes. For
example, I think mandoc provides an excellent manual page system, and
Void uses it by default.</p>
<h2 id="simple-stable-and-follows-the-unix-philosophy">Simple, stable, and follows the Unix philosophy</h2>
<p><a href="https://www.openbsd.org/">OpenBSD</a> is a Berkeley Software Distribution
(BSD) system that has a strong focus on security, portability,
simplicity, and correctness. OpenBSD features some of the best
documentation of any project I&#8217;ve used, and it introduced me to a lot of
software that I still admire to this day. For me, it remains unmatched
on the server side due to OpenBSD&#8217;s simplicity and secure by default
approach. Development moves in a more deliberate, controlled manner
compared to Linux, which moves rapidly and more chaotically. <a href="https://www.anthes.is/why-openbsd.html">Here are
some more of my thoughts on OpenBSD</a>.</p>
<h2 id="a-secure-mobile-operating-system">A secure mobile operating system</h2>
<p><a href="https://grapheneos.org/">GrapheneOS</a> is a version of the Android
operating system that focuses on privacy and security. It&#8217;s specifically
designed for Google Pixel devices due to the merits of that hardware.
GrapheneOS delivers unique advantages including sandboxed Google Play
services, extensive system hardening, and secure replacement
applications. For mobile operating systems, I know of nothing more
secure.</p>
<h2 id="reproducible-declaratively-built-operating-system">Reproducible, declaratively built operating system</h2>
<p><a href="https://nixos.org">NixOS</a> presents a different method of system
management: describing your desired system in a configuration file and
then issuing a single command to build it. This approach offers definite
advantages and <a href="https://www.anthes.is/nixos-pros-cons.html">I&#8217;ve written more about NixOS
here</a>.</p>

]]></description>
</item>

<item>
<guid>https://www.anthes.is/domain-migration.html</guid>
<link>https://www.anthes.is/domain-migration.html</link>
<pubDate>Mon, 03 Oct 2022 00:00:00 +0200</pubDate>
<title>Migrating to anthes.is</title>
<description><![CDATA[

<h1 id="migrating-to-anthes.is">Migrating to anthes.is</h1>
<p>Hey all. I know it has been a while since I have written anything. I am still
around and I have some articles planned that I would like to write, though I
feel it is important to first announce that this website&#8217;s domain name is
changing.</p>
<h2 id="table-of-contents">Table of contents</h2>
<!-- mtoc-start -->
<ul>
<li><a href="https://www.anthes.is/#when-is-this-happening">When is this happening?</a></li>
<li><a href="https://www.anthes.is/#what-will-happen-to-the-old-domain-name">What will happen to the old domain name?</a></li>
<li><a href="https://www.anthes.is/#why-change-the-domain-name">Why change the domain name?</a>
<ul>
<li><a href="https://www.anthes.is/#dnssec">DNSSEC</a></li>
<li><a href="https://www.anthes.is/#top-level-domains-matter">Top-level domains matter</a></li>
</ul></li>
<li><a href="https://www.anthes.is/#wrapping-up">Wrapping up</a></li>
<li><a href="https://www.anthes.is/#verification-optional">Verification (optional)</a>
<ul>
<li><a href="https://www.anthes.is/#with-gpg">With GPG</a></li>
<li><a href="https://www.anthes.is/#with-signify">With signify</a></li>
</ul></li>
</ul>
<!-- mtoc-end -->
<h2 id="when-is-this-happening">When is this happening?</h2>
<p>Though I have already registered <code>anthes.is</code>, I would like to wait a short
period before creating a <code>301 Moved Permanently</code> redirect. I plan to do the
migration at some point after this date and time.</p>
<pre><code>Wed Oct  5 18:00:00 UTC 2022
</code></pre>
<h2 id="what-will-happen-to-the-old-domain-name">What will happen to the old domain name?</h2>
<p>My plan at the moment is to keep <code>amissing.link</code> registered indefinitely. If I
change my mind and decide to drop ownership of <code>amissing.link</code>, I will
try my best to write about it several months in advance.</p>
<p>That said, do try to switch bookmarks and anything else like that to the new
domain, as this was a decision made with more than aesthetics in mind.</p>
<h2 id="why-change-the-domain-name">Why change the domain name?</h2>
<h3 id="dnssec">DNSSEC</h3>
<p>I have wanted to support DNSSEC for some time, given that DNS has some known
weaknesses. It is important to me that readers are directed to the right place
when they try to visit my website, and adding DNSSEC support makes this outcome
more likely.</p>
<p>Note that this will only make a difference under the right conditions (when
those trying to access my website are using resolvers that support DNSSEC). Even
so, I want to do my part in securing DNS. As I understand it, anything that
relies on DNS can benefit from the integrity provided by DNSSEC, so any other
services I host here will benefit from this change as well.</p>
<p>Unfortunately, the domain registrar I use does not support DNSSEC for the
<code>.link</code> top-level domain. Certainly there are ways to keep the old domain and
support DNSSEC, such as transferring to a different registrar or using an
external name server, but neither address a separate issue that this does.</p>
<h3 id="top-level-domains-matter">Top-level domains matter</h3>
<p>I have learned more about top-level domains between the time <code>amissing.link</code> was
first registered and now. The location and jurisdiction of the registry that
owns a top-level domain can have some consequences, and I did not know that
until recently. For instance, the <code>.us</code> top-level domain stipulates, among other
things, that all contact information must be provided without omission so that
it can be made public.</p>
<p>I am not all that interested in law or reading through legal documents. There
are many other things I would much rather learn about or do. So I hope it is no
surprise that I want to run this server in peace, and that I would prefer to
worry about things like this as little as possible. Although I have not
personally been in a situation where this was relevant, I would also like to
keep it that way, so that is why I opted for <code>.is</code> as a top-level domain.</p>
<p>The <a href="https://www.eff.org/">EFF</a> has a <a href="https://www.eff.org/files/2017/08/02/domain_registry_whitepaper.pdf">white-paper on Internet
registries</a>
that may explain this whole thing better than I can.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>Originally I had planned to write some about the personal meaning behind the new
domain name I chose, but it turned out to be a lengthier explanation than I
anticipated. I decided against including it in this announcement to keep things
brief and on topic, but I would like to write about it in the next article, so
stay tuned.</p>
<h2 id="verification-optional">Verification (optional)</h2>
<p>For those interested in verifying that I am really the one doing this, I have
made the Markdown for this page <a href="https://www.anthes.is/migration/domain-migration.txt">available as a .txt
file</a>, and added a <a href="https://www.anthes.is/migration/gpg/SHA256.sig">signed SHA256
file</a>. The signature can be validated with the
<a href="https://www.anthes.is/pubkeys/eurydice.asc">appropriate public key</a>.</p>
<p>I also decided to create a <a href="https://man.openbsd.org/signify"><code>signify(1)</code></a>
keypair for those that prefer <code>signify</code> to GPG. As before, the <a href="https://www.anthes.is/migration/signify/SHA256.sig">signed SHA256
file</a> is validated with the <a href="https://www.anthes.is/pubkeys/sigkey.pub">appropriate public
key</a>.</p>
<p>I think it makes sense to support both methods because I have not published that
<code>signify</code> key before, despite preferring the simplicity of <code>signify</code>.</p>
<h3 id="with-gpg">With GPG</h3>
<p>Create a temporary directory and change directory.</p>
<pre><code>$ mktemp -d
&#47;tmp&#47;tmp.jeT8T5wNgp
$ cd &#47;tmp&#47;tmp.jeT8T5wNgp
</code></pre>
<p>Fetch needed files.</p>
<pre><code>$ ftp https:&#47;&#47;amissing.link&#47;migration&#47;domain-migration.txt \
&#62; https:&#47;&#47;amissing.link&#47;migration&#47;gpg&#47;SHA256.sig \
&#62; https:&#47;&#47;amissing.link&#47;pubkeys&#47;eurydice.asc &#62; &#47;dev&#47;null
</code></pre>
<p>Import the public key.</p>
<pre><code>$ gpg --import eurydice.asc
gpg: key 53670DEBCF375780: public key "Ashlen &#60;eurydice@riseup.net&#62;" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
</code></pre>
<p>Verify that I issued the checksum. Note the placeholders for hours, minutes, and
seconds.</p>
<pre><code>$ TZ=UTC gpg --verify SHA256.sig
gpg: Signature made Mon Oct  3 hh:mm:ss 2022 UTC
gpg:                using EDDSA key 90965AE120F8E848979DEA4853670DEBCF375780
gpg: Good signature from "Ashlen &#60;eurydice@riseup.net&#62;" [ultimate]
</code></pre>
<p>Verify that the checksum of <code>domain-migration.txt</code> matches.</p>
<pre><code>$ sha256 -C SHA256.sig domain-migration.txt
(SHA256) domain-migration.txt: OK
</code></pre>
<h3 id="with-signify">With signify</h3>
<p>Create a temporary directory and change directory.</p>
<pre><code>$ mktemp -d
&#47;tmp&#47;tmp.RVBcIwTTal
$ cd &#47;tmp&#47;tmp.RVBcIwTTal
</code></pre>
<p>Fetch needed files.</p>
<pre><code>$ ftp https:&#47;&#47;amissing.link&#47;migration&#47;domain-migration.txt \
&#62; https:&#47;&#47;amissing.link&#47;migration&#47;signify&#47;SHA256.sig \
&#62; https:&#47;&#47;amissing.link&#47;pubkeys&#47;sigkey.pub &#62; &#47;dev&#47;null
</code></pre>
<p>Verify that I issued the checksum and that the checksum of
<code>domain-migration.txt</code> matches.</p>
<pre><code>$ signify -Cp sigkey.pub -x SHA256.sig
Signature Verified
domain-migration.txt: OK
</code></pre>

]]></description>
</item>
</channel></rss>
