<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>AI on Erik Failing</title><link>https://erikfailing.com/tags/ai/</link><description>Recent content in AI on Erik Failing</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 31 Oct 2026 11:01:45 -0500</lastBuildDate><atom:link href="https://erikfailing.com/tags/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>How I Built This Site</title><link>https://erikfailing.com/posts/how-i-built-this-site/</link><pubDate>Fri, 30 Oct 2026 17:45:00 -0500</pubDate><guid>https://erikfailing.com/posts/how-i-built-this-site/</guid><description>Hugo, PaperMod, GitHub Actions and a custom domain, with an LLM helping along the way.</description><content:encoded><![CDATA[<p>This site is a static <span class="accent accent-hugo">Hugo</span> site using the PaperMod theme, built and deployed by <span class="accent accent-flow">GitHub Actions</span> to
GitHub Pages, with my Squarespace domain pointed at it. Below is how the pieces fit together, the
design decisions I made along the way, and how I used an LLM to help (and where I had to catch its
mistakes).</p>
<h2 id="the-setup-in-one-picture">The Setup In One Picture</h2>
<p>The whole thing is pretty simple once you see the flow:</p>
<ol>
<li>I write Markdown and push to <code>main</code>.</li>
<li>A GitHub Actions workflow installs Hugo, builds the site, and uploads the result.</li>
<li>GitHub Pages serves it, and my domain points at GitHub Pages.</li>
</ol>
<p>There&rsquo;s no server for me to patch, no database, and no hosting bill. For a personal site I think
that&rsquo;s the right amount of infrastructure: <span class="accent accent-point">the attack surface is basically a folder of HTML files</span>.</p>
<h2 id="why-hugo-and-why-papermod">Why Hugo, And Why PaperMod</h2>
<p>I went with Hugo because it&rsquo;s a <span class="accent accent-iron">single binary</span>, it builds fast (this site currently builds in under
100 ms locally), and the output is plain static files. No Node toolchain to keep updated, which I
was honestly happy to skip.</p>
<p>For the theme, the first plan an LLM helped me brainstorm was actually <span class="accent accent-flood">Blowfish</span>. It looks great,
but when I compared them, PaperMod won for a couple of practical reasons:</p>
<ul>
<li><strong>Readability.</strong> PaperMod&rsquo;s article page is 18px text in a single ~720px column, with a lot less
visual chrome. People who read writeups are the main audience, so <span class="accent accent-point">the article page is what has
to be good</span>.</li>
<li><strong>Build compatibility.</strong> Blowfish needs Hugo&rsquo;s extended edition and capped its supported version
below what the official GitHub Pages workflow installs. PaperMod just works with standard Hugo.</li>
</ul>
<p>The theme is pulled in as a <span class="accent accent-git">Git submodule</span>, so the repo only stores a pointer to one specific
commit of PaperMod instead of a copy of its files:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">git submodule add --depth<span class="o">=</span><span class="m">1</span> https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
</span></span></code></pre></div><p>Updating it is a deliberate step (<code>git submodule update --remote</code>, check locally, commit), so a
theme change <span class="accent accent-ward">never lands on the live site by surprise</span>. I also never edit the theme&rsquo;s files. Every
customization lives in my own <code>layouts/</code> and <code>assets/</code> folders, which Hugo uses in place of the
theme&rsquo;s versions.</p>
<h2 id="build-and-deploy-with-github-actions">Build And Deploy With GitHub Actions</h2>
<p>The workflow is <span class="accent accent-flow">Hugo&rsquo;s official GitHub Pages workflow</span>, with two small changes. I pulled it from
Hugo&rsquo;s docs repo directly and diffed my copy against it, so I know exactly what&rsquo;s different:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">HUGO_VERSION</span><span class="p">:</span><span class="w"> </span><span class="m">0.169.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">TZ</span><span class="p">:</span><span class="w"> </span><span class="l">America/Chicago            </span><span class="w"> </span><span class="c"># change 1: my time zone, for dates Hugo prints</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="c"># ...</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload artifact</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-pages-artifact@v5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">include-hidden-files</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">   </span><span class="c"># change 2: publish /.well-known/security.txt</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span><span class="w">
</span></span></span></code></pre></div><p>The second one was a fun one to find. The upload step <span class="accent accent-glitch accent-grad">skips anything starting with a dot</span> by
default, which is a sensible default, but it means <code>/.well-known/</code> never gets published. More on
why I wanted that folder below.</p>
<p>A few things I like about this setup:</p>
<ul>
<li>The <span class="accent accent-flow">build job</span> only gets <code>contents: read</code>, <code>pages: write</code> and <code>id-token: write</code>. It can&rsquo;t push
to the repo.</li>
<li>If the build fails, nothing deploys, so <span class="accent accent-point">the live site stays on the last good version</span>.</li>
<li>The base URL comes from the repo&rsquo;s Pages settings at build time, so the same workflow worked at
<code>erikfailing.github.io</code> first and then at <code>erikfailing.com</code> <span class="accent accent-trophy">with zero changes</span>.</li>
</ul>
<h2 id="pointing-the-domain-at-it">Pointing The Domain At It</h2>
<p>My domain is registered through <span class="accent accent-iron">Squarespace</span>, so this was mostly clicking through their DNS panel
in the right order:</p>
<ol>
<li><strong><span class="accent accent-point">Verify the domain with GitHub first.</span></strong> GitHub gives you a TXT record
(<code>_github-pages-challenge-&lt;username&gt;</code>), and once it&rsquo;s verified, only my account can publish a
Pages site on that domain. This prevents <span class="accent accent-crimson">domain takeover</span> if the repo ever gets deleted or Pages
gets turned off while DNS still points at GitHub.</li>
<li><strong>Delete Squarespace&rsquo;s default records</strong> (their parking page A records and <code>www</code> CNAME).</li>
<li><strong>Add GitHub&rsquo;s records:</strong> four <code>A</code> records and four <code>AAAA</code> records for the apex, plus a <code>CNAME</code>
for <code>www</code> pointing to <code>erikfailing.github.io</code>.</li>
<li><strong>Set the custom domain in the repo and turn on <span class="accent accent-ward">Enforce HTTPS</span></strong> once GitHub finished issuing the
certificate.</li>
</ol>
<p>No <span class="accent accent-crimson">wildcard records</span>, since a wildcard is an easy way to end up with a subdomain someone else can
claim.</p>
<h2 id="a-few-security-flavored-touches">A Few Security-Flavored Touches</h2>
<p>It&rsquo;s a personal site, so I didn&rsquo;t go overboard, but there were a few things I wanted as a
security engineer:</p>
<ul>
<li><strong><code>security.txt</code>.</strong> There&rsquo;s an RFC 9116 file at <code>/.well-known/security.txt</code> that points to the
repo&rsquo;s <span class="accent accent-ward">private vulnerability reporting</span>, plus a short <code>SECURITY.md</code> policy. It has an <code>Expires</code>
field, so it&rsquo;s a once-a-year chore to bump the date.</li>
<li><strong>No third-party requests for fonts.</strong> I self-host the font instead of loading it from Google
Fonts, so loading a page <span class="accent accent-ward">doesn&rsquo;t send visitors&rsquo; IPs anywhere else</span>.</li>
<li><strong><code>robots.txt</code> that blocks <span class="accent accent-arcane">AI training but not AI search</span>.</strong> It turns out the big AI companies
use separate crawler names for training and for search (e.g. <code>GPTBot</code> vs <code>OAI-SearchBot</code>,
<code>ClaudeBot</code> vs <code>Claude-SearchBot</code>). So I asked the training crawlers to stay out while still
letting search engines and AI search assistants read and cite the site:</li>
</ul>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">User-agent: GPTBot
</span></span><span class="line"><span class="cl">User-agent: ClaudeBot
</span></span><span class="line"><span class="cl">User-agent: Google-Extended
</span></span><span class="line"><span class="cl">User-agent: Applebot-Extended
</span></span><span class="line"><span class="cl">User-agent: Meta-ExternalAgent
</span></span><span class="line"><span class="cl">User-agent: CCBot
</span></span><span class="line"><span class="cl">User-agent: Bytespider
</span></span><span class="line"><span class="cl">Disallow: /
</span></span></code></pre></div><p>(robots.txt is a request, not access control, but the reputable crawlers do honor it.)</p>
<p><strong>Update (October 31, 2026):</strong> I took these rules back out. They only stop crawlers polite enough
to ask, so they weren&rsquo;t keeping out anyone I&rsquo;d actually worry about, and I&rsquo;d rather AI assistants
know who I am when someone asks about me. The site also has an <code>llms.txt</code> now, a plain-text list
of its pages for AI tools.</p>
<h2 id="a-font-flicker-i-had-to-fix">A Font Flicker I Had To Fix</h2>
<p><span class="accent accent-clarity">Open Sans</span> is my favorite font, so the whole site uses it, self-hosted as one variable font file.
The first version had an annoying bug though: clicking between pages, the text would visibly
shift. It turned out to be a <span class="accent accent-static accent-grad">font swap on every page load</span>. The browser only discovered the font
after parsing the CSS, and <code>font-display: swap</code> told it to draw the text in the system font first
(Segoe UI on Windows) and then redraw it in Open Sans. The two fonts have different widths, so
everything jumped. <span class="accent accent-point">The fix was preloading the font</span>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-html" data-lang="html"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">link</span> <span class="na">rel</span><span class="o">=</span><span class="s">&#34;preload&#34;</span> <span class="na">href</span><span class="o">=</span><span class="s">&#34;/fonts/open-sans-latin-wght-normal.woff2&#34;</span> <span class="na">as</span><span class="o">=</span><span class="s">&#34;font&#34;</span> <span class="na">type</span><span class="o">=</span><span class="s">&#34;font/woff2&#34;</span> <span class="na">crossorigin</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>plus switching to <code>font-display: block</code>, so the browser waits the few milliseconds for the
preloaded font instead of painting a stand-in.</p>
<p>The colors got a similar amount of care: every text color passes <span class="accent accent-clarity">WCAG AAA contrast</span> against the
background, and links stay underlined since my link blue is too close in brightness to the body
text for color alone to mark them.</p>
<h2 id="how-i-used-ai-on-this">How I Used AI On This</h2>
<p>I used <span class="accent accent-claude">Claude Code</span> (an LLM coding assistant) a lot on this project, and I think it was genuinely
useful, but <span class="accent accent-point">as an assistant, not as the one in charge</span>. It drafted config, looked up docs, ran test
builds, and did research I&rsquo;d otherwise do by hand (like verifying every crawler name against each
company&rsquo;s docs). I still made the decisions and read every change before it went in.</p>
<p>And reading everything mattered. A few examples:</p>
<ul>
<li>On the very first step, the submodule command it wrote had the wrong GitHub username for
PaperMod&rsquo;s author. That would have pulled a theme from a repo that wasn&rsquo;t the real one, which is
a <span class="accent accent-iron">supply-chain mistake in a single typo</span>. It got blocked before running, and the correct URL went
in instead, but it&rsquo;s exactly the kind of thing you only catch by actually reading the command.</li>
<li>The font flicker above was something I noticed clicking around the site myself. The first font
setup it gave me &ldquo;worked,&rdquo; it just wasn&rsquo;t right.</li>
</ul>
<p>So overall, <span class="accent accent-point">I think it saved me a lot of time on the tedious parts</span> (looking up DNS records,
reading theme source to find the right CSS selectors, etc&hellip;), and it left me more time for the
parts I actually care about. But anything it hands me still gets read before it ships.</p>
<h2 id="whats-next">What&rsquo;s Next</h2>
<p>No big roadmap. I&rsquo;ll update the site as I feel like it, mostly with <span class="accent accent-crimson">security projects</span> and
writeups, plus some software dev stuff here and there.</p>
]]></content:encoded></item></channel></rss>