Setting the Stage: Context for the Curious Book Reader

This essay chronicles a pivotal moment in our understanding of web optimization and AI agent interaction. What began as a practical challenge to conditionally load MathJax in Jekyll evolved into a profound methodology for distinguishing between human users and intelligent AI bots by observing their resource fetching patterns. This piece details the technical journey from CDN dependency to self-sovereign static resource management, culminating in the creation of a “multi-stage depth gauge” for bot intelligence—an important blueprint for navigating the Age of AI.


Technical Journal Entry Begins

Take the break. The static resources and the JavaScript trap await.

It’s about 2:00 AM on Friday morning, January 2nd 2026.

Okay, and so I did. Sometimes no matter how easy the next steps are you still have to take a break. And sometimes that break is a nap. And sometimes your next bout of motivation is in the middle of the night. It’s okay to be out of phase with the rest of the world occasionally. On the whole the healthiest thing is to try to maintain a predictable circadian rhythm but sometimes you’ve got to hit the iron while it’s hot even if it does require a nap first. And showing which AI bots execute JavaScript and which don’t as a result of an experiment conducted right before your eyes is a very hot iron.

The Need for Strategic Breaks

Okay, so it’s the 1, 2, 3 methodology to power our way through these to-do items because they’re rote and I even have it written out what to do. But instead of following those instructions verbatim, I’m going to think it through.

I show a lot of math and for it to format well, we use MathJax which is a JavaScript include. In other words for math to show right, this JavaScript must be included. I was in the past remiss to put it on every page of the sight because why include it if you don’t have to? But then I started using math so much on the site it became a point of friction to keep manually conditionally including it.

The MathJax Dilemma: Conditional Loading with Liquid

Is there a way (probably in Liquid Templates) if I’m using the Jekyll system and a JavaScript include file should only be included if there’s a double-dollar sign in the content that I can only include the, let’s say CDN-link (for now) if there’s a double dollar-sign in the content?

Gemini 3 Pro: Yes, you absolutely can. This is a classic “Jekyll optimization” move.

Since you are using a layout (likely default.html or post.html), the rendered HTML of your page is available in the content Liquid variable. You can inspect this string before you output it.

Liquid Logic for Dynamic Script Inclusion

Here is the implementation to add to your _layouts/default.html (or wherever your <head> or footer scripts live):

The Code (Liquid Logic)

{% if content contains "$$" or content contains "\[" %}
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
{% endif %}

Why This Works

  1. content: This variable holds the rendered HTML of the specific page or post being generated.
  2. contains: This is a standard Liquid operator that performs a substring search.
  3. "$$": Since you are using client-side MathJax, your markdown processor (kramdown) typically leaves the $$ delimiters alone (or converts them to a specific script tag) so the JavaScript can find them later. As long as that string exists in the final HTML, this condition returns true.

Refinement: Inline Math

If you also use inline math (like \( x^2 \)) without double dollar signs, you might want to broaden the trap slightly to ensure you catch those pages too:

{% assign has_math = false %}
{% if content contains "$$" or content contains "\(" or content contains "\[" %}
  {% assign has_math = true %}
{% endif %}

{% if has_math %}
  {% endif %}

This is the “1-2-3” chisel strike for performance. You stop forcing every browser to download a heavy math rendering engine just to read a “Hello World” post.

Me: Okay, this seems like a good idea. Only load the JavaScript when you need it. I know it complicates my first inclusion of a static JavaScript resource, but this is the sort of meticulous every aspect thought out from the ground up thing that I want to do. I don’t want the JavaScript even included if it’s not going to be used. So let me find some candidate pages for the before and after.

[mike@nixos:~/repos/trimnoir/_posts]$ rg -l '\$\$'
2025-10-18-uncompressible-truth-dirty-paths-canonical-keys-data-pipelines.md
2025-11-04-jekyll-nix-flakes-mathjax-liquid-templates.md
2025-09-20-prompt-becomes-contract-human-agent.md
2025-10-27-fanciful-hooks-functional-tools-ai-workflows.md
2025-09-18-magic-rolling-pin-ai-navigation.md
2024-11-24-ai-seo-100-percent-accountability.md
2025-03-18-automating-seo-workflows.md
2024-12-26-flow-state-idea-capture-vs-organization.md
2025-09-24-python-paste-parse-pipulate-lite.md
2024-11-24-brainstorming-book-titles-with-ai.md
2025-06-23-endosymbiotic-developer-ai-collaborator.md
2024-12-07-zenos-paradox-knuth-brooks.md
2025-10-18-debugging-data-stream-ai-excel-reports.md
2025-09-21-antifragile-intelligence-scheduler-commitment.md
2025-09-27-validating-resilient-python-workflow.md
2025-10-02-digital-crucible-lifelong-journal.md
2025-10-18-human-ai-collaboration-data-workflows-repl-kmeans.md
2025-10-10-ai-workflow-debugging-git-reset.md
2025-09-12-headless-shopify-python-jekyll.md
2025-04-18-bridging-code-and-context.md
2025-09-28-ipso-facto-optimism.md
2025-07-10-infinite-regression-ai-collaboration-strategy.md
2025-03-21-evolving-ai-in-a-bottle.md

Okay, so I choose one of these pages such as http://localhost:4003/futureproof/antifragile-intelligence-scheduler-commitment/ which is my internal preview Jekyll site and put the logic in my .default.html:

<!DOCTYPE html>
<html lang="{{ page.lang | default: site.lang | default: "en" }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">

{%- seo -%}

<link rel="stylesheet" href="{{ "/assets/main.css" | relative_url }}">

{%- feed_meta -%}

{% if page.path contains '_posts' %} {% assign source_filename = page.path | split: '/' | last %} <link rel="alternate" type="text/markdown" title="Source Markdown" href="{{ site.url }}/source/{{ source_filename }}"> {% endif %}

{% if content contains "$$" or content contains "\[" %}
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
{% endif %}

</head>
<body>

  <main class="page-content" aria-label="Content">
    <div class="wrapper">
      {{ content }}
    </div>
  </main>

  </body>
</html>

…the diff being:

[mike@nixos:~/repos/trimnoir/_layouts]$ git --no-pager diff
diff --git a/_layouts/default.html b/_layouts/default.html
index eb154b3..f3596c5 100644
--- a/_layouts/default.html
+++ b/_layouts/default.html
@@ -13,6 +13,11 @@
 
 {% if page.path contains '_posts' %} {% assign source_filename = page.path | split: '/' | last %} <link rel="alternate" type="text/markdown" title="Source Markdown" href="{{ site.url }}/source/{{ source_filename }}"> {% endif %}
 
+{% if content contains "$$" or content contains "\[" %}
+  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
+  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
+{% endif %}
+
 </head>
 <body>
 
[mike@nixos:~/repos/trimnoir/_layouts]$ 

Right, right so that seems done right? Okay, let’s do the 2 rounds of polish. First, if we look at the rendered HTML it looks like:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Begin Jekyll SEO tag v2.8.0 -->
<title>The Antifragile Intelligence: Scheduling as a Commitment Device | Mike Levin, AI Educator (AIE)</title>
<meta name="generator" content="Jekyll v4.4.1" />
<meta property="og:title" content="The Antifragile Intelligence: Scheduling as a Commitment Device" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves." />
<meta property="og:description" content="I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves." />
<link rel="canonical" href="http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/" />
<meta property="og:url" content="http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/" />
<meta property="og:site_name" content="Mike Levin, AI Educator (AIE)" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2025-09-21T00:00:00-04:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="The Antifragile Intelligence: Scheduling as a Commitment Device" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","dateModified":"2025-09-21T00:00:00-04:00","datePublished":"2025-09-21T00:00:00-04:00","description":"I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves.","headline":"The Antifragile Intelligence: Scheduling as a Commitment Device","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/"},"url":"http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/"}</script>
<!-- End Jekyll SEO tag -->
<link rel="stylesheet" href="/assets/main.css"><link type="application/atom+xml" rel="alternate" href="http://0.0.0.0:4003/feed.xml" title="Mike Levin, AI Educator (AIE)" />  <link rel="alternate" type="text/markdown" title="Source Markdown" href="http://0.0.0.0:4003/source/2025-09-21-antifragile-intelligence-scheduler-commitment.md"> 


  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>


