<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
  <channel>
    <title>nnix.com</title>
    <link>https://nnix.com</link>
    <description>novo nilbud by swamplight: coal’s short but we&#39;ve got plenty of bog in the yard</description>
    <generator>Zola</generator>
    <language>en</language>
    <atom:link href="https://nnix.com/rss.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Thu, 10 Sep 2026 00:00:00 +0000</lastBuildDate>
    <item>
      <title>issy</title>
      <pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/issy/</link>
      <guid>https://nnix.com/projects/issy/</guid>
      <description xml:base="https://nnix.com/projects/issy/">&lt;p&gt;Source lives at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/issy&quot;&gt;github.com/davidemerson/issy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;issy is a text editor that cares about typesetting, a little. It’s written in Zig, runs on Linux, macOS, and OpenBSD, and ships as a single binary. Current release is v1.4.1.&lt;/p&gt;
&lt;h3 id=&quot;why&quot;&gt;why&lt;/h3&gt;
&lt;p&gt;I spend a lot of my day staring at text, and I wanted something with my own hotkeys and my own style preferences. But not something bloated.&lt;/p&gt;
&lt;p&gt;Most of all, I wanted a real print path. I want the PDF to look typeset, not like someone screenshot a terminal and hit print.&lt;/p&gt;
&lt;h3 id=&quot;two-themes&quot;&gt;two themes&lt;/h3&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/syntax-highlight.png&quot; alt=&quot;default dark theme with restrained syntax highlighting&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;the default theme. black background, violet keywords, soft green strings, dim comments.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/syntax-highlight-paper.png&quot; alt=&quot;paper theme based on solarized light&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;the paper theme. Solarized Light, warm cream background, violet keywords, cyan strings. Designed for readability in bright environments.&lt;/p&gt;
&lt;p&gt;Every color in either theme can be overridden per-key in &lt;code&gt;~/.issyrc&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;print-to-pdf&quot;&gt;print to pdf&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Ctrl+P&lt;/code&gt; (or &lt;code&gt;--print&lt;/code&gt; on the command line) renders the current buffer to a real PDF 1.4 file.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/pdf-export.png&quot; alt=&quot;printed pdf export of editor.zig in berkeley mono&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Headless from the command line:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;issy --font &amp;quot;Berkeley Mono.ttf&amp;quot; --print output.pdf source.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The print theme is tuned for ink on white paper. Recommended fonts: Berkeley Mono, Iosevka, JetBrains Mono, Commit Mono. Font metrics are sanity-checked on load to be sure you’ve made a sensible choice.&lt;/p&gt;
&lt;h3 id=&quot;multiple-cursors&quot;&gt;multiple cursors&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Ctrl+D&lt;/code&gt; selects the word under the cursor and adds a cursor at the next occurrence. Press it again to keep adding. Each press arms a rename: the word is highlighted at every secondary cursor, and the first character you type replaces it everywhere in one shot. From there, every edit, whether typing, Enter, Tab, backspace, delete, or paste, applies at all cursors simultaneously, and &lt;code&gt;Ctrl+Z&lt;/code&gt; undoes the whole multi-cursor tick as one step.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/multi-cursor.gif&quot; alt=&quot;multi-cursor rename demo&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;incremental-search&quot;&gt;incremental search&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Ctrl+F&lt;/code&gt; enters search mode; each keystroke re-runs the search from the position where you started and jumps the cursor to the first match. While the prompt is open, every visible match is highlighted, the match at the cursor is underlined, and a dim &lt;code&gt;3/17&lt;/code&gt;-style counter sits after the pattern. Search is smart-case: all-lowercase patterns match case-insensitively, any uppercase letter makes the match exact. &lt;code&gt;Tab&lt;/code&gt; toggles whole-word matching (shown as &lt;code&gt;[w]&lt;/code&gt; in the prompt). &lt;code&gt;Down&lt;/code&gt; or &lt;code&gt;Ctrl+G&lt;/code&gt; walks to the next match, &lt;code&gt;Up&lt;/code&gt; walks to the previous one, &lt;code&gt;Enter&lt;/code&gt; confirms, and &lt;code&gt;Escape&lt;/code&gt; cancels and returns the cursor to where it started.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/incremental-search.gif&quot; alt=&quot;incremental search demo&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;keyboard-and-mouse-selection&quot;&gt;keyboard and mouse selection&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Shift&lt;/code&gt; with an arrow key extends the selection one character at a time; &lt;code&gt;Ctrl+Shift+Left&lt;/code&gt; and &lt;code&gt;Ctrl+Shift+Right&lt;/code&gt; grow it a word at a time. Click places the cursor, double-click selects the word under it, triple-click selects the line, and shift-click extends from the existing anchor. Drag past the viewport edge and the view autoscrolls.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/word-selection.gif&quot; alt=&quot;word-wise keyboard selection demo&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;system-clipboard&quot;&gt;system clipboard&lt;/h3&gt;
&lt;p&gt;Copy and cut push to the OS clipboard via OSC 52, so copying from an SSH session lands in the host clipboard without extra plumbing (tmux wants &lt;code&gt;set -g set-clipboard on&lt;/code&gt; first). Copies over 100KB stay internal-only with a status notice.&lt;/p&gt;
&lt;p&gt;Since v1.3.0 the read direction works too. &lt;code&gt;Ctrl+V&lt;/code&gt; asks the terminal for the system clipboard over OSC 52, so text copied in any other application pastes straight in, and middle-click pastes the primary selection the same way.&lt;/p&gt;
&lt;h3 id=&quot;wide-glyphs&quot;&gt;wide glyphs&lt;/h3&gt;
&lt;p&gt;Wide text renders in place rather than as blanks, the cursor lands on the glyph you’d expect, and a click on either half of a wide glyph snaps to it.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/wide-glyphs.png&quot; alt=&quot;cjk and emoji in a markdown table with aligned columns&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;path-completion&quot;&gt;path completion&lt;/h3&gt;
&lt;p&gt;Type a partial directory or filename and press &lt;code&gt;Tab&lt;/code&gt; to auto-complete against what’s on disk.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/issy/path-completion.gif&quot; alt=&quot;path completion in the open-file prompt&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h3 id=&quot;swap-files&quot;&gt;swap files&lt;/h3&gt;
&lt;p&gt;While a buffer has unsaved changes, issy writes them to a sibling &lt;code&gt;.&amp;lt;name&amp;gt;.swp&lt;/code&gt; file every couple of seconds, and removes it on save, reload, file switch, or clean exit (SIGTERM and SIGHUP shut down gracefully and clean up too).&lt;/p&gt;
&lt;p&gt;Related: if the open file changes on disk underneath you (a &lt;code&gt;git pull&lt;/code&gt;, another editor), &lt;code&gt;Ctrl+R&lt;/code&gt; reloads, with a confirm prompt.&lt;/p&gt;
&lt;h3 id=&quot;per-file-cursor-memory&quot;&gt;per-file cursor memory&lt;/h3&gt;
&lt;p&gt;Quit a file and issy remembers the cursor position at &lt;code&gt;~/.cache/issy/positions.txt&lt;/code&gt;; reopening the same file restores the caret automatically.&lt;/p&gt;
&lt;h3 id=&quot;opt-in-editing-aids&quot;&gt;opt-in editing aids&lt;/h3&gt;
&lt;p&gt;Two behaviors I like but didn’t want to force on anyone, both off by default:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;auto_close_brackets = true&lt;/code&gt; makes typing an opener insert the matching pair with the cursor between them, as one undo step.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wrap_indent = true&lt;/code&gt; makes soft-wrap continuation rows hang under the wrapped line’s own leading whitespace instead of a flat 2 columns.&lt;/p&gt;
&lt;h3 id=&quot;install&quot;&gt;install&lt;/h3&gt;
&lt;h4 id=&quot;one-line-curl-linux-openbsd&quot;&gt;one-line curl (linux, openbsd)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;curl -sSL https://raw.githubusercontent.com/davidemerson/issy/main/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Drops &lt;code&gt;issy&lt;/code&gt; at &lt;code&gt;~/.local/bin/issy&lt;/code&gt;, verifies an Ed25519 signature over the release manifest against a public key baked into the script, verifies the binary’s SHA-256 against that signed manifest, seeds &lt;code&gt;~/.issyrc&lt;/code&gt; with commented defaults if you don’t already have one, records the release it just installed in &lt;code&gt;~/.cache/issy&lt;/code&gt; so the first launch doesn’t advertise an update you already have. Prebuilt binaries cover Linux amd64/arm64 and OpenBSD amd64. On platforms without a prebuilt (or when you invoke the installer on macOS), it falls through to &lt;code&gt;zig build -Doptimize=ReleaseSafe&lt;/code&gt; from source, with either Zig 0.15.x or 0.16.x on &lt;code&gt;PATH&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Flags: &lt;code&gt;--prefix DIR&lt;/code&gt; to relocate, &lt;code&gt;--version VER&lt;/code&gt; to pin to a specific release, &lt;code&gt;--no-rc&lt;/code&gt; to skip the &lt;code&gt;~/.issyrc&lt;/code&gt; seed, &lt;code&gt;--help&lt;/code&gt; for the rest. Prefer to read the script before piping it to a shell? Fetch it with &lt;code&gt;-o install.sh&lt;/code&gt; and run it once you’re happy.&lt;/p&gt;
&lt;p&gt;Prefer to skip the script entirely? The raw binaries (and the signed manifest to check them against) are attached to every &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/issy/releases/latest&quot;&gt;GitHub release&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;macos-via-homebrew&quot;&gt;macos via homebrew&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;brew tap davidemerson/issy https://github.com/davidemerson/issy
brew install issy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The formula tracks tagged releases (&lt;code&gt;vX.Y.Z&lt;/code&gt;), so upgrades are just &lt;code&gt;brew upgrade issy&lt;/code&gt;, the same one-liner you’d use for any Homebrew package. No &lt;code&gt;--fetch-HEAD&lt;/code&gt;, no uninstall-and-reinstall dance. CI rewrites the formula’s &lt;code&gt;url&lt;/code&gt; and &lt;code&gt;sha256&lt;/code&gt; on every tag push via a &lt;code&gt;release-tag&lt;/code&gt; job, so the versioned formula stays current without manual edits, and it stamps the release commit and its timestamp into the build so &lt;code&gt;issy --version&lt;/code&gt; reports the real SHA and update notifications work.&lt;/p&gt;
&lt;p&gt;Want the bleeding edge between releases? &lt;code&gt;brew install --HEAD issy&lt;/code&gt; still builds from &lt;code&gt;main&lt;/code&gt;, and &lt;code&gt;brew upgrade --fetch-HEAD issy&lt;/code&gt; pulls the latest commit.&lt;/p&gt;
&lt;p&gt;The formula pins its build dependency to &lt;code&gt;zig@0.15&lt;/code&gt; rather than the unversioned &lt;code&gt;zig&lt;/code&gt; formula, which is a moving target: Homebrew bumped it to 0.16 mid-cycle, and at the time that broke the build. issy itself builds on both series now, but the pin keeps Homebrew builds from moving out from under us, carries Apple’s Xcode 26.4 TBD compatibility patch, and is kept in sync with the &lt;code&gt;ZIG_VERSION&lt;/code&gt; used by CI.&lt;/p&gt;
&lt;p&gt;Prebuilt macOS binaries aren’t shipped because cross-compiled Mach-O from Linux has no code signature and Apple Silicon refuses to run it. Homebrew, or the curl installer (which falls through to a source build on macOS), produces a native host-signed binary that runs on both Intel and Apple Silicon with no &lt;code&gt;xattr&lt;/code&gt; or &lt;code&gt;codesign&lt;/code&gt; workarounds.&lt;/p&gt;
&lt;h4 id=&quot;openbsd&quot;&gt;openbsd&lt;/h4&gt;
&lt;p&gt;The curl installer downloads a prebuilt amd64 binary. Builds are verified on every push by a real OpenBSD 7.9 amd64 VM in CI (&lt;code&gt;openbsd-test&lt;/code&gt; job); the full unit and integration suite must pass on OpenBSD before main accepts a merge. An &lt;code&gt;editors/issy&lt;/code&gt; port was submitted to ports@openbsd.org at v1.3.1. Once it lands, &lt;code&gt;pkg_add issy&lt;/code&gt; will be the preferred path.&lt;/p&gt;
&lt;p&gt;Building from source: &lt;code&gt;pkg_add zig&lt;/code&gt; then &lt;code&gt;zig build -Doptimize=ReleaseSafe&lt;/code&gt;. &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;expect&lt;/code&gt; (also via &lt;code&gt;pkg_add&lt;/code&gt;) are needed if you want to run the integration test suite.&lt;/p&gt;
&lt;h4 id=&quot;build-from-source&quot;&gt;build from source&lt;/h4&gt;
&lt;p&gt;Requires &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://ziglang.org/download/&quot;&gt;Zig 0.15.x or 0.16.x&lt;/a&gt;; CI exercises 0.15.2 and 0.16.0. Zig 0.16 moved most of &lt;code&gt;std.fs&lt;/code&gt; under &lt;code&gt;std.Io&lt;/code&gt; with an explicit capability argument and dropped a handful of &lt;code&gt;std.posix&lt;/code&gt; calls. Rather than thread an Io handle through the editor, &lt;code&gt;src/fsx.zig&lt;/code&gt; wraps the thirty-odd file, environment, and process operations issy uses and dispatches at comptime on the toolchain version, so the same source builds on both series. Anything outside them is rejected at compile time with an actionable error instead of a page of inscrutable ones.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/davidemerson/issy
cd issy
zig build -Doptimize=ReleaseSafe
install -m 0755 zig-out/bin/issy ~/.local/bin/issy
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;cross-compile&quot;&gt;cross compile&lt;/h3&gt;
&lt;p&gt;Zig makes this trivial for Linux and macOS:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zig build -Dtarget=x86_64-linux-gnu
zig build -Dtarget=aarch64-linux-gnu
zig build -Dtarget=x86_64-macos
zig build -Dtarget=aarch64-macos
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or &lt;code&gt;zig build cross&lt;/code&gt; to build all targets at once. The exception is OpenBSD: Zig doesn’t ship OpenBSD libc headers, and modern OpenBSD kills processes that make raw syscalls outside libc, so the OpenBSD release binary is built natively inside the CI VM instead of cross-compiled.&lt;/p&gt;
&lt;h3 id=&quot;usage&quot;&gt;usage&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;issy [options] [file[:line]]

issy main.zig
issy src/editor.zig:42    # open at line 42
issy draft.md:5           # works for new files too: creates draft.md
issy newdoc.md            # start a new file at that path
issy                      # empty buffer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Command-line options:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Flag&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--version&lt;/code&gt;, &lt;code&gt;-v&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Print version and exit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--help&lt;/code&gt;, &lt;code&gt;-h&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Print usage and exit&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--config FILE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Use a specific config file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--theme NAME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Override theme (&lt;code&gt;default&lt;/code&gt;, &lt;code&gt;paper&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--font PATH&lt;/code&gt;&lt;/td&gt;&lt;td&gt;TTF/OTF font for PDF output&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--no-config&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Skip loading config file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--print FILE&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Export to PDF and exit (no TUI)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--rollback&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Swap in the previous binary (if auto-update has run) and exit&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;keybindings&quot;&gt;keybindings&lt;/h3&gt;
&lt;p&gt;The ones you’d expect, mostly.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+S&lt;/td&gt;&lt;td&gt;Save, from any mode (a buffer with no filename prompts for a path, with Tab completion)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+Q / Ctrl+W&lt;/td&gt;&lt;td&gt;Quit (on unsaved changes: Enter or Ctrl+Q again discards, Ctrl+S saves and stays, Escape cancels)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+Z / Ctrl+Y&lt;/td&gt;&lt;td&gt;Undo / redo (typing runs coalesce within 500ms, one step per word)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+C / Ctrl+X&lt;/td&gt;&lt;td&gt;Copy / cut (also push to the OS clipboard via OSC 52)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+V&lt;/td&gt;&lt;td&gt;Paste the system clipboard via OSC 52 (falls back to the internal clipboard if the terminal won’t answer)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+Shift+V / Shift+Insert&lt;/td&gt;&lt;td&gt;Terminal-native paste of the clipboard / primary selection, as bracketed paste&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+A&lt;/td&gt;&lt;td&gt;Select all&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+F&lt;/td&gt;&lt;td&gt;Incremental search (Tab toggles whole-word, Up/Down walk matches)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+G&lt;/td&gt;&lt;td&gt;Find next match&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+H&lt;/td&gt;&lt;td&gt;Search and replace (Tab switches fields, Enter replaces next, Ctrl+A replaces all as one undo step)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+O&lt;/td&gt;&lt;td&gt;Open file&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+N&lt;/td&gt;&lt;td&gt;New empty buffer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+P&lt;/td&gt;&lt;td&gt;Export to PDF&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+R&lt;/td&gt;&lt;td&gt;Reload file from disk&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+L&lt;/td&gt;&lt;td&gt;Go to line&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+D&lt;/td&gt;&lt;td&gt;Add cursor at next occurrence of word (and arm a rename)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+Left / Ctrl+Right&lt;/td&gt;&lt;td&gt;Jump by word&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shift+Arrow&lt;/td&gt;&lt;td&gt;Extend selection by character&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+Shift+Left / Right&lt;/td&gt;&lt;td&gt;Extend selection by word&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Ctrl+/ or F1&lt;/td&gt;&lt;td&gt;Show keybindings overlay&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Middle-click pastes the primary selection at the click point.&lt;/p&gt;
&lt;h3 id=&quot;configuration&quot;&gt;configuration&lt;/h3&gt;
&lt;p&gt;The installer seeds &lt;code&gt;~/.issyrc&lt;/code&gt; on first run with every setting commented out, so you can see what’s available and uncomment what you want. Unknown keys are ignored; missing keys fall back to compiled-in defaults. The editor watches &lt;code&gt;~/.issyrc&lt;/code&gt; for mtime changes and reloads live, so edits to config land in the running editor without a restart. The reload is debounced until the new mtime has held still for a full tick, so a half-written file from a non-atomic external save is never applied. The full reference is in &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/issy/blob/main/CONFIGURATION.md&quot;&gt;CONFIGURATION.md&lt;/a&gt; in the repo. A minimal example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tab_width = 4
expand_tabs = true
line_numbers = true
right_margin = 100
cursor_style = bar
font_file = &amp;quot;/path/to/font.ttf&amp;quot;

