<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>Biome of Ideas</title>
        <link>https://brandonirizarry.xyz</link>
        <description>My personal website and blog</description>
        <language>en-us</language>
        <image>
            <title>Biome of Ideas</title>
            <link>https://brandonirizarry.xyz</link>
            <url>https://brandonirizarry.xyz/static/bitmap.png</url>
            <width>80</width>
            <height>80</height>
        </image>
        <item>
            <title>Buildablog v2</title>
            <link>https://brandonirizarry.xyz/posts/2026-03-12</link>
            <guid>https://brandonirizarry.xyz/posts/2026-03-12</guid>
            <pubDate>Thu, 12 Mar 2026 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;I Finally Did It&lt;/h1&gt;&#xA;&lt;p&gt;I finally solved a problem I mentioned in &lt;a href=&#34;/posts/2026-03-06&#34;&gt;a previous post&lt;/a&gt;. There,&#xA;I had left things halfway: I had only installed some infrastructure,&#xA;in the form of Cgit, that was partially&#xA;suggestive of a solution.&lt;/p&gt;&#xA;&lt;p&gt;Here, I document how I finally leveraged that piece as part of a&#xA;&lt;em&gt;full&lt;/em&gt; solution to the problem.&lt;/p&gt;&#xA;&lt;p&gt;Previously, the steps for updating my blog&#39;s content had been:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Commit all changes.&lt;/li&gt;&#xA;&lt;li&gt;Push the changes to GitHub.&lt;/li&gt;&#xA;&lt;li&gt;Log in via SSH into my VPS.&lt;/li&gt;&#xA;&lt;li&gt;Perform a &lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;brandons_blog&lt;/code&gt; directory, and run &lt;code&gt;git pull&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;This has now been reduced to two steps:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Commit all changes.&lt;/li&gt;&#xA;&lt;li&gt;Push to &lt;code&gt;https://git.brandonirizarry.xyz/brandons_blog&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h1&gt;How it Works&lt;/h1&gt;&#xA;&lt;p&gt;Previous versions of Buildablog would read posts directly from the&#xA;local filesystem. For starting out, this was an entirely intuitive and&#xA;sensible thing to do. However, I had run into a wall, since I couldn&#39;t&#xA;push to a non-bare remote, and Buildablog needs to see actual files in&#xA;order to publish them.&lt;/p&gt;&#xA;&lt;p&gt;I also wanted to keep things conceptually simple and avoid something&#xA;like a separate call to &lt;code&gt;scp&lt;/code&gt; or &lt;code&gt;rsync&lt;/code&gt; — I would only rely on&#xA;Git. After all, this is how Git forges themselves work: push, and&#xA;everything is just there, present. Not just present, but presumably&#xA;usable in some form or another. I wanted my application to take&#xA;advantage of this intuitive simplicity.&lt;/p&gt;&#xA;&lt;p&gt;Luckily, &lt;a href=&#34;https://go-git.github.io/docs/&#34;&gt;go-git&lt;/a&gt; comes to the rescue here. At first I tried to&#xA;implement Git-based reads alongside conventional filesystem reads, but&#xA;couldn&#39;t figure out how to make these two methods play nicely in the&#xA;same codebase. So I decided to throw out the latter, relying solely on&#xA;reading from a Git repo.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;allArticles&lt;/code&gt; function commandeers this logic. It reads all&#xA;articles from the blog repo. This is what it currently looks like:&lt;/p&gt;&#xA;&lt;pre style=&#34;color:#cad3f5;background-color:#24273a;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 1&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#ed8796&#34;&gt;func&lt;/span&gt; allArticles[F types.Frontmatter](repo &lt;span style=&#34;color:#ed8796&#34;&gt;string&lt;/span&gt;) ([]types.Article[F], &lt;span style=&#34;color:#ed8796&#34;&gt;error&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 2&lt;/span&gt;&lt;span&gt;&#x9;fs &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:=&lt;/span&gt; memfs.&lt;span style=&#34;color:#8aadf4&#34;&gt;New&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 3&lt;/span&gt;&lt;span&gt;&#x9;genre &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:=&lt;/span&gt; (&lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#91d7e3&#34;&gt;new&lt;/span&gt;(F)).&lt;span style=&#34;color:#8aadf4&#34;&gt;Genre&lt;/span&gt;()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 4&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 5&lt;/span&gt;&lt;span&gt;&#x9;_, err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:=&lt;/span&gt; git.&lt;span style=&#34;color:#8aadf4&#34;&gt;Clone&lt;/span&gt;(memory.&lt;span style=&#34;color:#8aadf4&#34;&gt;NewStorage&lt;/span&gt;(), fs, &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;&amp;amp;&lt;/span&gt;git.CloneOptions{&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 6&lt;/span&gt;&lt;span&gt;&#x9;&#x9;URL: repo,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 7&lt;/span&gt;&lt;span&gt;&#x9;})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 8&lt;/span&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 9&lt;/span&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt;, fmt.&lt;span style=&#34;color:#8aadf4&#34;&gt;Errorf&lt;/span&gt;(&lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;can&amp;#39;t clone repository %s: %w&amp;#34;&lt;/span&gt;, repo, err)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;10&lt;/span&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;11&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;12&lt;/span&gt;&lt;span&gt;&#x9;log.&lt;span style=&#34;color:#8aadf4&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;Successfully cloned repository %s&amp;#34;&lt;/span&gt;, repo)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;13&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;14&lt;/span&gt;&lt;span&gt;&#x9;entries, err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:=&lt;/span&gt; fs.&lt;span style=&#34;color:#8aadf4&#34;&gt;ReadDir&lt;/span&gt;(&lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;+&lt;/span&gt; genre)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;15&lt;/span&gt;&lt;span&gt;&#x9;If err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;16&lt;/span&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt;, err&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;17&lt;/span&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;18&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;19&lt;/span&gt;&lt;span&gt;&#x9;log.&lt;span style=&#34;color:#8aadf4&#34;&gt;Printf&lt;/span&gt;(&lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#34;Successfully fetched genre entries for &amp;#39;%s&amp;#39;&amp;#34;&lt;/span&gt;, genre)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;20&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;21&lt;/span&gt;&lt;span&gt;&#x9;articles, err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;:=&lt;/span&gt; entriesToArticles[F](fs, genre, entries)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;22&lt;/span&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;if&lt;/span&gt; err &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;23&lt;/span&gt;&lt;span&gt;&#x9;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt;, err&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;24&lt;/span&gt;&lt;span&gt;&#x9;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;25&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;26&lt;/span&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#c6a0f6&#34;&gt;return&lt;/span&gt; articles, &lt;span style=&#34;color:#f5a97f&#34;&gt;nil&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;27&lt;/span&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There are five pivotal steps that can be outlined here:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Create the in-memory filesystem: &lt;code&gt;fs := memfs.New()&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Clone the blog repo worktree into this filesystem:&#xA;&lt;code&gt;git.Clone(memory.NewStorage(), fs, &amp;amp;git.CloneOptions{...}&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Read the given &lt;em&gt;genre&lt;/em&gt; from within the in-memory worktree:&#xA;&lt;code&gt;entries, err := fs.ReadDir(&amp;quot;./&amp;quot; + genre)&lt;/code&gt;. I go into more detail&#xA;on genres in the Buildablog &lt;a href=&#34;https://github.com/BrandonIrizarry/buildablog/blob/main/README.md#frontmatter&#34;&gt;README&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Run some code that marshals each Markdown entry under the genre&#xA;folder into an article struct that later on gets used inside a Go&#xA;template: &lt;code&gt;articles, err := entriesToArticles[F](fs, genre, entries)&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Return these articles, along with an error, to the REST endpoint&#xA;handler call site.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h1&gt;Flexibility&lt;/h1&gt;&#xA;&lt;p&gt;The blog repo itself is configurable via the &lt;code&gt;BLOGDIR&lt;/code&gt; environment&#xA;variable. This name is a throwback from when it was using the local&#xA;filesystem directly; now, it can also be set to an &lt;code&gt;https&lt;/code&gt; remote&#xA;repo. On my VPS, I have it set to &lt;code&gt;/var/git/brandons_blog&lt;/code&gt;, which&#xA;indeed was my endgame all along; the only admin-type thing I had to do&#xA;was mark it as a safe repo using &lt;code&gt;git config&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now, the one drawback to all this is that I&#39;ve gotten used to seeing&#xA;immediate feedback once I edit my content. Now, I have to remember to&#xA;commit changes first when testing locally.&lt;/p&gt;&#xA;&lt;p&gt;Anyway, really good stuff.&lt;/p&gt;&#xA;</description>
        </item>
        <item>
            <title>Making Cgit Repos Installable as Go Packages </title>
            <link>https://brandonirizarry.xyz/posts/2026-03-07</link>
            <guid>https://brandonirizarry.xyz/posts/2026-03-07</guid>
            <pubDate>Sat, 07 Mar 2026 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;Putting my private Git server to good use&lt;/h1&gt;&#xA;&lt;p&gt;In my quest to learn the real ins and outs of Go by going through &lt;em&gt;The&#xA;Go Programming Language&lt;/em&gt;, I decided to use my newly minted private&#xA;repo &lt;a href=&#34;https://git.brandonirizarry.xyz&#34;&gt;hub&lt;/a&gt; to store exercises from the book as packages I can reuse&#xA;for later exercises. For example, I decided to make the lissajous&#xA;example from Chapter 1 into a separate installable &lt;a href=&#34;https://git.brandonirizarry.xyz/lissajous&#34;&gt;package&lt;/a&gt;. The&#xA;lissajous package is used to create a GIF of a &lt;a href=&#34;https://en.wikipedia.org/wiki/Lissajous_curve&#34;&gt;Lissajous curve&lt;/a&gt;,&#xA;which was a staple visual effect in old sci-fi movies.&lt;/p&gt;&#xA;&lt;p&gt;However, simply &amp;quot;go-getting&amp;quot; from the repo&#39;s URL, as you would in the&#xA;case of GitHub, isn&#39;t that simple. There were multiple hiccups along&#xA;the way which I had to overcome. What follows is my best attempt to&#xA;piece together what I did to finally enable Go package installation&#xA;from my private Git server. Hopefully this account will serve two&#xA;purposes:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Set people straight who are looking for answers to this same&#xA;question.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Serve as a reference for my future self in case I have to do this&#xA;again.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h1&gt;Steps&lt;/h1&gt;&#xA;&lt;h2&gt;Inject the appropriate HTML &lt;code&gt;meta&lt;/code&gt; tag from Nginx&lt;/h2&gt;&#xA;&lt;p&gt;This &lt;a href=&#34;https://anirudh.fi/go-get-cgit&#34;&gt;excellent&lt;/a&gt; blog post by Anirudh&#xA;Oppiliappan set me on the right path for fixing an error I encountered&#xA;early on involving a missing &lt;code&gt;go-import&lt;/code&gt; something-or-other:&lt;/p&gt;&#xA;&lt;p&gt;I ended up putting the &lt;code&gt;sub_filter&lt;/code&gt; stuff inside the &lt;code&gt;location @cgit&lt;/code&gt;&#xA;block:&lt;/p&gt;&#xA;&lt;pre style=&#34;color:#cad3f5;background-color:#24273a;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 1&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#c6a0f6&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;@cgit&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 2&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;fastcgi_params&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 3&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;/usr/lib/cgit/cgit.cgi&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 4&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;PATH_INFO&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$uri&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 5&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;QUERY_STRING&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$args&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 6&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;HTTP_HOST&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$server_name&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 7&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_pass&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;unix:/run/fcgiwrap.socket&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 8&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 9&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;# Make our repos go-gettable.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;10&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;sub_filter&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#39;&amp;lt;/head&amp;gt;&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;11&lt;/span&gt;&lt;span&gt;         &lt;span style=&#34;color:#a6da95&#34;&gt;&amp;#39;&amp;lt;meta&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;name=&amp;#34;go-import&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;content=&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f4dbd6&#34;&gt;$host$uri&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;git&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;https://&lt;/span&gt;&lt;span style=&#34;color:#f4dbd6&#34;&gt;$host$uri&amp;#34;&amp;gt;&amp;lt;/head&amp;gt;&amp;#39;&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;12&lt;/span&gt;&lt;span&gt;        &#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;13&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;sub_filter_once&lt;/span&gt; &lt;span style=&#34;color:#eed49f&#34;&gt;on&lt;/span&gt; ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;14&lt;/span&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Remembering the semicolons here is important. You can always use &lt;code&gt;sudo nginx -t&lt;/code&gt; to test whether your current config is valid.&lt;/p&gt;&#xA;&lt;h2&gt;Configure Git to use SSH for the Git server&lt;/h2&gt;&#xA;&lt;p&gt;Add this block to your home directory&#39;s &lt;code&gt;.gitconfig&lt;/code&gt; file, making the&#xA;appropriate substitution for &amp;quot;example.com&amp;quot;:&lt;/p&gt;&#xA;&lt;pre style=&#34;color:#cad3f5;background-color:#24273a;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;1&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#c6a0f6&#34;&gt;[url &amp;#34;git@example.com:&amp;#34;]&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;2&lt;/span&gt;&lt;span&gt;&#x9;&lt;span style=&#34;color:#8aadf4&#34;&gt;insteadOf&lt;/span&gt; &lt;span style=&#34;color:#91d7e3;font-weight:bold&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;https://git.example.com/&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Alternatively, you can issue the equivalent command line invocation:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;git config --global url.&amp;quot;git@example.com&amp;quot;.insteadOf &amp;quot;https://git.example.com/&amp;quot;&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Now, whenever you invoke &lt;code&gt;go get&lt;/code&gt;, you&#39;ll be prompted for your SSH&#xA;password.&lt;/p&gt;&#xA;&lt;p&gt;Note that, in this case, the &lt;code&gt;git&lt;/code&gt; in &lt;code&gt;git@example.com&lt;/code&gt; refers to the&#xA;Linux &lt;em&gt;user&lt;/em&gt; on your remote server that owns the directory containing&#xA;all your Git repos. This syntax mirrors the one used when logging into&#xA;the same server via SSH, viz. &lt;code&gt;ssh git@example.com&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h2&gt;Set GOPRIVATE (optional?)&lt;/h2&gt;&#xA;&lt;p&gt;I&#39;m not sure I need to set this in my case, since AFAICT this has more&#xA;to do with close-sourcing code, which isn&#39;t my intention here. But I&#xA;threw it in just in case.&lt;/p&gt;&#xA;&lt;p&gt;Since I want &lt;strong&gt;all&lt;/strong&gt; my repos to be potentially installable as Go&#xA;packages for now, so I use a glob to indicate that:&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;go env -w GOPRIVATE=git.brandonirizarry.xyz/*&lt;/code&gt;&lt;/p&gt;&#xA;&lt;p&gt;Initially, I had set &lt;code&gt;GOPRIVATE&lt;/code&gt; to point to the &lt;code&gt;lissajous&lt;/code&gt; repo&#xA;only, though this glob technique should work also (and be way easier&#xA;to maintain.)&lt;/p&gt;&#xA;</description>
        </item>
        <item>
            <title>Adding a CGit Subdomain To My Site</title>
            <link>https://brandonirizarry.xyz/posts/2026-03-06</link>
            <guid>https://brandonirizarry.xyz/posts/2026-03-06</guid>
            <pubDate>Fri, 06 Mar 2026 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;Motivation&lt;/h1&gt;&#xA;&lt;p&gt;To update the content of my blog, I have to do something of a&#xA;dance. My blog&#39;s content started out life as a subdirectory of my SSG&#xA;project, but now lives in its own separate Git repo. This is super&#xA;convenient since I now can host my blog wherever I want on my VPS&#39;s&#xA;filesystem, for example in a folder called &lt;code&gt;brandons_blog&lt;/code&gt;. So right&#xA;now what I&#39;m doing is this:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Commit all changes.&lt;/li&gt;&#xA;&lt;li&gt;Push the changes to GitHub, where it&#39;s currently hosted in a&#xA;somewhat centralized manner.&lt;/li&gt;&#xA;&lt;li&gt;Log in via SSH into my VPS.&lt;/li&gt;&#xA;&lt;li&gt;Perform a &lt;code&gt;cd&lt;/code&gt; into the &lt;code&gt;brandons_blog&lt;/code&gt; directory, and run &lt;code&gt;git pull&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;However, I thought, &amp;quot;wouldn&#39;t it be nice if I could push &lt;strong&gt;directly&lt;/strong&gt;&#xA;to the VPS repo?&amp;quot; And so I started working on that idea in the obvious&#xA;manner: add the VPS repo as a remote, such that I would be pushing to&#xA;&lt;code&gt;vps/main&lt;/code&gt; alongside of &lt;code&gt;origin/main&lt;/code&gt; (where &lt;code&gt;origin&lt;/code&gt; points to&#xA;GitHub).&lt;/p&gt;&#xA;&lt;p&gt;It turns out that this alone is a shade more complicated that it would&#xA;seem: I had to first add a new &lt;code&gt;git&lt;/code&gt; user (see &lt;a href=&#34;https://landchad.net/git/&#34;&gt;this tutorial&lt;/a&gt;),&#xA;and then adjust my SSH configuration appropriately to allow for this&#xA;pseudo-user to log in via SSH (since I would be pushing into a&#xA;directory now owned by it.)&lt;/p&gt;&#xA;&lt;p&gt;I quickly learned, to my dismay, that I couldn&#39;t push to the VPS&#xA;remote if it&#39;s not a bare repo. A bare repo is one initialized with&#xA;&lt;code&gt;git init --bare&lt;/code&gt;, so that it doesn&#39;t have a working directory&#xA;populated with files. However, the buildablog server expects to see a&#xA;working directory with blog files (not just blobs, for example), so&#xA;this doesn&#39;t solve my problem.&lt;/p&gt;&#xA;&lt;h1&gt;Cgit&lt;/h1&gt;&#xA;&lt;p&gt;What I ended up doing in the end didn&#39;t solve this problem, but it&#xA;ended up becoming an interesting rabbit hole in its own right.&lt;/p&gt;&#xA;&lt;p&gt;I ended up adding the Cgit web interface to my site, available via&#xA;&lt;a href=&#34;https://git.brandonirizarry.xyz&#34;&gt;https://git.brandonirizarry.xyz&lt;/a&gt;. I checked out a &lt;a href=&#34;https://landchad.net/cgit/&#34;&gt;tutorial&lt;/a&gt; on&#xA;how to do it, but their suggested Nginx setup was off in some&#xA;parts.&lt;/p&gt;&#xA;&lt;p&gt;After a ton of false starts, I ended up doing slightly different. Note&#xA;that this assumes that you&#39;ve already followed the aforementioned&#xA;tutorial on setting up your &lt;code&gt;git&lt;/code&gt; user and its home directory. As a&#xA;quick addon to that, I suggest adjusting the permissions for the&#xA;&lt;code&gt;/var/git&lt;/code&gt; directory to 775 (I had initially found they were set to&#xA;770.) This allows the Cgit web interface to actually read and display&#xA;your hosted repos, which is after all the point.&lt;/p&gt;&#xA;&lt;p&gt;Here&#39;s what I did. I&#39;m phrasing these in the imperative mood since it&#xA;reads better than beginning everything with &amp;quot;I&amp;quot; + past-tense, and the&#xA;steps themselves are also suitable as a potential HOWTO for my future&#xA;self:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Add two new &lt;em&gt;external records&lt;/em&gt; for &lt;code&gt;git.brandonirizarry.xyz&lt;/code&gt; to my&#xA;site&#39;s DNS configuration over on Epik: the A (IPv4) and AAAA (IPv6)&#xA;records, per usual if you&#39;re already somewhat familiar with this&#xA;thing.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Start out by adding the Nginx configuration of the &lt;code&gt;http&lt;/code&gt; version&#xA;of the site. Not only does this make adding the TLS certificate&#xA;later on painless, you can immediately verify that your new&#xA;subdomain is, in fact, being hosted. Add the following server block&#xA;to your published Nginx configuration, and then reload Nginx&#xA;(e.g. &lt;code&gt;sudo systemctl restart nginx.service&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;pre style=&#34;color:#cad3f5;background-color:#24273a;-webkit-text-size-adjust:none;&#34;&gt;&lt;code&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 1&lt;/span&gt;&lt;span&gt;&lt;span style=&#34;color:#c6a0f6&#34;&gt;server&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 2&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;listen&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;80&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 3&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;listen&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;[::]:80&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 4&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 5&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;# Replace this with your actual site.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 6&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;server_name&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;git.example.org&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 7&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 8&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;root&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;/usr/share/cgit&lt;/span&gt; ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt; 9&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;try_files&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$uri&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;@cgit&lt;/span&gt; ;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;10&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;11&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;location&lt;/span&gt; ~ &lt;span style=&#34;color:#8bd5ca&#34;&gt;/.+/(info/refs|git-upload-pack)&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;12&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;include&lt;/span&gt;             &lt;span style=&#34;color:#a6da95&#34;&gt;fastcgi_params&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;13&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt;       &lt;span style=&#34;color:#a6da95&#34;&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;/usr/lib/git-core/git-http-backend&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;14&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt;       &lt;span style=&#34;color:#a6da95&#34;&gt;PATH_INFO&lt;/span&gt;           &lt;span style=&#34;color:#f4dbd6&#34;&gt;$uri&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;15&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt;       &lt;span style=&#34;color:#a6da95&#34;&gt;GIT_HTTP_EXPORT_ALL&lt;/span&gt; &lt;span style=&#34;color:#f5a97f&#34;&gt;1&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;16&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;17&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#6e738d;font-style:italic&#34;&gt;# This part assumes your git user&amp;#39;s home directory is /var/git.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;18&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt;       &lt;span style=&#34;color:#a6da95&#34;&gt;GIT_PROJECT_ROOT&lt;/span&gt;    &lt;span style=&#34;color:#a6da95&#34;&gt;/var/git&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;19&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt;       &lt;span style=&#34;color:#a6da95&#34;&gt;HOME&lt;/span&gt;                &lt;span style=&#34;color:#a6da95&#34;&gt;/var/git&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;20&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_pass&lt;/span&gt;        &lt;span style=&#34;color:#a6da95&#34;&gt;unix:/run/fcgiwrap.socket&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;21&lt;/span&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;22&lt;/span&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;23&lt;/span&gt;&lt;span&gt;    &lt;span style=&#34;color:#8bd5ca&#34;&gt;location&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;@cgit&lt;/span&gt; {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;24&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;include&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;fastcgi_params&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;25&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;SCRIPT_FILENAME&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;/usr/lib/cgit/cgit.cgi&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;26&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;PATH_INFO&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$uri&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;27&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;QUERY_STRING&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$args&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;28&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_param&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;HTTP_HOST&lt;/span&gt; &lt;span style=&#34;color:#f4dbd6&#34;&gt;$server_name&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;29&lt;/span&gt;&lt;span&gt;        &lt;span style=&#34;color:#8bd5ca&#34;&gt;fastcgi_pass&lt;/span&gt; &lt;span style=&#34;color:#a6da95&#34;&gt;unix:/run/fcgiwrap.socket&lt;/span&gt;;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;30&lt;/span&gt;&lt;span&gt;    }&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span style=&#34;white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#8087a2&#34;&gt;31&lt;/span&gt;&lt;span&gt;}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;ol start=&#34;3&#34;&gt;&#xA;&lt;li&gt;Add a TLS certificate for your subdomain. I admit that I took a&#xA;somewhat nonlinear path in achieving my setup, but this should be&#xA;as simple as running &lt;code&gt;sudo certbot --nginx&lt;/code&gt;, and then selecting&#xA;your subdomain from the menu options. Here I&#39;m assuming you&#39;ve&#xA;already gotten a certificate for your main site, hence you need a&#xA;certificate only for your new subdomain.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;From there you shouldn&#39;t even have to restart Nginx: you should see&#xA;that your subdomain is available over &lt;code&gt;https&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h1&gt;Five Strikes and You&#39;re Out&lt;/h1&gt;&#xA;&lt;p&gt;I learned, through banging my head against various misconfigurations&#xA;(both from the DNS and Nginx sides), that Let&#39;s Encrypt (what Certbot&#xA;uses to issue the certificate) &lt;a href=&#34;https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-identifier-per-account&#34;&gt;imposes a rate limit&lt;/a&gt; on&#xA;certificate issues per identifier (five per hour), which doesn&#39;t&#xA;forgive botched attempts at certificate registration. The solution&#xA;here is to run Certbot with the &lt;code&gt;--test-cert&lt;/code&gt; flag, which uses Let&#39;s&#xA;Encrypt&#39;s staging area, which has a much more forgiving rate limit.&lt;/p&gt;&#xA;&lt;p&gt;In digging a bit through the Let&#39;s Encrypt &lt;a href=&#34;https://community.letsencrypt.org/&#34;&gt;forums&lt;/a&gt;, I learned&#xA;about two super helpful sites for debugging DNS and certificate&#xA;issues:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://letsdebug.net&#34;&gt;https://letsdebug.net&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;Super helpful for figuring out issues with bad certificates.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://dnsviz.net&#34;&gt;https://dnsviz.net&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;For debugging a site&#39;s DNS config, which I was messing up since I&#xA;wasn&#39;t sure in the beginning how to properly add a subdomain to my&#xA;DNS records (somewhat confusingly, Epik places the word&#xA;&amp;quot;subdomain&amp;quot; alongside the CNAME section, making me think CNAME had&#xA;something to do with it, which it doesn&#39;t.)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h1&gt;A Ghost in the Machine?&lt;/h1&gt;&#xA;&lt;p&gt;I finally managed to &lt;a href=&#34;https://git.brandonirizarry.xyz&#34;&gt;host&lt;/a&gt; my Cgit dashboard on my site, which&#xA;currently contains only my blog repo. I even managed to share the link&#xA;with a friend of mine, who was successfully able to view it from their&#xA;end.&lt;/p&gt;&#xA;&lt;p&gt;However, when going through some exercises in &lt;em&gt;The Go Programming&#xA;Language&lt;/em&gt; (a story for another time), I happened to cavalierly make a&#xA;GET request to that subdomain, which then reported a TLS error. In my&#xA;mind this seemed somewhat bonkers, since, after all, everything was&#xA;already up and running, no? So late that evening I had to jump back&#xA;onto the VPS and do some bespoke troubleshooting.&lt;/p&gt;&#xA;&lt;p&gt;It looked like there were some redundant server blocks in my Nginx&#xA;config file that were added while I was throwing everything and the&#xA;kitchen sink at getting a valid TLS certificate. So what I did in the&#xA;end was remove everything Certbot had added concerning my &lt;code&gt;git&lt;/code&gt;&#xA;subdomain, essentially reverting back to just the server block shown&#xA;just above, and repeating those exact steps — including first&#xA;verifying service over &lt;code&gt;http&lt;/code&gt;. This part of the process for me was the&#xA;most satisfying, since it proves that the mere act of publishing a&#xA;website on the Web is, at its core, not all that difficult. One thing&#xA;different this time though was that, per the options Cerbot presents&#xA;you, it sufficed to reinstall the existing certificate, as opposed to&#xA;applying for a new one.)&lt;/p&gt;&#xA;&lt;p&gt;After that, everything was in order! I even checked the site the next&#xA;morning just to make sure it had stayed that way. To date, everything&#xA;looks good.&lt;/p&gt;&#xA;&lt;h1&gt;In the End...&lt;/h1&gt;&#xA;&lt;p&gt;In the end, I didn&#39;t actually solve my initial problem, but still went&#xA;down an interesting rabbit hole, and now have a convenient tool at my&#xA;disposal — my own poor-man&#39;s GitHub — for personal use. For now, I may&#xA;well only use it for throwaway Go packages, in case I don&#39;t feel like&#xA;using workspaces.&lt;/p&gt;&#xA;</description>
        </item>
        <item>
            <title>Building My Own SSG</title>
            <link>https://brandonirizarry.xyz/projects/2026-03-01</link>
            <guid>https://brandonirizarry.xyz/projects/2026-03-01</guid>
            <pubDate>Sun, 01 Mar 2026 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;Motivation&lt;/h1&gt;&#xA;&lt;p&gt;While looking for options to host my blog, and having tried Hugo and&#xA;Eleventy, I decided to write my own SSG.&lt;/p&gt;&#xA;&lt;p&gt;I had completed the &lt;a href=&#34;https://www.boot.dev/courses/build-static-site-generator-python&#34;&gt;Build a Static Site Generator in Python&lt;/a&gt;&#xA;course on boot.dev, and so already had some inkling of what&#39;s involved&#xA;here. That course requires the learner to manually parse Markdown into&#xA;HTML; in my project (which uses Go for the backend), Markdown parsing&#xA;is forwarded over to the &lt;a href=&#34;https://github.com/adrg/frontmatter&#34;&gt;frontmatter&lt;/a&gt; and &lt;a href=&#34;https://github.com/yuin/goldmark&#34;&gt;goldmark&lt;/a&gt; libraries&#xA;(with help from the &lt;a href=&#34;https://github.com/yuin/goldmark-highlighting/v2&#34;&gt;goldmark-highlighting&lt;/a&gt; extension library for&#xA;prettifying code listings.)&lt;/p&gt;&#xA;&lt;p&gt;Basically, writing my own SSG from scratch felt like it was going to&#xA;be at most not that much harder than learning the ins and outs of&#xA;configuring an existing SSG like Hugo or Eleventy. It would also in&#xA;the end allow me to configure my site at the atomic level, so to&#xA;speak - I write and maintain the server endpoints, the HTML Go&#xA;templates, the CSS styling, you get the idea.&lt;/p&gt;&#xA;&lt;p&gt;Using Go templates can be daunting at first, but Section 4.6 of of&#xA;&lt;em&gt;The Go Programming Language&lt;/em&gt; — Text and HTML Templates — helps. Jon&#xA;Calhoun has a good &lt;a href=&#34;https://www.calhoun.io/intro-to-templates/&#34;&gt;mini course&lt;/a&gt; on templates, which I had already&#xA;completed in its entirety when I needed knowledge of templates for a&#xA;previous project I completed, called &lt;a href=&#34;https://github.com/BrandonIrizarry/Juices&#34;&gt;Juices&lt;/a&gt;. Another thing I&#xA;learned while working on my SSG is that template parsing should not be&#xA;done when serving an endpoint, since this creates a significant&#xA;performance bottleneck.&lt;/p&gt;&#xA;&lt;p&gt;Generics also came in handy when I wanted to reuse the exact same code&#xA;for differing frontmatter layouts (e.g., project posts like this one,&#xA;versus ordinary blog posts.)&lt;/p&gt;&#xA;&lt;h1&gt;RSS&lt;/h1&gt;&#xA;&lt;p&gt;Setting up RSS for my site (once you know the tricks) turns out to be&#xA;surprisingly straightforward, especially with Go&#39;s ability to marshal&#xA;all things structured (XML, TOML, JSON, etc.) to and from itself. At&#xA;one point I was torn between using either Atom or RSS for my feed,&#xA;since the former is &lt;a href=&#34;https://nullprogram.com/blog/2013/09/23/&#34;&gt;touted&lt;/a&gt; as strictly superior to the latter; in&#xA;the end I decided that RSS is good enough to get things started for&#xA;now.&lt;/p&gt;&#xA;&lt;p&gt;I did face a last-minute curveball when I realized I had to integrate&#xA;the &lt;em&gt;projects&lt;/em&gt; listing to the RSS feed. To this end, I needed a&#xA;reliable way to sort RSS item fields by date, which necessitated some&#xA;beefing up of the rss package itself.&lt;/p&gt;&#xA;&lt;p&gt;Two notable resources for learning about interfacing RSS with Go code&#xA;are:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.wikihow.com/Create-an-RSS-Feed&#34;&gt;Create an RSS Feed (WikiHow)&lt;/a&gt;&lt;br&gt;&#xA;Super helpful for getting the details of the layout right.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=b2E1JpC38Pg&#34;&gt;Build Your Own RSS Feed Generator in Go (YouTube)&lt;/a&gt;&lt;br&gt;&#xA;Helpful for understanding the gory details of what the acutal&#xA;marshalling logic looks like.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;With regard to RSS, I feel &lt;em&gt;all&lt;/em&gt; blogs should have an RSS/Atom&#xA;feed. There are some fantastic ones out there that don&#39;t!&lt;/p&gt;&#xA;&lt;p&gt;When setting up RSS, I finally grokked something while reading a &lt;a href=&#34;https://pluralistic.net/2024/10/16/keep-it-really-simple-stupid/&#34;&gt;blog&#xA;post&lt;/a&gt; by Cory Doctorow where he advocates for RSS:&lt;/p&gt;&#xA;&lt;p&gt;I realized that &lt;strong&gt;RSS is a genre of social media.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h1&gt;Sources of Inspiration&lt;/h1&gt;&#xA;&lt;p&gt;I took (and continue to take) inspiration from blogs I&#39;ve seen in the&#xA;wild, such as:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://maurycyz.com/&#34;&gt;https://maurycyz.com/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;I ripped off a lot of CSS (and general design decisions) from this&#xA;site. 😁 The great thing about this site is that it proves that you&#xA;can be both a highly intelligent and original thinker without being&#xA;a techie conformist or all-around hipster with regard to things&#xA;like site aesthetic, opinions, and so on. Such concerns create&#xA;performance anxiety which get in the way of you actually making&#xA;something &lt;em&gt;good&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;As a shout out, the author even gives some quick lessons on setting&#xA;up a blog and writing the HTML and CSS for a simple website (which&#xA;I also took pointers from! This guy probably did a better job of&#xA;explaining what &lt;code&gt;width: 100%;&lt;/code&gt; means than a lot of resources I&#39;ve&#xA;seen online.)&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://nullprogram.com/&#34;&gt;https://nullprogram.com/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;For pointers on how to establish a convention for blog post&#xA;slugs. This author uses the date-based scheme, which I adopted in&#xA;the end.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://elly.town/&#34;&gt;https://elly.town/&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;For general compactness of aesthetic. While I don&#39;t plan on going&#xA;that route, the nod to S-expression syntax is nice.&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;</description>
        </item>
        <item>
            <title>Smoothing Over More Markdown Pain Points</title>
            <link>https://brandonirizarry.xyz/posts/2025-12-05</link>
            <guid>https://brandonirizarry.xyz/posts/2025-12-05</guid>
            <pubDate>Fri, 05 Dec 2025 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;I Couldn&#39;t Keep It Together&lt;/h1&gt;&#xA;&lt;p&gt;As I go about editing these blogs as Markdown buffers inside Emacs,&#xA;I&#39;ve been running into a snag of sorts. Previously, I had been&#xA;exporting Org to Markdown one way or another. I observed how the&#xA;Markdown output inserts an anchor tag above a given section as a way&#xA;to link to it from the table of contents. I decided to continue this&#xA;practice in my now hand-wrought Markdown. However, manually keeping&#xA;the table of contents in sync with changes in the outlining of the&#xA;content itself—adding and removing sections, renaming sections, and so&#xA;on—is a pain.  And so I came up with a way to sync the two, using&#xA;Emacs Lisp. Emacs Lisp, or Elisp for short, is the Emacs editor&#39;s&#xA;extension language: the language you use to write Emacs plugins.&lt;/p&gt;&#xA;&lt;h1&gt;Elisp For The Win&lt;/h1&gt;&#xA;&lt;p&gt;&lt;a href=&#34;2025-12-03&#34;&gt;Having written&lt;/a&gt; about my zany Elisp-based Java build system made&#xA;me recall those times: I could once again rise to the challenge, and&#xA;solve this new problem with Elisp. That&#39;s exactly what I did. I wrote&#xA;two functions, &lt;code&gt;bcimd-generate-toc&lt;/code&gt; and &lt;code&gt;bcimd-remove-toc&lt;/code&gt;. The first&#xA;one regenerates the table of contents based on the current set of&#xA;level-1 headings. The second one erases the existing table of&#xA;contents, along with the connected anchor tags. It&#39;s used by the first&#xA;function to start out with a clean slate before defining the new table&#xA;of contents.&lt;/p&gt;&#xA;&lt;p&gt;I decided to collect these functions into an installable package. It&#39;s&#xA;currently available through Emacs&#39; version-control installation&#xA;mechanisms (for example, &lt;code&gt;package-vc-install&lt;/code&gt;.) See the &lt;a href=&#34;https://github.com/BrandonIrizarry/bcimd&#34;&gt;project&#xA;README&lt;/a&gt; for more details.&lt;/p&gt;&#xA;&lt;p&gt;I find Emacs&#39; VC-based package installation facilities extremely&#xA;convenient for writing my own bespoke stuff which I otherwise have to&#xA;manage locally. I store it remotely, and install it as an &lt;em&gt;official&lt;/em&gt;&#xA;package, much like how Go packages work. In this way, I can even share&#xA;my work with the community.&lt;/p&gt;&#xA;&lt;h1&gt;Yet Another Yasnippet Testimonial&lt;/h1&gt;&#xA;&lt;p&gt;I also decided to go the extra mile and use a &lt;a href=&#34;Yasnippet&#34;&gt;Yasnippet&lt;/a&gt; snippet&#xA;that generates some stock front matter. In particular, the title of a&#xA;given blog post is ripped directly from the name of the file itself,&#xA;which first undergoes some on-the-fly formatting. I got this idea from&#xA;&lt;a href=&#34;https://weblog.masukomi.org/2024/07/19/using-org-mode-with-hugo/&#34;&gt;another blog&lt;/a&gt; where the author runs with the whole Yasnippet idea&#xA;to set up her &lt;code&gt;ox-hugo&lt;/code&gt; front matter. In fact, this is what turned me&#xA;on to the idea of Yasnippet as a useful tool in general; that is, it&#xA;isn&#39;t just a lazy man&#39;s way of inserting a for-loop into source code.&lt;/p&gt;&#xA;&lt;h1&gt;Now I Can Keep It Together!&lt;/h1&gt;&#xA;&lt;p&gt;I now use table-of-contents regeneration frequently: writing the&#xA;package was a worthwhile investment of time.The only minor hiccup is&#xA;that I have to remember to leave two spaces in between headers, so&#xA;that the anchor tag doesn&#39;t eliminate all whitespace between sections,&#xA;an effect which looks aesthetically jarring. I may address this in the&#xA;future, but I first need to see how this package interacts with, for&#xA;example, level-2 headers. Other ideas include running&#xA;table-of-contents generation as an &lt;code&gt;after-save-hook&lt;/code&gt;, and eventually&#xA;writing a full-blown minor-mode. But for now, I&#39;m taking it easy on&#xA;this project: I still have to work on other things.&lt;/p&gt;&#xA;</description>
        </item>
        <item>
            <title>Writing My Blog With Eleventy</title>
            <link>https://brandonirizarry.xyz/posts/2025-12-03</link>
            <guid>https://brandonirizarry.xyz/posts/2025-12-03</guid>
            <pubDate>Wed, 03 Dec 2025 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;Introduction&lt;/h1&gt;&#xA;&lt;p&gt;This is &lt;em&gt;at least&lt;/em&gt; my third time trying to start a blog.&lt;/p&gt;&#xA;&lt;p&gt;First, I experimented with using Org Mode&#39;s HTML exporting feature to&#xA;create posts; unfortunately, that didn&#39;t get me far, though there are&#xA;some &lt;a href=&#34;https://one.tonyaldon.com/&#34;&gt;interesting attempts&lt;/a&gt; by others to this end. I might&#39;ve&#xA;published this material at some point, but at any rate it didn&#39;t stay&#xA;up long. An early topic from this time include a post about a &lt;a href=&#34;https://github.com/BrandonIrizarry/Hydraulic-Make&#34;&gt;Java&#xA;build system&lt;/a&gt; I once wrote that scanned a &lt;code&gt;.java&lt;/code&gt; file for its&#xA;dependencies (defined by things like package imports and code syntax),&#xA;so that those would get passed into &lt;code&gt;javac&lt;/code&gt; along with the target&#xA;file.&lt;/p&gt;&#xA;&lt;h1&gt;Hugo&lt;/h1&gt;&#xA;&lt;p&gt;I then started writing a blog using Hugo. Hugo was my first encounter&#xA;with an SSG. Because of this, I was a bit impatient with Hugo, and hit&#xA;a wall every time I came across any sort of complexity. I also got&#xA;frustrated with how themes never follow a consistent template; each&#xA;does something different, with different elements, and so each one&#xA;effectively has different rules. In the end, I published a blog post&#xA;or two on GitHub pages using this setup. It was passable, but in the&#xA;end configuring it still felt wonky and cargo-culted.&lt;/p&gt;&#xA;&lt;p&gt;Another reason for why I didn&#39;t have success with Hugo was my use of&#xA;&lt;code&gt;ox-hugo&lt;/code&gt;. It&#39;s a fun package, and you can tell the author put a &lt;em&gt;lot&lt;/em&gt;&#xA;of love into it. However, using Org Mode as a middleman between you&#xA;and Hugo obfuscates the nature of Hugo, something I&#39;m realizing now as&#xA;I go deeper into using Eleventy.&lt;/p&gt;&#xA;&lt;h1&gt;Eleventy: The Soup Actually Tastes Good&lt;/h1&gt;&#xA;&lt;p&gt;I went ahead and did a little bit of &amp;quot;shopping&amp;quot; for SSGs. I ran into&#xA;&lt;a href=&#34;https://www.11ty.dev&#34;&gt;Eleventy&lt;/a&gt;. I watched the author&#39;s &lt;a href=&#34;https://www.youtube.com/watch?v=kzf9A9tkkl4&#34;&gt;intro video&lt;/a&gt;, and&#xA;immediately took a liking to it. After a few false starts, I cloned&#xA;their &lt;a href=&#34;https://github.com/11ty/eleventy-base-blog&#34;&gt;official starter project&lt;/a&gt;, tweaked it here and there, and&#xA;the rest is what you&#39;re currently looking at.&lt;/p&gt;&#xA;&lt;p&gt;A huge shift in my thinking which made the leap from Hugo to Eleventy&#xA;possible occurred when I learned to stop worrying and love the&#xA;Markdown.&lt;/p&gt;&#xA;&lt;p&gt;I used to think of Markdown as an icky, second-rate version of Org&#xA;Mode. Then, I eventually got the hang of writing Markdown using Emacs&#39;&#xA;&lt;code&gt;markdown-mode&lt;/code&gt; package, which is a &lt;a href=&#34;https://jblevins.org/projects/markdown-mode/&#34;&gt;masterpiece&lt;/a&gt; of a plugin: it&#xA;makes the experience of writing Markdown rival that of using Org, and&#xA;smoothes out a lot of Markdown&#39;s pain points (significant whitespace,&#xA;noisy links, etc.) And so I slowly let go of the attachment of using&#xA;Org Mode in all the things, and embraced the idea of writing blog&#xA;posts directly in Markdown; this also alleviated the complexity of&#xA;sundry issues arising from exporting from Org to Markdown.&lt;/p&gt;&#xA;&lt;p&gt;At first, Eleventy looks like a deceptively complex pile of language&#xA;soup: JS, Markdown, templating languages, HTML, and CSS—at times all&#xA;occurring within the same file—all somehow live under one&#xA;roof. However, tweaking the starter project ended up being a&#xA;relatively easy, even pleasant experience.&lt;/p&gt;&#xA;&lt;h1&gt;Painless Deployment&lt;/h1&gt;&#xA;&lt;p&gt;Even deployment is simple. This site&#39;s content is version-controlled&#xA;locally. I then build the site, then simply &lt;code&gt;scp&lt;/code&gt; the &lt;code&gt;_site&lt;/code&gt;&#xA;directory to the appropriate directory in my VPS, where this blog is&#xA;hosted. The previous remote &lt;code&gt;_site&lt;/code&gt; directory is simply overwritten&#xA;with the new files. I don&#39;t need a GitHub workflow, as I did when&#xA;using Hugo with GitHub pages; I don&#39;t even need to push to a remote&#xA;repo. Copying the files suffices.&lt;/p&gt;&#xA;&lt;h1&gt;Conclusion&lt;/h1&gt;&#xA;&lt;p&gt;On the one hand, I&#39;m nowhere near able to make something like the&#xA;starter project from scratch. On the other hand, neither am I&#xA;daunted. Eleventy in a sense reminds me of Emacs, in that there&#39;s a&#xA;certain joy to be found in its eclectic complexity. I look forward to&#xA;continue using Eleventy as I grow this blog.&lt;/p&gt;&#xA;</description>
        </item>
        <item>
            <title>Understanding Pratt Parsing</title>
            <link>https://brandonirizarry.xyz/posts/2025-12-02</link>
            <guid>https://brandonirizarry.xyz/posts/2025-12-02</guid>
            <pubDate>Tue, 02 Dec 2025 00:00:00 UTC</pubDate>
            <description type="html">&lt;h1&gt;Introduction&lt;/h1&gt;&#xA;&lt;p&gt;I&#39;ve forgotten how I came across Pratt parsing specifically. I had&#xA;been working on an interpreter for a programming language based on the&#xA;one loosely described in Greg Michaelson&#39;s &lt;em&gt;An Introduction to&#xA;Functional Programming Through Lambda Calculus&lt;/em&gt;. I had managed to&#xA;implement simple arithmetic, and even extended the basic lambda&#xA;calculus spec with assignment expressions (a feat which I was very&#xA;proud of.)&lt;/p&gt;&#xA;&lt;p&gt;However, some parts of my implementation felt a bit hacky (for&#xA;example, how I had implemented &lt;code&gt;letrec&lt;/code&gt;), and my implementation of&#xA;lazy evaluation, while mostly complete, ultimately turned out to be&#xA;buggy.&lt;/p&gt;&#xA;&lt;p&gt;At first, I decided to rewrite the project from scratch. One major&#xA;guiding factor was to narrow the scope of the project, borrowing some&#xA;advice from &lt;a href=&#34;https://learncodethehardway.com/blog/32-very-deep-not-boring-beginner-projects/&#34;&gt;Zed Shaw&lt;/a&gt;. I got around to writing a new tokenizer,&#xA;and then I was on to writing the parser. My initial parser used the&#xA;recursive descent technique, taking advantage of the simplified lambda&#xA;calculus grammar used in Michaelson&#39;s text (for example, parentheses&#xA;are always used for application terms there.)&lt;/p&gt;&#xA;&lt;p&gt;This time though, I wanted to try something different. And so,&#xA;rummaging through the internets, I stumbled across Pratt parsing.&lt;/p&gt;&#xA;&lt;h1&gt;&amp;quot;It&#39;s like a burrito&amp;quot;&lt;/h1&gt;&#xA;&lt;p&gt;Understanding Pratt parsing ended up being much harder than I&#xA;expected. I ended up searching through a bunch of examples online:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html&#34;&gt;Alex Kladov&#39;s&lt;/a&gt; Rust-based tutorial.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing&#34;&gt;Eli Bendersky&#39;s&lt;/a&gt; Python-based tutorial.&lt;/li&gt;&#xA;&lt;li&gt;Bob Nystrom&#39;s &lt;a href=&#34;https://journal.stuffwithstuff.com/2011/03/19/pratt-parsers-expression-parsing-made-easy/&#34;&gt;intro&lt;/a&gt; to the subject, as well as the relevant&#xA;chapter in his &lt;a href=&#34;https://craftinginterpreters.com/compiling-expressions.html&#34;&gt;Crafting Interpreters&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Vaughan Pratt&#39;s &lt;a href=&#34;https://tdop.github.io/&#34;&gt;original paper&lt;/a&gt;. Shout-out to the legend who&#xA;put this up as a GitHub Pages site!&lt;/li&gt;&#xA;&lt;li&gt;Douglas Crockford&#39;s celebrated &lt;a href=&#34;https://crockford.com/javascript/tdop/tdop.html&#34;&gt;article&lt;/a&gt; on the subject deserves&#xA;honorable mention, though my JavaScript is currently rusty and so I&#xA;didn&#39;t look into it in any depth.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Alex Kladov in his post calls Pratt parsing the &amp;quot;monad tutorial of&#xA;syntactic analysis&amp;quot;. As I had recently become familiar with the&#xA;concept of &amp;quot;monad&amp;quot; in a &lt;a href=&#34;https://en.wikipedia.org/wiki/Monad_(philosophy)&#34;&gt;philosophical&lt;/a&gt; sense, I aksed ChatGPT&#xA;where the reference comes from: it turns out that it&#39;s an inside joke&#xA;involving &lt;em&gt;Haskell&lt;/em&gt; monads. I&#39;m not an expert, but my readings have&#xA;given me enough of an inkling to see the connection: Pratt parsing&#xA;isn&#39;t a discrete &amp;quot;thing&amp;quot; with exactly one shape: it&#39;s more of a&#xA;technique, if you will—a design pattern—which can assume various&#xA;manifestations.&lt;/p&gt;&#xA;&lt;p&gt;A good example of this is iteration, because we all recognize it when&#xA;we see it, even when we don&#39;t know the language—but no one syntactic&#xA;construction sufficiently defines it. For-loops, while-loops, Python&#xA;generator functions, and &lt;a href=&#34;https://mitp-content-server.mit.edu/books/content/sectbyfn/books_pres_0/6515/sicp.zip/full-text/book/book-Z-H-11.html#%25_sec_1.2.1&#34;&gt;optimized tail-recursive functions&lt;/a&gt; all&#xA;count as iteration.&lt;/p&gt;&#xA;&lt;p&gt;After struggling for some time, I finally managed to distill the&#xA;essence of the algorithm, which I present here as pseudocode:&lt;/p&gt;&#xA;&lt;pre&gt;&lt;code&gt;parse(level):&#xA;    t ← next(stream)&#xA;    acc ← nud_dispatch(t)&#xA;&#xA;    while level &amp;lt; precedence(peek(stream)):&#xA;        t ← next(stream)&#xA;        acc ← led_dispatch(t, acc)&#xA;&#xA;    return acc&#xA;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Unwrapping what this does exactly is a surprisingly nuanced task,&#xA;precisely because the algorithm is more about technique than&#xA;structure. Because of this, I won&#39;t pretend to be up to the task&#xA;here. Nevertheless, a brief synopsis is warranted.&lt;/p&gt;&#xA;&lt;p&gt;There is a global &lt;code&gt;stream&lt;/code&gt; of tokens, such that &lt;code&gt;next(stream)&lt;/code&gt;&#xA;consumes and returns the next token, and &lt;code&gt;peek(stream)&lt;/code&gt; returns the&#xA;next token without consuming it.  The function &lt;code&gt;nud_dispatch&lt;/code&gt;&#xA;interprets &lt;code&gt;t&lt;/code&gt; as a &lt;em&gt;null denotation&lt;/em&gt;, or &amp;quot;nud&amp;quot; for short, and&#xA;initializes &lt;code&gt;acc&lt;/code&gt;. The function &lt;code&gt;led_dispatch&lt;/code&gt; interprets &lt;code&gt;t&lt;/code&gt; as a&#xA;&lt;em&gt;left denotation&lt;/em&gt;, or &amp;quot;led&amp;quot; for short. It accumulates a value into&#xA;&lt;code&gt;acc&lt;/code&gt; using the existing value of &lt;code&gt;acc&lt;/code&gt; (hence the choice of name.)&#xA;Both dispatch functions call &lt;code&gt;parse&lt;/code&gt; recursively in all but the most&#xA;trivial cases.&lt;/p&gt;&#xA;&lt;p&gt;When &lt;code&gt;peek&lt;/code&gt;ing the stream reveals a token with higher precedence than&#xA;the current &lt;code&gt;level&lt;/code&gt;, the while loop exits and &lt;code&gt;acc&lt;/code&gt; is returned.&lt;/p&gt;&#xA;&lt;p&gt;The algorithm is initialized by calling &lt;code&gt;parse(0)&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h1&gt;Down To Brass Tacks&lt;/h1&gt;&#xA;&lt;p&gt;My approach was to take Eli Bendersky&#39;s full source code at the bottom&#xA;of his post, and start chiseling away at it. What I ended up with was&#xA;the same simple arithmetic calculator, only with a different&#xA;architecture: I moved away from Eli&#39;s object-oriented approach towards&#xA;something closer to the formulation given in the previous section.&lt;/p&gt;&#xA;&lt;p&gt;In the end, I was amazed at how simple and robust the actual&#xA;implementation turned out to be! I feel that what I came up with (at&#xA;this stage, anyway) is arguably simpler than even many of the examples&#xA;I initially came across: for example, it isn&#39;t necessary to add space&#xA;between precedence levels (10, 20, etc.), since you can use an enum to&#xA;take care of any ordering needed. Also, using even and odd precedence&#xA;levels (for handling right associativity) is unnecessary. For example,&#xA;say you have precedence levels &lt;code&gt;MULTIPLICATION = 2&lt;/code&gt; and&#xA;&lt;code&gt;EXPONENTIATION = 3&lt;/code&gt;. The algorithm cleverly avoids clashing&#xA;&lt;code&gt;EXPONENTIATION-1&lt;/code&gt; with &lt;code&gt;MULTIPLICATION&lt;/code&gt; when enforcing right&#xA;associativity for exponentiation. I found this to be one of the more&#xA;remarkable aspects of the algorithm.&lt;/p&gt;&#xA;&lt;h1&gt;Wanting More&lt;/h1&gt;&#xA;&lt;p&gt;To be fair, my calculator app technically doesn&#39;t parse arithmetic&#xA;expressions: it evaluates them wholesale. This is OK: instead of&#xA;accumulating an AST, I&#39;m accumulating an arithmetic result.&lt;/p&gt;&#xA;&lt;p&gt;Because of how compelling the calculator app turned out to be, I&#xA;decided to stop work on the lambda calculus project, and instead work&#xA;on expanding the calculator into a full-blown programming language,&#xA;albeit a simple one. I&#39;ve already made progress in this direction: in&#xA;addition to arithmetic (including trig functions!), the application&#xA;currently supports variable assignment.&lt;/p&gt;&#xA;&lt;p&gt;Ideally, I&#39;d like something with&#xA;&lt;!-- raw HTML omitted --&gt;&lt;!-- raw HTML omitted --&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Booleans&lt;/li&gt;&#xA;&lt;li&gt;Conditionals&lt;/li&gt;&#xA;&lt;li&gt;Loops&lt;/li&gt;&#xA;&lt;li&gt;Functions&lt;/li&gt;&#xA;&lt;li&gt;Proper lexical scoping, even for conditional and loop blocks&#xA;&lt;!-- raw HTML omitted --&gt;&lt;!-- raw HTML omitted --&gt;&#xA;I&#39;ll see how many of these I manage.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item>
    </channel>
</rss>