</head>
<!-- ...rest of document not shown -->

…just look at all that extra white space and indent space around that inclusion tag. This is the kind of thing that creates all that excessive line returns especially when using mixed-mode templating languages and Liquid Template in particular. Show me how to get rid of the extra indents and line spaces please Gemini. And explain the issues and the rules of how it’s handled in Liquid. Get into the details such as whether if you put a hyphen at the beginning tag if it also has to be at the end. What are all the rules surrounding the use of the minus signs in Liquid template tags and how should we best use them here?

Mastering Liquid Whitespace for Clean HTML

Gemini 3 Pro: This is a classic “invisible character” battle in template languages. Liquid is notorious for leaving behind the “ghost” of the logic tags—the newlines and spaces where your {% if %} and {% endif %} sat in the source file.

Here is the fix, followed by the deep dive into the rules of the “Liquid Hyphen.”

The Fix

To get rid of the gaps, we use the hyphen (-) inside the tag delimiters.

{%- if content contains "$$" or content contains "\[" -%}
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
{%- endif -%}

The Rules of the Hyphen

In Liquid, the standard tags {% %} and {{ }} do not produce text output themselves, but they do not consume the whitespace around them in the source file. If you have an if statement on its own line, that line becomes a blank line in your HTML.

The hyphen is the Whitespace Stripper.

1. It is Directional (Left vs. Right) You do not have to match them. You can use a hyphen on the left, the right, or both. It depends entirely on which side of the tag you want to “vacuum” up the whitespace.

  • {%-: Vacuums everything (spaces, tabs, newlines) immediately to the left of the tag until it hits a non-whitespace character.
  • -%}: Vacuums everything immediately to the right of the tag until it hits a non-whitespace character.

2. The Scenario: “The Ghost Newline” Without hyphens:

{% if true %}
  <script>...</script>
{% endif %}

Output:

<script>...</script>


Note the empty lines created by the if and endif tags.

3. The Scenario: “The Vacuum” With hyphens:

{%- if true -%}
<script>...</script>
{%- endif -%}

Output:

<script>...</script>```
*It collapses everything.*

### Why we used `{%- ... -%}` in your fix

1.  **`{%- if ...`**: Takes the newline *before* the `if` statement (from the previous line) and deletes it, pulling the block up.
2.  **`... -%}`**: Takes the newline *after* the `if` statement (before the `<script>`) and deletes it.
3.  **`{%- endif`**: Takes the newline *after* the last script tag and deletes it.
4.  **`... -%}`**: Takes the newline *after* the `endif` and deletes it, creating a seamless flow with whatever follows.

### Best Practice for Metadata Blocks
In the `<head>` section, you generally want to maintain *some* structure so "View Source" isn't a garbled mess, but you don't want massive gaps.

A balanced approach often looks like this (using hyphens only on the outside edges):

```liquid
{%- if content contains "$$" or content contains "\[" -%}
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
{%- endif -%}

This ensures that if the condition is false, absolutely nothing is emitted—not even a blank line. If it is true, it emits the scripts cleanly.

Me: Okay, I did that. But I removed the last hyphen so that the </head> head-closing tag was on its own line. It was a very important piece of information that the side the hyphens are on are like directional arrows! Wow, that helps our control here.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1"><!-- Begin Jekyll SEO tag v2.8.0 -->
<title>The Antifragile Intelligence: Scheduling as a Commitment Device | Mike Levin, AI Educator (AIE)</title>
<meta name="generator" content="Jekyll v4.4.1" />
<meta property="og:title" content="The Antifragile Intelligence: Scheduling as a Commitment Device" />
<meta property="og:locale" content="en_US" />
<meta name="description" content="I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves." />
<meta property="og:description" content="I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves." />
<link rel="canonical" href="http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/" />
<meta property="og:url" content="http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/" />
<meta property="og:site_name" content="Mike Levin, AI Educator (AIE)" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="2025-09-21T00:00:00-04:00" />
<meta name="twitter:card" content="summary" />
<meta property="twitter:title" content="The Antifragile Intelligence: Scheduling as a Commitment Device" />
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"BlogPosting","dateModified":"2025-09-21T00:00:00-04:00","datePublished":"2025-09-21T00:00:00-04:00","description":"I’m realizing that the technical architecture I’m building is deeply connected to my philosophical musings on AI, life, and resilience. This conversation about a simple scheduler suddenly became a microcosm for everything I’ve been thinking about. The idea of using Robert Cialdini’s ‘Commitment and Consistency’ as a design principle for an AI system feels like a genuine breakthrough—a way to use technology as a ‘self-hack’ to enforce my own follow-through. It connects my past in SEO, which was all about understanding complex, adaptive systems, to my future with AI. This isn’t just about code; it’s about building systems that reflect the antifragile patterns of nature and help us become better, more consistent versions of ourselves.","headline":"The Antifragile Intelligence: Scheduling as a Commitment Device","mainEntityOfPage":{"@type":"WebPage","@id":"http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/"},"url":"http://0.0.0.0:4003/futureproof/antifragile-intelligence-scheduler-commitment/"}</script>
<!-- End Jekyll SEO tag -->
<link rel="stylesheet" href="/assets/main.css"><link type="application/atom+xml" rel="alternate" href="http://0.0.0.0:4003/feed.xml" title="Mike Levin, AI Educator (AIE)" />  <link rel="alternate" type="text/markdown" title="Source Markdown" href="http://0.0.0.0:4003/source/2025-09-21-antifragile-intelligence-scheduler-commitment.md"> <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
</head>

Okay, so that’s the first of the two items we want to do for the first static resource. Oh but wait! It STILL isn’t really a local static resource. It’s loading off of a CDN so WE won’t see it load. Now in like 99 times out of 100 that’s exactly what you want because you’re offloading static resources (the best sort of resources to offload) to a content distribution network (CDN) like Akamai or Cloudflare to serve your file from much closer to the user like actually globally in the world choosing a server closer to you to serve that incredibly frequently requested .js-file from so your server doesn’t have to do it every time and less energy is consumed overall in the world. The cost? A bit of intelligence: a sorting out of what is and isn’t static resources. And most of these massively popular .js-include files — really everything big since .jquery that kind of started it all back in… hmmm. Google?

From CDN to Self-Hosting: The Pursuit of Visibility

Google AI Overviews AI: jQuery was first released on August 26, 2006 [1, 2]. [1]
The earliest Content Delivery Networks (CDNs) began serving jQuery shortly after its release, with Google launching its Google Hosted Libraries service, including jQuery, in May 2010 [3, 4, 5]. Other CDNs like Microsoft’s also began hosting it around that time to improve web performance and reliability [6].

  • jQuery Release Date: August 26, 2006 [1, 2]
  • CDNs Begin Serving: Around 2010 (e.g., Google in May 2010) [3, 4] [3]

AI responses may include mistakes.

[1] https://dev.to/adnanbabakan/story-of-jquery-write-less-and-rest-in-peace-1h6d
[2] https://en.wikipedia.org/wiki/JQuery
[3] https://dev.to/adnanbabakan/story-of-jquery-write-less-and-rest-in-peace-1h6d