[theme.paper]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Newer keys worth knowing about: &lt;code&gt;swap_files&lt;/code&gt; and &lt;code&gt;system_clipboard&lt;/code&gt; (both on by default, see above), &lt;code&gt;auto_close_brackets&lt;/code&gt; and &lt;code&gt;wrap_indent&lt;/code&gt; (opt-in, see above), and &lt;code&gt;autoupdate&lt;/code&gt; (below).&lt;/p&gt;
&lt;h3 id=&quot;syntax-highlighting&quot;&gt;syntax highlighting&lt;/h3&gt;
&lt;p&gt;C, C++, Zig, Python, JavaScript, TypeScript, Rust, Go, Ruby, Java, Shell, HTML, CSS, JSON, YAML, TOML, Makefile, Dockerfile, Markdown, and TeX/LaTeX. Twenty in total, which covers just about everything I touch on a given day. Language is detected by file extension or well-known filename (&lt;code&gt;Makefile&lt;/code&gt;, &lt;code&gt;Dockerfile&lt;/code&gt;, &lt;code&gt;Gemfile&lt;/code&gt;, shell dotfiles like &lt;code&gt;.bashrc&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Multi-line constructs carry across lines: C block comments, Python triple-quoted strings, JS/TS template literals, and Zig &lt;code&gt;\\&lt;/code&gt; line strings all highlight correctly, even when their opening delimiter is scrolled off-screen. The renderer keeps a per-line syntax-state cache so this stays cheap.&lt;/p&gt;
&lt;h3 id=&quot;auto-update&quot;&gt;auto update&lt;/h3&gt;
&lt;p&gt;Release builds check for newer versions on startup. The check is an HTTPS request to a &lt;code&gt;commit.txt&lt;/code&gt; asset on the latest GitHub release.&lt;/p&gt;
&lt;p&gt;If you’d rather not have this, &lt;code&gt;notify_updates = false&lt;/code&gt; disables the check entirely.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>dotfiles</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/dotfiles/</link>
      <guid>https://nnix.com/projects/dotfiles/</guid>
      <description xml:base="https://nnix.com/projects/dotfiles/">&lt;p&gt;Updated scripts are maintained at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/dotfiles.git&quot;&gt;github.com/davidemerson/dotfiles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;These dotfiles configure a workstation on Debian Linux, OpenBSD, or macOS from a single POSIX shell script. No Salt, Ansible, or other configuration management tools, just &lt;code&gt;sh provision.sh&lt;/code&gt;. The script is idempotent and safe to re-run after pulling updates.&lt;/p&gt;
&lt;p&gt;Supported platforms are Debian 13+, OpenBSD 7.8+, and macOS with Homebrew.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;how it works&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;provision.sh&lt;/code&gt; detects the OS via &lt;code&gt;uname -s&lt;/code&gt; and branches accordingly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debian Linux&lt;/strong&gt;: installs packages via &lt;code&gt;apt-get&lt;/code&gt;, configures sway (Wayland), foot, waybar, wofi, swaylock, mako. Logs in through greetd + tuigreet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenBSD&lt;/strong&gt;: installs packages via &lt;code&gt;pkg_add&lt;/code&gt;, configures i3 (X11) with patched builds of st and dmenu, enables the xenodm login greeter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt;: installs packages via Homebrew (installing Homebrew itself if missing), deploys &lt;code&gt;.zshrc&lt;/code&gt;, &lt;code&gt;.wezterm.lua&lt;/code&gt;, and the shared configs. No window manager configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Linux and OpenBSD it runs as root and prompts for the username to provision (creating the user if needed, putting it in &lt;code&gt;sudo&lt;/code&gt; or &lt;code&gt;wheel&lt;/code&gt;, and setting bash as the login shell). It also prompts for a hostname and makes sure the hostname resolves in &lt;code&gt;/etc/hosts&lt;/code&gt;, which prevents slow lookups. On macOS it runs as your normal user.&lt;/p&gt;
&lt;h4 id=&quot;os-conditional-blocks&quot;&gt;os-conditional blocks&lt;/h4&gt;
&lt;p&gt;Some config files need different content per OS. Rather than maintaining separate files, I use simple markers. From &lt;code&gt;.bashrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# @@IF_OPENBSD@@
if [ &amp;quot;$(tty)&amp;quot; = &amp;quot;/dev/ttyC0&amp;quot; ]; then
	startx
fi
# @@END_IF@@
# @@IF_LINUX@@
if [ &amp;quot;$(tty)&amp;quot; = &amp;quot;/dev/tty1&amp;quot; ] &amp;amp;&amp;amp; ! systemctl is-active --quiet greetd 2&amp;gt;/dev/null; then
	exec sway
fi
# @@END_IF@@
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At deploy time, &lt;code&gt;sed&lt;/code&gt; strips blocks for other OSes and removes the marker comments, leaving a clean config file. There is also a &lt;code&gt;@@HOME@@&lt;/code&gt; marker that gets replaced with the target user’s home directory (&lt;code&gt;.issyrc&lt;/code&gt; uses it for an absolute font path). This replaces the Jinja2/Salt templating from the earlier version of this project with zero dependencies. &lt;code&gt;.gitconfig&lt;/code&gt; uses the same trick to enable delta as the diff pager only on Linux, where it’s packaged.&lt;/p&gt;
&lt;h4 id=&quot;file-routing&quot;&gt;file routing&lt;/h4&gt;
&lt;p&gt;Not every dotfile deploys on every OS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Linux&lt;/strong&gt; gets sway, swaylock, waybar, wofi, foot, and mako configs. Skips i3, i3status, dunst, &lt;code&gt;.xinitrc&lt;/code&gt;, and &lt;code&gt;.zshrc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenBSD&lt;/strong&gt; gets i3, i3status, dunst, &lt;code&gt;.xinitrc&lt;/code&gt;, and the &lt;code&gt;lock&lt;/code&gt; and &lt;code&gt;volnotify&lt;/code&gt; scripts. Skips sway and friends, and &lt;code&gt;.zshrc&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;macOS&lt;/strong&gt; gets &lt;code&gt;.zshrc&lt;/code&gt; and &lt;code&gt;.wezterm.lua&lt;/code&gt; instead of &lt;code&gt;.bashrc&lt;/code&gt;/&lt;code&gt;.bash_profile&lt;/code&gt;, and skips all window manager configs.&lt;/li&gt;
&lt;li&gt;Everything else (git, ssh, tmux, issy, fonts, cursors, theming, &lt;code&gt;.Xresources&lt;/code&gt;, and the &lt;code&gt;workstation&lt;/code&gt;, &lt;code&gt;shot&lt;/code&gt;, and &lt;code&gt;sysinfo&lt;/code&gt; scripts) deploys on all three. Some of it is inert outside its home OS, which is fine.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One file is special: &lt;code&gt;.config/workstation.conf&lt;/code&gt; is seeded once and never overwritten on re-runs, so each machine keeps its own values.&lt;/p&gt;
&lt;h3 id=&quot;procedure&quot;&gt;procedure&lt;/h3&gt;
&lt;h4 id=&quot;linux-debian&quot;&gt;linux (debian)&lt;/h4&gt;
&lt;p&gt;VMWare Workstation settings (if applicable):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enable Virtualize IOMMU to prevent keyboard lag.&lt;/li&gt;
&lt;li&gt;Enable 3D Acceleration with ~2GB VRAM.&lt;/li&gt;
&lt;li&gt;Enable Enhanced Keyboard if available to avoid Windows lock conflicts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Standard Debian installation, selecting Desktop, SSH Server, and Standard System Utilities. Then:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;su -
apt update &amp;amp;&amp;amp; apt install git
git clone https://github.com/davidemerson/dotfiles.git
cd dotfiles/
sh provision.sh
reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reboot, greetd presents tuigreet on vt7. Logging in runs &lt;code&gt;/usr/local/bin/sway-session&lt;/code&gt;, a login shell wrapper so the session inherits the environment from &lt;code&gt;.bashrc&lt;/code&gt;, which then launches sway. &lt;code&gt;Mod4+Return&lt;/code&gt; opens foot. If greetd isn’t running for some reason, a tty1 console login starts sway on its own, so a broken greeter never locks me out of the desktop.&lt;/p&gt;
&lt;p&gt;Beyond the desktop, the Linux run also sets the console font to Berkeley Mono (see below), masks gdm in favor of greetd, sets the timezone to &lt;code&gt;America/New_York&lt;/code&gt;, pins NTP servers for systemd-timesyncd with a Cloudflare fallback, installs Sublime Text from the official apt repository, sets Google Chrome as the default browser through update-alternatives and the per-user xdg default, and installs open-vm-tools-desktop when it detects VMware.&lt;/p&gt;
&lt;h4 id=&quot;openbsd&quot;&gt;openbsd&lt;/h4&gt;
&lt;p&gt;Standard OpenBSD installation. On smaller disks, ensure &lt;code&gt;/usr/local&lt;/code&gt; has at least 2GB (the full install uses about 1.6GB in &lt;code&gt;/usr/local&lt;/code&gt;).&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pkg_add git
git clone https://github.com/davidemerson/dotfiles.git
cd dotfiles/
sh provision.sh
reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After reboot, xenodm presents a login on a solid black background. Logging in runs &lt;code&gt;~/.xsession&lt;/code&gt;, which launches i3. &lt;code&gt;Mod4+Return&lt;/code&gt; opens st.&lt;/p&gt;
&lt;p&gt;Why xenodm? The VMware SVGA adapter has no DRM/KMS driver on OpenBSD, so Xorg needs aperture access, which means running as root. xenodm provides that without making Xorg setuid. The provisioning script comments out xconsole in &lt;code&gt;Xsetup_0&lt;/code&gt; (otherwise i3 tiles it fullscreen as the only window) and keeps the script executable after editing it. xenodm reads &lt;code&gt;~/.xsession&lt;/code&gt; rather than &lt;code&gt;~/.xinitrc&lt;/code&gt;, so &lt;code&gt;.xinitrc&lt;/code&gt; is mirrored to &lt;code&gt;.xsession&lt;/code&gt;: the same i3 session launches whether you come in through xenodm or through &lt;code&gt;startx&lt;/code&gt; (which &lt;code&gt;.bashrc&lt;/code&gt; still runs automatically on a ttyC0 console login).&lt;/p&gt;
&lt;p&gt;Beyond the desktop, the script does a fair amount of OpenBSD system configuration:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;doas&lt;/strong&gt; for the wheel group (&lt;code&gt;permit persist :wheel&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timezone&lt;/strong&gt; set to &lt;code&gt;America/New_York&lt;/code&gt; (same as Linux). The bar carries a separate UTC clock.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UTF-8 locale&lt;/strong&gt; exported in &lt;code&gt;.bashrc&lt;/code&gt; for proper Unicode in st and btop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NTP&lt;/strong&gt;: writes &lt;code&gt;/etc/ntpd.conf&lt;/code&gt; with pool servers, time.cloudflare.com, the VMware host-time sensor, and HTTPS constraints, and sets &lt;code&gt;ntpd -s&lt;/code&gt; so the clock steps at boot. OpenNTPD only steps at startup though; a running daemon just slews, so a VM that jumps mid-run (snapshot, suspend, clone) never catches up. A small clock-guard script runs from cron every 10 minutes, checks the vmt0 sensor delta, and restarts ntpd (with an &lt;code&gt;rdate&lt;/code&gt; step) if the guest has drifted more than 10 seconds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;noatime&lt;/strong&gt;: FFS partitions get &lt;code&gt;noatime&lt;/code&gt; in fstab and remounted live, which skips access-time writes and reduces I/O.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Console font&lt;/strong&gt; set to Spleen 8x16 where the display driver supports it (simplefb on VMware arm64 does not).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VMware Xorg config&lt;/strong&gt;: when the script detects VMware it pins the vmware driver and sets a 4K default mode with a large virtual size. open-vm-tools isn’t packaged for OpenBSD, so there’s no dynamic host-window resize; use xrandr to switch between the listed modes.&lt;/li&gt;
&lt;li&gt;Removes the default &lt;code&gt;/etc/i3/config&lt;/code&gt; that conflicts with the user config.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why i3 instead of sway on OpenBSD?&lt;/strong&gt; OpenBSD on VMware uses a framebuffer with no DRM/GPU driver, which means sway (Wayland) can’t create a rendering backend. i3 on X11 works out of the box. The i3 config mirrors the sway config (same keybindings, colors, gaps, workspaces) and adds a few things of its own: thin 2px borders with no title bars, auto-floating for dialogs and pop-ups, and an st scratchpad terminal started at launch.&lt;/p&gt;
&lt;h4 id=&quot;macos&quot;&gt;macos&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/davidemerson/dotfiles.git ~/dotfiles
cd ~/dotfiles
sh provision.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run as your normal user, not root. The script installs Homebrew if missing, then CLI tools, WezTerm, and the application casks. Since CoreText doesn’t scan &lt;code&gt;~/.fonts&lt;/code&gt;, the font is also copied to &lt;code&gt;~/Library/Fonts&lt;/code&gt; so WezTerm can find it.&lt;/p&gt;
&lt;h4 id=&quot;sshd-hardening&quot;&gt;sshd hardening&lt;/h4&gt;
&lt;p&gt;On Linux and OpenBSD, the script sets sshd to key-only auth (&lt;code&gt;PasswordAuthentication no&lt;/code&gt;, &lt;code&gt;KbdInteractiveAuthentication no&lt;/code&gt;). The candidate config is validated with &lt;code&gt;sshd -t&lt;/code&gt; before it replaces the real one, so a bad edit can never lock you out. macOS keeps its own Remote Login settings.&lt;/p&gt;
&lt;h4 id=&quot;manual-steps-after-provisioning&quot;&gt;manual steps after provisioning&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Place the SSH key at &lt;code&gt;~/.ssh/id_d_nnix.pem&lt;/code&gt; (plus matching &lt;code&gt;.pub&lt;/code&gt;). Both &lt;code&gt;.ssh/config&lt;/code&gt; and &lt;code&gt;.gitconfig&lt;/code&gt; reference that path, for auth and for commit signing. If you use a different key, edit &lt;code&gt;.gitconfig&lt;/code&gt;, &lt;code&gt;.ssh/config&lt;/code&gt;, and &lt;code&gt;.config/git/allowed_signers&lt;/code&gt; in the repo first. If the private key has no sibling &lt;code&gt;.pub&lt;/code&gt;, generate one with &lt;code&gt;ssh-keygen -y&lt;/code&gt;, so &lt;code&gt;workstation&lt;/code&gt; can tell when the key is already loaded.&lt;/li&gt;
&lt;li&gt;Fill in &lt;code&gt;~/.config/workstation.conf&lt;/code&gt; if you want the &lt;code&gt;workstation&lt;/code&gt; command (see below).&lt;/li&gt;
&lt;li&gt;Sign in to 1Password and enable its browser integration in Chrome. Chrome is allow-listed by default, so there’s nothing else to configure.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-gets-installed&quot;&gt;what gets installed&lt;/h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;/th&gt;&lt;th&gt;Linux&lt;/th&gt;&lt;th&gt;OpenBSD&lt;/th&gt;&lt;th&gt;macOS&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Window Manager&lt;/td&gt;&lt;td&gt;Sway (Wayland)&lt;/td&gt;&lt;td&gt;i3 (X11)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Greeter&lt;/td&gt;&lt;td&gt;greetd + tuigreet&lt;/td&gt;&lt;td&gt;xenodm&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Terminal&lt;/td&gt;&lt;td&gt;foot&lt;/td&gt;&lt;td&gt;st (patched)&lt;/td&gt;&lt;td&gt;WezTerm&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Status Bar&lt;/td&gt;&lt;td&gt;waybar&lt;/td&gt;&lt;td&gt;i3bar + i3status&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Launcher&lt;/td&gt;&lt;td&gt;wofi&lt;/td&gt;&lt;td&gt;dmenu (patched)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Lock&lt;/td&gt;&lt;td&gt;swaylock&lt;/td&gt;&lt;td&gt;i3lock via &lt;code&gt;lock&lt;/code&gt; script&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Notifications&lt;/td&gt;&lt;td&gt;mako&lt;/td&gt;&lt;td&gt;dunst&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Volume&lt;/td&gt;&lt;td&gt;pamixer + wob&lt;/td&gt;&lt;td&gt;sndioctl via &lt;code&gt;volnotify&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Clipboard&lt;/td&gt;&lt;td&gt;wl-clipboard + cliphist&lt;/td&gt;&lt;td&gt;clipmenu&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Privilege&lt;/td&gt;&lt;td&gt;sudo&lt;/td&gt;&lt;td&gt;doas&lt;/td&gt;&lt;td&gt;sudo&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Browser&lt;/td&gt;&lt;td&gt;Google Chrome&lt;/td&gt;&lt;td&gt;Chromium&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Password manager&lt;/td&gt;&lt;td&gt;1Password + CLI&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;1Password + CLI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mail&lt;/td&gt;&lt;td&gt;Fastmail&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Fastmail&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Notes / Tasks&lt;/td&gt;&lt;td&gt;Joplin, Todoist&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Joplin, Todoist&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Media&lt;/td&gt;&lt;td&gt;VLC, Audacity&lt;/td&gt;&lt;td&gt;VLC, Audacity&lt;/td&gt;&lt;td&gt;VLC, Audacity&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Meetings&lt;/td&gt;&lt;td&gt;Zoom&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Zoom&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Git GUI&lt;/td&gt;&lt;td&gt;GitHub Desktop (community)&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;GitHub Desktop&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Networking&lt;/td&gt;&lt;td&gt;ZeroTier&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;AI CLI&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Editor&lt;/td&gt;&lt;td&gt;issy (default), micro, nano, Sublime Text&lt;/td&gt;&lt;td&gt;issy (default), nano&lt;/td&gt;&lt;td&gt;issy (default), micro, nano&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Shell&lt;/td&gt;&lt;td&gt;bash&lt;/td&gt;&lt;td&gt;bash&lt;/td&gt;&lt;td&gt;zsh&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Multiplexer&lt;/td&gt;&lt;td&gt;tmux, herdr&lt;/td&gt;&lt;td&gt;tmux (base)&lt;/td&gt;&lt;td&gt;tmux, herdr&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Remote shell&lt;/td&gt;&lt;td&gt;mosh&lt;/td&gt;&lt;td&gt;mosh&lt;/td&gt;&lt;td&gt;mosh&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Fetch&lt;/td&gt;&lt;td&gt;pfetch + sysinfo&lt;/td&gt;&lt;td&gt;pfetch + sysinfo&lt;/td&gt;&lt;td&gt;pfetch + sysinfo&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Firmware / ECC&lt;/td&gt;&lt;td&gt;fwupd, rasdaemon&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Smart card&lt;/td&gt;&lt;td&gt;pcscd + libccid + opensc&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tools&lt;/td&gt;&lt;td&gt;htop, btop, nmap, screen, lsd, ethtool&lt;/td&gt;&lt;td&gt;htop, btop, nmap, screen, lsd&lt;/td&gt;&lt;td&gt;htop, btop, nmap, lsd&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Font&lt;/td&gt;&lt;td&gt;Berkeley Mono&lt;/td&gt;&lt;td&gt;Berkeley Mono&lt;/td&gt;&lt;td&gt;Berkeley Mono&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;pfetch isn’t packaged on Debian or OpenBSD, so the script fetches the upstream script directly to &lt;code&gt;/usr/local/bin&lt;/code&gt;. herdr ships prebuilt binaries only: Homebrew on macOS, a GitHub release binary on Linux, and no OpenBSD builds, so it’s skipped there. Claude Code installs per-user to &lt;code&gt;~/.local/bin/claude&lt;/code&gt; on Linux and macOS, and is skipped on OpenBSD.&lt;/p&gt;
&lt;h4 id=&quot;desktop-applications&quot;&gt;desktop applications&lt;/h4&gt;
&lt;p&gt;On Linux the apps come from wherever upstream actually ships them, which is not consistent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;1Password&lt;/strong&gt; from the upstream apt repo with debsig-verify, plus the &lt;code&gt;op&lt;/code&gt; CLI. &lt;code&gt;Mod4+p&lt;/code&gt; for quick access, &lt;code&gt;Mod4+Shift+p&lt;/code&gt; for the main window, &lt;code&gt;Mod4+Shift+z&lt;/code&gt; to lock.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Todoist&lt;/strong&gt; (&lt;code&gt;Mod4+t&lt;/code&gt;) and &lt;strong&gt;Joplin&lt;/strong&gt; as official AppImages under &lt;code&gt;/opt&lt;/code&gt;, each with a &lt;code&gt;/usr/local/bin&lt;/code&gt; wrapper and a &lt;code&gt;.desktop&lt;/code&gt; launcher.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fastmail&lt;/strong&gt; (&lt;code&gt;Mod4+e&lt;/code&gt;) as the official Flatpak from Flathub.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VLC&lt;/strong&gt; and &lt;strong&gt;Audacity&lt;/strong&gt; from apt. &lt;strong&gt;Zoom&lt;/strong&gt; from the official &lt;code&gt;.deb&lt;/code&gt;, which self-updates in-app because there’s no upstream apt repo. &lt;strong&gt;GitHub Desktop&lt;/strong&gt; is the community &lt;code&gt;shiftkey&lt;/code&gt; build, since GitHub ships no official Linux app.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;macOS gets the same set as official casks, including the genuine GitHub Desktop. OpenBSD gets VLC and Audacity from packages; none of the rest have OpenBSD builds, so they’re skipped.&lt;/p&gt;
&lt;h3 id=&quot;built-from-source&quot;&gt;built from source&lt;/h3&gt;
&lt;p&gt;Three things are compiled rather than installed from packages:&lt;/p&gt;
&lt;h4 id=&quot;issy&quot;&gt;issy&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://nnix.com/projects/issy&quot;&gt;issy&lt;/a&gt; is my text editor, and it’s the default &lt;code&gt;EDITOR&lt;/code&gt; on all three platforms. The script builds it from the latest source with Zig. issy needs Zig 0.15.x (0.16 breaks it), so the script verifies any zig on PATH and otherwise installs a pinned one: &lt;code&gt;zig@0.15&lt;/code&gt; via Homebrew, &lt;code&gt;pkg_add zig&lt;/code&gt; on OpenBSD, or the official 0.15.2 tarball on Linux. Re-runs compare the installed commit (from &lt;code&gt;issy --version&lt;/code&gt;) against upstream HEAD and rebuild only when upstream is newer. If Homebrew already manages issy on macOS, brew keeps ownership and the script just runs &lt;code&gt;brew upgrade&lt;/code&gt;. Configured via &lt;code&gt;~/.issyrc&lt;/code&gt;, which points at the Berkeley Mono font file for PDF printing.&lt;/p&gt;
&lt;h4 id=&quot;st-and-dmenu-openbsd&quot;&gt;st and dmenu (openbsd)&lt;/h4&gt;
&lt;p&gt;The stock st and dmenu packages stay installed as fallbacks (and for terminfo), but the binaries are replaced with builds from &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/bakkeby/st-flexipatch&quot;&gt;st-flexipatch&lt;/a&gt; and &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/bakkeby/dmenu-flexipatch&quot;&gt;dmenu-flexipatch&lt;/a&gt;, pinned to specific commits, with &lt;code&gt;config.h&lt;/code&gt; and &lt;code&gt;patches.h&lt;/code&gt; kept in this repo.&lt;/p&gt;
&lt;p&gt;st patches: clipboard (selection auto-copies to the system CLIPBOARD), keyboard-select (mouseless copy), scrollback with mouse wheel, anysize, bold-is-not-bright, boxdraw. dmenu patches: fuzzy match with highlighting, case-insensitive, centered, line-height padding, border.&lt;/p&gt;
&lt;p&gt;The builds are stamped with the flexipatch commit plus the OS release, and rebuild when the pinned commit changes, when &lt;code&gt;uname -r&lt;/code&gt; changes (a sysupgrade breaks old binaries), when the binary fails &lt;code&gt;ldd&lt;/code&gt;, or when the on-disk binary isn’t ours (checked by looking for Berkeley Mono in &lt;code&gt;strings&lt;/code&gt; output, because &lt;code&gt;pkg_add -u&lt;/code&gt; reinstalls the stock package over the patched one).&lt;/p&gt;
&lt;h4 id=&quot;the-console-font-linux&quot;&gt;the console font (linux)&lt;/h4&gt;
&lt;p&gt;The Linux console runs Berkeley Mono too. &lt;code&gt;scripts/build-console-font.sh&lt;/code&gt; rasterizes &lt;code&gt;bmv.otf&lt;/code&gt; into a ~16x32 PSF bitmap, which lands in &lt;code&gt;/usr/share/consolefonts&lt;/code&gt; and gets set through console-setup’s &lt;code&gt;FONT=&lt;/code&gt;. The greeter’s vt7 isn’t covered by console-setup, so a &lt;code&gt;greetd.service&lt;/code&gt; drop-in runs &lt;code&gt;setfont&lt;/code&gt; there as well. tuigreet’s password mask is &lt;code&gt;•&lt;/code&gt; rather than the default &lt;code&gt;※&lt;/code&gt;, because Berkeley Mono has no glyph for U+203B. tuigreet also has no flag to suppress its window title, so &lt;code&gt;scripts/patch-tuigreet-title.sh&lt;/code&gt; handles that, re-applied automatically after upgrades via an APT hook.&lt;/p&gt;
&lt;h3 id=&quot;keybindings&quot;&gt;keybindings&lt;/h3&gt;
&lt;p&gt;Consistent across sway (Linux) and i3 (OpenBSD):&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Return&lt;/td&gt;&lt;td&gt;Terminal (foot / st)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + d&lt;/td&gt;&lt;td&gt;Launcher (wofi / dmenu)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + b&lt;/td&gt;&lt;td&gt;Browser (Chrome / Chromium)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + z&lt;/td&gt;&lt;td&gt;Lock screen&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + c&lt;/td&gt;&lt;td&gt;Clipboard history (cliphist / clipmenu)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+q&lt;/td&gt;&lt;td&gt;Kill window&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + w&lt;/td&gt;&lt;td&gt;Kill window (alias)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+e&lt;/td&gt;&lt;td&gt;Exit (with confirmation)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+s&lt;/td&gt;&lt;td&gt;Shutdown (with confirmation)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + j/k/i/l&lt;/td&gt;&lt;td&gt;Focus left/down/up/right&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+j/k/i/l&lt;/td&gt;&lt;td&gt;Move window&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + h/v&lt;/td&gt;&lt;td&gt;Split horizontal/vertical&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + 1-0&lt;/td&gt;&lt;td&gt;Switch workspace&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+1-0&lt;/td&gt;&lt;td&gt;Move to workspace&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+space&lt;/td&gt;&lt;td&gt;Toggle floating&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + m/n&lt;/td&gt;&lt;td&gt;Volume up/down&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + r&lt;/td&gt;&lt;td&gt;Enter resize mode&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+c&lt;/td&gt;&lt;td&gt;Reload config&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+r&lt;/td&gt;&lt;td&gt;Reload (sway) / restart (i3)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Linux adds the application bindings and Wayland screenshots:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + t&lt;/td&gt;&lt;td&gt;Todoist&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + e&lt;/td&gt;&lt;td&gt;Fastmail&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + p&lt;/td&gt;&lt;td&gt;1Password quick access&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+p&lt;/td&gt;&lt;td&gt;1Password main window&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+z&lt;/td&gt;&lt;td&gt;1Password lock&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Print / Shift+Print&lt;/td&gt;&lt;td&gt;Screenshot, full screen / select region&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;OpenBSD adds a few of its own:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Key&lt;/th&gt;&lt;th&gt;Action&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Shift+m&lt;/td&gt;&lt;td&gt;Mute toggle&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + `&lt;/td&gt;&lt;td&gt;Scratchpad terminal&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Print or Mod4 + p&lt;/td&gt;&lt;td&gt;Screenshot, full screen&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Mod4 + Print or Mod4 + Shift+p&lt;/td&gt;&lt;td&gt;Screenshot, select region&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;helper-scripts&quot;&gt;helper scripts&lt;/h3&gt;
&lt;p&gt;Deployed to &lt;code&gt;~/.local/bin&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;workstation&lt;/code&gt;&lt;/strong&gt; (all OSes): one-shot mosh into the admin workstation. Loads the SSH key into the agent (using the Keychain on macOS), makes sure &lt;code&gt;~/.ssh/config&lt;/code&gt; has Host entries, probes the primary overlay path, falls back to the secondary automatically, and &lt;code&gt;exec&lt;/code&gt;s mosh. Targets live in &lt;code&gt;~/.config/workstation.conf&lt;/code&gt; (user, key, optional key fingerprint, hosts, labels, ssh aliases), which provisioning seeds once and never overwrites, so each machine can point somewhere different. The tracked copy is intentionally blank since the repo is public, and the script refuses to run until it’s filled in. &lt;code&gt;workstation -h&lt;/code&gt; shows the configured paths; &lt;code&gt;workstation &amp;lt;label&amp;gt;&lt;/code&gt; forces one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;shot&lt;/code&gt;&lt;/strong&gt; (all OSes): screenshot to &lt;code&gt;~/pictures/screenshots&lt;/code&gt;, copied to the clipboard, with a notification. It picks its tooling from the session: grim/slurp and wl-copy under Wayland, scrot and xclip under X11. &lt;code&gt;shot region&lt;/code&gt; for interactive selection. It only claims “clipboard” in the notification when the copy actually worked.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;sysinfo&lt;/code&gt;&lt;/strong&gt; (all OSes): appends CPU cores/sockets/threads, per-physical-disk usage, and the current epoch to the login fetch, styled to match pfetch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;lock&lt;/code&gt;&lt;/strong&gt; (OpenBSD): screenshots the display, pixelates it with ImageMagick, and hands it to i3lock. Triggered by &lt;code&gt;Mod4+z&lt;/code&gt;, by xss-lock on X screensaver idle, and by xautolock as a backup timer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;volnotify&lt;/code&gt;&lt;/strong&gt; (OpenBSD): adjusts sndio volume and shows a dunst OSD with a progress bar. Stacks rather than piling up notifications.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Linux, sway handles the equivalents natively: swayidle locks with swaylock at 15 minutes and again before sleep, powers outputs off at 30, and volume goes through pamixer with a wob overlay bar. The waybar volume module sits right of memory: scroll to change, left-click opens pavucontrol to pick an output device, right-click mutes.&lt;/p&gt;
&lt;h3 id=&quot;clipboard-and-ssh-agent&quot;&gt;clipboard and ssh-agent&lt;/h3&gt;
&lt;p&gt;Two small things that make the desktop behave like the terminal does.&lt;/p&gt;
&lt;p&gt;On sway, &lt;code&gt;wl-paste --primary --watch wl-copy&lt;/code&gt; mirrors the primary selection into the clipboard, so selecting text anywhere makes it pasteable with Ctrl+Shift+V, matching how patched st behaves on OpenBSD. &lt;code&gt;wl-paste --watch cliphist store&lt;/code&gt; keeps a history, so a selection that clobbers something you copied earlier is recoverable; &lt;code&gt;Mod4+c&lt;/code&gt; opens the history in a wofi picker.&lt;/p&gt;
&lt;p&gt;On Linux and OpenBSD, &lt;code&gt;.bashrc&lt;/code&gt; starts one shared per-user &lt;code&gt;ssh-agent&lt;/code&gt; bound to a fixed socket in &lt;code&gt;$XDG_RUNTIME_DIR&lt;/code&gt; and reuses it across every shell and the WM session it launches. Without it, a fresh sway or i3 session has no agent at all, and both &lt;code&gt;workstation&lt;/code&gt; and SSH commit signing have nowhere to load the key. macOS uses the launchd agent instead. &lt;code&gt;workstation&lt;/code&gt; will start the same shared agent itself if it can’t reach one.&lt;/p&gt;
&lt;h3 id=&quot;maintenance-and-hardening-linux&quot;&gt;maintenance and hardening (linux)&lt;/h3&gt;
&lt;p&gt;Beyond Debian’s stock &lt;code&gt;fstrim&lt;/code&gt;, &lt;code&gt;logrotate&lt;/code&gt;, and &lt;code&gt;fwupd-refresh&lt;/code&gt; timers, &lt;code&gt;configure_maintenance&lt;/code&gt; adds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;unattended-upgrades&lt;/strong&gt; for all Debian updates (main, updates, security), removing unused dependencies and old kernels. It never auto-reboots; a needed reboot is flagged, not forced.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;needrestart&lt;/strong&gt;, report-only, so I know which services want restarting and whether the kernel needs a reboot.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bounded logs&lt;/strong&gt;: the persistent journal is capped at &lt;code&gt;SystemMaxUse=1G&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;smartmontools&lt;/strong&gt; watching drive health.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;weekly health check&lt;/strong&gt;: &lt;code&gt;scripts/healthcheck&lt;/code&gt; installs to &lt;code&gt;/usr/local/bin&lt;/code&gt; and runs from &lt;code&gt;healthcheck.timer&lt;/code&gt;, logging reboot-required, disk usage, failed units, SMART/NVMe wear, ECC error counts, temperatures, and pending updates to the journal. Read it with &lt;code&gt;journalctl -t healthcheck&lt;/code&gt;; the target user is added to the &lt;code&gt;systemd-journal&lt;/code&gt; group so it doesn’t need sudo. Every probe is guarded, so it’s a harmless no-op wherever a subsystem is absent, like in a VM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;configure_hardening&lt;/code&gt; adds an nftables host firewall (default-deny inbound, allowing loopback, established/related, ICMP, SSH, mosh on UDP 60000-61000, and ZeroTier on UDP 9993, with outbound open), a conservative sysctl drop-in (&lt;code&gt;kptr_restrict&lt;/code&gt;, &lt;code&gt;dmesg_restrict&lt;/code&gt;, &lt;code&gt;yama.ptrace_scope&lt;/code&gt;, no ICMP redirects or source routing, syncookies, &lt;code&gt;fs.protected_*&lt;/code&gt;), zram compressed swap (zstd, half of RAM, so memory spikes stay off the NVMe), and systemd-oomd for graceful behavior under memory pressure.&lt;/p&gt;
&lt;p&gt;fwupd is installed but the script never flashes firmware. That’s deliberate and stays out of band: &lt;code&gt;fwupdmgr refresh &amp;amp;&amp;amp; fwupdmgr update&lt;/code&gt;, by hand, when I mean it. rasdaemon is enabled to log ECC/MCE events, which is useful wherever the kernel EDAC layer sees a memory controller and a no-op otherwise.&lt;/p&gt;
&lt;h3 id=&quot;shell-environment&quot;&gt;shell environment&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.bashrc&lt;/code&gt; (Linux/OpenBSD) and &lt;code&gt;.zshrc&lt;/code&gt; (macOS) implement the same two-line prompt: &lt;code&gt;user@host @ cwd&lt;/code&gt; on the first line, a blue &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; on the input line, and a right-aligned status badge (navy with a timestamp on success, inverted light gray with the exit code on failure). bash paints the badge onto the first line with cursor positioning; zsh uses &lt;code&gt;RPROMPT&lt;/code&gt;, so it sits beside the input line instead.&lt;/p&gt;
&lt;p&gt;Aliases in both:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias ls=&amp;#39;lsd -laF&amp;#39;
alias ll=&amp;#39;lsd -laF&amp;#39;
alias la=&amp;#39;lsd -la&amp;#39;
alias top=&amp;#39;btop&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both set &lt;code&gt;EDITOR=issy&lt;/code&gt;, 20k lines of deduplicated history, and colored man pages via &lt;code&gt;LESS_TERMCAP&lt;/code&gt; (light blue headings, navy standout). On Linux there’s also fzf (Ctrl-R for history, Ctrl-T for files) and fd, aliased from &lt;code&gt;fdfind&lt;/code&gt;. Interactive logins print a minimal fetch: hostname header, pfetch (os, shell, uptime, memory), then sysinfo’s cpu, disk, and epoch lines. It’s suppressed inside tmux to avoid per-pane spam.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.bash_profile&lt;/code&gt; sources &lt;code&gt;.profile&lt;/code&gt; (for PATH on OpenBSD) and then &lt;code&gt;.bashrc&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;theming&quot;&gt;theming&lt;/h3&gt;
&lt;p&gt;Everything uses one palette: grayscale plus navy &lt;code&gt;#1f2d4d&lt;/code&gt; and light blue &lt;code&gt;#8fb6e0&lt;/code&gt;/&lt;code&gt;#bcd6f2&lt;/code&gt;, on black.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Font&lt;/strong&gt;: Berkeley Mono Variable NNIX (&lt;code&gt;~/.fonts/bmv.otf&lt;/code&gt;), mapped as the generic &lt;code&gt;monospace&lt;/code&gt; family via &lt;code&gt;~/.config/fontconfig/fonts.conf&lt;/code&gt;, so every fontconfig client inherits it. It’s the terminal font in foot, st, and WezTerm, the UI font in sway, i3, waybar, wofi, swaylock, dmenu, mako, and dunst, and (rasterized to PSF) the Linux console font.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cursor&lt;/strong&gt; (Linux/OpenBSD): the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/wintermute-cell/xcursor-plan9&quot;&gt;plan9 Xcursor theme&lt;/a&gt;, vendored at &lt;code&gt;~/.icons/plan9&lt;/code&gt; and set as the default for X11 (&lt;code&gt;Xcursor.theme&lt;/code&gt;, &lt;code&gt;~/.icons/default&lt;/code&gt;), sway (&lt;code&gt;seat xcursor_theme&lt;/code&gt;), and GTK.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Terminals&lt;/strong&gt;: foot and WezTerm carry near-identical 16-color grayscale-plus-blue palettes (WezTerm’s whites are a touch brighter); the patched st has the palette compiled in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;tmux&lt;/strong&gt;: matching status bar, navy active window, light blue pane borders. Splits with &lt;code&gt;|&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt; keep the current path, and pane focus is j/k/i/l like the window managers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;btop&lt;/strong&gt;: a custom &lt;code&gt;nnix&lt;/code&gt; theme, plus vim keys. &lt;strong&gt;micro&lt;/strong&gt; gets a matching &lt;code&gt;nnix&lt;/code&gt; colorscheme.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GTK 3/4&lt;/strong&gt;: prefer-dark plus the plan9 cursor. On Linux &lt;code&gt;prefer-dark&lt;/code&gt; is also set as a system dconf default so libadwaita, the xdg portal, and Chrome follow it, and Qt apps are pushed dark with &lt;code&gt;adwaita-qt&lt;/code&gt;/&lt;code&gt;adwaita-qt6&lt;/code&gt; via &lt;code&gt;QT_STYLE_OVERRIDE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Notifications&lt;/strong&gt;: mako on Linux, dunst on OpenBSD, both black with a navy frame.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;man pages&lt;/strong&gt;: colored via &lt;code&gt;LESS_TERMCAP&lt;/code&gt; exports.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bars&lt;/strong&gt;: waybar shows workspaces, window title, network, CPU, load and network histograms (&lt;code&gt;loadgraph.sh&lt;/code&gt; and &lt;code&gt;netgraph.sh&lt;/code&gt;, drawn in the same gray-to-blue ramp), RAM, volume, and two clocks, Eastern with a &lt;code&gt;%Z&lt;/code&gt; label and UTC. i3status shows ethernet IP, CPU, load, and the same two clocks. The system clock is Eastern on both, and the UTC clock is an explicit per-module override.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;git&quot;&gt;git&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.gitconfig&lt;/code&gt; signs commits and tags with the SSH key (&lt;code&gt;gpg.format = ssh&lt;/code&gt;), verified against &lt;code&gt;~/.config/git/allowed_signers&lt;/code&gt;, and carries a set of defaults I want everywhere: &lt;code&gt;main&lt;/code&gt; as the initial branch, fast-forward-only pulls, &lt;code&gt;autoSetupRemote&lt;/code&gt; on push, pruning fetches, autostash on rebase, &lt;code&gt;zdiff3&lt;/code&gt; conflict style, and histogram diffs with move detection. On Linux it also uses delta as the pager and interactive diff filter. &lt;code&gt;.ssh/config&lt;/code&gt; carries the GitHub identity, with &lt;code&gt;UseKeychain&lt;/code&gt; on macOS.&lt;/p&gt;
&lt;h3 id=&quot;repository-structure&quot;&gt;repository structure&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;provision.sh              # the whole show (POSIX sh)
validate.sh               # checks all expected files exist
Makefile                  # make validate / provision / backup
DEPENDENCIES.md           # platform and package inventory
README.md, LICENSE
.gitignore                # excludes backups, SSH keys, session junk
scripts/
├── healthcheck               # weekly health check (Linux, → /usr/local/bin)
├── build-console-font.sh     # rasterizes bmv.otf into a console PSF
├── BerkeleyMonoNNIX.psf.gz   # the resulting console font
└── patch-tuigreet-title.sh   # drops tuigreet&amp;#39;s window title
st/config.h, patches.h    # patched st build config (OpenBSD)
dmenu/config.h, patches.h # patched dmenu build config (OpenBSD)
dotfiles/
├── .bashrc, .bash_profile   # bash: prompt, aliases, ssh-agent, sway/startx autostart
├── .zshrc                    # zsh: same prompt and aliases (macOS)
├── .xinitrc                  # xrdb, cursor, caps→escape, key repeat, dbus + i3
├── .Xresources               # crisp Xft rendering, plan9 cursor
├── .gitconfig                # identity, SSH commit signing, defaults, delta
├── .ssh/config               # GitHub host entry
├── .tmux.conf                # tmux behavior + palette
├── .wezterm.lua              # WezTerm: font, palette, panes (macOS)
├── .issyrc                   # issy editor settings
├── .fonts/bmv.otf            # Berkeley Mono Variable NNIX
├── .icons/plan9/             # plan9 cursor theme
├── .local/bin/               # workstation, lock, shot, volnotify, sysinfo
└── .config/
    ├── sway/, waybar/, foot/, swaylock/, wofi/, mako/  # Linux desktop
    ├── i3/, i3status/, dunst/                          # OpenBSD desktop
    ├── fontconfig/fonts.conf                     # monospace = Berkeley Mono
    ├── gtk-3.0/, gtk-4.0/                        # dark theme + cursor
    ├── btop/                                     # nnix theme
    ├── micro/, sublime-text-3/                   # other editor settings
    ├── git/allowed_signers                       # SSH signature verification
    └── workstation.conf                          # per-machine, seeded once
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;make validate&lt;/code&gt; runs &lt;code&gt;validate.sh&lt;/code&gt;, which checks that every expected file is present. &lt;code&gt;make backup&lt;/code&gt; copies the existing dotfiles (&lt;code&gt;.config&lt;/code&gt;, the shell rc files, &lt;code&gt;.gitconfig&lt;/code&gt;, &lt;code&gt;.ssh&lt;/code&gt;, &lt;code&gt;.wezterm.lua&lt;/code&gt;, &lt;code&gt;.xinitrc&lt;/code&gt;, &lt;code&gt;.tmux.conf&lt;/code&gt;, &lt;code&gt;.issyrc&lt;/code&gt;, &lt;code&gt;.Xresources&lt;/code&gt;, &lt;code&gt;.local&lt;/code&gt;) into a timestamped &lt;code&gt;backups/&lt;/code&gt; directory before you overwrite them.&lt;/p&gt;
&lt;h3 id=&quot;notes&quot;&gt;notes&lt;/h3&gt;
&lt;h4 id=&quot;re-applying-updates&quot;&gt;re-applying updates&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;cd /path/to/dotfiles
git pull
sh provision.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All operations are idempotent. Package managers skip installed packages, from-source builds skip when their stamps match, and files are overwritten with the current version (except &lt;code&gt;workstation.conf&lt;/code&gt;).&lt;/p&gt;
&lt;h4 id=&quot;after-an-openbsd-sysupgrade&quot;&gt;after an openbsd sysupgrade&lt;/h4&gt;
&lt;p&gt;OpenBSD has no cross-release binary compatibility, so from-source binaries (st, dmenu, issy) stop loading after a sysupgrade. Run &lt;code&gt;pkg_add -u&lt;/code&gt; first, then re-run &lt;code&gt;provision.sh&lt;/code&gt;. Order matters: &lt;code&gt;pkg_add -u&lt;/code&gt; reinstalls the stock st/dmenu over the patched builds, and the provision run detects that and rebuilds them.&lt;/p&gt;
&lt;h4 id=&quot;vmware-svga-emulation&quot;&gt;vmware svga emulation&lt;/h4&gt;
&lt;p&gt;On Linux, sway launches with &lt;code&gt;WLR_NO_HARDWARE_CURSORS=1&lt;/code&gt; in &lt;code&gt;.bashrc&lt;/code&gt; to work around VMware SVGA limitations. Remove this on bare metal.&lt;/p&gt;
&lt;h4 id=&quot;windows-lock-command&quot;&gt;windows lock command&lt;/h4&gt;
&lt;p&gt;If running VMWare on a Windows host, disable Win+L via registry:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\DisableLockWorkstation&lt;/code&gt; set to &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;additional-references&quot;&gt;additional references&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://jcs.org/&quot;&gt;jcs on openbsd&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://eradman.com/&quot;&gt;eradman/dotfiles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>hatband</title>
      <pubDate>Thu, 10 Sep 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/hatband/</link>
      <guid>https://nnix.com/projects/hatband/</guid>
      <description xml:base="https://nnix.com/projects/hatband/">&lt;p&gt;Source lives at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/hatband&quot;&gt;github.com/davidemerson/hatband&lt;/a&gt;. The site is &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://hatband.link&quot;&gt;hatband.link&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hatband is a business card exchange app incapable of, and uninterested in, mining your data. It shows your contact details as a QR code, from the Lock Screen if you like, and remembers where you met the people you scan.&lt;/p&gt;
&lt;h3 id=&quot;why&quot;&gt;why&lt;/h3&gt;
&lt;p&gt;Exchanging contact information with someone you’ve just met, and don’t yet trust, is an unsolved problem.&lt;/p&gt;
&lt;p&gt;The apps that do it well want your address book, and you hand over everyone you know so that a stranger can have your phone number. The rest assume you are one person with one set of details, and that seems unlikely too. I have a card for the day job, one for the things I build, and one for people I’d rather not have calling the day job.&lt;/p&gt;
&lt;p&gt;So Hatband does two things&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It collects nothing, and&lt;/li&gt;
&lt;li&gt;it lets you carry more than one card.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;personas&quot;&gt;personas&lt;/h3&gt;
&lt;p&gt;Your persona is the full set of fields: name, company, phone, email, website, GitHub, LinkedIn, Mastodon, Signal, Calendly, SSH key, GPG fingerprint, and anything custom you want to add.&lt;/p&gt;
&lt;h3 id=&quot;how-a-card-travels&quot;&gt;how a card travels&lt;/h3&gt;
&lt;p&gt;A card is a QR code. Point a camera at it and you get a link to hatband.link with the card packed into the URL fragment, which a browser never sends to the server. The page decodes it in the browser, verifies the signature, and offers Add to contacts.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/hatband/link-molly-bloom.png&quot; alt=&quot;hatband.link showing a scanned card for Molly Bloom&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;what someone without the app sees. This one is a compact card, the Lock Screen kind, which carries a fingerprint of the key rather than a signature.&lt;/p&gt;
&lt;p&gt;The same card can go out as a &lt;code&gt;.hatband&lt;/code&gt; file, as a bubble in Messages, or printed as SVG, PNG or a PDF card. Someone with the app lands in a review sheet, and those without get the web page.&lt;/p&gt;
&lt;h3 id=&quot;what-leaves-the-phone&quot;&gt;what leaves the phone&lt;/h3&gt;
&lt;p&gt;Key lookups against WKD, keys.openpgp.org, GitHub and Mastodon; Safari for a link you tapped; Apple’s map tiles when the Where tab opens.&lt;/p&gt;
&lt;p&gt;There is no Hatband server, and no analytics, heck, no third-party code at all: the app links nothing it didn’t write except Apple’s frameworks. Crash reports stay on the phone until you choose to share one. I have no way of knowing you installed it.&lt;/p&gt;
&lt;p&gt;Scanned cards live in a Class A store, each sealed under a Keychain key that the app lock puts behind Face ID. They stay out of backups unless you opt in. Your own card is plaintext, so showing it never prompts. Export puts the seed, your card and everyone you’ve met into one file sealed with a passphrase.&lt;/p&gt;
&lt;h3 id=&quot;the-format&quot;&gt;the format&lt;/h3&gt;
&lt;p&gt;A card is a CBOR map with small integer keys, encoded deterministically and signed with Ed25519, about 256 bytes. It travels as &lt;code&gt;https://hatband.link/#1&amp;lt;base32&amp;gt;&lt;/code&gt;, or as a file behind the magic bytes &lt;code&gt;HB1\0&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;get-it&quot;&gt;get it&lt;/h3&gt;
&lt;p&gt;The App Store listing is &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://apps.apple.com/app/id6809843834&quot;&gt;apps.apple.com/app/id6809843834&lt;/a&gt;, live once review is through. iOS only for now.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Six Memos for the Next Milennium</title>
      <pubDate>Wed, 02 Sep 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/six-memos-next-milennium/</link>
      <guid>https://nnix.com/reading/six-memos-next-milennium/</guid>
      <description xml:base="https://nnix.com/reading/six-memos-next-milennium/">&lt;p&gt;Calvino, Italo. (1988). (rated 5/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;Picnic read, and yet a 5/5. Get a blanket, steal a dog, skip the food because someone in the park will sell you baklava if you wait long enough.&lt;/p&gt;
&lt;p&gt;This is the Calvino version of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/reading/this-craft-of-verse/&quot;&gt;This Craft of Verse&lt;/a&gt; (Borges) and it’s a lot of fun. The kind of book which expands your reading list because of its irresistable citations. For having read this, I am obliged now to read Paolo Zellini, Dante Aligheri, Carlo Emilio Gadda, Thomas Mann, Georges Perec, and perhaps Alfred Jarry (if I can find a translation).&lt;/p&gt;
&lt;p&gt;There are five essays: Lightness, Quickness, Exactitude, Visibility, Multiplicity. Calvino died with this work in progress.&lt;/p&gt;
&lt;p&gt;Multiplicity is my favorite, though I am partial for its references to Joyce and Borges.&lt;/p&gt;
&lt;p&gt;Quickness contains a neat exposition of a story about Charlemagne, with different versions featuring different cadences by virtue of elaboration upon or omission of various themes. Calvino pragmatically and convincingly demonstrates how this cadence impacts a story.&lt;/p&gt;
&lt;p&gt;Tons of notable passages,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The motor age has forced speed on us as a measurable quantity, the records of which are milestones in the history of the progress of bath men and machines. But mental speed cannot be measured and does not allow comparisons or competitions; nor can it display its results in a historical perspective. Mental speed is valuable for its own sake, for the pleasure it gives to anyone who is sensitive to such a thing, and not for the practical use that can be made of it. A swift piece of reasoning is not necessarily better than a long-pondered one. Far from it. But it communicates something special that is derived simply from its very swiftness.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Quickness&lt;/em&gt;, p. 45&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the innovation of art,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;…we see how the sluggishness of the human consciousness in emerging from its anthropocentric parochialism can be abolished in an instant by poetic invention.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Lightness&lt;/em&gt;, p. 22&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On the lapidary work of editing,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;On folio 265 of the Codex Atlanticus, Leonardo begins to jot down evidence to prove a theory of the growth of the earth.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;After giving examples of buried cities swallowed up by the soil, he goes on to the marine fossils found in the mountains and in particular to certain bones that he supposes must have belonged to an antediluvian sea monster. At this moment his imagination must have been caught by a vision of the immense animal as it was swimming among the waves. At any rate, he turns the page upside down and tries to capture the image of the animal, three times attempting a sentence that will convey all the wonder of that evocation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“O how many times were you seen among the waves of the great swollen ocean, with your black and bristly back, looming like a mountain, and with grave and stately bearing!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Then he tries to give more movement to the monster’s progress by introducing the verb volteggiare (to whirl).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“And many times were you seen among the waves of the great swollen ocean, and with stately and grave bearing go swirling in the sea waters. And with your black and bristly back, looming like a mountain, defeating and overwhelming them!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;But the word volteggiare seems to him to have lessened the impression of grandeur and majesty that he wants to evoke. So he chooses the verb solcare (to furrow) and alters the whole construction of the passage, giving it compactness and rhythm with sure literary judgment:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;O how many times were you seen among the waves of the great swollen ocean, looming like a mountain, defeating and overwhelming them, and with your black and bristly back furrowing the sea waters, and with stately and grave bearing!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;His pursuit of the apparition, which is presented almost as a symbol of the solemn force of nature, gives us an inkling of how Leonardo’s imagination worked. I leave you this image at the very end of my talk so that you may carry it in your memories as long as possible, in all its transparency and its mystery.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Exactitude&lt;/em&gt;, p. 79-80&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Oh, and Dante has beautiful words.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Come per acqua cupa cosa grave / (Like some heavy thing in deep water)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Paradiso&lt;/em&gt; III.123&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>How to Guarantee a Life of Misery</title>
      <pubDate>Wed, 19 Aug 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/how-to-guarantee-a-life-of-misery/</link>
      <guid>https://nnix.com/reading/how-to-guarantee-a-life-of-misery/</guid>
      <description xml:base="https://nnix.com/reading/how-to-guarantee-a-life-of-misery/">&lt;p&gt;Munger, Charlie. (1986). (rated 4/5)&lt;/p&gt;&lt;p&gt;Charlie Munger’s commencement address to the Harvard School, Los Angeles, June 13, 1986. Collected later as the first talk in &lt;em&gt;Poor Charlie’s Almanack&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Full text &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://jamesclear.com/great-speeches/how-to-guarantee-a-life-of-misery-by-charlie-munger&quot;&gt;here, via James Clear&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;be-unreliable&quot;&gt;be unreliable&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“Do not faithfully do what you have engaged to do. If you will only master this one habit you will more than counterbalance the combined effect of all your virtues, howsoever great. If you like being distrusted and excluded from the best human contribution and company, this prescription is for you. Master this one habit and you can always play the role of the hare in the fable, except that instead of being outrun by one fine turtle you will be outrun by hordes and hordes of mediocre turtles and even by some mediocre turtles on crutches.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;learn-only-from-experience&quot;&gt;learn only from experience&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“You can see the results of not learning from others’ mistakes by simply looking about you. How little originality there is in the common disasters of mankind -drunk driving deaths, reckless driving maimings, incurable venereal diseases, conversion of bright college students into brainwashed zombies as members of destructive cults, business failures through repetition of obvious mistakes made by predecessors, various forms of crowd folly, and so on. I recommend as a memory clue to finding the way to real trouble from heedless, unoriginal error the modern saying: “If at first you don’t succeed, well, so much for hang gliding.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;quit-at-the-first-second-or-third-reverse&quot;&gt;quit at the first, second, or third reverse&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“there is so much adversity out there, even for the lucky and wise, this will guarantee that, in due course, you will be permanently mired in misery. Ignore at all cost the lesson contained in the accurate epitaph written for himself by Epictetus: ‘Here lies Epictetus, a slave, maimed in body, the ultimate in poverty, and favoured by Gods.’ “&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;minimize-objectivity&quot;&gt;minimize objectivity&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“minimizing objectivity will help you lessen the compromises and burdens of owning worldly goods, because objectivity does not work only for great physicists and biologists. It also adds power to the work of a plumbing contractor in Bemidji. Therefore, if you interpret being true to yourself as requiring that you retain every notion of your youth you will be safely underway, not only toward maximizing ignorance, but also toward whatever misery can be obtained through unpleasant experiences in business.”&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>org</title>
      <pubDate>Mon, 03 Aug 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/org/</link>
      <guid>https://nnix.com/projects/org/</guid>
      <description xml:base="https://nnix.com/projects/org/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;There are three components to my system: task management, priority management, and storage. This is where I keep a record of how I run my system, for myself and others who might be interested.&lt;/p&gt;
&lt;h1 id=&quot;task-management&quot;&gt;task management&lt;/h1&gt;
&lt;p&gt;I’ve used Getting Things Done (GTD), this strangely cultish method by David Allen, since about the time it came out in 2001. That’s a long time now, basically since high school. It has become quite a habit for me, and I would highly recommend anyone start this habit sooner rather than later - you’ll get it wrong a number of times and eventually settle on the fixtures and features of the method which resonate with you. For me, the durable bits have been:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inboxes&lt;/li&gt;
&lt;li&gt;Next Actions&lt;/li&gt;
&lt;li&gt;Recurring Actions&lt;/li&gt;
&lt;li&gt;Dependencies (“waiting for”)&lt;/li&gt;
&lt;li&gt;Tags&lt;/li&gt;
&lt;li&gt;Ideas&lt;/li&gt;
&lt;li&gt;Processing (Review)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Things I have not been able to get sticking in the last 20-something years of doing this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strategic work (why am I here, what do I care about, what do I do?). I am terrible at the existential stuff. This is true in life as well as GTD.&lt;/li&gt;
&lt;li&gt;Tasks to Calendar (blocking time for minor tasks). I do this well for large tasks and projects, but small stuff snowplows sometimes, and I’m working on it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you’re just starting this journey yourself, it’s a great idea to get the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.amazon.com/Getting-Things-Done-Stress-Free-Productivity/dp/0142000280&quot;&gt;original book&lt;/a&gt;, and to check out others’ summaries of their processes, like this one and the excellent &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://gtdfh.liw.fi/&quot;&gt;GTD For Hackers&lt;/a&gt;. Keep in mind that when you read any account of such a method, you probably won’t implement things exactly as others have done. I suspect very few people read “Getting Things Done” and simply do all of it - you’ll figure out what 30% of the method works for you, and do that. You might hack the other 70% into something more bespoke for your own life and workflow.&lt;/p&gt;
&lt;p&gt;Whatever you do, consistency and refinement is important, progress not perfection.&lt;/p&gt;
&lt;h2 id=&quot;inboxes&quot;&gt;inboxes&lt;/h2&gt;
&lt;p&gt;Inboxes are the single most important part of the whole method. An inbox can be anything, but it’s always a dumping ground, functionally. My most-used inboxes are&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the physical one on my desk at home (for physical things like papers and small objects)&lt;/li&gt;
&lt;li&gt;the physical one on my desk at work&lt;/li&gt;
&lt;li&gt;the digital one in my task application (I use Todoist)&lt;/li&gt;
&lt;li&gt;a notebook I carry with me in my pack&lt;/li&gt;
&lt;li&gt;a notebook I keep on my desk at home / at work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is occasionally supplemented by any number of ephemeral or contingent inboxes. Maybe you’re traveling and all you have is a napkin. Maybe you’re in some random office you don’t keep stocked with your usual stuff and you grab a piece of paper from the printer and fill it with notes. Whatever.&lt;/p&gt;
&lt;p&gt;The important thing is that you build a practice of processing these inboxes. Get stuff out of your head and into these inboxes so that you can stop thinking about that stuff, and do it with the assurance that you have a process by which you will process it into appropriate places later.&lt;/p&gt;
&lt;h3 id=&quot;processing-the-inbox&quot;&gt;processing the inbox&lt;/h3&gt;
&lt;p&gt;I do this with everything that ends up in an inbox of mine:&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/org/gtd_process.png&quot; alt=&quot;a flowchart illustrating how to deal with anything in your inbox&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Full size PDF version of the above is &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/images/org/gtd_process.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On a regular basis, and this has a different meaning for you than it does for me, you need to process your inboxes. I process all my inboxes at least weekly. Each item in the inbox should be subjected to the above flowchart, no exceptions.&lt;/p&gt;
&lt;p&gt;Processing can take anywhere from a few minutes to an hour or so. If it’s taking more time than an hour, you’re probably doing it wrong, or you’ve got a huge backlog. I go into detail about how I do my processing in the “processing” section below, but suffice it to say that what we’ve covered so far is:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;thing(head, object, instruction, artifact) &amp;gt;
 inbox &amp;gt;
 processing workflow &amp;gt;
 next action / idea / recurring action / storage
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;lists&quot;&gt;lists&lt;/h2&gt;
&lt;p&gt;The lists, functionally, are super important. Their names less so, but I have selected names which remind me of their functions.&lt;/p&gt;
&lt;h3 id=&quot;next-actions&quot;&gt;next actions&lt;/h3&gt;
&lt;p&gt;Next Actions is a list of tasks which are ready to be done. This means that they are already decomposed into a single, specific thing which needs to be done and which is unencumbered by other dependencies. If you need a phone number to call someone to make an appointment for something, put it in the action to avoid such a dependency. Friction kills forward motion. As an example:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;Make an appointment with dentist&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;is less good than,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;Schedule a regular cleaning with the dentist&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;is less good than,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;Call 212-293-3847 and schedule regular cleaning with Joe Dentist&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This might sound silly and pedantic, but trust me, you’ll learn fast that having the information you need at hand to do the thing you need your future self to do will make that thing more likely to get done. I don’t have a universal standard to apply here other than to say that if you think you need a piece of information to accomplish that action in the future, you should put it in the action itself. If that seems daunting or pointless, it’s possible the action is not in fact a Next Action, but needs to be further decomposed into smaller steps which can be accomplished separately or are dependent on serial execution (serial dependency).&lt;/p&gt;
&lt;p&gt;Most things on Next Actions need a date on which you’ll do them, or by which you’ll do them, whichever works for you. It’s ok if some things don’t have dates associated with them, so long as you have validated that they are in fact actionable, and you have some plan or cadence on which you’ll pick one of them up and do it, like a block of time for getting these things done.&lt;/p&gt;
&lt;h3 id=&quot;recurring-actions&quot;&gt;recurring actions&lt;/h3&gt;
&lt;p&gt;Recurring Actions are subject to the same criteria as Next Actions, except they occur at regular intervals. You might need to clean out your old public key identities annually, for example, or write your friend for their birthday on the day each year, or do your GTD processing weekly. The point is simple, but subtle: separate your recurring actions list from your Next Actions list to avoid clutter. Even though they’re similar, it’s way too easy to lose important one-time tasks in a sea of recurring ones you’ll inevitably build over time.&lt;/p&gt;
&lt;p&gt;Obviously, since recurring actions need a period on which they will recur, everything in here needs a date for completion or it doesn’t make sense for this list.&lt;/p&gt;
&lt;h3 id=&quot;ideas&quot;&gt;ideas&lt;/h3&gt;
&lt;p&gt;Ideas, in my world, are not subject to as stringent a criteria as actions. Some of them could be actions, but are not actionable yet. Some of them are whole projects, or large amounts of effort which will definitely require decomposition in the future before I can act on them. It’s ok, just get them into your Ideas list, and tag them appropriately (more on tags later) and you’ll revisit them when appropriate.&lt;/p&gt;
&lt;p&gt;A few examples of things I keep on my Ideas list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Books people have told me to read (and who recommended them, so I can follow up)&lt;/li&gt;
&lt;li&gt;Things I want to make/build in the future&lt;/li&gt;
&lt;li&gt;Gift ideas for people, even vague ones which might not be fully developed yet into something makeable / buyable&lt;/li&gt;
&lt;li&gt;Places I want to visit, and how I found out about them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is easily the most diverse category of processed items outside my “reference / storage” category. What distinguishes Ideas from mere storage, however, is the possiblity that they’ll be actionable in the future. If something is truly not going to promote action, ever, then it should be archived off into storage, or deleted.&lt;/p&gt;
&lt;p&gt;Nothing in Ideas needs to have a date. Some things might, but it certainly isn’t required. Ideas are part of a review process for potential promotion to Actions, so they are fine to be left undated when appropriate.&lt;/p&gt;
&lt;h3 id=&quot;dependencies-waiting-for&quot;&gt;dependencies (“waiting for”)&lt;/h3&gt;
&lt;p&gt;This is a list of things that would be Next Actions, except that you delegated them to others, or need others to do them, and you’re waiting on that to happen. Everything in here needs a date on which you’ll follow up with the entity intended to be doing the action.&lt;/p&gt;
&lt;p&gt;You delegate more than you think you do, even if you don’t have a traditional relationship of delegation with someone, as might a person in a corporate org structure. That insurance quote you’re waiting on? The book you must read which gets published on February 11 next year? The dates you’re waiting on from your mother so you can plan a visit to see her? All these things are effectively delegations or dependencies which should be tracked here.&lt;/p&gt;
&lt;p&gt;This is the one list I implement as a tag rather than as a place, because dependencies arise in every context and I don’t want four copies of the same list. Anything blocked on someone else gets &lt;code&gt;waiting-for&lt;/code&gt;, wherever it already lives. The &lt;code&gt;waiting, undated&lt;/code&gt; view exists to catch the failure mode: a dependency with no follow-up date is a dependency you have silently stopped tracking, so that view should always be empty.&lt;/p&gt;
&lt;h2 id=&quot;tags&quot;&gt;tags&lt;/h2&gt;
&lt;p&gt;Tags allow me to sort the jumble of things in the various lists into contexts. Contexts for me fall into three classes, and each class gets a prefix so the tag list stays legible and so filters can match a whole class at once:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;p-&lt;/code&gt; people I deal with a lot, whether personal or work related. &lt;code&gt;p-personone&lt;/code&gt;, &lt;code&gt;p-persontwo&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;w-&lt;/code&gt; my various jobs, employers, clients, and the recurring meetings attached to them. &lt;code&gt;w-myjob&lt;/code&gt;, &lt;code&gt;w-exec-weekly&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;c-&lt;/code&gt; categories. &lt;code&gt;c-reading&lt;/code&gt;, &lt;code&gt;c-gift&lt;/code&gt;, &lt;code&gt;c-phone&lt;/code&gt;, &lt;code&gt;c-gtd&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The prefixes are the whole point. Without them the tag list is a flat pile of proper nouns and common nouns with no way to tell which is which, and every filter has to enumerate tags by hand. Enumerated filters rot the moment someone leaves or a category gets renamed. With prefixes, &lt;code&gt;@p-*&lt;/code&gt; is every task tied to a human being, and that view keeps working without maintenance.&lt;/p&gt;
&lt;p&gt;Structural tags, which describe a task’s state rather than its context, stay unprefixed. There is currently exactly one: &lt;code&gt;waiting-for&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I use these tags to build filters and views as appropriate. If I need to see what phone calls I need to make or schedule, I call up &lt;code&gt;c-phone&lt;/code&gt;. If I need to see everything I owe a particular person, I call up their &lt;code&gt;p-&lt;/code&gt; tag. If I need to see everything I’m doing for a given client, I call up their &lt;code&gt;w-&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;A caution learned the hard way: renaming a personal tag does not reliably propagate to tasks living in shared projects. After any rename, check the shared label list too, or you will end up with two tags that look identical and filter differently.&lt;/p&gt;
&lt;p&gt;The line between a “project” and a “tag” can be blurry, but generally I try to get away with tags until I clearly need a project, such as when I need to collaborate on something with others, or keep information explicitly separate from other information, such as two customer projects which cannot be commingled. Tags are usually more powerful than projects, without the compartmentalization issues which projects have.&lt;/p&gt;
&lt;h2 id=&quot;implementation&quot;&gt;implementation&lt;/h2&gt;
&lt;p&gt;Everything above is method. This is how it’s actually built, in Todoist, as of this writing. I keep this section current because a method you can’t locate in the tool is a method you don’t have.&lt;/p&gt;
&lt;h3 id=&quot;structure&quot;&gt;structure&lt;/h3&gt;
&lt;p&gt;One project per context. Inside each, the lists are sections, not separate projects:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Personal&lt;/code&gt;, &lt;code&gt;dayjob&lt;/code&gt;, &lt;code&gt;consulting&lt;/code&gt;, &lt;code&gt;Board Service&lt;/code&gt; each contain &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;recurring&lt;/code&gt;, and &lt;code&gt;ideas&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FDCP&lt;/code&gt; is a property and keeps sections per trade (procurement, exterior, interior, and so on) plus a &lt;code&gt;recurring&lt;/code&gt; section for the maintenance backbone.&lt;/li&gt;
&lt;li&gt;Genuinely shared projects stay separate and untouched, because collaboration is one of the two things that earns project status.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing sits unsectioned. An unsectioned task is a task in no list, which is the same as a task in no system.&lt;/p&gt;
&lt;p&gt;The rules that keep this from sprawling again, checked quarterly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not more than ten top-level projects.&lt;/li&gt;
&lt;li&gt;Not more than ten sections per project.&lt;/li&gt;
&lt;li&gt;A project must be shared with someone, or must hold information that cannot commingle with anything else. Otherwise it is a tag. This is the same test as before, but written down now, because an untested heuristic drifts.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;views&quot;&gt;views&lt;/h3&gt;
&lt;p&gt;The lists are where things live; the views are how I see across them. Each maps to a step in a review.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Today&lt;/code&gt; — &lt;code&gt;today | overdue&lt;/code&gt;, everything due across every context.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Next actions&lt;/code&gt; — every &lt;code&gt;next&lt;/code&gt; section, plus FDCP’s trade sections.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Next: undated&lt;/code&gt; — the same, filtered to items with no date. Most next actions should have a date; this is the backlog that quietly stopped being actionable.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Waiting for&lt;/code&gt; and &lt;code&gt;Waiting, undated&lt;/code&gt; — the dependencies list and its failure mode.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Ideas&lt;/code&gt; — every &lt;code&gt;ideas&lt;/code&gt; section.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Recurring&lt;/code&gt; — every &lt;code&gt;recurring&lt;/code&gt; section. Mostly used quarterly to catch rot, since recurring tasks accumulate references to tools you stopped using years ago.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;People&lt;/code&gt; — &lt;code&gt;@p-*&lt;/code&gt;, everything tied to a human.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Weekly review&lt;/code&gt; — a single multi-query view rendering inbox, waiting-for, undated next actions, and ideas in one screen. Four steps of the weekly review in one click.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A note on filter syntax, since it took some checking: section names match across all projects, so &lt;code&gt;/next&lt;/code&gt; returns every next-actions section at once, and tag prefixes support wildcards, so &lt;code&gt;@p-*&lt;/code&gt; works. Those two facts are what make one flat set of views cover an arbitrary number of contexts.&lt;/p&gt;
&lt;h2 id=&quot;processing&quot;&gt;processing&lt;/h2&gt;
&lt;p&gt;The processing habit is what allows all of this to work. With assured processing, you will rest easy knowing that things you commit to your inboxes, and thence to various lists, will not be lost. If you do not build a processing habit, this whole thing just becomes a pile-up of things you know you should do but which will get lost, along with the whole point of having a system for, uh, getting things done.&lt;/p&gt;
&lt;p&gt;If you feel or sense that your processing habit is not well or regularly implemented, you’ll lose faith in your inbox system, which will cause a feedback into your offloading habit. You’ll start trying to remember stuff lest you drop it, and that’ll inevitably cascade into forgetting stuff, and dropping it anyway.&lt;/p&gt;
&lt;p&gt;Anyone with any reasonable amount of things they’re responsible for cannot mentally track them all. Get stuff out of your head (the inbox habit) and process it so you are assured of the integrity of your system, and feel good committing important things to its functioning.&lt;/p&gt;
&lt;h3 id=&quot;daily-review&quot;&gt;daily review&lt;/h3&gt;
&lt;p&gt;Every day, I do the following, all of which are very basic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clean out my email inbox, processing emails according to my inbox workflow.&lt;/li&gt;
&lt;li&gt;Review the Next Actions which are scheduled for the day, and defer them or reschedule them if they are not able to be done.&lt;/li&gt;
&lt;li&gt;Check off the things I’ve done, to keep status updated.&lt;/li&gt;
&lt;li&gt;If I took notes or created especially nonstandard inboxes (random slips of paper, artifacts in my bag, pockets, etc), get those processed according to my inbox workflow.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;weekly-review&quot;&gt;weekly review&lt;/h3&gt;
&lt;p&gt;Weekly review is more complicated, and takes about an hour for me, normally at the end of a Friday as I’m closing out my week:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clear off my desk, process anything that should be an inbox item.&lt;/li&gt;
&lt;li&gt;Empty my todoist inbox, in case anything fell through the cracks (“Inbox” is the default project Todoist commits something to when it has no other direction, so sometimes things end up in there by default or mistake.)&lt;/li&gt;
&lt;li&gt;Review all Next Actions (&lt;code&gt;Next actions&lt;/code&gt;) to ensure things are completed which were completed, and to reschedule, reshuffle, or elaborate on other tasks in there before next week starts. Then check &lt;code&gt;Next: undated&lt;/code&gt; and either give those items a date or admit they belong in Ideas.&lt;/li&gt;
&lt;li&gt;Review Delegations (&lt;code&gt;Waiting for&lt;/code&gt;) to be sure nothing is falling through the cracks, particularly to be sure everything has a date on which you will follow up about that thing. &lt;code&gt;Waiting, undated&lt;/code&gt; should be empty.&lt;/li&gt;
&lt;li&gt;Review Projects under way. Where you have projects in lists or systems outside your main actions/delegations/recurring/ideas, make sure to prune, elaborate, and update those weekly. Often for me, these are in a project management system, but they also might be projects broken out from the main lists because of the clutter they would otherwise produce.&lt;/li&gt;
&lt;li&gt;Review current-week calendar: use it as a prompt for tasks, projects, or things you didn’t commit to your system in the moment, but should.&lt;/li&gt;
&lt;li&gt;Review next-week calendar: use it as a prompt for tasks, projects, or things you need to commit to your system before next week occurs. Also reorder and deconflict next week while you’re at it, since you’re probably quadruple booked for something.&lt;/li&gt;
&lt;li&gt;Update any status dashboards, particularly the ones you owe folks you work with. This might not be relevant to you, but it’s a good time to do it if you must.&lt;/li&gt;
&lt;li&gt;Review your Ideas list. Promote things to Actions where appropriate, or elaborate on things which have new details.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;quarterly-review&quot;&gt;quarterly review&lt;/h3&gt;
&lt;p&gt;Quarterly, I only do a few things, but they are more time consuming when they need to be done:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rethink any Projects which should be demoted to tags, or tags which should be promoted to projects. Apply the test rather than the vibe: shared, or cannot commingle, or it’s a tag. Check the counts against the caps while you’re in there.&lt;/li&gt;
&lt;li&gt;Work the &lt;code&gt;Recurring&lt;/code&gt; view end to end. Recurring tasks rot in a way one-time tasks don’t, because nothing forces you to reread them. Look for references to tools you no longer use, recurrence strings that have accreted junk over the years, and tasks whose whole premise has expired.&lt;/li&gt;
&lt;li&gt;Clean up old stuff to storage where it’s no longer needed.&lt;/li&gt;
&lt;li&gt;Make sure any integrations between systems (such as calendar integrations with your task management app, availability integrations with the links you send out for scheduling, or any agent connectors with access to your mail and tasks) are working and properly configured. For anything holding an OAuth grant, confirm the scope is still the minimum you need and revoke what isn’t.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;annual-review&quot;&gt;annual review&lt;/h3&gt;
&lt;p&gt;I’m terrible at this, but I do make a stab at doing the stuff I consider “existential”. What do you do? What do you want to be doing? What even are you? Honestly, it usually devolves into an extra-big rethink of my org structure annually, and I can’t claim to have had any profound insight into my being, but maybe you’ll have more luck. The GTD Methodology formally treats this with more detail and weight if you’re interested in trying yourself, it’s called “Focus Horizons” there.&lt;/p&gt;
&lt;p&gt;For my part, I spend about half a day per year just carefully thinking through my system and updating stuff like this page with how I’ll modify it or what I need to do better. My system is too tactical to get wrapped up in the big stuff.&lt;/p&gt;
&lt;h1 id=&quot;priority-management&quot;&gt;priority management&lt;/h1&gt;
&lt;p&gt;This one isn’t complicated, but it’s a handy tool when deconflicting the stuff you think you need to do (the whole body of your tasks) with the reality of time, and how much you can accomplish. Some people like to use OODA loops (I don’t), some people like to use matrices (I do). What you use isn’t important, just make sure you have a good way to apply some criteria with which you’re familiar to the tasks you’ll soon have in excrutiating detail from the above processes.&lt;/p&gt;
&lt;p&gt;Here I’ve outlined the two systems I personally use. They are both 2x2 matrices, don’t be too confused, there’s nothing to this. Heck, you don’t even need to actually fill out the matrix with tasks, unless that’s your jam. I personally just use these frameworks as ways to think about any given task I’m presented.&lt;/p&gt;
&lt;h2 id=&quot;impact-effort-matrix&quot;&gt;Impact-Effort Matrix&lt;/h2&gt;
&lt;p&gt;There are four boxes in this 2x2 matrix, high/low impact, and high/low effort. I rank things to be done according to this criteria:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;(do first)&lt;/code&gt; High Impact, Low Effort: Quick Wins. These are the tasks or projects that you should tackle first. They offer the highest return on investment for your effort.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(do second)&lt;/code&gt; High Impact, High Effort: Major Projects. These items are important but require significant time and resources. Plan and execute them after the quick wins.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(do third)&lt;/code&gt; Low Impact, Low Effort: Fill-ins. These can be done when you have spare time. They won’t make a big difference but are easy to complete.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(do last, defer)&lt;/code&gt; Low Impact, High Effort: Thankless Tasks. These tasks require a lot of effort but don’t provide much return. Avoid or minimise these tasks whenever possible.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;eisenhower-matrix&quot;&gt;Eisenhower Matrix&lt;/h2&gt;
&lt;p&gt;Similar to the Impact-Effort Matrix, this is a 2x2, but ranking importance (instead of impact) and urgency (instead of effort). It also includes delegation and deletion concepts, which can be helpful if what you need is a filter more than an order of operation.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;(do soon)&lt;/code&gt; Important and urgent: do it. These are the tasks that you want to do as soon as possible. Crises, pressing problems and other things where not acting now has negative consequences.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(schedule)&lt;/code&gt; Important but not urgent: schedule it. Find a time for these tasks and do them then. This quadrant is typically where deep work happens – tasks that contribute to your projects or long-term goals.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(delegate)&lt;/code&gt; Urgent but not important: delegate it. If you can, find someone who can do these tasks for you. If you can’t delegate it, schedule it but always try to do the important-but-not-urgent tasks first. These are often administrative tasks or things that have deadlines but aren’t critical.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(delete)&lt;/code&gt; Not urgent and not important: don’t do it. These tasks aren’t worth your time and you shouldn’t do them at all. Avoidance activities, busy work and entertainment goes into this quadrant.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&quot;storage-reference&quot;&gt;storage &amp;amp; reference&lt;/h1&gt;
&lt;p&gt;This is where the output of tasks goes, where documents I generate, code I produce, photos I store, and reference materials I want to keep hang out. Nothing in here is necessarily actionable, that’s why it’s reference or for storage.&lt;/p&gt;
&lt;p&gt;The biggest challenge for me has been to find a method which is extensible to an almost arbitrary scope while remaining accessible to my recollection of where things should be. I have implemented a system inspired by &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://johnnydecimal.com/&quot;&gt;Johnny Decimal&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not more than three levels deep for folders&lt;/li&gt;
&lt;li&gt;not more than ten subcategories per second or third level taxonomy&lt;/li&gt;
&lt;li&gt;where used, dates are in &lt;code&gt;YYYY-MM-DD&lt;/code&gt; format. This is very important for sorting and filtering&lt;/li&gt;
&lt;li&gt;rules were meant to be broken sometimes, as when photos are simply better stored by date, and when reading is better stored by the Dewey Decimal Categories.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Like the Library of Babel, this results in a taxonomy which is very large, but not infinite, so you can at least orient yourself a bit.&lt;/p&gt;
&lt;p&gt;I keep a PDF reference of my folder structure &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/pdfs/org_index.pdf&quot;&gt;here&lt;/a&gt;, both for my own records and for anyone who would like to use it to make their own folder structure.&lt;/p&gt;
&lt;h2 id=&quot;top-level&quot;&gt;top level&lt;/h2&gt;
&lt;p&gt;I have a full-time job, a side-gig or two, and, naturally, a personal life. As such, I’ve implemented projects as the first layer of my taxonomy.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;000&lt;/code&gt; - [Reserved for Management]&lt;/li&gt;
&lt;li&gt;&lt;code&gt;100&lt;/code&gt; - Emerson&lt;/li&gt;
&lt;li&gt;&lt;code&gt;200&lt;/code&gt; - Extended Family&lt;/li&gt;
&lt;li&gt;&lt;code&gt;500&lt;/code&gt; - Consulting, Company Management&lt;/li&gt;
&lt;li&gt;&lt;code&gt;501&lt;/code&gt; to &lt;code&gt;899&lt;/code&gt; - Consulting, per-customer&lt;/li&gt;
&lt;li&gt;&lt;code&gt;900&lt;/code&gt; - [Reserved]&lt;/li&gt;
&lt;li&gt;&lt;code&gt;901&lt;/code&gt; to &lt;code&gt;999&lt;/code&gt; - Full-time Employment &amp;amp; Board Work&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;second-level&quot;&gt;second level&lt;/h2&gt;
&lt;p&gt;Areas are the second layer of my taxonomy. There are never more than ten areas per project. Under &lt;code&gt;100 Emerson&lt;/code&gt; each area starts with 100 and possesses a decimal identifier, such as &lt;code&gt;10&lt;/code&gt; for &lt;code&gt;Personal Administration&lt;/code&gt;. The combined title for personal administration, when is &lt;code&gt;110 Personal Administration&lt;/code&gt;. Here’s what I have under &lt;code&gt;100 Emerson&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;110&lt;/code&gt; Personal Administration&lt;/li&gt;
&lt;li&gt;&lt;code&gt;120&lt;/code&gt; Home, Auto, Staff&lt;/li&gt;
&lt;li&gt;&lt;code&gt;130&lt;/code&gt; Reading&lt;/li&gt;
&lt;li&gt;&lt;code&gt;140&lt;/code&gt; Technology&lt;/li&gt;
&lt;li&gt;&lt;code&gt;150&lt;/code&gt; Multimedia&lt;/li&gt;
&lt;li&gt;&lt;code&gt;160&lt;/code&gt; Projects, Travel, Hobbies&lt;/li&gt;
&lt;li&gt;&lt;code&gt;170&lt;/code&gt; Archives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;180&lt;/code&gt; Offspring&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;third-level&quot;&gt;third level&lt;/h2&gt;
&lt;p&gt;Categories are the third layer of my taxonomy. There are never more than ten categories per area. As an example, within &lt;code&gt;110 Personal Administration&lt;/code&gt; I have the following categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;111&lt;/code&gt; Tax Returns&lt;/li&gt;
&lt;li&gt;&lt;code&gt;112&lt;/code&gt; Trust &amp;amp; Will&lt;/li&gt;
&lt;li&gt;&lt;code&gt;113&lt;/code&gt; Insurance&lt;/li&gt;
&lt;li&gt;&lt;code&gt;114&lt;/code&gt; Identification &amp;amp; Representation&lt;/li&gt;
&lt;li&gt;&lt;code&gt;115&lt;/code&gt; Personal Plans&lt;/li&gt;
&lt;li&gt;&lt;code&gt;116&lt;/code&gt; Personal Investments&lt;/li&gt;
&lt;li&gt;&lt;code&gt;117&lt;/code&gt; Templates &amp;amp; Design Assets&lt;/li&gt;
&lt;li&gt;&lt;code&gt;118&lt;/code&gt; Health Information&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;reading&quot;&gt;reading&lt;/h2&gt;
&lt;p&gt;I use the standard Dewey Decimal high level categories for digital reading material which I store. Note that this is different than my reading list, for which I have a web app &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/reading&quot;&gt;hosted here&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;132.01&lt;/code&gt; Computer Science, Information, General Works&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.02&lt;/code&gt; Philosophy &amp;amp; Psychology&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.03&lt;/code&gt; Social Sciences&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.04&lt;/code&gt; Language&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.05&lt;/code&gt; Pure Science&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.06&lt;/code&gt; Technology&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.07&lt;/code&gt; Arts &amp;amp; Recreation &amp;amp; Travel&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.08&lt;/code&gt; Literature&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.09&lt;/code&gt; History &amp;amp; Geography&lt;/li&gt;
&lt;li&gt;&lt;code&gt;132.10&lt;/code&gt; Leadership and Business&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;projects&quot;&gt;Projects&lt;/h2&gt;
&lt;p&gt;Within &lt;code&gt;160 Projects&lt;/code&gt;, I give each project a random unique three-digit alphanumeric suffix. This allows me to have a large space of potential project names. As an example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;160.a15&lt;/code&gt; org&lt;/li&gt;
&lt;li&gt;&lt;code&gt;160.p4j&lt;/code&gt; chainsaw logmilling&lt;/li&gt;
&lt;li&gt;&lt;code&gt;160.w6b&lt;/code&gt; welding cart&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;photos-music&quot;&gt;photos &amp;amp; music&lt;/h2&gt;
&lt;p&gt;Photos &amp;amp; Music (generally referred to as media) are somewhat exempt from decimalization, since I’ve found no better way to store them than by-date. Dates are always in &lt;code&gt;YYYY-MM-DD&lt;/code&gt; format.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>uncertainty</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/uncertainty/</link>
      <guid>https://nnix.com/projects/uncertainty/</guid>
      <description xml:base="https://nnix.com/projects/uncertainty/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;I added a measurement uncertainty engine to C47 on my DM42n. Enter the variables of a formula along with their standard uncertainties, and the calculator returns the result together with the uncertainty of that result, plus a budget showing which input is responsible for the error.&lt;/p&gt;
&lt;p&gt;This is compliant with ISO/IEC Guide 98-3, the standard for stating measurement uncertainty. This is something you’d normally need a spreadsheet for, and rarely find in a basic calculator.&lt;/p&gt;
&lt;p&gt;I got my inspiration here from DB48X, another firmware for the same hardware, which has an uncertain number type that propagates a standard deviation through operations. It lacks the rest of the ISO/IEC 98-3 stuff like sensitivity coefficients, a ranked budget, an expanded uncertainty with its coverage factor, effective degrees of freedom.&lt;/p&gt;
&lt;p&gt;Oh, and I tried to make this run on the DM42 also, which is a capacity feat.&lt;/p&gt;
&lt;h1 id=&quot;space&quot;&gt;space&lt;/h1&gt;
&lt;p&gt;The DM42 has 704 KB of internal flash for the program. The firmware ships in numbered packages, which are different subsets of the features chosen to fit:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;package&lt;/th&gt;&lt;th&gt;flash used&lt;/th&gt;&lt;th&gt;free&lt;/th&gt;&lt;th&gt;engine&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;721,808 / 720,896&lt;/td&gt;&lt;td&gt;over by 912&lt;/td&gt;&lt;td&gt;out&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;720,680 / 720,896&lt;/td&gt;&lt;td&gt;216&lt;/td&gt;&lt;td&gt;out&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;719,528 / 720,896&lt;/td&gt;&lt;td&gt;1,368&lt;/td&gt;&lt;td&gt;out&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;699,040 / 720,896&lt;/td&gt;&lt;td&gt;21,856&lt;/td&gt;&lt;td&gt;in&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Package 1 does not fit, and did not before I touched it: upstream is 784 bytes over on that package today. So three of the four have no room and the feature is compiled out of them.&lt;/p&gt;
&lt;p&gt;The source keeps a calibrated table of what each option costs. Mine is 6,448 bytes of flash, measured by building package 4 twice with the option on and off.&lt;/p&gt;
&lt;h1 id=&quot;step&quot;&gt;step&lt;/h1&gt;
&lt;p&gt;The C47 uses a fifteen point stencil, exact for polynomials to degree fourteen, and then takes its step as h = x × 10⁻¹⁶. A central difference trades truncation error against rounding error and the balance sits near ε^(1/(p+1)); function values come back at 34 digits, so the optimum for that stencil is around 10⁻², not 10⁻¹⁶. At 10⁻¹⁶ the result is rounding noise and all fourteen orders are thrown away.&lt;/p&gt;
&lt;p&gt;The firmware’s own checked-in test expectation for the first derivative of x³ at 5, which is 75:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;74.99999999999999999893939393939394
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;20 digits out of 34. The second derivative gets 7.5, since dividing by h² amplifies the same noise.&lt;/p&gt;
&lt;p&gt;So I did not reuse it. A two point central difference at h = max(|x|,1) × 10⁻¹¹ measures 22.6 digits using two evaluations instead of fifteen. Nearly three decades better for an eighth of the work. The sophistication was in the wrong place since the step decides whether the stencil matters at all.&lt;/p&gt;
&lt;p&gt;Reading it also turned up four defects, the worst being that f’ and f“ returned a silently wrong answer for any program taking its input as a named variable. Fixed upstream now.&lt;/p&gt;
&lt;h1 id=&quot;use&quot;&gt;use&lt;/h1&gt;
&lt;p&gt;The UNCT menu structure looks as follows:&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;U.EDIT&lt;/td&gt;&lt;td&gt;the input table, one row per variable: u(x) and degrees of freedom&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.CORR&lt;/td&gt;&lt;td&gt;correlations, if the inputs are not independent&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.P&lt;/td&gt;&lt;td&gt;level of confidence, 95 or 0.95, both work&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.CALC&lt;/td&gt;&lt;td&gt;y in Y, u_c(y) in X&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.EXP&lt;/td&gt;&lt;td&gt;k in Y, U in X&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.BUDG&lt;/td&gt;&lt;td&gt;the ranked budget&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;U.Y&lt;/td&gt;&lt;td&gt;which variable is the measurand, for a formula written with =&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;A run is short. Pick the formula with EQN, then:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;U.EDIT      fill in u for each variable
99 U.P      99 % confidence
U.CALC      50.000838 mm, u_c = 32 nm
U.BUDG      lS 25 nm, dtheta 16.68, d 9.7, dalpha 2.9
&lt;/code&gt;&lt;/pre&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.unc/utab.png&quot; alt=&quot;the matrix editor showing the input table, two columns of numbers&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;U.EDIT. One row per variable, u(x) in the first column and degrees of freedom in the second. Four of the six rows fit on screen.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.unc/ucalc.png&quot; alt=&quot;the stack after U.CALC, showing y and its uncertainty, with the UNCT menu below&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;U.CALC on the GUM’s end-gauge example. 50.000838 mm in Y, 3.171×10⁻⁵ mm in X, which is 31.71 nm.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.unc/uexp.png&quot; alt=&quot;the stack after U.EXP, showing the coverage factor and the expanded uncertainty&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;U.EXP at 99 %. k = 2.9208, U = 9.262×10⁻⁵ mm, the GUM’s 93 nm.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.unc/ubudg.png&quot; alt=&quot;the budget screen, six inputs ranked by contribution&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;The budget. Six inputs ranked by what they contribute, LS at 62.2 % down to alphaS at nothing.&lt;/p&gt;
&lt;p&gt;The combined number tells you how wrong you are; the budget tells you which measurement to go improve. Here it is LS, the calibration of the reference gauge, and no amount of care with the thermometer would help.&lt;/p&gt;
&lt;h1 id=&quot;tests&quot;&gt;tests&lt;/h1&gt;
&lt;p&gt;The GUM contains its own worked examples in Annex H, with published answers. That is an unusually good acceptance test, because it was written by the people who defined the method.&lt;/p&gt;
&lt;p&gt;The engine reproduces H.1, the calibration of an end gauge, giving 50.000838 mm with a combined uncertainty of 32 nm. It reproduces H.2, simultaneous resistance and reactance, which is the correlated example and exercises the covariance terms. It reproduces H.3, a thermometer calibration, both with and without the correlation between the fitted intercept and slope.&lt;/p&gt;
&lt;p&gt;The GUM notes that including second order terms raises H.1’s uncertainty from 32 nm to 34 nm. This engine is first order by construction, so 32 nm is its correct answer, and getting 34 would mean the sensitivities were wrong. Encoding the number the approximation should produce, rather than the most precise number in the document, is the difference between a test that checks the method and a test that checks nothing.&lt;/p&gt;
&lt;h1 id=&quot;cost&quot;&gt;cost&lt;/h1&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;flash&lt;/td&gt;&lt;td&gt;6,448 bytes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;static memory&lt;/td&gt;&lt;td&gt;40 bytes&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;arena, on first use&lt;/td&gt;&lt;td&gt;about 2.2 KB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;evaluations per budget&lt;/td&gt;&lt;td&gt;2 per variable, plus one, except an &lt;code&gt;=&lt;/code&gt; equation which is 2 per variable&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
</description>
    </item>
    <item>
      <title>He Was a Crook</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/he-was-a-crook/</link>
      <guid>https://nnix.com/reading/he-was-a-crook/</guid>
      <description xml:base="https://nnix.com/reading/he-was-a-crook/">&lt;p&gt;Thompson, Hunter S. (1994). (rated 4/5)&lt;/p&gt;&lt;p&gt;Thompson’s obituary for Richard Nixon, published in Rolling Stone on June 16, 1994.&lt;/p&gt;
&lt;p&gt;Full text &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://archive.is/V64Zu&quot;&gt;here, via archive.is&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Thompson simply declined to participate in the elder statesman obituary. The thesis is in the title and he spends the rest of it refusing to soften.&lt;/p&gt;
&lt;p&gt;It’s not “The Kentucky Derby is Decadent and Depraved,” there’s less invention here and less of the gonzo apparatus; the contempt does most of the work, and it’s contempt with thirty years of receipts in tow.&lt;/p&gt;
&lt;p&gt;A 4 rather than a 5 because sustained hatred is a narrow instrument, and there are stretches where the venom is running ahead of the writing.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Middle Passage: From Misery to Meaning in Midlife</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/middle-passage/</link>
      <guid>https://nnix.com/reading/middle-passage/</guid>
      <description xml:base="https://nnix.com/reading/middle-passage/">&lt;p&gt;Hollis, James. (1993). (rated 4/5)&lt;/p&gt;&lt;p&gt;Extremely compact. It’s a bit over a hundred pages and there’s nothing padding it out, no anecdote stretched to fill a chapter, no idea restated three times for the benefit of an imagined skimmer. Hollis says the thing and moves on. I wish more books in this territory were built this way, and the parsimony is a good part of why this one is useful.&lt;/p&gt;
&lt;p&gt;It’s Jungian, and if that’s going to bother you it will bother you early. It didn’t bother me.&lt;/p&gt;
&lt;p&gt;Not every reader is going to relate to every element of the middle passage Hollis illuminates. Some of what he describes is plainly someone else’s life and not mine. That seems like the right expectation to bring to a book of this kind rather than a flaw in it. What’s here is practical, and there’s an element of being seen in it, at least in part, which is most of what I’d ask for.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Miracle of Mindfulness: An Introduction to the Practice of Meditation</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/miracle-of-mindfulness/</link>
      <guid>https://nnix.com/reading/miracle-of-mindfulness/</guid>
      <description xml:base="https://nnix.com/reading/miracle-of-mindfulness/">&lt;p&gt;Nhat Hanh, Thich. (1975). (rated 3/5)&lt;/p&gt;&lt;p&gt;A light book and a good overview of introductory practice. Nothing in it is difficult to pick up, which I take to be the point.&lt;/p&gt;
&lt;p&gt;If I’m meditating, I think I’m more of a minimalist, along the lines of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/reading/catching-the-big-fish/&quot;&gt;TM&lt;/a&gt;. The practices here are more active, more involved, and more engaged with the world, in that they’re trying to cultivate awareness in the middle of ordinary activity rather than apart from it. There are times in my day when I could use something like that. I still prefer the simplicity of a mantra and a moment outside the bustle.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Montessori: The Science Behind the Genius (queued)</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/montessori-science-behind-genius/</link>
      <guid>https://nnix.com/reading/montessori-science-behind-genius/</guid>
      <description xml:base="https://nnix.com/reading/montessori-science-behind-genius/">&lt;p&gt;Lillard, Angeline Stoll. (2017). (rated NR/5) (recommended by Julian P.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>Nonviolent Communication</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/nonviolent-communication/</link>
      <guid>https://nnix.com/reading/nonviolent-communication/</guid>
      <description xml:base="https://nnix.com/reading/nonviolent-communication/">&lt;p&gt;Rosenberg, Marshall. (2015). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;Helpful and practical, particularly for deescalation and for discussions which have already gone contentious. It gives you something to do when a conversation is heating up, which is more than most books of this kind manage.&lt;/p&gt;
&lt;p&gt;It is also clearly awkward and stilted for everyday use. I don’t think that makes it useless every day, but it does mean you can’t expect to speak this way and convey a direct message at the same time. Sometimes the direct message is the thing that’s needed, and this is not the technique for that.&lt;/p&gt;
&lt;p&gt;The anecdotes are sometimes hilarious, mostly in a stilted, awkward way. The examples read like transcripts from a place where nobody is ever in a hurry and everyone has agreed in advance to be patient. Strange, but useful nonetheless, and I remember the odd ones better than I’d remember reasonable ones.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Odyssey (Wilson translation)</title>
      <pubDate>Mon, 27 Jul 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/odyssey-wilson/</link>
      <guid>https://nnix.com/reading/odyssey-wilson/</guid>
      <description xml:base="https://nnix.com/reading/odyssey-wilson/">&lt;p&gt;Homer. (2017). (rated 3/5)&lt;/p&gt;&lt;p&gt;I read this because I hadn’t read this translation yet, and because I was going to see the Nolan movie.&lt;/p&gt;
&lt;p&gt;Fitzgerald is the translation I read before, both for the purpose of reading the work itself and for the purpose of reading &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/projects/ulysses&quot;&gt;Ulysses&lt;/a&gt;, where the correspondences aren’t literal but are buried well enough to be independent of translation anyway.&lt;/p&gt;
&lt;p&gt;Wilson is less beautiful than Fitzgerald. There’s no doubt in that for me. It is also far more readable: vernacular, less flowery, less interested in being grand. Frankly, the average student who’s being made to read Homer should be handed the Wilson, since it makes the story accessible without, to my memory, messing with anything important.&lt;/p&gt;
&lt;p&gt;If you’re the kind who has to read it again, or wants to, Fitzgerald is worth a try as well. The language is beautiful and you’ll already know the plot.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>This Is How You Lose the Time War (queued)</title>
      <pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/this-is-how-you-lose-time-war/</link>
      <guid>https://nnix.com/reading/this-is-how-you-lose-time-war/</guid>
      <description xml:base="https://nnix.com/reading/this-is-how-you-lose-time-war/">&lt;p&gt;El-Mohtar, Amal. (2019). (rated NR/5) (recommended by Dustin D.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>slab bench</title>
      <pubDate>Mon, 25 May 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/slab-bench/</link>
      <guid>https://nnix.com/projects/slab-bench/</guid>
      <description xml:base="https://nnix.com/projects/slab-bench/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;I’m making a bench from one of the white oak slabs I &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/projects/logmilling&quot;&gt;chainsaw-milled&lt;/a&gt; and later &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/projects/oak-table&quot;&gt;kiln-dried&lt;/a&gt;. The slab finishing, base design, and fabrication are captured here.&lt;/p&gt;
&lt;p&gt;Note that my work environment is primitive, so don’t judge my finishing techniques too harshly. I basically picked a good string of days and set up a couple sawhorses in the carport. It’s not a clean room.&lt;/p&gt;
&lt;h2 id=&quot;slab-dimensions&quot;&gt;slab dimensions&lt;/h2&gt;
&lt;div&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;dimension&lt;/th&gt;
    &lt;th&gt;value&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;thickness&lt;/td&gt;
    &lt;td&gt;48 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;length&lt;/td&gt;
    &lt;td&gt;1720 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;width&lt;/td&gt;
    &lt;td&gt;388 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;total finished surface (both faces + all edges)&lt;/td&gt;
    &lt;td&gt;~17 ft² (~1.55 m²)&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;h1 id=&quot;finishing&quot;&gt;finishing&lt;/h1&gt;
&lt;p&gt;The plan was Rubio Monocoat Oil Plus 2C in Dark Oak. I switched to &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.realmilkpaint.com/&quot;&gt;Real Milk Paint Dark Half&lt;/a&gt; because Rubio is fussy about temperature and humidity during application and the 5-day cure, and my carport is fussy about nothing. Unsatisfied with the tung oil finish, I sanded everything off and pivoted again.&lt;/p&gt;
&lt;h2 id=&quot;sanding&quot;&gt;sanding&lt;/h2&gt;
&lt;p&gt;Random-orbital sander with hook-and-loop discs. Through 150 grit on both faces, both end grains, and all four edges.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;80 grit to knock down planer marks and any cup, until it read flat against a straightedge.&lt;/li&gt;
&lt;li&gt;100 grit to remove the 80-grit scratch pattern.&lt;/li&gt;
&lt;li&gt;120 grit to refine.&lt;/li&gt;
&lt;li&gt;150 grit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Between grits, I vacuumed the slab and the floor and wiped with a dry rag. I checked each step under a raking light at the edge of the slab and re-sanded any swirls or remaining mill marks before stepping up.&lt;/p&gt;
&lt;h2 id=&quot;first-coat-flood&quot;&gt;first coat (flood)&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Vacuumed sanding dust off everything. Wiped with a clean dry rag.&lt;/li&gt;
&lt;li&gt;Flooded the surface with Dark Half from the can, using a foam brush. Generous, kept it wet without puddling. End grain pulled it in fast and got more.&lt;/li&gt;
&lt;li&gt;Waited 40 minutes.&lt;/li&gt;
&lt;li&gt;Wiped the excess off with a clean cotton rag in firm passes, until the surface read uniformly oiled but not wet.&lt;/li&gt;
&lt;li&gt;Let it sit four hours.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-mistake&quot;&gt;the mistake&lt;/h2&gt;
&lt;p&gt;I sanded the slab to 220 grit between the first coat and the next, and kept sanding between the coats after that. This is wrong. &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.rmpfinishes.com/blog/tips/how-to-apply-tung-oil-to-wood/&quot;&gt;Real Milk Paint says so directly&lt;/a&gt; - don’t sand between coats of tung oil. Partially-cured oil doesn’t abrade like a film finish. It gums up, smears across the surface, loads the disc, and turns into a hazy slurry of oil and sawdust that obscures the grain underneath.&lt;/p&gt;
&lt;p&gt;I kept going anyway. Second coat with 220 grit beforehand, then 400, then 600. By the end the slab looked like it had been wiped with mud. Pigment streaks, no grain definition, a soft uneven film instead of the deep clear figure I’d built up.&lt;/p&gt;
&lt;h2 id=&quot;the-recovery&quot;&gt;the recovery&lt;/h2&gt;
&lt;p&gt;Citrus solvent dissolves uncured and partly-cured tung oil. It’s also the thinner inside Dark Half itself, so I knew it was compatible with whatever was on the slab. I doused the surface with straight citrus solvent (d-limonene degreaser) and worked it with cotton rags until the goop lifted off. It took a lot of rubbing, and I had to keep at it before the surface layers fully cured - once tung oil is hard, citrus solvent doesn’t lift it cleanly anymore.&lt;/p&gt;
&lt;p&gt;The deep tung oil from the first coat survived. It had penetrated into the grain and cured down there well before the surface mess started up top. End state: the slab is back to bare-feeling wood, sanded to 150 grit, with Dark Half permanently saturating the fibers below. The grain reads clean, the pigment that absorbed into the open pores is still in place, and the slab is darker than raw white oak with no streaky topcoat.&lt;/p&gt;
&lt;h2 id=&quot;rebuilding-the-finish&quot;&gt;rebuilding the finish&lt;/h2&gt;
&lt;p&gt;I rebuilt the finish properly. Real Milk Paint’s directions for &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.rmpfinishes.com/blog/tips/how-to-apply-tung-oil-to-wood/&quot;&gt;their thinned tung oil products&lt;/a&gt; call for 3 to 5 thin wipe-on coats for everyday-use furniture, no abrasion between coats. The wood was already saturated below the surface from the original first coat, so the later coats absorbed less and built surface sheen more than they would have on raw wood.&lt;/p&gt;
&lt;p&gt;After the strip, I let the slab sit overnight in the carport so any residual citrus solvent could flash off. I did not sand again before recoating. The strip left the surface in a good state, and the pigment that absorbed into the open pores of the wood was sitting just below the surface - sanding at that point would have scrubbed off some of that pigment and lightened the slab unevenly. Raised grain from the solvent wipe-down laid back down under the next coat’s penetrate-and-wipe pass on its own.&lt;/p&gt;
&lt;p&gt;Then, for each coat:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Apply Dark Half thin with a lint-free cotton rag (an old undershirt works), not a foam brush. Just enough to wet the surface evenly, no flood.&lt;/li&gt;
&lt;li&gt;Let it soak 15 to 30 minutes.&lt;/li&gt;
&lt;li&gt;Wipe all excess off with a clean dry cotton rag. The surface should feel dry to the touch when you’re done, not tacky.&lt;/li&gt;
&lt;li&gt;Wait 24 hours before the next coat. Real Milk Paint allows as little as 2 to 3 hours between coats of their solvent-thinned blends, but overnight is safer and there’s no rush.&lt;/li&gt;
&lt;li&gt;Repeat until you’ve built three more coats on top of the existing first-coat penetration.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No sanding between coats. Not orbital, not by hand, not steel wool. If a coat dries with a nib or a speck, dampen a rag with a little Dark Half on the next pass and the irregularity dissolves back into the new coat.&lt;/p&gt;
&lt;p&gt;After the final coat the slab cured for 30 days. Tung oil is touchable in a day or two but doesn’t reach full hardness for about a month. Once it was cured I put a paste wax topcoat over it - &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.rmpfinishes.com/&quot;&gt;Real Milk Paint’s Soft Wax&lt;/a&gt; is the matched pairing, and any beeswax-and-carnauba furniture wax (Liberon Black Bison, Briwax) does the same job. Wax on cured tung oil gives a smooth hand-feel, adds abrasion protection for a surface that gets sat on, and brings the satin sheen up a touch without going glossy. It’s sacrificial and needs a refresh every couple of years.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.slb/IMG_9661.jpg&quot; alt=&quot;the finished slab on sawhorses, top face up&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.slb/IMG_9662.jpg&quot; alt=&quot;close detail of the cathedral grain and ray fleck under the finish&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h2 id=&quot;oily-rag-disposal&quot;&gt;oily rag disposal&lt;/h2&gt;
&lt;p&gt;Tung oil rags will spontaneously combust. They cure by oxidation, oxidation produces heat, heat speeds the oxidation, you get a runaway in a few hours and a fire in the rag pile.&lt;/p&gt;
&lt;p&gt;Everyone seems to have various disposal methods involving the trash, but this seems cumbersome, and I put them in the fire pit and burn them.&lt;/p&gt;
&lt;h1 id=&quot;base&quot;&gt;base&lt;/h1&gt;
&lt;p&gt;The base is two rectangular end frames joined by a single spine that runs the length of the bench along the underside of the slab, all of it built from rectangular tube steel, 1.5“ x 0.75“, 0.125“ wall, and finished black.&lt;/p&gt;
&lt;p&gt;I designed this as splayed trapezoidal ends with a low stretcher down at the floor, and built it square instead. Vertical legs, and the connecting member moved up under the slab where it also does the work of carrying the fasteners. The wider footprint of a splay buys side-to-side stability that a bench this heavy doesn’t especially need, and the low stretcher was one more thing to catch a foot on.&lt;/p&gt;
&lt;p&gt;The slab is 1720 mm long and the bench seats two adults comfortably. Finished height is 18“ (457 mm) at the top of the slab, which is on the tall side for a bench and right for slipping up to a 30“ table. Slab is 48 mm thick, so the steel frame stands 409 mm.&lt;/p&gt;
&lt;h2 id=&quot;design&quot;&gt;design&lt;/h2&gt;
&lt;div&gt;
&lt;table&gt;
  &lt;tr&gt;
    &lt;th&gt;dimension&lt;/th&gt;
    &lt;th&gt;value&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;tube section&lt;/td&gt;
    &lt;td&gt;1.5&quot; x 0.75&quot;, 0.125&quot; wall&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;finished bench height (top of slab to floor)&lt;/td&gt;
    &lt;td&gt;457 mm (18&quot;)&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;steel frame height&lt;/td&gt;
    &lt;td&gt;409 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;end frame width&lt;/td&gt;
    &lt;td&gt;280 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;end frame inset from each slab end&lt;/td&gt;
    &lt;td&gt;250 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;spine length (between end frames)&lt;/td&gt;
    &lt;td&gt;1220 mm&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;slab overhang past each end frame (lateral)&lt;/td&gt;
    &lt;td&gt;54 mm per side&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;leveling feet&lt;/td&gt;
    &lt;td&gt;M8 threaded inserts, 4 corners total&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Each end frame is a plain rectangle: two vertical legs, a top tube against the underside of the slab, and a bottom tube on the floor carrying the leveling feet. No splay and no cross-bracing. Side-to-side, the mass of the slab and the width of the frames handle it. Long-axis stability comes from the slab plus the spine.&lt;/p&gt;
&lt;p&gt;The spine runs from one end frame to the other, welded to the middle of each top tube and sitting tight against the underside of the slab. Because it’s up at slab level rather than down at the floor, there’s nothing between the legs for feet to find when someone slides up to the bench.&lt;/p&gt;
&lt;p&gt;Fasteners pass up through the spine and through each end frame’s top tube into the slab, so the load is spread along the centerline instead of hanging off four corner points. White oak across a 388 mm wide slab can move 3 to 5 mm with the season, so the fastening has to let the slab breathe across its width rather than pinning it rigid in both axes - bolt a slab down hard in both directions and it cracks, or it tears the fasteners out.&lt;/p&gt;
&lt;p&gt;I’m using steel knife-thread inserts (the kind with aggressive external wood threads and an internal hex drive), not brass press-ins. Brass strips out of oak under load. The inserts seat flush in the underside of the slab and are reversible: I can lift the base off any time.&lt;/p&gt;
&lt;h2 id=&quot;cuts-and-welds&quot;&gt;cuts and welds&lt;/h2&gt;
&lt;p&gt;Cuts are all done on a chop saw with a metal-cutting blade. Everything is square-cut, which is the advantage of dropping the splay: no angle setup, and every joint is flat against flat.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Top and bottom tubes: 280 mm long, ends cut square, two of each.&lt;/li&gt;
&lt;li&gt;Legs: cut to stand the frame at 409 mm overall, square at both ends.&lt;/li&gt;
&lt;li&gt;Spine: 1220 mm, ends cut square, welded to the middle of each top tube.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tack-welded both frames on a flat surface (the concrete slab of the carport works, with the tubes shimmed level). Once everything was square I ran full welds at each joint. MIG is what I have - the welds are heavier than TIG but they grind out fine. After welding I checked the frames were still flat and not pulled by the heat, then tacked the spine between them and welded that out.&lt;/p&gt;
&lt;p&gt;Threaded inserts for the leveling feet: drill 9 mm holes in the underside of the bottom tubes at each corner, weld M8 nuts inside, then thread in M8 leveling feet with plastic bases. The plastic keeps the steel off the floor and lets me dial out any rocking.&lt;/p&gt;
&lt;h2 id=&quot;finishing-the-steel&quot;&gt;finishing the steel&lt;/h2&gt;
&lt;p&gt;The plan was bright steel kept bright: scotch-brite to a uniform satin, then clear oil-based polyurethane over it. I went black instead.&lt;/p&gt;
&lt;p&gt;Bright bare steel wants to be the thing you look at, and the slab is the thing worth looking at here. A dark frame gets out of the way and lets the top read as the object, which is also why the base is deliberately plain. The other half of it is upkeep. Clear coat over bright mild steel is a maintenance finish in a humid climate: any break in the film, any fingerprint left under it, and rust blooms where you can see it through the clear.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Grind all welds flush with a flap disc.&lt;/li&gt;
&lt;li&gt;Strip mill scale from every surface. 60-grit flap disc to start, then 120.&lt;/li&gt;
&lt;li&gt;Wipe every surface down with acetone on a lint-free rag. Same drill as the slab dust-removal step, and gloves stay on from here.&lt;/li&gt;
&lt;li&gt;Coat it black, thin coats rather than one heavy one.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Raw mild steel flash-rusts fast in humid air, especially in a carport, so the degrease and the first coat still want to happen in the same session, within an hour or two rather than the next morning. Bare hands are the other thing to watch: fingerprints leave salts and oils that come back through a finish weeks later.&lt;/p&gt;
&lt;h2 id=&quot;assembly&quot;&gt;assembly&lt;/h2&gt;
&lt;p&gt;The frame and the slab were joined once both finishes had cured.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Flip the slab upside down on a moving blanket.&lt;/li&gt;
&lt;li&gt;Set the frame on the underside, positioned with 250 mm of slab overhang at each end and centered side-to-side.&lt;/li&gt;
&lt;li&gt;Mark the fastener locations through the steel, then lift the frame back off.&lt;/li&gt;
&lt;li&gt;Drill pilot holes at each mark, depth set with a tape flag on the bit so nothing punches through the 48 mm slab.&lt;/li&gt;
&lt;li&gt;Set the frame back in position and run the fasteners up through the spine and the end frames into the slab, by hand first.&lt;/li&gt;
&lt;li&gt;Snug everything down. Not gorilla-tight - the slab needs to be able to move across its width with the season.&lt;/li&gt;
&lt;li&gt;Flip the bench upright and level it with the M8 feet on whatever floor it lands on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It comes apart the same way it went together, so moving it is a matter of backing out the fasteners and carrying two pieces instead of one heavy awkward one.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.slb/IMG_9961.jpg&quot; alt=&quot;the steel base bolted to the underside of the slab, bench upside down on the floor&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h1 id=&quot;finished&quot;&gt;finished&lt;/h1&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.slb/IMG_9963.jpg&quot; alt=&quot;the finished bench from above&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.slb/IMG_9962.jpg&quot; alt=&quot;the finished bench in place in the living room&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Portraits: John Berger on Artists (reading)</title>
      <pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/portraits-john-berger/</link>
      <guid>https://nnix.com/reading/portraits-john-berger/</guid>
      <description xml:base="https://nnix.com/reading/portraits-john-berger/">&lt;p&gt;Berger, John. (2015). (rated NR/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;Fantastic book. I am not done with this yet, but I think I know what score it’s getting. I’m not sure whether or not this is a definitive text about art, but it is a worthwhile read about the human condition. In fact, Berger’s occasional bypass of the subject at hand might be the most charming characteristic of the book, for in parallax we see the art as more than an object.&lt;/p&gt;
&lt;p&gt;In other words, you don’t have to care about any of these artists or works to enjoy this book, and if you do care about them, all the better.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Introduction to Civil War</title>
      <pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/introduction-to-civil-war/</link>
      <guid>https://nnix.com/reading/introduction-to-civil-war/</guid>
      <description xml:base="https://nnix.com/reading/introduction-to-civil-war/">&lt;p&gt;Tiqqun. (2010). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;Lots of pretty words, lots of cool ideas, and I’m not sure where any of this leads us. A worthwhile, pleasant read on a lazy Sunday though. I would love to debate this with someone who has more investment in making the people indigestible to Empire.&lt;/p&gt;
&lt;p&gt;Naturally, I could not help but love the characterization of Bloom.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Transmission of Affect</title>
      <pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/transmission-of-affect/</link>
      <guid>https://nnix.com/reading/transmission-of-affect/</guid>
      <description xml:base="https://nnix.com/reading/transmission-of-affect/">&lt;p&gt;Brennan, Teresa. (2004). (rated 4/5) (recommended by Harper Z.)&lt;/p&gt;&lt;p&gt;The book is interdisciplinary, for sure: psychoanalysis, endocrinology, theology, classical philosophy, social theory. The argument is large, the material in the last couple chapters is sketchier than the first several. Historical claims about premodern openness are more asserted than demonstrated, but the central provocation (affects are not your private property) is pretty cool, strange, and easily understood experientially.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Conversations with Adam and Natasha (queued)</title>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/conversations-adam-natasha/</link>
      <guid>https://nnix.com/reading/conversations-adam-natasha/</guid>
      <description xml:base="https://nnix.com/reading/conversations-adam-natasha/">&lt;p&gt;Laing, R.D.. (1977). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>Fleabag: The Scriptures (queued)</title>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/fleabag-the-scriptures/</link>
      <guid>https://nnix.com/reading/fleabag-the-scriptures/</guid>
      <description xml:base="https://nnix.com/reading/fleabag-the-scriptures/">&lt;p&gt;Waller-Bridge, Phoebe. (2019). (rated NR/5) (recommended by Julian P.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>McSweeney&#39;s #81 (queued)</title>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/mcsweeney-81/</link>
      <guid>https://nnix.com/reading/mcsweeney-81/</guid>
      <description xml:base="https://nnix.com/reading/mcsweeney-81/">&lt;p&gt;Various. (2026). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Economy of Cities (queued)</title>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/the-economy-of-cities/</link>
      <guid>https://nnix.com/reading/the-economy-of-cities/</guid>
      <description xml:base="https://nnix.com/reading/the-economy-of-cities/">&lt;p&gt;Jacobs, Jane. (1969). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>nnix.com</title>
      <pubDate>Tue, 07 Apr 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/nnix/</link>
      <guid>https://nnix.com/projects/nnix/</guid>
      <description xml:base="https://nnix.com/projects/nnix/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;I migrated from Gemini in September 2024, and not everything came along for the ride, so this is a bit of a reset. I like Gemini a lot, but maintaining a content bridge to the web was a pain, and the audience with native Gemini clients is tiny, so interoperability with others is poor.&lt;/p&gt;
&lt;p&gt;The web isn’t such a bad place if you have discretion.&lt;/p&gt;
&lt;p&gt;This site is a collection of plaintext markdown files stored in git (I use GitHub for this at writing). When I push a change to the main branch of the repository hosting these files, a GitHub action kicks off and compiles the markdown files into html using &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/&quot;&gt;Zola&lt;/a&gt;, a static site generator. There’s also a separate action that validates the build on pull requests, so I can catch breakage before it hits production.&lt;/p&gt;
&lt;p&gt;You don’t need wordpress for a few pages. Heck, you don’t need wordpress for even a hundred pages. I’m not saying it’s a bad idea, just that it’s convoluted to design, maybe more expensive, and definitely harder to administer than this kind of setup, which can be run in an S3 bucket from a github repository.&lt;/p&gt;
&lt;p&gt;The entire site, without graphics, is under 80 kb. I will not permit it to exceed 128 kb, but this is a personal constraint. It’s not Gemini-small, but it’s in the spirit of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://perfectmotherfuckingwebsite.com/&quot;&gt;one of my favorite sites on the web&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want something similar, follow this doc. I’m sure I will too when I want to remember what I did with this thing years from now.&lt;/p&gt;
&lt;h1 id=&quot;assumptions&quot;&gt;assumptions&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;You have a domain, foo.com or the like (idk $10/year?)&lt;/li&gt;
&lt;li&gt;You have an AWS Account (free)&lt;/li&gt;
&lt;li&gt;You have a GitHub Account (free)&lt;/li&gt;
&lt;li&gt;You know some stuff about DNS and can poke your way around the AWS Console.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;aws-s3&quot;&gt;AWS S3&lt;/h1&gt;
&lt;p&gt;You have an S3 bucket, &lt;code&gt;foo.com&lt;/code&gt;, in &lt;code&gt;us-east-1&lt;/code&gt;. It has ARN &lt;code&gt;arn:aws:s3:::foo.com&lt;/code&gt; and static website hosting is turned on. The static website origin is at:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://foo.com.s3-website-us-east-1.amazonaws.com/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have a bucket policy which defaults to public access:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;Id&amp;quot;: &amp;quot;Policy1726679145347&amp;quot;,
  &amp;quot;Version&amp;quot;: &amp;quot;2012-10-17&amp;quot;,
  &amp;quot;Statement&amp;quot;: [
    {
      &amp;quot;Sid&amp;quot;: &amp;quot;Stmt1726679140826&amp;quot;,
      &amp;quot;Action&amp;quot;: [
        &amp;quot;s3:GetObject&amp;quot;
      ],
      &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
      &amp;quot;Resource&amp;quot;: &amp;quot;arn:aws:s3:::foo.com/*&amp;quot;,
      &amp;quot;Principal&amp;quot;: &amp;quot;*&amp;quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Remember to change the “Resource” line to your actual ARN, which is probably not truly foo.com.&lt;/p&gt;
&lt;h2 id=&quot;aws-cloudfront&quot;&gt;AWS CloudFront&lt;/h2&gt;
&lt;p&gt;There’s a cloudfront distribution at:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://d3mip7fsd83s7mvq5.cloudfront.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with ARN:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;arn:aws:cloudfront::8986388484237:distribution/E4DE38M2FUA4N
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It points to the foo.com bucket origin above and has a foo.com CNAME attached to it with a custom certificate.&lt;/p&gt;
&lt;h1 id=&quot;foo-com-certificate&quot;&gt;foo.com Certificate&lt;/h1&gt;
&lt;p&gt;CloudFront needs a cert, so we have AWS ACM issue one for foo.com. ECDSA P-256 certs now work with CloudFront, but RSA 2048 is still the safe default if you don’t want to think about it.&lt;/p&gt;
&lt;h1 id=&quot;foo-com-a-record&quot;&gt;foo.com A record&lt;/h1&gt;
&lt;p&gt;We use the A record alias feature in Route53 to point to the CloudFront distribution for foo.com. This only works once the custom CNAME and certificate are in place, of course.&lt;/p&gt;
&lt;h1 id=&quot;aws-bucket-policy-setup&quot;&gt;AWS Bucket Policy Setup&lt;/h1&gt;
&lt;p&gt;We created a new policy by logging on to AWS Console and going to IAM &amp;gt; Policies &amp;gt; Create policy. Switch from the visual editor to JSON and paste the following snippet. Remember to update your bucket ARN names under “Resource”:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &amp;quot;Version&amp;quot;: &amp;quot;2012-10-17&amp;quot;,
  &amp;quot;Statement&amp;quot;: [
    {
      &amp;quot;Sid&amp;quot;: &amp;quot;AccessToWebsiteBuckets&amp;quot;,
      &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
      &amp;quot;Action&amp;quot;: [
        &amp;quot;s3:PutBucketWebsite&amp;quot;,
        &amp;quot;s3:PutObject&amp;quot;,
        &amp;quot;s3:PutObjectAcl&amp;quot;,
        &amp;quot;s3:GetObject&amp;quot;,
        &amp;quot;s3:ListBucket&amp;quot;,
        &amp;quot;s3:DeleteObject&amp;quot;
      ],
      &amp;quot;Resource&amp;quot;: [
        &amp;quot;arn:aws:s3:::foo.com&amp;quot;,
        &amp;quot;arn:aws:s3:::foo.com/*&amp;quot;
      ]
    },
    {
      &amp;quot;Sid&amp;quot;: &amp;quot;AccessToCloudfront&amp;quot;,
      &amp;quot;Effect&amp;quot;: &amp;quot;Allow&amp;quot;,
      &amp;quot;Action&amp;quot;: [
        &amp;quot;cloudfront:GetInvalidation&amp;quot;,
        &amp;quot;cloudfront:CreateInvalidation&amp;quot;
      ],
      &amp;quot;Resource&amp;quot;: &amp;quot;*&amp;quot;
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once the policy is created you need to create a new user under IAM &amp;gt; Users. Give it a name such as foo-com-github-user. On the “Set Permissions” step select “Attach Policies Directly” and find the policy we created in the last step.&lt;/p&gt;
&lt;p&gt;From the list of users click on your newly created account and then open the Security Credentials tab. Under Access keys select &amp;gt; Create access key and choose Command Line Interface (CLI). Click “I understand the above recommendation” and then Create access key. Note the Access Key ID and Secret Access Key.&lt;/p&gt;
&lt;h1 id=&quot;github-setup&quot;&gt;GitHub Setup&lt;/h1&gt;
&lt;p&gt;Next we need to create the Github Actions workflows. We need workflow files in the .github/workflows directory of our repository. This can be done by navigating to the Actions tab in GitHub or by committing the files from your machine.&lt;/p&gt;
&lt;p&gt;First, store your AWS credentials and CloudFront distribution ID as GitHub secrets. Go to your repo’s Settings &amp;gt; Secrets and variables &amp;gt; Actions, and add:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt; — the access key you recorded earlier&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt; — the corresponding secret key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLOUDFRONT_DISTRIBUTION_ID&lt;/code&gt; — the ID of your CloudFront distribution (e.g. &lt;code&gt;E4DE38M2FUA4N&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;deploy-workflow&quot;&gt;deploy workflow&lt;/h2&gt;
&lt;p&gt;.github/workflows/publish.yaml handles building and deploying on push to main:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Build and Publish to AWS

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Install Zola
        uses: taiki-e/install-action@v2
        with:
          tool: zola@0.22.1

      - name: Build Zola Site
        run: zola build

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Sync to S3
        run: |
          # HTML/XML/JSON: short cache, revalidate often
          aws s3 sync public/ s3://foo.com \
            --delete \
            --cache-control &amp;quot;max-age=300, must-revalidate&amp;quot; \
            --exclude &amp;quot;*&amp;quot; \
            --include &amp;quot;*.html&amp;quot; \
            --include &amp;quot;*.xml&amp;quot; \
            --include &amp;quot;*.json&amp;quot;

          # Static assets: long cache, immutable
          aws s3 sync public/ s3://foo.com \
            --cache-control &amp;quot;max-age=31536000, immutable&amp;quot; \
            --exclude &amp;quot;*.html&amp;quot; \
            --exclude &amp;quot;*.xml&amp;quot; \
            --exclude &amp;quot;*.json&amp;quot;

      - name: Invalidate CloudFront cache
        run: |
          aws cloudfront create-invalidation \
            --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \
            --paths &amp;quot;/*&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The S3 sync uses two passes with different cache-control headers. HTML and other frequently-changing files get a short 5-minute cache with must-revalidate, while static assets (images, CSS, fonts, JS) get a one-year immutable cache. CloudFront invalidation ensures the CDN picks up new content immediately after deploy.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;workflow_dispatch&lt;/code&gt; trigger lets you kick off a deploy manually from the GitHub Actions tab, which is handy when you need to redeploy without a code change.&lt;/p&gt;
&lt;h2 id=&quot;pr-build-check&quot;&gt;PR build check&lt;/h2&gt;
&lt;p&gt;.github/workflows/pr-check.yaml validates the site builds on pull requests:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: PR Build Check

on:
  pull_request:
    branches:
      - main

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Install Zola
        uses: taiki-e/install-action@v2
        with:
          tool: zola@0.22.1

      - name: Build Zola Site
        run: zola build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just a build check — no deploy. It catches broken templates or bad markdown before you merge to main.&lt;/p&gt;
&lt;h1 id=&quot;zola-init&quot;&gt;Zola init&lt;/h1&gt;
&lt;p&gt;Clone your GitHub repo to a machine on which you can install Zola. This is easiest on a MacOS or Linux machine, but you can do it in Windows too.&lt;/p&gt;
&lt;p&gt;Follow the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://www.getzola.org/documentation/getting-started/installation/&quot;&gt;Zola install instructions&lt;/a&gt; and then navigate to the cloned repo directory on your local machine and execute:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zola init
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;… in the root directory of the repo. That gets a basic site framework which the “zola build” command in the above github workflow can work upon.&lt;/p&gt;
&lt;p&gt;Commit/push the changes to your repo back up to GitHub, and you should see the action execute in GitHub, hopefully with success - check out https://github.com/youruser/foo.com/actions page for status.&lt;/p&gt;
&lt;p&gt;If the action is not successful, just read the logs and see what went wrong.&lt;/p&gt;
&lt;h1 id=&quot;adding-a-template-and-other-basics&quot;&gt;adding a template and other basics&lt;/h1&gt;
&lt;p&gt;I host the template I use on github.com. It’s a basic one, but a good place to start so you know how things fit together when working.&lt;/p&gt;
&lt;h2 id=&quot;favicon&quot;&gt;favicon&lt;/h2&gt;
&lt;p&gt;You need a favicon. I don’t get in to the whole static size thing because I’m not fancy enough to care about old browsers, so I recommend simply uploading an svg version of your favicon to &lt;code&gt;/static/favicon.svg&lt;/code&gt; which transforms to &lt;code&gt;/favicon.svg&lt;/code&gt; when the site is compiled. You’ll need to specify this favicon file in your &lt;code&gt;/templates/head.html&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;og-image&quot;&gt;og:image&lt;/h2&gt;
&lt;p&gt;If you want your site to render an og:image when it gets posted to the various social sites, you need to upload an og:image of the correct size (1200x630 pixels) and refer to it in &lt;code&gt;/templates/head.html&lt;/code&gt;. I put mine at &lt;code&gt;/static/nnix.png&lt;/code&gt; which transforms to &lt;code&gt;/nnix.png&lt;/code&gt; at compile time, and I refer to it in &lt;code&gt;/templates/head.html&lt;/code&gt; which places it on the head for all my various pages.&lt;/p&gt;
&lt;h2 id=&quot;icons&quot;&gt;icons&lt;/h2&gt;
&lt;p&gt;There are innumerable icons you can add for various purposes, such as mobile device icons and preview pages. Don’t overthink this, your website doesn’t need very many of these unless you’re hosting a webapp, which we aren’t. Check out the ones I have in &lt;code&gt;/static/&lt;/code&gt; and steal from there as you see fit. Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Android 192x192 icon&lt;/li&gt;
&lt;li&gt;Apple Touch icon&lt;/li&gt;
&lt;li&gt;MS Tile 150x150 icon&lt;/li&gt;
&lt;li&gt;Safari Pinned Tab icon&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;bimi&quot;&gt;bimi&lt;/h2&gt;
&lt;p&gt;There’s a BIMI icon in my template for this site, but that’s an email thing. It makes your emails look branded from you. It’s really not interesting for most people, I’d just forget about it. I did this because I can, not because I needed to.&lt;/p&gt;
&lt;h1 id=&quot;writing-projects&quot;&gt;writing projects&lt;/h1&gt;
&lt;p&gt;I mainly use my website as a project listing. It’s got one main directory for the “bloglike” stuff, which is basically a page per project which generates a table of contents for the project directory page. This main directory is &lt;code&gt;/content/projects/&lt;/code&gt;. Images are stored in &lt;code&gt;/static/images&lt;/code&gt; which, when rendered, becomes &lt;code&gt;/images&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Otherwise, there’s a main page located at &lt;code&gt;/_index.md&lt;/code&gt; which renders to &lt;code&gt;/index.html&lt;/code&gt;, and it’s just a landing page for my site.&lt;/p&gt;
&lt;h1 id=&quot;note&quot;&gt;note&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Use GitHub secrets for all credentials and distribution IDs. The workflow examples above reference secrets, so nothing sensitive is committed to the repo. This means your repo can be public without leaking keys.&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    <item>
      <title>Mountain in the Sea</title>
      <pubDate>Mon, 30 Mar 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/mountain-in-the-sea/</link>
      <guid>https://nnix.com/reading/mountain-in-the-sea/</guid>
      <description xml:base="https://nnix.com/reading/mountain-in-the-sea/">&lt;p&gt;Nayler, Ray. (2023). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;This is a good story, well written, a light read for vacation without being superficial. The device of the octopus as an alternative intelligence is not particularly well developed, and is almost peripheral to the story of human conquest and greed. There’s a side plotline about robotic fishing vessels (which aren’t so robotic after all) which could almost justify being the main plotline, as it’s far more interesting than the octopus colony.&lt;/p&gt;
&lt;p&gt;All in all, though, it deserves its popularity. A worthwhile read.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>How to Change Your Mind</title>
      <pubDate>Wed, 18 Mar 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/how-to-change-your-mind/</link>
      <guid>https://nnix.com/reading/how-to-change-your-mind/</guid>
      <description xml:base="https://nnix.com/reading/how-to-change-your-mind/">&lt;p&gt;Pollan, Michael. (2018). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;A relatively fast read given the page count, and I always like Michael Pollan’s style. This might be the single best consolidated history of psychedelics I have read, and I’ve read a few. It is also modern enough to contain the contemporary history, which is in rapid flux since the reboot of research in the early 2000s.&lt;/p&gt;
&lt;p&gt;He tempers the mystical without disparaging it, which I think is important for a practice which may have real scientific benefits but comes from both genuine and ulterior movies mystically.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Lunario sentimental</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/lunario-sentimental/</link>
      <guid>https://nnix.com/projects/lunario-sentimental/</guid>
      <description xml:base="https://nnix.com/projects/lunario-sentimental/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;One of my favorite books, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/reading/this-craft-of-verse/&quot;&gt;a lecture&lt;/a&gt; by Jorge Luis Borges on writing, cites a Leopoldo Lugones work for which I can find no English translation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“The Argentine poet Lugones, way back in the year 1909, wrote that he thought poets were always using the same metaphors, and that he would try his hand at discovering new metaphors for the moon. And in fact he concocted many hundreds of them. He also said, in the foreword to a book called &lt;em&gt;Lunario sentimental&lt;/em&gt;, that every word is a dead metaphor.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Borges, &lt;em&gt;This Craft of Verse&lt;/em&gt;, p. 22 (The Metaphor)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am intrigued! Let’s get a translation out in the world.&lt;/p&gt;
&lt;h1 id=&quot;timeline&quot;&gt;timeline&lt;/h1&gt;
&lt;body&gt;
    &lt;div class=&quot;timeline&quot;&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;24 December 2024&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;we&#39;re up to page 71 in translation work&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;25 December 2024&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;poetry typesetting template complete&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;01 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;we&#39;re up to page 201 in translation work&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;06 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;we&#39;re finished with initial translation work&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;7 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;typesetting all poetry begins&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;13 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;the poetry typesetting is progressing well, about 50%, and I&#39;ve developed a typesetting method for the plays, which was something of a task, since the dramatist package does not get along well with XeLaTeX inside of a book document type&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;14 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;poetry typesetting complete&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;15 January 2025&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;prose typesetting complete&lt;/div&gt;
        &lt;/div&gt;
        &lt;div class=&quot;timeline-item&quot;&gt;
            &lt;div class=&quot;date&quot;&gt;February 2026&lt;/div&gt;
            &lt;div class=&quot;event&quot;&gt;endnotes, fidelity pass against the 1909 original, bilingual chapter titles, cover finalized, font licensing resolved, final typographic refinements. 278 pages, ready for print&lt;/div&gt;
        &lt;/div&gt;
    &lt;/div&gt;
&lt;/body&gt;
&lt;h1 id=&quot;original&quot;&gt;original&lt;/h1&gt;
&lt;p&gt;The original work was published in 1909, and has since entered the public domain. You can download it &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/images/lunariosentimental/lunario_sentimental_original.pdf&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The work contains 37 pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;four plays&lt;/li&gt;
&lt;li&gt;four works of prose&lt;/li&gt;
&lt;li&gt;29 poems&lt;/li&gt;
&lt;/ul&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/lunariosentimental/chapter-list.jpeg&quot; alt=&quot;an index of 37 pieces of prose, poetry and plays in Lunario Sentimental&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h1 id=&quot;translation&quot;&gt;translation&lt;/h1&gt;
&lt;p&gt;Spanish to English translation was done by Oskarina Perez. I hired her for her familiarity with the genre and the author.&lt;/p&gt;
&lt;p&gt;I did English editing work, in consultation with Oskarina.&lt;/p&gt;
&lt;p&gt;The chapter titles are kept in the original Spanish throughout the book, with English translations appearing in the table of contents. I like this convention for translated poetry — the Spanish titles have a sonic quality that English tends to flatten. “Jaculatoria Lunar” is better than “Lunar Ejaculatory Prayer.” The table of contents gives you both, so nobody’s locked out.&lt;/p&gt;
&lt;p&gt;43 endnotes cover classical references, obscure vocabulary, foreign phrases, and translation choices. They live in the back of the book to keep the poems clean on the page.&lt;/p&gt;
&lt;h1 id=&quot;typesetting&quot;&gt;typesetting&lt;/h1&gt;
&lt;p&gt;I typeset the work in XeLaTeX, to provide modern font support.&lt;/p&gt;
&lt;h2 id=&quot;body-font&quot;&gt;body font&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/EB_Garamond&quot;&gt;EB Garamond&lt;/a&gt; was chosen for its wide character support and, well, because it’s beautiful.&lt;/p&gt;
&lt;p&gt;Since this stuff matters, here’s how I’ve configured EB Garamond in &lt;code&gt;fontspec&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\setmainfont{EBGaramond}
    [
        UprightFont = EBGaramond-Regular,
        ItalicFont = EBGaramond-Italic,
        BoldFont = EBGaramond-Bold,
        BoldItalicFont = EBGaramond-BoldItalic,
        Ligatures = {Common, Rare},
        StylisticSet = 6,
        Numbers = Proportional,
        Numbers = OldStyle
    ]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I initially held off on &lt;code&gt;Ligatures = Rare&lt;/code&gt; because I wasn’t sure I could justify it ergonomically, but I’ve come around. The discretionary &lt;em&gt;ct&lt;/em&gt; and &lt;em&gt;st&lt;/em&gt; ligatures — where the letters get a connecting swash arc between them — are gorgeous and period-appropriate for a 1909 text. Words like “sentimental,” “architecture,” “distinction” all pick up these elegant old-style joins. It’s subtle enough that most readers won’t consciously notice, but it gives the text a distinctly hand-set, early-20th-century feel.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/lunariosentimental/rare_ligatures.png&quot; alt=&quot;lots of rare st ligatures&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/lunariosentimental/ligatures.png&quot; alt=&quot;a sick ligature on a Q&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h2 id=&quot;title-font&quot;&gt;title font&lt;/h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://fonts.google.com/specimen/Six+Caps&quot;&gt;Six Caps&lt;/a&gt; is the title font — an ultra-condensed display face under the SIL Open Font License. I had been using HFF High Tension, which is based on “Bamberg” from a Dover &lt;em&gt;Circus Alphabets&lt;/em&gt; book, but the licensing was ambiguous for commercial use. Six Caps has the same dramatic vertical compression and reads almost identically on a shelf, with the benefit of being unambiguously open source.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/lunariosentimental/high_tension.png&quot; alt=&quot;LUNARIO SENTIMENTAL in Six Caps font.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h2 id=&quot;poetry&quot;&gt;poetry&lt;/h2&gt;
&lt;p&gt;The poetry is set using the &lt;code&gt;poetry&lt;/code&gt; package. There’s really no trick to this, a poem looks as follows in the code,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\poemlinenumsfalse
\centerpoemoff
\begin{poem}
\textit{Che cotesta córtese opinione} \\
\textit{Ti fian chiavata in mezzo della testa.} \\!
\end{poem}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;plays&quot;&gt;plays&lt;/h2&gt;
&lt;p&gt;Plays were a bigger deal. I like the &lt;code&gt;dramatist&lt;/code&gt; package a lot, but it doesn’t work inside a &lt;code&gt;book&lt;/code&gt; document type in XeLaTeX. So I made my own environment to format the plays, headers, and dramatis personae sections. Here’s what it looks like in the preamble:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% Play environment with acts and scenes
\newenvironment{play}[1]{
    \section*{#1} % Title of the play
    \setlength{\parindent}{0pt} % No indentation
    \setlength{\parskip}{0.6\baselineskip}
    \obeylines
}{
    \setlength{\parindent}{1em} % Restore default indentation
    \setlength{\parskip}{0pt} % Restore default spacing
}

% Commands for structured formatting — consistent across all plays
\newcommand{\act}[1]{%
    \bigskip\bigskip
    \centerline{{\Large\spacedsc{Act #1}}}
    \medskip
}
\newcommand{\scene}[1]{%
    \bigskip
    \centerline{\spacedsc{Scene #1}}
    \medskip
}
\newcommand{\character}[1]{%
    \textbf{#1.---}\hspace{1em} % Bold character names with em dash
}
\newcommand{\stage}[1]{%
    \textit{(#1)} % Italics for stage directions
}

% Dramatis Personae Environment — centered block with small caps heading
\newenvironment{dramatispersonae}{%
    \par\bigskip
    \begin{center}
    \spacedsc{Dramatis Personae}\\[0.75\baselineskip]
    \begin{tabular}{@{}r@{\kern 1em}l@{}}
}{%
    \end{tabular}
    \end{center}
    \bigskip
}

\newcommand{\dpline}[2]{%
    \textit{#1} &amp;amp; #2 \\[3pt]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here’s what a play looks like in code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;\begin{play}{ }

\begin{dramatispersonae}
    \dpline{Dalinda}{23 years old, blonde. Sister of Jacinto.}
    \dpline{Jacinto}{26 years old. Brother of Dalinda.}
    \dpline{Reinaldo}{20 years old. Friend of Jacinto and Dalinda&amp;#39;s fiancé.}
\end{dramatispersonae}

\act{I}

\scene{1}

\character{Jacinto} This is an example dialogue line.

\character{Reinaldo} \stage{Turning to Jacinto} This is another example.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ends up looking as follows when compiled:&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/lunariosentimental/play_example.png&quot; alt=&quot;The first page of a play to illustrate compiled formatting.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h1 id=&quot;publication&quot;&gt;publication&lt;/h1&gt;
&lt;p&gt;TBD&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Critical Writings of James Joyce (queued)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/critical-writings-james-joyce/</link>
      <guid>https://nnix.com/reading/critical-writings-james-joyce/</guid>
      <description xml:base="https://nnix.com/reading/critical-writings-james-joyce/">&lt;p&gt;Mason, Ellsworth. (1959). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>In the Shape of a Human Body I am Visiting the Earth (reading)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/in-the-shape-of-a-human-body/</link>
      <guid>https://nnix.com/reading/in-the-shape-of-a-human-body/</guid>
      <description xml:base="https://nnix.com/reading/in-the-shape-of-a-human-body/">&lt;p&gt;Various. (2017). (rated NR/5)&lt;/p&gt;&lt;p&gt;Poetry compilation from McSweeney’s, “Poems from Far and Wide”.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Joyce and Feminism (queued)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/joyce-and-feminism/</link>
      <guid>https://nnix.com/reading/joyce-and-feminism/</guid>
      <description xml:base="https://nnix.com/reading/joyce-and-feminism/">&lt;p&gt;Scott, Bonnie Kime. (1984). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>Not Without Laughter (queued)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/not-without-laughter/</link>
      <guid>https://nnix.com/reading/not-without-laughter/</guid>
      <description xml:base="https://nnix.com/reading/not-without-laughter/">&lt;p&gt;Hughes, Langston. (1930). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>The Long Harpoon (queued)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/the-long-harpoon/</link>
      <guid>https://nnix.com/reading/the-long-harpoon/</guid>
      <description xml:base="https://nnix.com/reading/the-long-harpoon/">&lt;p&gt;Watson, Arthur C. (1929). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>Tribe (queued)</title>
      <pubDate>Mon, 23 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/tribe/</link>
      <guid>https://nnix.com/reading/tribe/</guid>
      <description xml:base="https://nnix.com/reading/tribe/">&lt;p&gt;Junger, Sebastian. (2016). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>burn box</title>
      <pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/burn-box/</link>
      <guid>https://nnix.com/projects/burn-box/</guid>
      <description xml:base="https://nnix.com/projects/burn-box/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;The burn box is a place to burn small papers safely indoors. It’s a small open-topped steel box, four panels welded to a base plate, and the base is drilled with vent holes so the fire can pull fresh air in from underneath while it’s going.&lt;/p&gt;
&lt;p&gt;The panels are stamped a word to a face: LIFE EXISTS AND IDENTITY. (Whitman, ofc)&lt;/p&gt;
&lt;h1 id=&quot;build&quot;&gt;build&lt;/h1&gt;
&lt;p&gt;The panels are thin sheet steel, tacked at the corners first and then welded out, with the vent holes drilled through the base after the box was closed up.&lt;/p&gt;
&lt;p&gt;Thin metal difficult. It’s far less forgiving on flux core MIG than thick metal is. On heavy stock you have plenty of latitude in both time and power: you can dwell, you can run hot, and the mass around the puddle carries the heat off. None of that is true on thin material. Dwell a half second too long or run a touch too hot and you’re not welding anymore, you’re cutting a hole and then trying to fill it back in.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.bbo/IMG_9296.jpg&quot; alt=&quot;the first panel tacked to the base plate, letters stamped into the face&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.bbo/IMG_9298.jpg&quot; alt=&quot;three panels tacked up, the box starting to take shape&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.bbo/IMG_9303.jpg&quot; alt=&quot;all four panels welded up, heat color around the joints&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.bbo/IMG_9308.jpg&quot; alt=&quot;drilling the vent holes through the base, box clamped in the drill press vise&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.bbo/IMG_9310.jpg&quot; alt=&quot;the finished burn box, vent hole visible at the base&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Cryopolitics: Frozen Life in a Melting World</title>
      <pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/cryopolitics/</link>
      <guid>https://nnix.com/reading/cryopolitics/</guid>
      <description xml:base="https://nnix.com/reading/cryopolitics/">&lt;p&gt;Radin, Joanna. (2017). (rated 4/5) (recommended by Harper Z.)&lt;/p&gt;&lt;p&gt;Sick book! A collection of articles about the ways in which our ability to cool ourselves, our food, our industry, etc is a hallmark of the anthropocene. It’s not the whole story, but it is an often overlooked narrative, and no opportunity is wasted here to tell that story.&lt;/p&gt;
&lt;p&gt;The piece on the history of refrigerated transport in the British Empire is especially good. I had not considered the ways in which that country would have been motivated to develop such a supply chain, but it makes a lot of sense.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Death and Life of Great American Cities</title>
      <pubDate>Thu, 19 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/death-and-life-great-american-cities/</link>
      <guid>https://nnix.com/reading/death-and-life-great-american-cities/</guid>
      <description xml:base="https://nnix.com/reading/death-and-life-great-american-cities/">&lt;p&gt;Jacobs, Jane. (1961). (rated 4/5)&lt;/p&gt;&lt;p&gt;Jane Jacobs has a lot of hot takes. She’s funny and well-written and an astute observer of human uses for a city. This book is fluffy, don’t let the size intimidate you away from a relatively fast read.&lt;/p&gt;
&lt;p&gt;In terms of content, variety is the name of the game here. The most interesting things she covers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The pernicious effects of financing on development, and the distinction between cataclysmic money (total redevelopment) and gradual money (optimization and minor improvement) as it relates to neighborhood health.&lt;/li&gt;
&lt;li&gt;The myth of the automobile as a cause of ill.&lt;/li&gt;
&lt;li&gt;What constitutes healthy and unhealthy mixes of commercial, industrial, and residential uses.&lt;/li&gt;
&lt;li&gt;The failure of the Radiant City, or really anything planned in advance rather than facilitated over time.&lt;/li&gt;
&lt;li&gt;Healthy and unhealthy scales at which blocks, buildings, and neighborhoods operate, and boundaries which both inhibit their health and support their health by providing beneficial constraint.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;She hates a lot of things, most things, really. That said, much of what she wrote in 1961 came to pass and reads as prescient in 2026.&lt;/p&gt;
&lt;p&gt;If you can read only one chapter, read the last chapter. It is really well written, and summarizes well her position on the relationship of the city to human habitation. In fact, the last chapter might best be read first, even if you plan to read the rest of the book.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In real life, barbarians (and peasant) are the least free of men – bound by tradition, ridden by caste, fettered by supersitions, riddled by suspicion and foreboding of whatever is strange. ‘City air makes free,’ was the medieval saying, when city air literally did make free the runaway serf. City air still makes free the runaways from company towns, from plantations, from factory-farms, from subsistence farms, from migrant picker routes, from mining villages, from one-class suburbs.”&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    <item>
      <title>Attached (queued)</title>
      <pubDate>Sun, 01 Feb 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/attached/</link>
      <guid>https://nnix.com/reading/attached/</guid>
      <description xml:base="https://nnix.com/reading/attached/">&lt;p&gt;Levine, Amir. (2010). (recommended by Julian P.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>The I Ching or Book of Changes</title>
      <pubDate>Thu, 15 Jan 2026 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/i-ching-book-of-changes/</link>
      <guid>https://nnix.com/reading/i-ching-book-of-changes/</guid>
      <description xml:base="https://nnix.com/reading/i-ching-book-of-changes/">&lt;p&gt;Baynes, Cary. (1967). (rated 2/5)&lt;/p&gt;&lt;p&gt;It’s fine, but there are more comprehensible resources out there for this sort of thing. This translation is too esoteric for the modern reader, relatively. The method of casting is the same whatever you use, so get a resource you can understand for the actual reading. This one’s definitive, but not necessarily relatable.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Inventing Kindergarten (queued)</title>
      <pubDate>Tue, 23 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/inventing-kindergarten/</link>
      <guid>https://nnix.com/reading/inventing-kindergarten/</guid>
      <description xml:base="https://nnix.com/reading/inventing-kindergarten/">&lt;p&gt;Brosterman, Norman. (1997). (rated NR/5) (recommended by Julian P.)&lt;/p&gt;</description>
    </item>
    <item>
      <title>meteorite patterns</title>
      <pubDate>Thu, 18 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/meteorite/</link>
      <guid>https://nnix.com/projects/meteorite/</guid>
      <description xml:base="https://nnix.com/projects/meteorite/">&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.met/original.jpeg&quot; alt=&quot;Widmanstätten pattern on a fragment of meteorite.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;h1 id=&quot;procedural-widmanstatten-pattern-generator&quot;&gt;procedural Widmanstätten pattern generator&lt;/h1&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://github.com/davidemerson/widmanstatten&quot;&gt;I keep the repo here up to date.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This project generates large-format, purely vector, procedurally generated meteorite-like surface patterns and outputs them as PDF files suitable for print, CNC, laser, or further vector processing.&lt;/p&gt;
&lt;p&gt;They look like this:&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.met/generated.png&quot; alt=&quot;Widmanstätten pattern on a fragment of meteorite.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;The algorithm was inspired by a slice of the Muonionalusta meteorite, discovered in northern Sweden in 1906. Muonionalusta is an iron-nickel meteorite that exhibits a Widmanstätten pattern when etched, revealing interlocking crystalline phases formed by extremely slow cooling over millions of years.&lt;/p&gt;
&lt;p&gt;This generator models the statistical and structural properties visible in etched meteorite slices. It establishes dominant crystallographic directions, draws layered cross-hatching, introduces imperfect, double-etched strokes, accounts for missing material and discontinuities, and basically establishes large-scale grain regions with local variation.&lt;/p&gt;
&lt;p&gt;The result is a pattern that looks geologic and crystalline.&lt;/p&gt;
&lt;h2 id=&quot;output-characteristics&quot;&gt;output characteristics&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Arbitrary size PDF (limited only by memory/time)&lt;/li&gt;
&lt;li&gt;No rasterization, no textures, no images&lt;/li&gt;
&lt;li&gt;Every run produces a unique pattern by default&lt;/li&gt;
&lt;li&gt;Optional deterministic regeneration via seed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;method&quot;&gt;method&lt;/h2&gt;
&lt;p&gt;The image is synthesized as the superposition of several independent line families, each representing a dominant crystallographic direction.&lt;/p&gt;
&lt;p&gt;Each family is generated using the following components:&lt;/p&gt;
&lt;h3 id=&quot;directional-angles&quot;&gt;directional angles&lt;/h3&gt;
&lt;p&gt;A small set of dominant line orientations is defined (~45°, ~135°, minor orthogonal components). Each stroke family samples from a weighted angular distribution with tight variance, producing strong directional bias without mechanical regularity.&lt;/p&gt;
&lt;h3 id=&quot;parallel-strokes&quot;&gt;parallel strokes&lt;/h3&gt;
&lt;p&gt;For each direction, an infinite set of nearly parallel lines is generated, spacing is randomized using a log-normal distribution, lines overshoot the page and are clipped implicitly&lt;/p&gt;
&lt;p&gt;This mimics the irregular lamella spacing seen in etched iron-nickel alloys.&lt;/p&gt;
&lt;h3 id=&quot;grain-modulation-field&quot;&gt;grain modulation field&lt;/h3&gt;
&lt;p&gt;A low-frequency fractal noise field (FBM over value noise) is evaluated across the surface.&lt;/p&gt;
&lt;p&gt;This grain field modulates stroke width, stroke opacity, dropout probability, ghost-stroke offsets.&lt;/p&gt;
&lt;p&gt;This produces large-scale “crystal regions” where texture coherently shifts.&lt;/p&gt;
&lt;h3 id=&quot;imperfection&quot;&gt;imperfection&lt;/h3&gt;
&lt;p&gt;No line is perfectly straight, and lateral jitter is applied along the normal direction, and jitter is smooth, not noisy, to avoid cartoon effects. Additional “ghost strokes” are drawn nearby to simulate double-etching.&lt;/p&gt;
&lt;h3 id=&quot;gaps-loss&quot;&gt;gaps, loss&lt;/h3&gt;
&lt;p&gt;Segments of strokes are randomly dropped based on local grain intensity, producing broken lines, etched voids, and discontinuities characteristic of physical material removal.&lt;/p&gt;
&lt;h2 id=&quot;randomness&quot;&gt;randomness&lt;/h2&gt;
&lt;p&gt;By default: Each run uses a cryptographically strong random seed. You will never get the same output twice unless you use the same seed.&lt;/p&gt;
&lt;p&gt;For reproducibility: You may supply an explicit seed. The seed used is printed after generation. Reusing the seed reproduces the pattern exactly.&lt;/p&gt;
&lt;h2 id=&quot;output&quot;&gt;output&lt;/h2&gt;
&lt;p&gt;Generated PDFs are automatically timestamped using UTC:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;meteorite-YYYY-MM-DD-HH-MM-SS.SS.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;meteorite-2025-12-18-14-03-22.17.pdf
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;use&quot;&gt;use&lt;/h2&gt;
&lt;h3 id=&quot;dependencies&quot;&gt;dependencies&lt;/h3&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;pip install reportlab
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;run&quot;&gt;run&lt;/h3&gt;
&lt;pre&gt;&lt;code data-lang=&quot;bash&quot;&gt;python3 meteorite.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script will generate a new, unique pattern, write a timestamped PDF, print the output path and seed.&lt;/p&gt;
&lt;h3 id=&quot;deterministic-regeneration&quot;&gt;deterministic regeneration&lt;/h3&gt;
&lt;pre&gt;&lt;code data-lang=&quot;python&quot;&gt;generate_meteorite_pdf(
    W=3000,
    H=1800,
    seed=123456789
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;coordinates-scale&quot;&gt;coordinates, scale&lt;/h2&gt;
&lt;p&gt;PDF units are points (1/72 inch), but since the output is vector, you may treat units as arbitrary.&lt;/p&gt;
&lt;p&gt;For example:
36×24 inches → 2592×1728 points&lt;/p&gt;
&lt;p&gt;Large architectural panels are feasible. I’m also thinking hipster graph paper or notebook paper.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>log bench</title>
      <pubDate>Tue, 16 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/log-bench/</link>
      <guid>https://nnix.com/projects/log-bench/</guid>
      <description xml:base="https://nnix.com/projects/log-bench/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;I &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https://nnix.com/projects/logmilling&quot;&gt;chainsaw-milled&lt;/a&gt; some slabs from a tree which died in my yard, of lightning strike. The piece at the top of the log was nothing special for nice furniture, but still large enough to make a simple bench from.&lt;/p&gt;
&lt;h1 id=&quot;process&quot;&gt;process&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Cut the log to rough dimensions with a chainsaw&lt;/li&gt;
&lt;li&gt;Plane the log flat. This took a lot of planing because there was such a prominent cup to the end piece.&lt;/li&gt;
&lt;li&gt;Seal with deck polyurethane. I hope this can take the weather&lt;/li&gt;
&lt;li&gt;Cut steel scrap for a base, weld&lt;/li&gt;
&lt;li&gt;Assemble&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;base-design&quot;&gt;base design&lt;/h1&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/base-plans.jpg&quot; alt=&quot;A sketch and cutlist for the steel base.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_4668.JPG&quot; alt=&quot;cut some steel to length&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_4669.JPG&quot; alt=&quot;weld up a frame&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_4670.JPG&quot; alt=&quot;frame all done&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_4672.JPG&quot; alt=&quot;stain the top&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_5677.jpeg&quot; alt=&quot;drill the frame&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_5680.jpeg&quot; alt=&quot;drill the top&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_5745.jpeg&quot; alt=&quot;bench all done&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/log-bench/IMG_5746.jpeg&quot; alt=&quot;bench all done&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>shoe rack</title>
      <pubDate>Tue, 16 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/shoe-rack/</link>
      <guid>https://nnix.com/projects/shoe-rack/</guid>
      <description xml:base="https://nnix.com/projects/shoe-rack/">&lt;h1 id=&quot;overview&quot;&gt;overview&lt;/h1&gt;
&lt;p&gt;Shoes accumulate near outside doors, it’s just the way of things. When you have a bunch of outside doors, you have a bunch of shoe piles. The one in the mudroom was particularly egregious.&lt;/p&gt;
&lt;h1 id=&quot;process&quot;&gt;process&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;Measure your shoes. You don’t need a rack larger than your largest shoes. This rack is built for men’s size 13 or so, with a maximum height of 15-16“&lt;/li&gt;
&lt;li&gt;Most shelves don’t need to be the height of your boots. Make one shelf for tall boots, one for mid boots, and many shelves for regular low shoes, which require not more than 8“ clearance from the ground&lt;/li&gt;
&lt;li&gt;Weld up the frame, remembering to keep everything square (weld inside beads last to help with warping).&lt;/li&gt;
&lt;li&gt;Weld on large casters with brakes, all four corners&lt;/li&gt;
&lt;li&gt;Grind and sand the frame to clean it up&lt;/li&gt;
&lt;li&gt;Paint frame&lt;/li&gt;
&lt;li&gt;Cut poplar shelves to size&lt;/li&gt;
&lt;li&gt;Finish poplar shelves with stain (“dark half” tung oil and half thinner) and polyurethane for water resistance&lt;/li&gt;
&lt;li&gt;Assemble, revel in order&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;photos&quot;&gt;photos&lt;/h1&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/rack-plan.jpg&quot; alt=&quot;A sketch and cutlist for the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4577.jpg&quot; alt=&quot;Building the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4579.jpg&quot; alt=&quot;Building the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4592.jpg&quot; alt=&quot;Building the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4594.jpg&quot; alt=&quot;Building the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4599.jpg&quot; alt=&quot;Unpainted&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4662.JPG&quot; alt=&quot;Painting the shoe rack.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/shoe-rack/IMG_4673.JPG&quot; alt=&quot;All done, in place.&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>Catching the Big Fish</title>
      <pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/catching-the-big-fish/</link>
      <guid>https://nnix.com/reading/catching-the-big-fish/</guid>
      <description xml:base="https://nnix.com/reading/catching-the-big-fish/">&lt;p&gt;Lynch, David. (2007). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;A book on meditation and reflections upon meditation by David Lynch, proponent of Transcedental Meditation and all around cool person, from the sounds of it. Also has two interviews, brief, at the end, with Ringo Starr and Paul McCartney, both interesting in the context of meditation.&lt;/p&gt;
&lt;p&gt;Worth reading if you’re into TM.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Meteorites</title>
      <pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/meteorites/</link>
      <guid>https://nnix.com/reading/meteorites/</guid>
      <description xml:base="https://nnix.com/reading/meteorites/">&lt;p&gt;Bevan, Alex. (2002). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;The preface says, “This book is written for people who are not scientists” which is perfect for a book which came alongside a meteroite present that I love and had next to me while I read a book which is plenty informative on the matter of meteorites without being written for scientists alone.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Small Houses</title>
      <pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/small-houses/</link>
      <guid>https://nnix.com/reading/small-houses/</guid>
      <description xml:base="https://nnix.com/reading/small-houses/">&lt;p&gt;Jodidio, Philip. (2025). (rated 3/5) (recommended by Julian P.)&lt;/p&gt;&lt;p&gt;Excellent mostly visual reference if you’re thinking about building a small cabin or dwelling or office or whatnot. What people can do with small spaces, for reasons aesthetic and economic, is far more interesting in many ways than what people can do with large ones.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>hearth</title>
      <pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/hearth/</link>
      <guid>https://nnix.com/projects/hearth/</guid>
      <description xml:base="https://nnix.com/projects/hearth/">&lt;p&gt;My hearth, heavily used in winter because of wood heating, was unstable due to a floating (separate from main slab) foundation under the concrete block pile which surrounds the stove. The block pile is sinking a little faster than the house, and the hearth is the interface, which cracks tiles very quickly after they are installed, almost seasonally.&lt;/p&gt;
&lt;p&gt;The originally specified hearth dimensions were also inadequate for safely containing embers, which would readily roll further than the tile surface from the elevated wood stove.&lt;/p&gt;
&lt;p&gt;Let’s try steel plate! It has no grout lines to clean, it’s not flammable, and it won’t crack when the slab shifts.&lt;/p&gt;
&lt;p&gt;I used 0.25“ thick AR400 steel plate. This hearth weighs about 383 pounds, so get yourself some lifting magnets and some friends to move it around if you have a similarly large dimensional requirement.&lt;/p&gt;
&lt;p&gt;First, we measure out the hearth size (paper tape)&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6528.jpg&quot; alt=&quot;hearth before demolition&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Then demolition&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6534.jpg&quot; alt=&quot;hearth after demolition&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Then leveling concrete so the surface is perfectly flat&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6548.jpg&quot; alt=&quot;hearth after leveling concrete&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Then we plasma cut a piece the correct size and grind off all the mill scale and oil and stuff. This is the hard part. It takes a really long time to grind a piece of steel that large.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6545.jpg&quot; alt=&quot;grinding plate steel&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;Once the steel is complete cleaned up with a grinder, the oxide process (cold gunblue):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clean the whole steel surface with alcohol or some power degreaser that leaves no residue. Clean a few times, thoroughly.&lt;/li&gt;
&lt;li&gt;Load a spray bottle which features a fine mist mode with cold bluing solution. I used Birchwood Presto Black BST4.&lt;/li&gt;
&lt;li&gt;Spray the whole surface with the solution from the mist bottle. Spray evenly, everything you want oxided.&lt;/li&gt;
&lt;li&gt;Wait 90 seconds.&lt;/li&gt;
&lt;li&gt;Use a hose to rinse off the oxide solution thoroughly.&lt;/li&gt;
&lt;li&gt;Use a leafblower or compressed air to dry the surface thoroughly.&lt;/li&gt;
&lt;li&gt;Repeat oxide process. I did one coat using the spray bottle, and then did two more coats (waiting 90 seconds, rinsing, and drying between coats) simply dumping and wiping the oxide solution on with paper towel. This gives a cool mottled effect from the spray, a runny effect from the dumping, and an even effect from the wiping.&lt;/li&gt;
&lt;li&gt;When you’re happy with the finish, do a final rinse and dry.&lt;/li&gt;
&lt;li&gt;Coat the steel thoroughly with a protective oil, I use Ballistol or Renaissance Wax. For this hearth, particularly Renaissance Wax, since it’s dries matte.&lt;/li&gt;
&lt;/ul&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6551.jpg&quot; alt=&quot;oxided and waxed&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;This is what the finish looks like close-up. I love the tool marks from grinding, and I am very happy with the appearance of the oxide.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6552.jpg&quot; alt=&quot;finish close up&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;The plate fit perfectly in the floor.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6566.jpg&quot; alt=&quot;perfect fit in floor&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
&lt;p&gt;All done, though the steel does get a little darker over the next few days under the wax finish.&lt;/p&gt;
&lt;div &gt;
  &lt;img src=&quot;https://nnix.com/images/160.0-hea/IMG_6578.jpg&quot; alt=&quot;finished&quot; loading=&quot;lazy&quot;&gt;
&lt;/div&gt;
</description>
    </item>
    <item>
      <title>No Man is an Island</title>
      <pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/no-man-is-an-island/</link>
      <guid>https://nnix.com/reading/no-man-is-an-island/</guid>
      <description xml:base="https://nnix.com/reading/no-man-is-an-island/">&lt;p&gt;Donne, John. (1631). (rated 4/5)&lt;/p&gt;&lt;p&gt;A neat collection of John Donne’s work. His meditations (on illness) are the best here: funny, hypochondriac, wise, and relevant.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>The Psychoanalysis of Fire (reading)</title>
      <pubDate>Wed, 19 Nov 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/psychoanalysis-of-fire/</link>
      <guid>https://nnix.com/reading/psychoanalysis-of-fire/</guid>
      <description xml:base="https://nnix.com/reading/psychoanalysis-of-fire/">&lt;p&gt;Bachelard, Gaston. (1938). (rated NR/5)&lt;/p&gt;</description>
    </item>
    <item>
      <title>oracle</title>
      <pubDate>Tue, 18 Nov 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>projects</category>
      <link>https://nnix.com/projects/oracle/</link>
      <guid>https://nnix.com/projects/oracle/</guid>
      <description xml:base="https://nnix.com/projects/oracle/">&lt;p&gt;get your fortune here.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>McSweeney&#39;s #77</title>
      <pubDate>Mon, 06 Oct 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/mcsweeney-77/</link>
      <guid>https://nnix.com/reading/mcsweeney-77/</guid>
      <description xml:base="https://nnix.com/reading/mcsweeney-77/">&lt;p&gt;Various. (2025). (rated 3/5)&lt;/p&gt;&lt;p&gt;The usual, in a good way.&lt;/p&gt;
&lt;p&gt;I liked “The Orts” by Emily Crossen and “Merlin” by Matthew Rohrer.&lt;/p&gt;
</description>
    </item>
    <item>
      <title>Circe</title>
      <pubDate>Thu, 02 Oct 2025 00:00:00 +0000</pubDate>
      <author>David Emerson</author>
      <category>reading</category>
      <link>https://nnix.com/reading/circe/</link>
      <guid>https://nnix.com/reading/circe/</guid>
      <description xml:base="https://nnix.com/reading/circe/">&lt;p&gt;Miller, Madeline. (2018). (rated 3/5) (recommended by Elizabeth B.)&lt;/p&gt;&lt;p&gt;Do I have trouble with novels in the first person? Perhaps!&lt;/p&gt;
&lt;p&gt;The story is fun though. In an traditional epic sense, we meet Circe as a rather undeveloped character, a powerful witch type in her palace on a remote island.&lt;/p&gt;
&lt;p&gt;Miller provides the missing back-story, and makes her a much more complicated, interesting individual.&lt;/p&gt;
&lt;p&gt;I also did not know or remember that her son (with Odysseus) kills Odysseus, even in original canon. That’s a typically dramatic and curious twist!&lt;/p&gt;
</description>
    </item>
  </channel>
</rss>