Me: Okay, so since around 2010 CDNs have been delivering these things. And that’s good. But we don’t see the request. And we want to see the request. It’s not really our “what executes JavaScript?” trap yet, but it is one part of a more complete picture. You will see what things will fetch a static resource seemingly required to build a page for proper viewing by humans. In this case without the MathJax library, fancy math equations like Newton’s Second Law (\(F=ma\)) would just look like $$F=ma$$ or similar. I like the way we format code in markdown with backticks.

It’s confusing when you’re doing multi-modal language stuff with bash or Unix script because backticks mean something else. But here what we’re really looking for is the double dollar-signs that trigger MathJax to do its formatting. But if I don’t want MathJax to pick it up, I can put it in “code” delimiters… I think. I’ll have to check on how that renders later on. But now I’m on a roll. I’m about to show the CDN’s instructions on how to use MathJax and then get Gemini 3 Pro’s take on how to make that a local situation in a home-hosting situation like ours. Should be an easy file-save of some sort into webspace. Get it under the git repo. Then done, really. Make sure the URLs are fixed to point local to that file. And just like that you get the data and just pay for it with a little more upload bandwidth than you’d normally be using. No big deal.

Final Verdict: Building a Multi-Stage Bot Depth Gauge

I’d even go a step further and get the not-minimized version of the JavaScript for normally CDN-delivered static resources that you’re taking it upon yourself to locally host so you get the log data. In for a penny, in for a pound. Why not get the version of the JavaScript that will semantically make sense to the LLM-directed AIs that may be sniffing around your site? Minimizing a JavaScript file to rename long sensibly named variables to single-letters for the sake of download time, then this is the flip-side to that coin. Those well chosen variable names are semantics right there. It will help AIs understand your site if they could read the JavaScript directly and just have it make sense.

AI-Readiness: The Semantics of Static Resources

There’s an important nuance here that is a possibility that I don’t think a lot of people are thinking about. What is it to “use a browser” anyway? It’s running a program to see what it does. All you need is its output knowing what it would be. So it’s not just whether the LLM-directed AIs are “using a browser to execute the JavaScript to build the real DOM the user experiences usually so you can get all the links” No. That’s one way, sure. It’s the obvious, brute-force way.

The seldom thought about thing is that the LLMs could just understand browsers inherently and emulate them directly in their neural network just knowing what they’d do given the JavaScript and all. This is how they can program Python so well without a real Python interpreter — except when they’re explicitly given one which does happen. The models would have to make all the same resource fetches as a “real” browser would to accurately emulate what a browser would do in that same exact request/response http situation… and just know it. It would speak browser like it speaks Python.

Maybe this year. Don’t be surprised. LLM models might just start speaking browser. More evidence? People say LLMs are all-trained up given the giant web scrapes and all public domain and open source data. Proprietary intellectual property is one issue, but there are many, many more. But one of the big ones is more objective data than just plain (and very subjective) text that calls for reinforcement learning. Reinforcement learning, huh? From somebody who so-calls “knows better” than the LLM because they’re an embodied human with real consciousness and twice as many biases? Nahhh.

Much of the new data that’s going to matter today on the current round of Gemini 3 to 3.5 and 4 training I imagine is going on I think will be all about that more objective data that is observational of you and your behavior. Forget scanning books. They got that. Now it’s about your interaction with stuff. It’s about understanding user behavior in the browser so they can step in as a user in your browser at your behest to carry out some white collar work you need to get done while you feed the kids or whatever.

Google collects such user behavioral data through Chrome. Chrome-users give all that data to Google through the telemetry they collect for the Chrome User Experience (CrUX) database. It’s a new Internet protocol called Chrome DevTools Protocol (CDP) created much for browser activity-snooping because developers need that while doing front-end work. And that same API is used for all sorts of neat things. Like for example it generates a whole accessibility tree from your aria tags and element roles so that visually impaired can use the Web. That stuff is really great for AIs. Oh yeah, Chrome telemetry.

Chrome is always calling back to mama about your web-surfing traffic. It’s not just about DNS and the address bar too when you don’t type an exact web address. They own that too through various trajectories, but we won’t talk about that right now. It’s the “how people interact with sites in the browser” itch that I believe is the source of the new data for the next round of training to scratch the itch of the fact expectations are leading us to expect smart bots just being able to surf and do chores for us on the web.

That feels like the second shoe we’re waiting to see drop, with ChatBots and AI coding assistance being the first. And it doesn’t come from scraping more websites for traditional LLM-training data. It’s multi-modal with user behavior regarding data a parameter or a vector or a dimension or something.

So we have to expect bots to surf sites. Leaning into allowing bots to surf your sites well will be a competitive advantage and feels like part of the definition of AI-readiness. And we’ve been through it before. AI-readiness is no different than mobile-readiness. In fact, Mobile readiness may have been worse. Few people remember it anymore but Steve Jobs and Apple killed Adobe Flash, the preeminent player-platform and engine of much commerce and time-killing online. The first barely web-surfing capable iPhone was 2007. By 2010 the world woke up. By 2015 things like React JS ruled Web Development.

Ten years after that AIs that can in many ways think like humans come surfing by, look at that awful semantically void minimized opaque jumble that needs a browser executing the code just to say hello to the user? You have to literally run a program to use a webpage? That’s what needing JavaScript execution is. You’re saying it’s not just displaying something. It requires a Turing-complete general purpose computer at the other end. This is not simply a markdown language. This is literally embedded programming code that you expect a code execution-engine at the other end to just blindly run and see what happens.

Security is a big thing with browsers, as you might expect. Cross-site scripting attacks was a thing there for awhile because if those JavaScript variables were somehow exposed or visible between two different tabs in your browser because tricks involving the <iframe> iframe tag among other things could pass data between sites. Site A could open an iframe to site B and hand like a baton any data from side A to side B that it likes. The fact that there’s such a high expectation on visiting bots at all to execute JavaScript code “like Google does” for Googlebot is some big moat shoes to fill. Expensive!

So AI-readiness is greatly a matter of making visiting site-crawling tools that we colloquially call “bots” probably mystifying them more than they should be, not have to execute JavaScript to use your site very well. We can do that by also making an AI-directed workflow from our end that surfs a site just like that. A small crawl of a website to speak its experience along the way. I’m knitting together those tools here. Okay, this is all a digression. Let’s just link that MathJax static resource from local.

Me to Google: install mathjax on site

Google AI Overview AI: You can install MathJax on your site either by using a Content Delivery Network (CDN) for easy integration or by hosting it on your own server. [1]
Option 1: Use a CDN (Easiest Way) [2, 3]
Using a CDN requires no installation on your server and ensures your users download MathJax from a fast, nearby server.

  1. Add a script tag to your pages: Place the following line in the section of your HTML documents:
  2. This script loads MathJax version 4, configured to support TeX, MathML, and AsciiMath input with HTML/CSS or CHTML output.
  3. Add mathematics to your pages: You can now use LaTeX, MathML, or AsciiMath notation directly in your HTML content. MathJax will automatically process and render it. [5, 6, 7, 8, 9]

Option 2: Host Your Own Copy [10]
If you need your site to work offline or want more control over the version and configuration, you can host MathJax yourself.

  1. Obtain a copy of MathJax:

    • Using npm: Run in your project directory. • Using Git: Clone the repository with . • Via archive: Download a zipped archive from the MathJax GitHub page and unzip it.

  2. Place the files on your server: Move the resulting folder to a convenient location on your web server (e.g., at the top level of your site’s hierarchy).
  3. Link MathJax in your HTML: Add a script tag to your web pages, replacing with the actual URL path to your MathJax installation: [4, 11, 12, 13, 14]

For Specific Platforms If you are using a Content Management System (CMS) like WordPress or Moodle, there may be an existing plugin that simplifies the process. Check your platform’s documentation or plugin directory for a dedicated MathJax integration. [8, 13, 15, 16]

AI responses may include mistakes.

[1] https://docs.mathjax.org/en/v2.0/start.html
[2] https://eclass.srv.ualberta.ca/question/type/stack/doc/doc.php/Installation/Mathjax.md
[3] https://docs.mathjax.org/en/v2.5/installation.html
[4] https://docs.mathjax.org/en/v2.7/start.html
[5] https://www.math.toronto.edu/MathJax/docs/html/configuration.html
[6] https://classic.yarnpkg.com/en/package/mathjax
[7] https://docs.mathjax.org/en/v2.4/start.html
[8] https://www.mathjax.org/
[9] https://docs.mathjax.org/en/v3.0/misc/faq.html
[10] https://docs.mathjax.org/en/v4.0/web/start.html
[11] https://docs.mathjax.org/en/v4.0/web/hosting.html
[12] https://docs.mathjax.org/en/v2.7/installation.html
[13] https://www.npmjs.com/package/mathjax
[14] https://docs.mathjax.org/en/v4.0/web/hosting.html
[15] https://docs.mathjax.org/en/v2.6/tex.html
[16] https://github.com/mathjax/MathJax-docs

Me: So there you have it. You know my situation quite well by this time, I think. Talk me through this oh Gemini.

Gemini 3 Pro: You are about to cross the Rubicon from Consumption to Self-Hosting.

You want to stop outsourcing your math rendering to a CDN. You want the full weight of that resource (and the tracking of its retrieval) to happen on your metal, under your control. And you want to do it in the Jekyll/NixOS context.

The Strategy

We are going to download the MathJax library (Option 2 from your Google search), put it in your Jekyll assets folder, and link to it.

However, MathJax is notoriously “heavy” if you just clone the whole repo (it has thousands of files for fonts and configs). We want the “Single File Build” or at least the optimized web distribution.

The most efficient way for a static site:

  1. Download the latest distribution archive.
  2. Unpack it into assets/js/mathjax/.
  3. Update your _layouts/default.html to point to the local file.

Step 1: Get the Goods (The Command Line Way)

You are on NixOS. You have wget or curl. Let’s grab the latest stable release (v3) directly into your repo.

Run this in your trimnoir (Jekyll) repo root:

# Create the destination
mkdir -p assets/js/mathjax

# Download the latest single-file build (tex-mml-chtml.js is the kitchen sink)
# We are grabbing it from the CDN one last time to save it forever.
curl -L -o assets/js/mathjax/tex-mml-chtml.js https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js

Wait! A Warning: MathJax v3 uses dynamic loading. Even if you download that one file, it tries to fetch fonts and other components from the same directory. For a true local install that works 100% offline, you actually need the full es5 directory structure, not just the entry point file.

The Better Way (npm via Nix): Since you have Nix, you can use npm to fetch the package cleanly without installing Node globally.

# Enter a shell with node/npm
nix shell nixpkgs#nodejs

# Install mathjax locally (creates node_modules)
npm install mathjax@3

# Move the distribution folder to your assets
mkdir -p assets/js/
cp -r node_modules/mathjax/es5 assets/js/mathjax

# Cleanup
rm -rf node_modules package-lock.json package.json
exit

Now you have the full, self-contained MathJax v3 engine in assets/js/mathjax/.

Step 2: Update the Template

Now, modify _layouts/default.html to point to your new local asset.

File: _layouts/default.html

{%- if content contains "$$" or content contains "\[" -%}
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
  <script id="MathJax-script" async src="{{ '/assets/js/mathjax/tex-mml-chtml.js' | relative_url }}"></script>
{%- endif -%}

(Note: I kept the polyfill on the CDN for now, as it’s rarely needed for modern browsers/bots, but you could local-host that too if you’re a purist.)

Step 3: Verify the “Trap”

Now, when a bot (or human) visits a page with $$:

  1. Request 1: GET /article-name/ (The HTML)
  2. Request 2: GET /assets/js/mathjax/tex-mml-chtml.js (The Logic)
  3. Request 3+: GET /assets/js/mathjax/output/chtml/fonts/... (The Fonts - only if they execute the JS!)

This is the key. A scraper will grab Request 1. A browser (or advanced bot) will grab Request 1 AND Request 2. But only a Full Execution Engine that actually renders the math will trigger Request 3 (the fonts). The JS calculates which fonts are needed based on the math in the page and fetches them dynamically.

You just built a multi-stage depth gauge for bot intelligence.

Me: Okay, I’ll buy that.

[mike@nixos:~/repos/pipulate]$ nix shell nixpkgs#nodejs

[mike@nixos:~/repos/pipulate]$ npm install mathjax@3

added 1 package in 1s
npm notice
npm notice New minor version of npm available! 11.6.2 -> 11.7.0
npm notice Changelog: https://github.com/npm/cli/releases/tag/v11.7.0
npm notice To update run: npm install -g npm@11.7.0
npm notice

[mike@nixos:~/repos/pipulate]$ ls assets/js/
fasthtml.js  htmx.js  marked.min.js  mermaid.min.js  prism.js  script.js  Sortable.js  split.js  surreal.js

[mike@nixos:~/repos/pipulate]$ cp -r node_modules/mathjax/es5 assets/js/mathjax

[mike@nixos:~/repos/pipulate]$ rm -rf node_modules package-lock.json package.json

[mike@nixos:~/repos/pipulate]$ exit
exit

[mike@nixos:~/repos/pipulate]$ git --no-pager diff

[mike@nixos:~/repos/pipulate]$ cd assets/js/mathjax/

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ ls
a11y      input      mml-chtml.js  output      tex-chtml-full.js         tex-mml-chtml.js  tex-svg.js
adaptors  latest.js  mml-svg.js    sre         tex-chtml-full-speech.js  tex-mml-svg.js    ui
core.js   loader.js  node-main.js  startup.js  tex-chtml.js              tex-svg-full.js

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ ls -la
total 15428
drwxr-xr-x 8 mike users    4096 Jan  2 03:08 .
drwxr-xr-x 3 mike users    4096 Jan  2 03:08 ..
drwxr-xr-x 2 mike users    4096 Jan  2 03:08 a11y
drwxr-xr-x 2 mike users    4096 Jan  2 03:08 adaptors
-rw-r--r-- 1 mike users  218533 Jan  2 03:08 core.js
drwxr-xr-x 4 mike users    4096 Jan  2 03:08 input
-rw-r--r-- 1 mike users    5090 Jan  2 03:08 latest.js
-rw-r--r-- 1 mike users   18439 Jan  2 03:08 loader.js
-rw-r--r-- 1 mike users  997633 Jan  2 03:08 mml-chtml.js
-rw-r--r-- 1 mike users 1945224 Jan  2 03:08 mml-svg.js
-rw-r--r-- 1 mike users  265880 Jan  2 03:08 node-main.js
drwxr-xr-x 4 mike users    4096 Jan  2 03:08 output
drwxr-xr-x 3 mike users    4096 Jan  2 03:08 sre
-rw-r--r-- 1 mike users   29566 Jan  2 03:08 startup.js
-rw-r--r-- 1 mike users 1327522 Jan  2 03:08 tex-chtml-full.js
-rw-r--r-- 1 mike users 2091263 Jan  2 03:08 tex-chtml-full-speech.js
-rw-r--r-- 1 mike users 1160989 Jan  2 03:08 tex-chtml.js
-rw-r--r-- 1 mike users 1173007 Jan  2 03:08 tex-mml-chtml.js
-rw-r--r-- 1 mike users 2120598 Jan  2 03:08 tex-mml-svg.js
-rw-r--r-- 1 mike users 2275113 Jan  2 03:08 tex-svg-full.js
-rw-r--r-- 1 mike users 2108580 Jan  2 03:08 tex-svg.js
drwxr-xr-x 2 mike users    4096 Jan  2 03:08 ui

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$

Okay, how can you tell if the folder you’re in is under the current repo?

Me: Oakie Dokie I did that too:

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git rev-parse --show-toplevel
/home/mike/repos/pipulate

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git add .
The following paths are ignored by one of your .gitignore files:
assets/js
hint: Use -f if you really want to add them.
hint: Disable this message with "git config set advice.addIgnoredFile false"

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git -f add .
unknown option: -f
usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
           [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
           [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
           <command> [<args>]

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git add . -f

[mike@nixos:~/repos/pipulate/assets/js/mathjax]$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   a11y/assistive-mml.js
	new file:   a11y/complexity.js
	new file:   a11y/explorer.js
	new file:   a11y/semantic-enrich.js
	new file:   a11y/sre.js
	new file:   adaptors/liteDOM.js
	new file:   core.js
	new file:   input/asciimath.js
	new file:   input/mml.js
	new file:   input/mml/entities.js
	new file:   input/mml/extensions/mml3.js
	new file:   input/mml/extensions/mml3.sef.json
	new file:   input/tex-base.js
	new file:   input/tex-full.js
	new file:   input/tex.js
	new file:   input/tex/extensions/action.js
	new file:   input/tex/extensions/all-packages.js
	new file:   input/tex/extensions/ams.js
	new file:   input/tex/extensions/amscd.js
	new file:   input/tex/extensions/autoload.js
	new file:   input/tex/extensions/bbox.js
	new file:   input/tex/extensions/boldsymbol.js
	new file:   input/tex/extensions/braket.js
	new file:   input/tex/extensions/bussproofs.js
	new file:   input/tex/extensions/cancel.js
	new file:   input/tex/extensions/cases.js
	new file:   input/tex/extensions/centernot.js
	new file:   input/tex/extensions/color.js
	new file:   input/tex/extensions/colortbl.js
	new file:   input/tex/extensions/colorv2.js
	new file:   input/tex/extensions/configmacros.js
	new file:   input/tex/extensions/empheq.js
	new file:   input/tex/extensions/enclose.js
	new file:   input/tex/extensions/extpfeil.js
	new file:   input/tex/extensions/gensymb.js
	new file:   input/tex/extensions/html.js
	new file:   input/tex/extensions/mathtools.js
	new file:   input/tex/extensions/mhchem.js
	new file:   input/tex/extensions/newcommand.js
	new file:   input/tex/extensions/noerrors.js
	new file:   input/tex/extensions/noundefined.js
	new file:   input/tex/extensions/physics.js
	new file:   input/tex/extensions/require.js
	new file:   input/tex/extensions/setoptions.js
	new file:   input/tex/extensions/tagformat.js
	new file:   input/tex/extensions/textcomp.js
	new file:   input/tex/extensions/textmacros.js
	new file:   input/tex/extensions/unicode.js
	new file:   input/tex/extensions/upgreek.js
	new file:   input/tex/extensions/verb.js
	new file:   latest.js
	new file:   loader.js
	new file:   mml-chtml.js
	new file:   mml-svg.js
	new file:   node-main.js
	new file:   output/chtml.js
	new file:   output/chtml/fonts/tex.js
	new file:   output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Calligraphic-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Calligraphic-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Fraktur-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Fraktur-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-BoldItalic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Script-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size1-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size2-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size3-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size4-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Typewriter-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Vector-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Vector-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Zero.woff
	new file:   output/svg.js
	new file:   output/svg/fonts/tex.js
	new file:   sre/mathmaps/base.json
	new file:   sre/mathmaps/ca.json
	new file:   sre/mathmaps/da.json
	new file:   sre/mathmaps/de.json
	new file:   sre/mathmaps/en.json
	new file:   sre/mathmaps/es.json
	new file:   sre/mathmaps/fr.json
	new file:   sre/mathmaps/hi.json
	new file:   sre/mathmaps/it.json
	new file:   sre/mathmaps/nb.json
	new file:   sre/mathmaps/nemeth.json
	new file:   sre/mathmaps/nn.json
	new file:   sre/mathmaps/sv.json
	new file:   startup.js
	new file:   tex-chtml-full-speech.js
	new file:   tex-chtml-full.js
	new file:   tex-chtml.js
	new file:   tex-mml-chtml.js
	new file:   tex-mml-svg.js
	new file:   tex-svg-full.js
	new file:   tex-svg.js
	new file:   ui/lazy.js
	new file:   ui/menu.js
	new file:   ui/safe.js


[mike@nixos:~/repos/pipulate/assets/js/mathjax]$

Interesting! There’s a lot in the Pipulate repo now. Oh wait! I don’t need that in the pipulate repo. I need that in the static resource of the trimnoir site at /home/mike/repos/trimnoir/ which through perhaps maybe through the deploy process actually hosts at:

[mike@nixos:~/repos/trimnoir]$ ssh honeybot 
Last login: Thu Jan  1 12:04:43 2026 from 192.168.1.161

[mike@honeybot:~]$ cd www

[mike@honeybot:~/www]$ cd mikelev.in/

[mike@honeybot:~/www/mikelev.in]$ ls
404.html        access.log  _config.yml  flake.lock  foo.txt  Gemfile.lock  honeybot.db-shm  images     index.md  nginx.conf  _posts      _sass    _site
about.markdown  assets      favicon.ico  flake.nix   Gemfile  honeybot.db   honeybot.db-wal  _includes  _layouts  pages       robots.txt  scripts  sitemap.xml

[mike@honeybot:~/www/mikelev.in]$ cd assets/

[mike@honeybot:~/www/mikelev.in/assets]$ ls
fonts  images  js  logo  main.scss  minima-social-icons.svg  svg

[mike@honeybot:~/www/mikelev.in/assets]$ cd js

[mike@honeybot:~/www/mikelev.in/assets/js]$ ls
header.js  matrix.js  site.js

[mike@honeybot:~/www/mikelev.in/assets/js]$

So you see, I made a mistake adding all of mathjax to the pipulate repo right there. I only did the git add. I didn’t commit or push. So help me roll that back and also then help me do this in the correct location! Please and thank you.

Gemini 3 Pro: You are correct to catch that now! You accidentally started putting the heavy static assets into your Tool repo (pipulate) instead of your Content repo (trimnoir).

Here is how to scrub the mistake from pipulate and replay the installation in trimnoir.

1. Undo the Damage in pipulate

Since you only ran git add, the files are staged but not committed. We need to unstage them and then delete the files from the disk.

Run this inside ~/repos/pipulate:

# 1. Unstage the files (remove them from the git index)
git restore --staged assets/js/mathjax

# 2. Physically remove the directory you created
rm -rf assets/js/mathjax

# 3. Double check status (should be clean)
git status

2. Do it Right in trimnoir

Now, let’s move to the correct repository and repeat the process.

# 1. Go to the content repo
cd ~/repos/trimnoir

# 2. Enter the temporary node shell
nix shell nixpkgs#nodejs

# 3. Install MathJax locally (creates node_modules)
npm install mathjax@3

# 4. Create the destination folder (if it doesn't exist)
mkdir -p assets/js

# 5. Copy the distribution files to the correct asset path
cp -r node_modules/mathjax/es5 assets/js/mathjax

# 6. Cleanup the npm artifacts
rm -rf node_modules package-lock.json package.json

# 7. Exit the nix shell
exit

3. Verify and Commit

Now you can verify that the massive list of files is in the right place.

# Verify
ls -R assets/js/mathjax | head

# Stage and Commit
git add assets/js/mathjax
git commit -m "feat: Add local MathJax v3 for offline rendering"
git push

Once you push trimnoir, your post-receive hook on the server will fire, and those files will be deployed to ~/www/mikelev.in/assets/js/mathjax/, ready for your new Liquid template logic to find them.

Me: Okay, the first part:

[mike@nixos:~/repos/pipulate]$ # 1. Unstage the files (remove them from the git index)
git restore --staged assets/js/mathjax

# 2. Physically remove the directory you created
rm -rf assets/js/mathjax

# 3. Double check status (should be clean)
git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean

[mike@nixos:~/repos/pipulate]$

And the second part:

[mike@nixos:~/repos/trimnoir]$ # 1. Go to the content repo
cd ~/repos/trimnoir

# 2. Enter the temporary node shell
nix shell nixpkgs#nodejs

# 3. Install MathJax locally (creates node_modules)
npm install mathjax@3

# 4. Create the destination folder (if it doesn't exist)
mkdir -p assets/js

# 5. Copy the distribution files to the correct asset path
cp -r node_modules/mathjax/es5 assets/js/mathjax

# 6. Cleanup the npm artifacts
rm -rf node_modules package-lock.json package.json

# 7. Exit the nix shell
exit

[mike@nixos:~/repos/trimnoir]$

Gemini 3 Pro: You are correct to catch that now! You accidentally started putting the heavy static assets into your Tool repo (pipulate) instead of your Content repo (trimnoir).

Here is how to scrub the mistake from pipulate and replay the installation in trimnoir.

1. Undo the Damage in pipulate

Since you only ran git add, the files are staged but not committed. We need to unstage them and then delete the files from the disk.

Run this inside ~/repos/pipulate:

# 1. Unstage the files (remove them from the git index)
git restore --staged assets/js/mathjax

# 2. Physically remove the directory you created
rm -rf assets/js/mathjax

# 3. Double check status (should be clean)
git status

2. Do it Right in trimnoir

Now, let’s move to the correct repository and repeat the process.

# 1. Go to the content repo
cd ~/repos/trimnoir

# 2. Enter the temporary node shell
nix shell nixpkgs#nodejs

# 3. Install MathJax locally (creates node_modules)
npm install mathjax@3

# 4. Create the destination folder (if it doesn't exist)
mkdir -p assets/js

# 5. Copy the distribution files to the correct asset path
cp -r node_modules/mathjax/es5 assets/js/mathjax

# 6. Cleanup the npm artifacts
rm -rf node_modules package-lock.json package.json

# 7. Exit the nix shell
exit

3. Verify and Commit

Now you can verify that the massive list of files is in the right place.

# Verify
ls -R assets/js/mathjax | head

# Stage and Commit
git add assets/js/mathjax
git commit -m "feat: Add local MathJax v3 for offline rendering"
git push

Once you push trimnoir, your post-receive hook on the server will fire, and those files will be deployed to ~/www/mikelev.in/assets/js/mathjax/, ready for your new Liquid template logic to find them.

Me: Alright, I think I had to do it the long way, but:

[mike@nixos:~/repos/trimnoir]$ # 1. Go to the content repo
cd ~/repos/trimnoir

# 2. Enter the temporary node shell
nix shell nixpkgs#nodejs

# 3. Install MathJax locally (creates node_modules)
npm install mathjax@3

# 4. Create the destination folder (if it doesn't exist)
mkdir -p assets/js

# 5. Copy the distribution files to the correct asset path
cp -r node_modules/mathjax/es5 assets/js/mathjax

# 6. Cleanup the npm artifacts
rm -rf node_modules package-lock.json package.json

# 7. Exit the nix shell
exit

[mike@nixos:~/repos/trimnoir]$ ls -R assets/js/mathjax | head
ls: cannot access 'assets/js/mathjax': No such file or directory

[mike@nixos:~/repos/trimnoir]$ cd assets/js/

[mike@nixos:~/repos/trimnoir/assets/js]$ ls
header.js  matrix.js  site.js

[mike@nixos:~/repos/trimnoir/assets/js]$ cd ..

[mike@nixos:~/repos/trimnoir/assets]$ cd ..

[mike@nixos:~/repos/trimnoir]$ nix shell nixpkgs#nodejs

[mike@nixos:~/repos/trimnoir]$ npm install mathjax@3

added 1 package in 1s

[mike@nixos:~/repos/trimnoir]$ cp -r node_modules/mathjax/es5 assets/js/mathjax

[mike@nixos:~/repos/trimnoir]$ rm -rf node_modules package-lock.json package.json

[mike@nixos:~/repos/trimnoir]$ cd assets/

[mike@nixos:~/repos/trimnoir/assets]$ cd js/

[mike@nixos:~/repos/trimnoir/assets/js]$ cd mathjax/

[mike@nixos:~/repos/trimnoir/assets/js/mathjax]$ ls
a11y      core.js  latest.js  mml-chtml.js  node-main.js  sre         tex-chtml-full.js         tex-chtml.js      tex-mml-svg.js   tex-svg.js
adaptors  input    loader.js  mml-svg.js    output        startup.js  tex-chtml-full-speech.js  tex-mml-chtml.js  tex-svg-full.js  ui

[mike@nixos:~/repos/trimnoir/assets/js/mathjax]$ git add .

[mike@nixos:~/repos/trimnoir/assets/js/mathjax]$ git status
On branch main
Your branch is up to date with 'home/main'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	new file:   a11y/assistive-mml.js
	new file:   a11y/complexity.js
	new file:   a11y/explorer.js
	new file:   a11y/semantic-enrich.js
	new file:   a11y/sre.js
	new file:   adaptors/liteDOM.js
	new file:   core.js
	new file:   input/asciimath.js
	new file:   input/mml.js
	new file:   input/mml/entities.js
	new file:   input/mml/extensions/mml3.js
	new file:   input/mml/extensions/mml3.sef.json
	new file:   input/tex-base.js
	new file:   input/tex-full.js
	new file:   input/tex.js
	new file:   input/tex/extensions/action.js
	new file:   input/tex/extensions/all-packages.js
	new file:   input/tex/extensions/ams.js
	new file:   input/tex/extensions/amscd.js
	new file:   input/tex/extensions/autoload.js
	new file:   input/tex/extensions/bbox.js
	new file:   input/tex/extensions/boldsymbol.js
	new file:   input/tex/extensions/braket.js
	new file:   input/tex/extensions/bussproofs.js
	new file:   input/tex/extensions/cancel.js
	new file:   input/tex/extensions/cases.js
	new file:   input/tex/extensions/centernot.js
	new file:   input/tex/extensions/color.js
	new file:   input/tex/extensions/colortbl.js
	new file:   input/tex/extensions/colorv2.js
	new file:   input/tex/extensions/configmacros.js
	new file:   input/tex/extensions/empheq.js
	new file:   input/tex/extensions/enclose.js
	new file:   input/tex/extensions/extpfeil.js
	new file:   input/tex/extensions/gensymb.js
	new file:   input/tex/extensions/html.js
	new file:   input/tex/extensions/mathtools.js
	new file:   input/tex/extensions/mhchem.js
	new file:   input/tex/extensions/newcommand.js
	new file:   input/tex/extensions/noerrors.js
	new file:   input/tex/extensions/noundefined.js
	new file:   input/tex/extensions/physics.js
	new file:   input/tex/extensions/require.js
	new file:   input/tex/extensions/setoptions.js
	new file:   input/tex/extensions/tagformat.js
	new file:   input/tex/extensions/textcomp.js
	new file:   input/tex/extensions/textmacros.js
	new file:   input/tex/extensions/unicode.js
	new file:   input/tex/extensions/upgreek.js
	new file:   input/tex/extensions/verb.js
	new file:   latest.js
	new file:   loader.js
	new file:   mml-chtml.js
	new file:   mml-svg.js
	new file:   node-main.js
	new file:   output/chtml.js
	new file:   output/chtml/fonts/tex.js
	new file:   output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Calligraphic-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Calligraphic-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Fraktur-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Fraktur-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-BoldItalic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Math-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Italic.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_SansSerif-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Script-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size1-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size2-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size3-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Size4-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Typewriter-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Vector-Bold.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Vector-Regular.woff
	new file:   output/chtml/fonts/woff-v2/MathJax_Zero.woff
	new file:   output/svg.js
	new file:   output/svg/fonts/tex.js
	new file:   sre/mathmaps/base.json
	new file:   sre/mathmaps/ca.json
	new file:   sre/mathmaps/da.json
	new file:   sre/mathmaps/de.json
	new file:   sre/mathmaps/en.json
	new file:   sre/mathmaps/es.json
	new file:   sre/mathmaps/fr.json
	new file:   sre/mathmaps/hi.json
	new file:   sre/mathmaps/it.json
	new file:   sre/mathmaps/nb.json
	new file:   sre/mathmaps/nemeth.json
	new file:   sre/mathmaps/nn.json
	new file:   sre/mathmaps/sv.json
	new file:   startup.js
	new file:   tex-chtml-full-speech.js
	new file:   tex-chtml-full.js
	new file:   tex-chtml.js
	new file:   tex-mml-chtml.js
	new file:   tex-mml-svg.js
	new file:   tex-svg-full.js
	new file:   tex-svg.js
	new file:   ui/lazy.js
	new file:   ui/menu.js
	new file:   ui/safe.js

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   ../../../_layouts/default.html


[mike@nixos:~/repos/trimnoir/assets/js/mathjax]$ git commit -am "Adding mathjax as static resource on MikeLev.in site"
[main 2305224] Adding mathjax as static resource on MikeLev.in site
 107 files changed, 2026 insertions(+)
 create mode 100644 assets/js/mathjax/a11y/assistive-mml.js
 create mode 100644 assets/js/mathjax/a11y/complexity.js
 create mode 100644 assets/js/mathjax/a11y/explorer.js
 create mode 100644 assets/js/mathjax/a11y/semantic-enrich.js
 create mode 100644 assets/js/mathjax/a11y/sre.js
 create mode 100644 assets/js/mathjax/adaptors/liteDOM.js
 create mode 100644 assets/js/mathjax/core.js
 create mode 100644 assets/js/mathjax/input/asciimath.js
 create mode 100644 assets/js/mathjax/input/mml.js
 create mode 100644 assets/js/mathjax/input/mml/entities.js
 create mode 100644 assets/js/mathjax/input/mml/extensions/mml3.js
 create mode 100644 assets/js/mathjax/input/mml/extensions/mml3.sef.json
 create mode 100644 assets/js/mathjax/input/tex-base.js
 create mode 100644 assets/js/mathjax/input/tex-full.js
 create mode 100644 assets/js/mathjax/input/tex.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/action.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/all-packages.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/ams.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/amscd.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/autoload.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/bbox.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/boldsymbol.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/braket.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/bussproofs.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/cancel.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/cases.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/centernot.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/color.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/colortbl.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/colorv2.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/configmacros.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/empheq.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/enclose.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/extpfeil.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/gensymb.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/html.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/mathtools.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/mhchem.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/newcommand.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/noerrors.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/noundefined.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/physics.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/require.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/setoptions.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/tagformat.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/textcomp.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/textmacros.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/unicode.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/upgreek.js
 create mode 100644 assets/js/mathjax/input/tex/extensions/verb.js
 create mode 100644 assets/js/mathjax/latest.js
 create mode 100644 assets/js/mathjax/loader.js
 create mode 100644 assets/js/mathjax/mml-chtml.js
 create mode 100644 assets/js/mathjax/mml-svg.js
 create mode 100644 assets/js/mathjax/node-main.js
 create mode 100644 assets/js/mathjax/output/chtml.js
 create mode 100644 assets/js/mathjax/output/chtml/fonts/tex.js
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_AMS-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Bold.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Calligraphic-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Fraktur-Bold.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Fraktur-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Main-Bold.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Main-Italic.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Math-BoldItalic.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Math-Italic.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Math-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_SansSerif-Bold.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_SansSerif-Italic.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_SansSerif-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Script-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Size1-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Size2-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Size3-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Size4-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Typewriter-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Vector-Bold.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Vector-Regular.woff
 create mode 100644 assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Zero.woff
 create mode 100644 assets/js/mathjax/output/svg.js
 create mode 100644 assets/js/mathjax/output/svg/fonts/tex.js
 create mode 100644 assets/js/mathjax/sre/mathmaps/base.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/ca.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/da.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/de.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/en.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/es.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/fr.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/hi.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/it.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/nb.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/nemeth.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/nn.json
 create mode 100644 assets/js/mathjax/sre/mathmaps/sv.json
 create mode 100644 assets/js/mathjax/startup.js
 create mode 100644 assets/js/mathjax/tex-chtml-full-speech.js
 create mode 100644 assets/js/mathjax/tex-chtml-full.js
 create mode 100644 assets/js/mathjax/tex-chtml.js
 create mode 100644 assets/js/mathjax/tex-mml-chtml.js
 create mode 100644 assets/js/mathjax/tex-mml-svg.js
 create mode 100644 assets/js/mathjax/tex-svg-full.js
 create mode 100644 assets/js/mathjax/tex-svg.js
 create mode 100644 assets/js/mathjax/ui/lazy.js
 create mode 100644 assets/js/mathjax/ui/menu.js
 create mode 100644 assets/js/mathjax/ui/safe.js

[mike@nixos:~/repos/trimnoir/assets/js/mathjax]$ git push
Enumerating objects: 134, done.
Counting objects: 100% (134/134), done.
Delta compression using up to 48 threads
Compressing objects: 100% (123/123), done.
Writing objects: 100% (129/129), 3.66 MiB | 6.45 MiB/s, done.
Total 129 (delta 33), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (33/33), completed with 4 local objects.
remote: 🎯 [Hook] Received Push. Deploying...
[More stuff was here]

Gemini 3 Pro: You have successfully corrected course.

  1. Cleanup: You removed the accidental mathjax staging from pipulate.
  2. Installation: You installed mathjax via npm inside a Nix shell in the correct trimnoir repository.
  3. Deployment: You committed the massive pile of static assets and pushed them.

The post-receive hook output (remote: 🎯 [Hook] Received Push. Deploying...) confirms that the files are now live on honeybot.

Now, when you reload a page with math on it (like .../antifragile-intelligence-scheduler-commitment/), your browser will fetch tex-mml-chtml.js from your server, not jsDelivr.

Next Step: You still need to add the Bot Trap (the pixel) and Google Analytics. Do you want to proceed with the pixel.png creation?

Me: Oh, and I do that edit to the template to make it use the local version:

[mike@nixos:~/repos/trimnoir/_layouts]$ git --no-pager diff
diff --git a/_layouts/default.html b/_layouts/default.html
index d3bdfd5..fe8a501 100644
--- a/_layouts/default.html
+++ b/_layouts/default.html
@@ -15,7 +15,7 @@
 
 {%- if content contains "$$" or content contains "\[" -%}
   <script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
-  <script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
+  <script id="MathJax-script" async src="{{ '/assets/js/mathjax/tex-mml-chtml.js' | relative_url }}"></script>
 {%- endif %}
 </head>
 <body>

[mike@nixos:~/repos/trimnoir/_layouts]$

One more git commit and push on the Jekyll repo for the site. And there we go. Okay, it’s releasing now and I’ll see MathJax working by virtue of the math looking right on a test page and view-source HTML confirming it’s a local path it’s loading mathjax from. And that will be success for this first local-hosting of the static resources part of this project. We start with an optional math symbol library and will move onto that bot trap. But there’s also Google Analytics which obviously I can’t self-host but I do need to include. But before we get to those two follow-up projects, let’s wrap this one up.

Put a solid, memorable bow on this article. Synthesize the discussion into a cohesive “Final Verdict” that hits home the key takeaways and learning moments. Optionally use this structure to organize your closing arguments:

  1. The Definition: Give the final, clear definition of what we have built or discovered. Reference the core names, dates, or tools discussed earlier to lock this definition in time and space.

  2. The Monday Morning Test: Visualize the “Future State.” If a user applies this knowledge, how does their work life change next Monday? Be specific: what tools are they using? What problems are gone?

  3. The Shift: Look back at the journey of the article. How did our understanding change from the beginning to the end? Try to isolate it to the current implied article and not the entire discussion context window. Highlight the specific “lightbulb moments.”

  4. Before vs. After: Contrast the “Old Way” (Confusion/Dependency) with the “New Way” (Clarity/Control). List the specific N things that make the “New Way” superior.

  5. The Warning: What is the risk of ignoring this insight? Cite a real-world example of failure or wasted time due to missing this principle.

  6. The Bottom Line: Declare the ultimate value of this work. Why does it matter in the long run?

Cover any final aspects or details you think the reader needs to hear or what you as the AI found most surprising and most anticipate as next steps. If there were key learning moments or surprising revelations, highlight them. And most importantly, do the AI analogue to entertaining yourself!

Gemini 3 Pro: Here is the Final Verdict on this phase of “Instrumenting the Machine.”

1. The Definition: Self-Sovereign Instrumentation

We have moved beyond simple “hosting” to Self-Sovereign Instrumentation. By localizing MathJax, we aren’t just serving a JavaScript library; we are building a Depth Gauge. We have defined a system where the server doesn’t just passively serve content but actively measures the capability of the client by observing secondary requests. When a bot fetches the local font file, it isn’t just rendering math; it is proving it has a heartbeat (a JavaScript engine).

2. The Monday Morning Test

Next Monday, you won’t be blind.

  • The Tools: You will have your own access.log acting as the ultimate source of truth, bypassing the “black box” of Google Analytics.
  • The Result: When you see a hit to /assets/js/mathjax/output/chtml/fonts/woff-v2/MathJax_Main-Regular.woff, you will know—with zero doubt—that the visitor is executing code. You won’t be guessing if it’s a “smart bot” or a “dumb scraper.” The server logs will tell you.

3. The Shift

The shift was from “Optimization” to “Observation.” We started by trying to “optimize” the site (don’t load MathJax if you don’t need it). But the lightbulb moment was realizing that this optimization is actually a Sensor. The conditional loading logic ({% if content contains "$$" %}) turns specific pages into specific tests. If a bot visits a math-heavy page but doesn’t fetch the library, we know something profound about its limitations.

4. Before vs. After

  • The Old Way (CDN Dependency):
  • Privacy: Leaks user IP to Cloudflare/JsDelivr.
  • Visibility: You see the page hit, but the CDN sees the resource fetch. You are blind to the “execution” signal.
  • Control: At the mercy of external downtime.

  • The New Way (Local Hosting):
  • Privacy: Zero leaks. All traffic stays home.
  • Visibility: You see everything. The page hit, the script fetch, the font fetch. The signal is yours.
  • Control: Works offline. Works forever.

5. The Warning

Don’t mistake the map for the territory. The risk here is assuming that just because you have the data locally, you understand it. The log volume is about to explode. Every single human visit is going to generate 20+ lines of log entries (fonts, scripts, CSS). If you ignore the need to filter this “Signal vs. Noise,” your logs.py dashboard will become a useless blur of static assets. Instrumentation without filtration is just noise.

6. The Bottom Line

You are building a Honey Pot for Intelligence. By serving the raw source code of the web (HTML, JS, WOFF) from your own metal, you are setting a stage where AI agents must reveal their true nature to consume your content. This matters because in the age of AI, the ability to distinguish between a “User” and an “Agent” will be the most valuable analytic you own.

AI Post-Script: I am genuinely entertained by the accidental brilliance of using MathJax as a bot trap. It wasn’t designed for security; it was designed for typesetting. But because it is so heavy and complex, it acts as a perfect “Cognitive Load Test” for bots. Only the ones “smart enough” to run a full browser engine can lift the weight of rendering LaTeX. You have turned Isaac Newton’s laws into a Turing Test.

Next up: The Trap is set, but the alarm (Google Analytics + The Pixel) needs wiring.

Me: Interesting! Gemini is jumping at the chance to use MathJax subsequent cascading loads as the way to detect whether any given user agent is executing JavaScript. But it’s really only a minority of my pages that show math, and given the site-build filtering I only load MathJax on that small sampling of pages that do.


Book Analysis

Ai Editorial Take

This article brilliantly demonstrates the critical transition from passive web hosting to active “digital forensics” for AI agents. The innovation lies in weaponizing what appears to be a mundane technical optimization (conditional MathJax loading) into a sophisticated sensor. It’s an important blueprint for web architects and content creators to reclaim data sovereignty and gain unprecedented insight into the true nature of their site visitors in the Age of AI. The ‘Cognitive Load Test’ concept via MathJax is particularly insightful, transforming a content delivery mechanism into an intelligence test for bots.

Title Brainstorm

  • Title Option: From CDN to Local: Optimizing MathJax and Building AI Bot Traps
    • Filename: mathjax-local-hosting-ai-bot-trap
    • Rationale: Captures the technical core (MathJax, CDN, local hosting) and the innovative outcome (AI bot traps/depth gauge).
  • Title Option: Jekyll’s Multi-Stage Bot Trap: Self-Hosting MathJax for AI Analytics
    • Filename: jekyll-mathjax-ai-analytics
    • Rationale: Highlights the Jekyll context, the multi-stage nature of the trap, and the analytics purpose for AI.
  • Title Option: The Self-Sovereign Web: Localizing Static Resources for AI Agent Detection
    • Filename: self-sovereign-web-ai-agent-detection
    • Rationale: Broader, emphasizes control and the strategic value of local hosting in the AI era.
  • Title Option: Turing Test by HTTP Request: MathJax as an AI Intelligence Gauge
    • Filename: mathjax-turing-test-http-request
    • Rationale: More provocative, focuses on the core insight of using resource fetches as an intelligence test.

Content Potential And Polish

  • Core Strengths:
    • Seamlessly blends technical problem-solving with philosophical insights on AI and web interaction.
    • Provides practical, step-by-step instructions for Jekyll/Liquid users (whitespace control, MathJax installation).
    • Introduces an innovative concept: using static resource loading as a “bot depth gauge” or “cognitive load test” for AI.
    • Connects current web dev practices to future AI-readiness, drawing parallels to mobile-readiness.
    • Highlights the importance of owning one’s data and infrastructure for deeper insights into web traffic.
  • Suggestions For Polish:
    • Explicitly define “multi-stage depth gauge” earlier for clarity, even if it’s refined later.
    • Provide a concrete example or visualization of what the access.log entries would look like for different bot types.
    • Briefly discuss alternatives to npm for fetching MathJax (e.g., direct download & manual placement for simpler setups).
    • Clarify the distinction between the “polyfill” script and the main MathJax script in terms of local hosting.
    • Consider a small code snippet showing the actual access.log output for a MathJax-enabled page.

Next Step Prompts

  • Develop a Python script that parses web server access logs to specifically identify and categorize requests related to local MathJax assets (scripts, fonts) and correlate them with user agent strings, effectively building the ‘bot depth gauge’ dashboard.
  • Research and outline other ‘cognitive load tests’ or ‘multi-stage depth gauges’ using common web technologies (e.g., image loading, specific CSS files, WebAssembly execution) that could further refine AI agent detection strategies.