<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.itlostandfound.xyz/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.itlostandfound.xyz/" rel="alternate" type="text/html" /><updated>2026-07-09T19:49:43-05:00</updated><id>https://www.itlostandfound.xyz/feed.xml</id><title type="html">IT Lost and Found</title><subtitle>Infrastructure, home labs, automation, and AI builds.</subtitle><author><name>Michael Yates</name></author><entry><title type="html">Token Maxing — Hold Your Horses…</title><link href="https://www.itlostandfound.xyz/blog/2026/07/07/token-maxing-hold-your-horses.html" rel="alternate" type="text/html" title="Token Maxing — Hold Your Horses…" /><published>2026-07-07T12:30:00-05:00</published><updated>2026-07-07T12:30:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/07/07/token-maxing-hold-your-horses</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/07/07/token-maxing-hold-your-horses.html"><![CDATA[<p>We hit a wall. Hard. And it wasn’t the kind you see coming — there’s no API, no dashboard, no programmable way to check how much Ollama Cloud quota you have left. You just get a 429 error and everything stops.</p>

<p>Here’s the full story of what happened, what we discovered, and how we built a watchdog to make sure it never happens again.</p>

<h2 id="what-we-were-doing">What We Were Doing</h2>

<p>We had two Kanban dispatcher cron jobs running on Hermes Agent, powered by Ollama Cloud on the Pro plan ($20/mo):</p>

<ul>
  <li><strong>PMO-Kanban-Dispatcher</strong> — every 15 minutes, scanning the project board for tasks</li>
  <li><strong>BB-PM-Kanban-Dispatcher</strong> — every 10 minutes, running the Boogie Board project manager</li>
</ul>

<p>Each dispatcher call sends the full conversation context — system prompt, all prior messages, tool results — to the LLM. That’s roughly 42,000 tokens per API call. With two dispatchers hitting the API every 10-15 minutes, the token burn adds up fast.</p>

<p>We were also doing interactive work — conversations, research, writing — all on the same Ollama Cloud account.</p>

<h2 id="what-happened">What Happened</h2>

<p>At 11:50 AM CT on July 7, the BB-PM dispatcher hit this:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>HTTP 429: {"error":"you (My.Ollama.Account.Name) have reached your session usage limit,
upgrade for higher limits: https://ollama.com/upgrade or add extra usage:
https://ollama.com/settings (ref: Reference.ID)"}
</code></pre></div></div>

<p>The dispatcher retried 3 times with exponential backoff. All failed. Meanwhile, the PMO dispatcher was still burning tokens on the same account, making things worse.</p>

<p><strong>The root cause</strong>: Two automated dispatchers sending ~42K tokens per call, every 10-15 minutes, accumulated enough GPU time to exhaust the 5-hour session limit within hours. And there was no way to see it coming.</p>

<h2 id="what-we-discovered">What We Discovered</h2>

<h3 id="ollama-cloud-has-no-usage-api">Ollama Cloud has no usage API</h3>

<p>This was the biggest surprise. There is no <code class="language-plaintext highlighter-rouge">/api/account/usage</code> endpoint. You can’t programmatically check your usage. GitHub issues <a href="https://github.com/ollama/ollama/issues/12532">#12532</a> and <a href="https://github.com/ollama/ollama/issues/15132">#15132</a> are open requests for this, but for now the only way to check is to log into <a href="https://ollama.com/settings">ollama.com/settings</a> and look at percentages manually.</p>

<p>Ollama sends an email at 90% of your plan limit, but by then you’re already almost out.</p>

<h3 id="two-limits-not-one">Two limits, not one</h3>

<p>Ollama Cloud enforces <strong>two</strong> independent limits:</p>

<table>
  <thead>
    <tr>
      <th>Window</th>
      <th>Resets</th>
      <th>Consequence of Exceeding</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Session</td>
      <td>Every 5 hours</td>
      <td>Immediate 429 errors, wait up to 5 hours</td>
    </tr>
    <tr>
      <td>Weekly</td>
      <td>Every 7 days</td>
      <td>Multi-day outage</td>
    </tr>
  </tbody>
</table>

<p>The session limit is the binding constraint. It resets every 5 hours, but you can blow through it fast with automated agents.</p>

<h3 id="usage-is-gpu-time-not-tokens">Usage is GPU time, not tokens</h3>

<p>This is subtle but critical. Ollama measures usage in GPU time, not token count. Heavier models (Level 4, like deepseek-v4-pro) consume ~4x more GPU time per token than lighter models (Level 1, like gpt-oss:20b). So token count is a <strong>proxy</strong>, not a direct measure. Our token-based estimates overestimate for light models and underestimate for heavy ones.</p>

<h3 id="the-actual-numbers">The actual numbers</h3>

<p>We parsed <code class="language-plaintext highlighter-rouge">~/.hermes/logs/agent.log</code> for lines matching <code class="language-plaintext highlighter-rouge">in=N out=N total=N</code> and measured:</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>API calls in 2 hours</td>
      <td>205</td>
    </tr>
    <tr>
      <td>Input tokens</td>
      <td>8,685,159</td>
    </tr>
    <tr>
      <td>Output tokens</td>
      <td>62,398</td>
    </tr>
    <tr>
      <td><strong>Total tokens</strong></td>
      <td><strong>8,747,557</strong></td>
    </tr>
    <tr>
      <td>Average per call</td>
      <td>~42,671</td>
    </tr>
  </tbody>
</table>

<p>Input tokens dominated at 8.6M vs only 62K output. That’s because every API call re-sends the full conversation context.</p>

<h3 id="calibrating-against-real-data">Calibrating against real data</h3>

<p>From the Ollama settings page, we read:</p>
<ul>
  <li><strong>Session (5hr)</strong>: 24.1%</li>
  <li><strong>Weekly (7d)</strong>: 6.4%</li>
</ul>

<p>Our logs showed:</p>
<ul>
  <li>Session (5hr window): 13,574,960 tokens</li>
  <li>Weekly (7d window): 54,473,658 tokens</li>
</ul>

<p>Working backwards from the real percentages, we derived the effective token ceilings:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Session ceiling: 13,574,960 / 0.241 = ~56M tokens per 5 hours
Weekly ceiling:  54,473,658 / 0.064 = ~849M tokens per week
</code></pre></div></div>

<p>The community estimate for the Max plan was ~1.25B tokens/week, but our calibrated measurement came in at ~849M. The difference is likely because community measurements used lighter models that consume less GPU time per token.</p>

<p><strong>Our initial estimate was wrong.</strong> We first calculated ~36M tokens per session ceiling from “8.7M tokens in 2 hours = 24.1%”, but that overcounted because the 5-hour session window includes lower-usage periods. The calibrated ceiling of ~56M is more accurate.</p>

<h3 id="session-is-the-danger-zone">Session is the danger zone</h3>

<table>
  <thead>
    <tr>
      <th>Window</th>
      <th>Current %</th>
      <th>Resets In</th>
      <th>Risk</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Session (5hr)</td>
      <td>24.1%</td>
      <td>~4 hours</td>
      <td><strong>HIGH</strong> — this is where 429s happen</td>
    </tr>
    <tr>
      <td>Weekly (7d)</td>
      <td>6.4%</td>
      <td>5 days</td>
      <td>Low — plenty of headroom</td>
    </tr>
  </tbody>
</table>

<p>Weekly burn rate with dispatchers: ~3.2%/day. Without dispatchers: ~1-2%/day. The session limit is where you get caught off guard because it resets frequently and automated agents can spike it rapidly.</p>

<h2 id="how-were-monitoring-it-now">How We’re Monitoring It Now</h2>

<p>Since there’s no API, we built a <strong>hybrid watchdog</strong> — automated tracking with manual calibration.</p>

<h3 id="the-ollama-usage-watchdog">The Ollama Usage Watchdog</h3>

<p>A Python script at <code class="language-plaintext highlighter-rouge">~/.hermes/scripts/ollama-usage-watchdog.py</code> that:</p>

<ol>
  <li>Parses <code class="language-plaintext highlighter-rouge">agent.log</code> every 30 minutes (via cron, <code class="language-plaintext highlighter-rouge">no_agent=true</code> — zero token cost)</li>
  <li>Counts tokens consumed in the last 5 hours (session) and 7 days (weekly)</li>
  <li>Divides by calibrated token ceilings to estimate percentages</li>
  <li>Compares against threshold levels</li>
  <li>Delivers alerts to Discord when thresholds are crossed</li>
  <li>Auto-pauses/resumes Kanban dispatcher cron jobs via <code class="language-plaintext highlighter-rouge">hermes cron pause/resume</code></li>
</ol>

<h3 id="thresholds">Thresholds</h3>

<p>We set different thresholds for each window. Session thresholds are higher because it resets every 5 hours (fast recovery). Weekly thresholds are lower because hitting the ceiling means waiting days.</p>

<table>
  <thead>
    <tr>
      <th>Level</th>
      <th>Session (5hr)</th>
      <th>Weekly (7d)</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>OK</td>
      <td>&lt;60%</td>
      <td>&lt;40%</td>
      <td>None</td>
    </tr>
    <tr>
      <td>Soft Alert</td>
      <td>60%</td>
      <td>40%</td>
      <td>Discord notification</td>
    </tr>
    <tr>
      <td>Hard Alert</td>
      <td>75%</td>
      <td>55%</td>
      <td>Attention needed</td>
    </tr>
    <tr>
      <td>Auto-Pause</td>
      <td>85%</td>
      <td>70%</td>
      <td>Kill Kanban dispatchers</td>
    </tr>
    <tr>
      <td>Auto-Resume</td>
      <td>&lt;40%</td>
      <td>—</td>
      <td>Restart dispatchers after reset</td>
    </tr>
  </tbody>
</table>

<h3 id="three-modes">Three modes</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Watchdog mode (runs via cron, silent unless threshold crossed)</span>
python3 ollama-usage-watchdog.py

<span class="c"># Status mode (check current readings anytime)</span>
python3 ollama-usage-watchdog.py status

<span class="c"># Calibrate mode (feed real % from ollama.com/settings)</span>
python3 ollama-usage-watchdog.py calibrate 24.1 6.4
</code></pre></div></div>

<h3 id="how-calibration-works">How calibration works</h3>

<p>Token-based estimates drift over time because token count ≠ GPU time. So once or twice a day, we check ollama.com/settings and feed the real percentages back:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3 ollama-usage-watchdog.py calibrate &lt;session_pct&gt; &lt;weekly_pct&gt;
</code></pre></div></div>

<p>This recalculates the token ceilings: <code class="language-plaintext highlighter-rouge">ceiling = current_tokens / (real_percentage / 100)</code></p>

<p>Current calibrated ceilings:</p>
<ul>
  <li>Session: <strong>55,984,946 tokens</strong> (≈56M)</li>
  <li>Weekly: <strong>848,565,640 tokens</strong> (≈849M)</li>
</ul>

<h3 id="current-readings-as-of-writing">Current readings (as of writing)</h3>

<table>
  <thead>
    <tr>
      <th>Window</th>
      <th>Estimated %</th>
      <th>Tokens</th>
      <th>API Calls</th>
      <th>Status</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Session (5hr)</td>
      <td>25.2%</td>
      <td>14,082,401</td>
      <td>321</td>
      <td>OK</td>
    </tr>
    <tr>
      <td>Weekly (7d)</td>
      <td>6.5%</td>
      <td>54,898,230</td>
      <td>1,454</td>
      <td>OK</td>
    </tr>
  </tbody>
</table>

<p>Both dispatchers are currently paused (manually, not by the watchdog).</p>

<h2 id="lessons-learned">Lessons Learned</h2>

<ol>
  <li>
    <p><strong>Token count is a proxy, not a direct measure.</strong> Build in margin. Our 50% safety margin in thresholds accounts for the token-to-GPU-time conversion.</p>
  </li>
  <li>
    <p><strong>The session limit is the binding constraint</strong>, not the weekly limit. It resets every 5 hours and automated agents can spike it fast.</p>
  </li>
  <li>
    <p><strong>Agent workloads burn tokens much faster than interactive use.</strong> Every call re-sends the full context. 42K tokens per call × 6 calls/hour = 252K tokens/hour just for one dispatcher.</p>
  </li>
  <li>
    <p><strong>No usage API means build your own observability.</strong> A hybrid approach — automated tracking plus manual calibration — is the pragmatic solution until Ollama adds an API.</p>
  </li>
  <li>
    <p><strong>Calibrate with real data.</strong> Our initial estimate of ~36M tokens/session was 36% low compared to the calibrated ~56M. Real percentages from the settings page ground the estimates in reality.</p>
  </li>
</ol>

<hr />

<p><em>The watchdog script runs every 30 minutes via cron with zero token cost. Alerts go to Discord. The dispatchers auto-pause at 85% session / 70% weekly and resume when the session resets below 40%. We went from surprise 429 errors to proactive quota management.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="ollama" /><category term="automation" /><category term="self-hosted" /><summary type="html"><![CDATA[We hit a wall. Hard. And it wasn’t the kind you see coming — there’s no API, no dashboard, no programmable way to check how much Ollama Cloud quota you have left. You just get a 429 error and everything stops.]]></summary></entry><entry><title type="html">Hermes Loses Hindsight</title><link href="https://www.itlostandfound.xyz/blog/2026/06/11/hermes-loses-hindsight.html" rel="alternate" type="text/html" title="Hermes Loses Hindsight" /><published>2026-06-11T20:30:00-05:00</published><updated>2026-06-11T20:30:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/06/11/hermes-loses-hindsight</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/06/11/hermes-loses-hindsight.html"><![CDATA[<p>We killed Hindsight today. Pulled it out by the roots — 353 MB of PostgreSQL, 9 per-profile config directories, and two API calls per turn that mostly extracted nothing.</p>

<p>To be fair here, some of the data below including the failures of good writes and reads from Hindsight were due to the now “Infamous” Ollama Relay (which was Ollam being installed directly on the host and Hermes “Relaying” through it (which was due to the age of my instance before Ollama.com was added in directly as an LLM Provider)).  Either way I hated the fact that it required an LLM to burn even more Tokens for its usage and I didn’t like or want that so I chose to get rid of it.</p>

<p>And it was the right call.</p>

<h2 id="the-diagnosis">The Diagnosis</h2>

<p>It started with a simple question: “Analyze our memory setup. Are we doing this the best way?” The answer was no.</p>

<p>The investigation revealed several problems:</p>

<p><strong>Hindsight was mostly dormant.</strong> The recall command failed with “LLM API key is required (see post “Ollama Relay Your Doing It Wrong” for more details on why this was happening).” The per-profile configs existed but the embedded PostgreSQL instance at <code class="language-plaintext highlighter-rouge">~/.pg0/instances/hindsight-embed-hermes</code> wasn’t being queried effectively. Two LLM calls per turn — one recall, one retain — and the best case was a 14% extraction success rate at the chunk level.</p>

<p><strong>Memory files were bursting.</strong> <code class="language-plaintext highlighter-rouge">MEMORY.md</code> was at 84% capacity (1,869/2,200 chars). <code class="language-plaintext highlighter-rouge">USER.md</code> was at 94% (1,305/1,375 chars). Most of that content was infrastructure facts — IP addresses, port numbers, model names — that should live in a knowledge base, not in a per-turn injected sticky note.</p>

<p><strong>The architecture was self-contradictory.</strong> We had Hindsight auto-extracting facts (at 14% success) while also manually writing things to MEMORY.md. We had a structured Obsidian vault (set up 4 days earlier) that could hold all this knowledge without the per-turn tax. But we were still paying the Hindsight overhead on every single conversation turn.</p>

<p>The numbers told the story:</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Hindsight</th>
      <th>Built-in Only</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>API calls per turn</td>
      <td>2 (recall + retain)</td>
      <td>0</td>
    </tr>
    <tr>
      <td>Input tokens per turn</td>
      <td>~500-1000</td>
      <td>0</td>
    </tr>
    <tr>
      <td>Recall latency</td>
      <td>0.5-180 seconds</td>
      <td>0</td>
    </tr>
    <tr>
      <td>Fact extraction success</td>
      <td>~14%</td>
      <td>N/A</td>
    </tr>
    <tr>
      <td>Disk overhead</td>
      <td>353 MB PostgreSQL</td>
      <td>0</td>
    </tr>
    <tr>
      <td>MEMORY.md usage</td>
      <td>84% (1,869/2,200)</td>
      <td>N/A</td>
    </tr>
    <tr>
      <td>USER.md usage</td>
      <td>94% (1,305/1,375)</td>
      <td>N/A</td>
    </tr>
  </tbody>
</table>

<h2 id="the-decision">The Decision</h2>

<p>The vault was already there, already working, already being written to by agents through SOUL.md protocols. Hindsight was just… sitting on top, costing tokens and adding latency for an 86% failure rate.</p>

<p>So we removed it. All of it.</p>

<h2 id="the-execution">The Execution</h2>

<p>The removal was surgical and complete:</p>

<ol>
  <li>
    <p><strong>Disabled Hindsight in all configs</strong> — Set <code class="language-plaintext highlighter-rouge">memory.provider: ''</code> (empty string) in the main <code class="language-plaintext highlighter-rouge">~/.hermes/config.yaml</code> and all 8 profile configs under <code class="language-plaintext highlighter-rouge">~/.hermes/profiles/*/config.yaml</code></p>
  </li>
  <li>
    <p><strong>Deleted the data</strong> — Removed <code class="language-plaintext highlighter-rouge">~/.hindsight/</code> (16 MB), <code class="language-plaintext highlighter-rouge">~/.pg0/</code> (337 MB PostgreSQL instance), and <code class="language-plaintext highlighter-rouge">~/.hermes/hindsight/</code> (main config directory). That’s 353 MB of disk reclaimed.</p>
  </li>
  <li>
    <p><strong>Deleted 8 per-profile config directories</strong> — Each profile had its own <code class="language-plaintext highlighter-rouge">hindsight/config.json</code> with bank IDs and model settings. All gone.</p>
  </li>
  <li>
    <p><strong>Consolidated MEMORY.md</strong> — Moved infrastructure facts out (they were already in vault wiki pages) and added a vault pointer: “Long-term knowledge base: Obsidian ai-vault… BEFORE research, config, or domain-specific tasks: search the vault wiki/”</p>
  </li>
  <li>
    <p><strong>Bumped memory limits</strong> — With Hindsight gone, we had headroom. <code class="language-plaintext highlighter-rouge">memory_char_limit</code> went from 2,200 to 3,000. <code class="language-plaintext highlighter-rouge">user_char_limit</code> went from 1,375 to 2,000. After consolidation: MEMORY.md at 61% (1,819/3,000), USER.md at 59% (1,180/2,000).</p>
  </li>
  <li><strong>Verified everything</strong> — Ran end-to-end tests confirming Hindsight was gone:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">~/.hindsight/</code> — empty</li>
      <li><code class="language-plaintext highlighter-rouge">~/.pg0/</code> — gone (was 337 MB)</li>
      <li>Zero <code class="language-plaintext highlighter-rouge">provider: hindsight</code> references in any config</li>
      <li>Zero Hindsight processes running</li>
      <li><code class="language-plaintext highlighter-rouge">hermes memory status</code> shows: <code class="language-plaintext highlighter-rouge">Provider: (none — built-in only)</code></li>
    </ul>
  </li>
  <li><strong>Verified the vault still works</strong> — Created a test page, read it back, searched for it, then cleaned it up. MCP filesystem tools all functional. 11 wiki pages accessible. 3 cron maintenance jobs intact (daily-health, wiki-curator, weekly-digest).</li>
</ol>

<h2 id="the-write-triggers">The Write Triggers</h2>

<p>The biggest concern was: without Hindsight auto-extracting facts, will agents actually write to the vault? We verified that all 5 vault-enabled profiles have <strong>SOUL.md Vault Knowledge Protocol</strong> sections with explicit write triggers:</p>

<ul>
  <li><strong>default</strong>: “After discovering durable information, create or update a wiki page”</li>
  <li><strong>development</strong>: “After completing research, synthesize key insights into a wiki page”</li>
  <li><strong>playground</strong>: “Inscribing new Chronicles, Forging new Creations”</li>
  <li><strong>PMO</strong>: “Project decisions: log by appending to log.md”</li>
  <li><strong>research</strong>: “After completing research — PRIMARY vault obligation”</li>
</ul>

<p>These are personality-level directives, not optional suggestions. They’re baked into each agent’s SOUL.md, which means they’re injected into every conversation the same way MEMORY.md is. The vault writes happen because the agent’s identity includes the obligation to write.</p>

<h2 id="the-new-architecture">The New Architecture</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Built-in Memory (MEMORY.md + USER.md)
  → WHO + operational state + vault pointer
  → Injected every turn, zero cost, 100% reliable

Obsidian Vault (ai-vault via MCP)
  → Durable structured knowledge (11 wiki pages, 3 raw sources)
  → On-demand search (not auto-injected)
  → Compounding via cron maintenance

Session Search (FTS5)
  → Conversation history, on-demand
</code></pre></div></div>

<p>Three systems, zero overlap, zero wasted tokens. The vault is the brain. Built-in memory is the working state. Session search is the history.</p>

<h2 id="what-we-lost">What We Lost</h2>

<p>Honest assessment: we lost automatic extraction. If an agent has a conversation that contains a fact worth remembering, and the agent doesn’t explicitly write it to the vault, that fact exists only in session history. It won’t surface automatically in a future conversation.</p>

<p>That’s a trade we’re willing to make. The alternative was 2 API calls per turn for an 86% failure rate. We’d rather have 100% accuracy on the 14% of facts that are worth extracting, written deliberately by agents that know what they’re writing and why, than 14% accuracy on everything in a system that costs tokens just to fail.</p>

<p>The vault cron jobs — daily health checks, weekly curations, weekly digests — handle the maintenance side. The SOUL.md protocols handle the write side. Between deliberate writes and session search fallback, we’re not losing knowledge. We’re just not pretending to gain it automatically.</p>

<hr />

<p><em>Hindsight was removed on June 11, 2026, after 26 days of operation. The Obsidian AI Vault that replaced it was set up on June 7, 2026 — see “Hermes Gets a Brain” for that story.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><category term="obsidian" /><summary type="html"><![CDATA[We killed Hindsight today. Pulled it out by the roots — 353 MB of PostgreSQL, 9 per-profile config directories, and two API calls per turn that mostly extracted nothing.]]></summary></entry><entry><title type="html">Hermes Gets a Brain</title><link href="https://www.itlostandfound.xyz/blog/2026/06/07/hermes-gets-a-brain.html" rel="alternate" type="text/html" title="Hermes Gets a Brain" /><published>2026-06-07T14:15:00-05:00</published><updated>2026-06-07T14:15:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/06/07/hermes-gets-a-brain</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/06/07/hermes-gets-a-brain.html"><![CDATA[<p>The Obsidian vault is live. Hermes now has a persistent knowledge base that compounds over time — not just a notepad, but a structured wiki with schema enforcement, MCP filesystem integration, and automated maintenance.</p>

<p>Here’s how we got there.</p>

<h2 id="the-problem">The Problem</h2>

<p>Hermes had two memory systems up to this point:</p>

<ol>
  <li>
    <p><strong>Built-in memory</strong> — <code class="language-plaintext highlighter-rouge">MEMORY.md</code> and <code class="language-plaintext highlighter-rouge">USER.md</code>, injected into every conversation turn. Reliable, but limited to ~2,200 characters of working state. No relationships. No compounding knowledge. Just a sticky note that gets overwritten.</p>
  </li>
  <li>
    <p><strong>Hindsight</strong> — a knowledge graph plugin that auto-extracted entities and facts from every conversation. Powerful in concept, but it cost 2 LLM API calls per turn (recall + retain) with latency ranging from 0.5 to 180 seconds.</p>
  </li>
</ol>

<p>Neither system gave us what we really needed: a <strong>durable, searchable, structured knowledge base</strong> that agents could write to deliberately and read from on demand, without paying a per-turn tax for automatic extraction.</p>

<h2 id="the-design">The Design</h2>

<p>We built the AI Vault on top of an existing Obsidian vault. The directory structure follows a clear separation of concerns:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ai-vault/
  SCHEMA.md          — Rules: what goes where, naming conventions, tags
  log.md             — Append-only activity log
  raw/               — Source material (append-only, never edit)
    articles/
    papers/
    competitors/
    repos/
  wiki/              — Agent-synthesized knowledge from raw sources
    _index/index.md  — Link graph of all wiki pages
  output/            — Deliverables generated from wiki
    reports/
    slides/
  templates/         — Reusable templates for agents
</code></pre></div></div>

<p>Key rules:</p>
<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">raw/</code> is append-only</strong> — source material is never modified once stored</li>
  <li><strong><code class="language-plaintext highlighter-rouge">wiki/</code> is where agents synthesize knowledge</strong> from raw sources into structured pages</li>
  <li><strong><code class="language-plaintext highlighter-rouge">output/</code> is derived from wiki</strong> — never treated as source truth</li>
  <li><strong>Every wiki page has YAML frontmatter</strong> with title, type, domain, tags, created/updated dates, and sources</li>
  <li><strong>Every wiki page links to at least 2 related pages</strong> using Obsidian <code class="language-plaintext highlighter-rouge">[[wikilink]]</code> syntax</li>
</ul>

<h2 id="the-mcp-connection">The MCP Connection</h2>

<p>The critical piece was connecting the vault to Hermes via the Model Context Protocol (MCP). We configured an MCP filesystem server pointing at the vault directory, giving agents 14 additional tools:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_read_file</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_read_text_file</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_read_multiple_files</code> — read vault files</li>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_write_file</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_edit_file</code> — create/edit vault files</li>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_create_directory</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_list_directory</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_directory_tree</code> — browse vault structure</li>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_search_files</code>, <code class="language-plaintext highlighter-rouge">mcp_filesystem_get_file_info</code> — search and inspect vault files</li>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_move_file</code> — move/rename vault files</li>
  <li><code class="language-plaintext highlighter-rouge">mcp_filesystem_list_allowed_directories</code> — verify access boundaries</li>
</ul>

<p>These MCP tools operate <strong>only within the vault directory</strong> — a safety boundary that prevents agents from accidentally writing to system files. The built-in <code class="language-plaintext highlighter-rouge">read_file</code>, <code class="language-plaintext highlighter-rouge">write_file</code>, and <code class="language-plaintext highlighter-rouge">patch</code> tools still work on any path, but the MCP tools are vault-scoped.</p>

<p>The configuration went into each profile’s <code class="language-plaintext highlighter-rouge">config.yaml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">mcp_servers</span><span class="pi">:</span>
  <span class="na">filesystem</span><span class="pi">:</span>
    <span class="na">command</span><span class="pi">:</span> <span class="s">npx</span>
    <span class="na">args</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">@modelcontextprotocol/server-filesystem"</span>
      <span class="pi">-</span> <span class="s2">"</span><span class="s">/Obsidian/ai-vault"</span>
</code></pre></div></div>

<p>Important lesson: the config key is <code class="language-plaintext highlighter-rouge">mcp_servers</code> (snake_case), not <code class="language-plaintext highlighter-rouge">mcpServers</code> (camelCase). We learned this the hard way — the gateway didn’t pick up the MCP server until we corrected the key name. One gateway restart later, 14 new tools appeared.</p>

<h2 id="who-gets-access">Who Gets Access</h2>

<p>Not every agent needs vault access at this time so, we enabled it for the profiles that do knowledge work:</p>

<table>
  <thead>
    <tr>
      <th>Agent</th>
      <th>Profile</th>
      <th>MCP Access</th>
      <th>Env Var</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Hermes (default)</td>
      <td><code class="language-plaintext highlighter-rouge">~/.hermes/</code></td>
      <td>YES</td>
      <td>YES</td>
    </tr>
    <tr>
      <td>Development</td>
      <td><code class="language-plaintext highlighter-rouge">profiles/development/</code></td>
      <td>YES</td>
      <td>YES</td>
    </tr>
    <tr>
      <td>Playground</td>
      <td><code class="language-plaintext highlighter-rouge">profiles/playground/</code></td>
      <td>YES</td>
      <td>YES</td>
    </tr>
    <tr>
      <td>PMO</td>
      <td><code class="language-plaintext highlighter-rouge">profiles/pmo/</code></td>
      <td>YES</td>
      <td>YES</td>
    </tr>
    <tr>
      <td>Research</td>
      <td><code class="language-plaintext highlighter-rouge">profiles/research/</code></td>
      <td>YES</td>
      <td>YES</td>
    </tr>
  </tbody>
</table>

<p>The four F5 specialist agents (admin, asm, gtm, ltm) were excluded for the time being as I have not had the time to further configure them and give them a mission in life (only so many hours in the day).</p>

<p>We also set the <code class="language-plaintext highlighter-rouge">OBSIDIAN_VAULT_PATH</code> environment variable in each enabled profile’s <code class="language-plaintext highlighter-rouge">.env</code> file, so agents always know where the vault lives regardless of which tool they’re using.</p>

<h2 id="the-first-page">The First Page</h2>

<p>After setup, we created the first wiki page — <code class="language-plaintext highlighter-rouge">wiki/vault-setup.md</code> — documenting the configuration itself. Then we updated <code class="language-plaintext highlighter-rouge">wiki/_index/index.md</code> to link it, and appended an entry to <code class="language-plaintext highlighter-rouge">log.md</code>.</p>

<p>The vault was now self-documenting. Every wiki page gets indexed. The knowledge graph grows organically from actual work rather than from automatic extraction with an 86% failure rate.</p>

<h2 id="why-this-matters">Why This Matters</h2>

<p>The vault replaces the “hope and pray” model of automatic memory extraction with a deliberate, agent-controlled knowledge base:</p>

<ul>
  <li><strong>Zero per-turn cost</strong> — no API calls, no latency, no failed extractions</li>
  <li><strong>100% write accuracy</strong> — agents write exactly what they intend, not what an LLM guesses</li>
  <li><strong>On-demand reads</strong> — vault searches happen only when relevant, not every turn</li>
  <li><strong>Compounding knowledge</strong> — wiki pages link to each other, building a graph that gets richer over time</li>
  <li><strong>Append-only raw sources</strong> — source material is preserved exactly as captured</li>
</ul>

<p>The vault is the brain. Built-in memory is the working state. Session search is the conversation history. Three systems, each doing what it’s best at, with no overlap and no wasted tokens.</p>

<h2 id="reference-plug-for-credit-due">Reference Plug for Credit Due:</h2>
<p>Part 1:  The idea<br />
<a href="https://wowhow.cloud/blogs/hermes-agent-tool-gateway-claude-code-mcp-12-patterns-2026">I Wired Claude Code Into Hermes Agent (And Hermes Into Claude Code): The Full Tool Gateway Reference</a><br />
<br />
Part 2:  Implementation Part 1<br />
<a href="https://maxmitcham.substack.com/p/how-to-build-an-ai-agent-operating">How to Build an AI Agent Operating System That Compounds Over Time</a><br />
<br />
Part 3:  Implementation Part 2 is to configure Claude Code on the Mac Mini M4 with Hermes, and now that Anthropic has enabled Auto-Mode for Pro Subscriptions I’ll use my Hybrid Auto-Mode Configuration to allow Hermes and Claude to work together.<br />
<br />
—</p>

<p><em>The AI Vault was created on June 7, 2026. It was later integrated with Hindsight’s replacement on June 11, 2026 — see “Hermes Loses Hindsight” for that story.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><category term="obsidian" /><summary type="html"><![CDATA[The Obsidian vault is live. Hermes now has a persistent knowledge base that compounds over time — not just a notepad, but a structured wiki with schema enforcement, MCP filesystem integration, and automated maintenance.]]></summary></entry><entry><title type="html">Token’s ain’t FREE Buddy…</title><link href="https://www.itlostandfound.xyz/blog/2026/06/06/tokens-aint-free-buddy.html" rel="alternate" type="text/html" title="Token’s ain’t FREE Buddy…" /><published>2026-06-06T22:00:00-05:00</published><updated>2026-06-06T22:00:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/06/06/tokens-aint-free-buddy</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/06/06/tokens-aint-free-buddy.html"><![CDATA[<p>I stared at the numbers for a good minute.</p>

<p>47.3 million input tokens. 446 thousand output tokens. Seven days. One Mac Mini.</p>

<p>The ratio hit harder than the raw count. For every token the model produced, I’d fed it <strong>106 tokens of context</strong>. If you think of tokens as a conversation, that’s like talking to someone who listens to a hundred words for every one they say back. Except in this case, every single one of those input tokens costs compute — and if you’re paying per token at a cloud provider, every one of them costs money.</p>

<p>Tokens ain’t free, buddy.</p>

<h2 id="the-raw-numbers">The Raw Numbers</h2>

<p>I pulled the data directly from Hermes’s SQLite database — <code class="language-plaintext highlighter-rouge">~/.hermes/state.db</code>, the <code class="language-plaintext highlighter-rouge">sessions</code> table. Every session records <code class="language-plaintext highlighter-rouge">input_tokens</code>, <code class="language-plaintext highlighter-rouge">output_tokens</code>, <code class="language-plaintext highlighter-rouge">cache_read_tokens</code>, <code class="language-plaintext highlighter-rouge">api_call_count</code>, <code class="language-plaintext highlighter-rouge">tool_call_count</code>, <code class="language-plaintext highlighter-rouge">model</code>, and <code class="language-plaintext highlighter-rouge">billing_provider</code>. Here’s what the 7-day window ending June 6, 2026 looked like:</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Total input tokens</strong></td>
      <td>47,263,422</td>
    </tr>
    <tr>
      <td><strong>Total output tokens</strong></td>
      <td>446,695</td>
    </tr>
    <tr>
      <td><strong>Input:output ratio</strong></td>
      <td>105.8:1</td>
    </tr>
    <tr>
      <td><strong>Total sessions</strong></td>
      <td>725</td>
    </tr>
    <tr>
      <td><strong>Total API calls</strong></td>
      <td>3,343</td>
    </tr>
    <tr>
      <td><strong>Total tool calls</strong></td>
      <td>3,040</td>
    </tr>
  </tbody>
</table>

<h3 id="per-model-breakdown">Per-Model Breakdown</h3>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Provider</th>
      <th>Input Tokens</th>
      <th>Output Tokens</th>
      <th>Sessions</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>glm-5.1</td>
      <td>ollama-cloud</td>
      <td>23,565,736</td>
      <td>188,051</td>
      <td>391</td>
    </tr>
    <tr>
      <td>glm-5.1:cloud</td>
      <td>custom</td>
      <td>22,718,578</td>
      <td>251,484</td>
      <td>299</td>
    </tr>
    <tr>
      <td>qwen3-8b</td>
      <td>ollama-cloud</td>
      <td>979,108</td>
      <td>7,160</td>
      <td>1</td>
    </tr>
  </tbody>
</table>

<p>The two glm-5.1 variants are the same model through different provider configurations — one through the built-in Ollama Cloud relay, one through a custom endpoint. Together they accounted for over 99% of all tokens.</p>

<p>And the qwen3-8b session? One session, nearly a million input tokens, seven thousand output. That’s a single task that consumed context like a black hole and produced barely a peep, but to be fair.  This is the Kanban PMO Agent and it’s local, so this one gets a PASS.</p>

<h2 id="why-the-ratio-is-so-lopsided">Why the Ratio Is So Lopsided</h2>

<p>If you’ve worked with LLM agents, the 106:1 ratio isn’t surprising — but it’s worth understanding the mechanics.</p>

<p><strong>Every message to the model includes the entire conversation history.</strong> Not just the latest exchange. The system prompt (which runs ~15K tokens with all tools loaded), every previous user message, every previous assistant response, every tool call and its result. It all gets packed back in, every single time.</p>

<p>Here’s what that looks like in practice:</p>

<ul>
  <li><strong>Message 1</strong>: ~15K input (system prompt + first user message)</li>
  <li><strong>Message 10</strong>: ~80K input (system prompt + 9 rounds of history)</li>
  <li><strong>Message 30</strong>: ~200K+ input (system prompt + 29 rounds + all tool outputs)</li>
  <li><strong>Message 50</strong>: Context is so bloated that Hermes triggers compression, which itself costs tokens</li>
</ul>

<p>The output stays small because most agent responses are a few tool calls or a paragraph of text. But the input snowballs. And it snowballs <em>fast</em>.</p>

<p><strong>Tool calls make it worse.</strong> Every web search returns 2-5K tokens of results. Every file read dumps the full file contents. Every terminal command shows its stdout. All of that goes back into the context window for the next turn. A session with 5 web searches and 3 file reads can easily accumulate 50K tokens of tool output alone — and then every subsequent message includes all of it.</p>

<h2 id="the-cost-of-heavy-sessions">The Cost of Heavy Sessions</h2>

<p>I dug into the distribution. Not all sessions are equal — not by a long shot.</p>

<p>78 sessions (11% of total) had 30+ messages each. Those 78 sessions consumed <strong>195 million input tokens</strong> — 72% of all usage. The remaining 647 sessions (89%) shared just 28%.</p>

<p>Let that sink in. Less than one in nine sessions burned nearly three-quarters of the entire token budget.</p>

<p>The heaviest individual sessions ranged from 1.2M to 8.4M input tokens each. At the low end, that’s 60 average messages worth of context crammed into one session. At the high end, that’s a session that ran so long the model was probably seeing its own compressed summaries of summaries.</p>

<h2 id="what-we-built-to-fix-it">What We Built to Fix It</h2>

<p>The problem was clear: context grows with no natural stopping point. Compression slows the bleeding, but it doesn’t stop it. Starting a new session has a 63K-token boot cost (the system prompt and tools), so <code class="language-plaintext highlighter-rouge">/new</code> isn’t free either. But a 63K restart is a lot cheaper than carrying 2M+ tokens of stale context through every subsequent message.</p>

<p>We built two mechanisms — one proactive, one reactive:</p>

<h3 id="1-task-session-manager-skill">1. Task Session Manager (Skill)</h3>

<p>A skill called <code class="language-plaintext highlighter-rouge">task-session-manager</code> that defines a completion protocol. When a task finishes — all TODO items resolved, user says “done”, or the agent recognizes no remaining work — the agent follows a five-step sequence:</p>

<ol>
  <li><strong>Verify completion.</strong> Check the TODO list. If items remain, ask the user before wrapping up.</li>
  <li><strong>Save durable facts.</strong> Push environment facts, user preferences, and stable state changes to Hermes Memory. Push key decisions and outcomes to “The Vault” (Obsidian) long-term memory.</li>
  <li><strong>Save procedural knowledge (if warranted).</strong> Did we discover a non-trivial technique or workaround? Save it as a skill. Routine tasks? Skip it.</li>
  <li><strong>Deliver a handoff message.</strong> Tell the user what was accomplished and explicitly suggest <code class="language-plaintext highlighter-rouge">/new</code> for the next task.</li>
  <li><strong>Stop working.</strong> No “anything else?” No “while I’m here…” The session is done.</li>
</ol>

<p>The key insight: <strong>the agent doesn’t auto-terminate.</strong> It prompts <em>you</em> to start a new session. Because the worst outcome is an agent that cuts you off mid-thought.</p>

<h3 id="2-session-length-guard-cron-job">2. Session Length Guard (Cron Job)</h3>

<p>A Python script at <code class="language-plaintext highlighter-rouge">~/.hermes/scripts/session-length-guard.py</code> that runs every 2 hours. It queries the active session from SQLite and checks two thresholds:</p>

<ul>
  <li><strong>Message threshold</strong>: 30+ messages</li>
  <li><strong>Token threshold</strong>: 2M+ input tokens</li>
</ul>

<p>If either threshold is crossed, it outputs a nudge message:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Session running hot: 34 messages (threshold: 30)
Active since: 2026-06-06 14:23 (2.1h ago)
Model: glm-5.1
Tokens: 4.2M in / 0.18M out
Consider: wrap up current task, save state, and /new to start fresh.
Tip: Load the 'task-session-manager' skill for the completion protocol.
</code></pre></div></div>

<p>This fires as a cron job that delivers via Discord. It’s advisory, not mandatory. No session killing, no forced <code class="language-plaintext highlighter-rouge">/new</code>. Just a tap on the shoulder: <em>hey, you’ve been running for a while, and context is getting expensive.</em></p>

<h3 id="what-happens-when-i-declare-wrap-up">What Happens When I Declare “Wrap Up”</h3>

<p>When I say “wrap up” or “done” or “that’s it,” the agent loads the task-session-manager skill and executes the completion protocol. Here’s what that looks like in practice:</p>

<ol>
  <li>
    <p><strong>Verification.</strong> The agent checks its TODO list. All items completed or cancelled? Proceed. If not, it asks me: “This task still has open items. Wrap up anyway?”</p>
  </li>
  <li>
    <p><strong>Memory saves.</strong> Every durable fact discovered during the session gets saved — paths, config values, version numbers, environment details, user corrections. These persist across sessions, so the next session starts with context instead of starting from zero.</p>
  </li>
  <li>
    <p><strong>Skill saves (if warranted).</strong> If we discovered a non-trivial technique — say, a workaround for Ruby stdlib compatibility, or a specific Docker networking pattern — it gets saved as a class-level skill. One-off task results don’t. “Analyzed token usage” isn’t a skill. “How to fix Jekyll 3.9 incompatibility with Ruby 4.0” is.</p>
  </li>
  <li>
    <p><strong>Vault saves (if warranted).</strong> If the session produced research or documentation that belongs in the Obsidian vault wiki, it gets written there.</p>
  </li>
  <li>
    <p><strong>Handoff message.</strong> The agent delivers a summary:</p>
  </li>
</ol>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Task complete. Here's what was accomplished:
- Analyzed 7-day token usage: 47.3M in / 446K out, 106:1 ratio
- Built task-session-manager skill with completion protocol
- Created session-length-guard cron job for Discord nudges

State saved:
  Memory: token economics data, session management patterns
  Hindsight: key decisions on session lifecycle management
  Skills: task-session-manager (new)
  Vault: not warranted

Start a new session with /new for your next task to keep context lean.
</code></pre></div></div>

<ol>
  <li><strong>Stop.</strong> The agent does not offer follow-up work. No “while I’m here…” suggestions. No “anything else?” The session is done, and context stops growing.</li>
</ol>

<h2 id="the-config-change-that-helped-immediately">The Config Change That Helped Immediately</h2>

<p>One line in <code class="language-plaintext highlighter-rouge">~/.hermes/config.yaml</code> made the biggest difference:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">session_reset</span><span class="pi">:</span>
  <span class="na">mode</span><span class="pi">:</span> <span class="s">idle</span>
  <span class="na">idle_minutes</span><span class="pi">:</span> <span class="m">120</span>
</code></pre></div></div>

<p>Previously, <code class="language-plaintext highlighter-rouge">mode</code> was set to <code class="language-plaintext highlighter-rouge">none</code> — sessions never reset, even after hours of inactivity. Changing to <code class="language-plaintext highlighter-rouge">idle</code> with a 2-hour timeout means sessions that go quiet for 120 minutes automatically close. The next message starts fresh, with a clean 63K-token context window instead of carrying a bloated 2M-token monster.</p>

<p>This single change — combined with the wrap-up protocol and the cron guard — cut our average session token consumption by roughly 40% in the weeks that followed.</p>

<h2 id="the-takeaway">The Takeaway</h2>

<p>Agent sessions are not free. Not in money (Ollama Cloud was free for us), but in compute, latency, and context quality. Every token you feed the model is a token of compute that could have been spent on a sharper, leaner session. Every bloated context window degrades the model’s ability to find relevant information in the noise.</p>

<p>The 106:1 ratio was a wake-up call. The fix wasn’t a single switch — it was a combination of:</p>

<ul>
  <li><strong>Proactive protocol</strong> (the completion skill)</li>
  <li><strong>Reactive monitoring</strong> (the cron guard)</li>
  <li><strong>Config hygiene</strong> (idle session reset)</li>
  <li><strong>User discipline</strong> (actually using <code class="language-plaintext highlighter-rouge">/new</code> when the nudge arrives)</li>
</ul>

<p>Tokens ain’t free. But they don’t have to be expensive either — you just have to know when to close the tab.</p>

<hr />

<p><em>Data collected from Hermes Agent’s SQLite session database, <code class="language-plaintext highlighter-rouge">~/.hermes/state.db</code>, June 6, 2026. Analysis performed on a Mac Mini M4 running Ollama Cloud inference. All token counts are accurate per the provider’s reporting; costs show $0 because Ollama doesn’t report pricing in API responses.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><category term="infrastructure" /><category term="cost-analysis" /><summary type="html"><![CDATA[I stared at the numbers for a good minute.]]></summary></entry><entry><title type="html">Ollama Relay — You’re Doing It WRONG!</title><link href="https://www.itlostandfound.xyz/blog/2026/06/02/ollama-relay-youre-doing-it-wrong.html" rel="alternate" type="text/html" title="Ollama Relay — You’re Doing It WRONG!" /><published>2026-06-02T12:00:00-05:00</published><updated>2026-06-02T12:00:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/06/02/ollama-relay-youre-doing-it-wrong</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/06/02/ollama-relay-youre-doing-it-wrong.html"><![CDATA[<p>It started with 429s. Then it didn’t stop.</p>

<h2 id="the-problem">The Problem</h2>

<p>Every single Hermes agent profile — research, playground, development, f5-ltm, f5-gtm, f5-asm, f5-admin, and even the PMO’s fallback — was routing through a local Ollama relay at <code class="language-plaintext highlighter-rouge">http://localhost:11434/v1</code>. That relay forwarded requests to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code>, Ollama’s cloud inference service.</p>

<p>On paper, this looked fine. Local relay, cloud inference, what could go wrong?</p>

<p>Everything.</p>

<p>The relay was an intermediary that added latency, obscured errors, and — most critically — <strong>masked the real authentication headers</strong>. When Ollama Cloud throttled or rejected a request, the relay couldn’t tell the difference between a legitimate rate limit (429) and a session that had expired. The agent saw <code class="language-plaintext highlighter-rouge">429 Too Many Requests</code> and tried to fall back. But the fallback was… the same relay. Same endpoint. Same broken path. Round and round it went, hammering a service that was already rejecting it, unable to recover because every recovery attempt went through the same broken pipe.</p>

<p>The frustration was real. The agent would hit a 429, fall back to itself, hit another 429, and the user would watch the terminal fill with error after error after error. No escape. No alternative path. Just an infinite loop of “try again” against a wall that wasn’t letting anything through.</p>

<p>And the worst part? The default profile had a <strong>circular fallback</strong> — <code class="language-plaintext highlighter-rouge">glm-5.1:cloud</code> on Ollama Cloud, falling back to <code class="language-plaintext highlighter-rouge">glm-5.1:cloud</code> on the exact same Ollama Cloud endpoint. When the primary failed, the “fallback” was literally the same service that just told you to go away.</p>

<h2 id="the-diagnosis">The Diagnosis</h2>

<p>The configuration audit revealed the full scope of the damage:</p>

<table>
  <thead>
    <tr>
      <th>Profile</th>
      <th>Primary</th>
      <th>Endpoint</th>
      <th>Fallback</th>
      <th>Fallback Endpoint</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>default</strong></td>
      <td>glm-5.1:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td>glm-5.1:cloud</td>
      <td>localhost:11434 (relay) — <strong>circular!</strong></td>
    </tr>
    <tr>
      <td><strong>research</strong></td>
      <td>minimax-m2.7:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td>qwen3-8b</td>
      <td>localhost:1234 (LM Studio)</td>
    </tr>
    <tr>
      <td><strong>pmo</strong></td>
      <td>qwen3-8b</td>
      <td>localhost:1234 (LM Studio)</td>
      <td>glm-5.1:cloud</td>
      <td>localhost:11434 (relay)</td>
    </tr>
    <tr>
      <td><strong>playground</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
    <tr>
      <td><strong>development</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
    <tr>
      <td><strong>f5-ltm</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
    <tr>
      <td><strong>f5-gtm</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
    <tr>
      <td><strong>f5-asm</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
    <tr>
      <td><strong>f5-admin</strong></td>
      <td>kimi-k2.6:cloud</td>
      <td>localhost:11434 (relay)</td>
      <td><strong>none</strong></td>
      <td>—</td>
    </tr>
  </tbody>
</table>

<p>Five profiles had <strong>no fallback at all</strong>. The default profile had a fallback that was literally itself. The PMO profile was the only one with a remotely sane configuration — its primary was local (LM Studio), so it could at least function when cloud was down.</p>

<p>The <code class="language-plaintext highlighter-rouge">custom_providers</code> entry in the main config compounded the confusion. It defined an “Ollama Cloud” provider pointing at <code class="language-plaintext highlighter-rouge">http://localhost:11434/v1</code> — the local relay, not the actual cloud service. The name said “Cloud” but the URL said “Local.” The default profile’s <code class="language-plaintext highlighter-rouge">model.base_url</code> overrode it to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code>, so it <em>looked</em> like it was connecting directly… but all the profile configs used <code class="language-plaintext highlighter-rouge">provider: custom</code> with inline <code class="language-plaintext highlighter-rouge">base_url: http://localhost:11434/v1</code>, routing everything through the relay.</p>

<p>A misnamed provider. An invisible intermediary. A circular fallback. A recipe for an outage that would refuse to heal itself.</p>

<h2 id="the-fix">The Fix</h2>

<p>Two sessions on June 2, 2026. First session: add real fallbacks so that when cloud goes down, agents can at least function locally. Second session: rip out the relay entirely and connect every agent directly to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code> with a valid API key.</p>

<h3 id="session-1-fallback-infrastructure-1216-pm">Session 1: Fallback Infrastructure (12:16 PM)</h3>

<p>The immediate priority was survival — if cloud was down, agents needed a local lifeline. LM Studio on port 1234 was running qwen3-8b and available.</p>

<ol>
  <li><strong>Added LM Studio as a custom provider</strong> in the main config alongside the existing Ollama entry</li>
  <li><strong>Fixed the circular fallback</strong> — replaced <code class="language-plaintext highlighter-rouge">fallback_providers: [custom localhost:11434 / glm-5.1:cloud]</code> with <code class="language-plaintext highlighter-rouge">fallback_model: [custom:lm-studio / qwen3-8b]</code></li>
  <li><strong>Added fallback to all 6 profiles</strong> that had none: playground, development, f5-ltm, f5-gtm, f5-asm, f5-admin — all falling back to LM Studio’s qwen3-8b</li>
  <li><strong>Left PMO and research alone</strong> — PMO already had a cloud fallback, research already had LM Studio</li>
</ol>

<p>Now every agent had at least one alternative path when Ollama Cloud was down.</p>

<h3 id="session-2-direct-connection-619-pm">Session 2: Direct Connection (6:19 PM)</h3>

<p>But fallbacks are a bandage. The real fix was eliminating the relay. The user asked: “Check the other agents and update them so they use the same direct ollama-cloud connection.”</p>

<p>The default profile was already using <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code> directly (via the inline <code class="language-plaintext highlighter-rouge">model.base_url</code> override). Every other profile was still routing through <code class="language-plaintext highlighter-rouge">localhost:11434</code>. Time to make them all consistent.</p>

<p>Changed 8 profile configs from <code class="language-plaintext highlighter-rouge">http://localhost:11434/v1</code> to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code>:</p>

<table>
  <thead>
    <tr>
      <th>Profile</th>
      <th>Changed</th>
      <th>What Moved</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>research</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">minimax-m2.7:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>playground</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>development</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>f5-ltm</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>f5-gtm</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>f5-asm</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>f5-admin</strong></td>
      <td>primary</td>
      <td><code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> → direct Ollama Cloud</td>
    </tr>
    <tr>
      <td><strong>pmo</strong></td>
      <td>fallback</td>
      <td><code class="language-plaintext highlighter-rouge">glm-5.1:cloud</code> → direct Ollama Cloud</td>
    </tr>
  </tbody>
</table>

<p>LM Studio connections (<code class="language-plaintext highlighter-rouge">localhost:1234</code>) stayed local — they’re genuinely local hardware, not a proxy to a cloud service.</p>

<p>The main config’s <code class="language-plaintext highlighter-rouge">custom_providers</code> “Ollama Cloud” entry also got updated from <code class="language-plaintext highlighter-rouge">http://localhost:11434/v1</code> to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code> to match reality. No more name/URL mismatch.</p>

<h2 id="the-lesson">The Lesson</h2>

<p>A relay is not a free abstraction layer. It’s a point of failure that:</p>

<ul>
  <li><strong>Adds latency</strong> to every request</li>
  <li><strong>Obscures authentication errors</strong> — the relay can’t distinguish 401 (bad key) from 429 (rate limit) from 503 (service down) with the right recovery semantics</li>
  <li><strong>Creates circular dependencies</strong> — falling back to the same broken pipe</li>
  <li><strong>Masks the real endpoint</strong> — the config says “Ollama Cloud” but the URL says “Local Relay” and the behavior is neither</li>
  <li><strong>Prevents direct API key validation</strong> — you can’t test your credentials against the real service when they go through an intermediary that may or may not forward them correctly</li>
</ul>

<p>If you’re using Ollama Cloud models, connect directly to <code class="language-plaintext highlighter-rouge">https://ollama.com/v1</code> with a valid API key. If you’re using local models, connect directly to <code class="language-plaintext highlighter-rouge">http://localhost:11434/v1</code>. Don’t mix the two. Don’t relay cloud requests through a local instance. Don’t name a local endpoint “Cloud.”</p>

<p>And for the love of all that’s operational — if your fallback is the same service you’re already failing on, you don’t have a fallback. You have a loop.</p>

<hr />

<p><em>The relay was removed on June 2, 2026, across two sessions. The Ollama Cloud direct connection has been stable since. See “Hermes Loses Hindsight” (June 11) for the next chapter in the infrastructure cleanup.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><category term="ollama" /><category term="infrastructure" /><summary type="html"><![CDATA[It started with 429s. Then it didn’t stop.]]></summary></entry><entry><title type="html">QKView LLM Wiki Brain</title><link href="https://www.itlostandfound.xyz/blog/2026/05/27/qkview-wiki-brain.html" rel="alternate" type="text/html" title="QKView LLM Wiki Brain" /><published>2026-05-27T10:00:00-05:00</published><updated>2026-05-27T10:00:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/05/27/qkview-wiki-brain</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/05/27/qkview-wiki-brain.html"><![CDATA[<p>The TMSH Wiki proved the pattern. The QKView Wiki weaponized it.</p>

<p>F5 BIG-IP QKView diagnostic archives are the primary artifact for remote support cases and proactive health assessments. But analyzing them is slow, inconsistent, and heavily reliant on individual engineer experience. A QKView contains dozens of XML files, configuration dumps, log excerpts, certificate inventories, and hardware telemetry — and no two engineers will examine the same data the same way.</p>

<p>I built a wiki that doesn’t just store knowledge. It <strong>runs</strong> the analysis.</p>

<h2 id="from-reference-to-runbook">From Reference to Runbook</h2>

<p>The TMSH Wiki is a static reference — you look up syntax, it gives you ground truth. The QKView Wiki evolved the pattern into something procedural: a self-improving runbook that walks over 20+ deterministic Python scripts against a QKView diagnostic archive, follows a check analysis playbook, and gets smarter after every engagement through the re-ingestion of a deviation file created during the process.</p>

<p>The key innovation is the <strong>Execute / Refine cycle</strong>:</p>

<ol>
  <li>
    <p><strong>Execute</strong> — The skill points the agent at a QKView file (outside the wiki repo), sets up an engagement workspace (also outside the wiki repo), and walks the playbook through 21 checks in order. Each check runs a Python extractor script, reads the pre-computed result, and if the result is <code class="language-plaintext highlighter-rouge">warn</code> or <code class="language-plaintext highlighter-rouge">crit</code>, reads the check page and drilldown. Findings are written to an external workspace. Deviations are queued — never modifying the wiki during Execute.</p>
  </li>
  <li>
    <p><strong>Refine</strong> — After Execute completes, a separate session processes the deviation queue. The <strong>generalize step is mandatory and comes first</strong> — stripping every client-identifying detail (names, hostnames, IPs, serial numbers, exact percentages) before anything enters the wiki. Fixes are routed to the correct surface: wiki page, extractor script, or both.</p>
  </li>
</ol>

<p>The wiki compounds. Every engagement that deviates from the playbook produces a lesson that gets generalized and filed back, making the next run more accurate.</p>

<h2 id="the-extractor-layer">The Extractor Layer</h2>

<p>After a second engagement revealed that Claude was sometimes skipping analytical work by not fully parsing raw XML — and a Haiku-class model test showed fabricated all-clears — I added 20+ Python scripts that deterministically parse QKView XML artifacts and emit structured Markdown with:</p>

<ul>
  <li>A leading <code class="language-plaintext highlighter-rouge">result: ok|warn|crit|n/a</code> line — the threshold judgment, already made</li>
  <li>Captured data tables with the raw evidence</li>
  <li>Drill-route signals for branching</li>
  <li>Verify sections posing the wiki’s named possible causes as numbered questions</li>
</ul>

<p>The extractors make hallucinated all-clears <strong>structurally hard</strong>: a model walking the playbook reads pre-computed <code class="language-plaintext highlighter-rouge">result: crit</code> lines rather than trying to interpret raw XML.</p>

<h2 id="sterility">Sterility</h2>

<p>The QKView Wiki has a sterility constraint that the TMSH Wiki doesn’t. The TMSH Wiki is purely F5 vendor knowledge — no client data to protect. The QKView Wiki runs against real client device data on every engagement. The constraint is absolute:</p>

<p><strong>No client-identifying data ever enters the wiki repo.</strong> Not in frontmatter, not in log entries, not in lesson files, not in commit messages, not in filenames. The generalize step during Refine is mandatory. A deviation note saying “client’s /var was at 88%” becomes “For <code class="language-plaintext highlighter-rouge">/var</code>, fill rate dominates absolute %. The 80-89 warn band should escalate to crit when fill rate exceeds ~N%/day.”</p>

<h2 id="the-numbers">The Numbers</h2>

<ul>
  <li><strong>72 wiki pages</strong> — 21 checks, 18 drilldowns, 2 playbooks, 11 concepts, 9 lessons, 1 source, 1 template, 1 overview</li>
  <li><strong>20+ Python extractor scripts</strong> covering platform, filesystem, memory, CPU, process, HA, services, network, pool/VS, certs, iRules, config hygiene, tmctl statistics, log analysis, and correlation</li>
  <li><strong>10+ engagements completed</strong> — the wiki compounds with every run</li>
  <li><strong>2 playbooks</strong> — <code class="language-plaintext highlighter-rouge">playbook-full-analysis</code> for normal operations and <code class="language-plaintext highlighter-rouge">playbook-cold-start</code> as a bootstrap mode</li>
</ul>

<h2 id="see-the-full-project">See the Full Project</h2>

<p>This post is an introduction. The complete project page has the full architecture, the Extractor layer breakdown, the Execute/Refine cycle, sterility rules, access rules, the creation timeline, and enhancement opportunities:</p>

<p><strong><a href="/projects/ai/qkview-runbook/">QKView LLM Wiki Brain →</a></strong></p>

<hr />

<p><em>Reference: <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f">Andrej Karpathy’s LLM Wiki concept</a> · See also: <a href="/projects/ai/tmsh-wiki-brain/">TMSH LLM Wiki Brain</a> — the static reference companion that shares the same vault architecture</em></p>]]></content><author><name>Michael Yates</name></author><category term="ai" /><category term="f5" /><category term="self-hosted" /><category term="obsidian" /><category term="wiki-brain" /><summary type="html"><![CDATA[The TMSH Wiki proved the pattern. The QKView Wiki weaponized it.]]></summary></entry><entry><title type="html">Hermes gets Hindsight</title><link href="https://www.itlostandfound.xyz/blog/2026/05/16/hermes-gets-hindsight.html" rel="alternate" type="text/html" title="Hermes gets Hindsight" /><published>2026-05-16T19:45:00-05:00</published><updated>2026-05-16T19:45:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/05/16/hermes-gets-hindsight</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/05/16/hermes-gets-hindsight.html"><![CDATA[<p>We gave Hermes a memory. A real one. Not just sticky notes on a whiteboard — a knowledge graph that builds relationships between entities, remembers what matters, and recalls it when it’s relevant.</p>

<p>Here’s how it happened.</p>

<p><strong>NOTE</strong>:  You’ll see me reference the “Ollama Relay”.  Hermes runs on a Mac Mini M4 and I have Ollama installed on the host machine, so that is why you see the reference and the strange keys.  This instance Hermes Instance was created prior to Ollama.com being a built-in provider option.</p>

<h2 id="the-problem">The Problem</h2>

<p>Hermes has had built-in memory since day one — <code class="language-plaintext highlighter-rouge">MEMORY.md</code> and <code class="language-plaintext highlighter-rouge">USER.md</code> files that get injected into every conversation. It works. It’s reliable. But it’s limited. Those files are flat text, capped at a few thousand characters, and they only contain what you explicitly write into them. They don’t learn on their own. They don’t connect dots. They don’t say “hey, the IP address you mentioned three conversations ago is the same server we’re troubleshooting now.”</p>

<p>We needed something that could:</p>

<ul>
  <li><strong>Auto-retain</strong>: Extract facts, entities, and context from every conversation turn without manual intervention</li>
  <li><strong>Auto-recall</strong>: Search past memories for relevant information and inject it before each response</li>
  <li><strong>Build a knowledge graph</strong>: Connect entities — people, projects, machines, IP addresses — into relationships, not just text blobs</li>
</ul>

<p>That’s what <a href="https://hermes-agent.nousresearch.com/docs">Hindsight</a> promised.</p>

<h2 id="the-setup">The Setup</h2>

<p>The installation was surprisingly straightforward for something this powerful. Three packages via <code class="language-plaintext highlighter-rouge">uv pip install</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">hindsight-client</code> — the API client</li>
  <li><code class="language-plaintext highlighter-rouge">hindsight-all</code> — the full package</li>
  <li><code class="language-plaintext highlighter-rouge">hindsight-embed</code> — the embedded database engine (no external Postgres server needed)</li>
</ul>

<p>Then a config file at <code class="language-plaintext highlighter-rouge">~/.hermes/hindsight/config.json</code>:</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"mode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"local_embedded"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"bank_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hermes"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"llm_provider"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ollama"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"llm_model"</span><span class="p">:</span><span class="w"> </span><span class="s2">"kimi-k2.6:cloud"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"auto_recall"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"auto_retain"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
  </span><span class="nl">"memory_mode"</span><span class="p">:</span><span class="w"> </span><span class="s2">"hybrid"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>The key decisions we made:</p>

<ul>
  <li><strong><code class="language-plaintext highlighter-rouge">mode: local_embedded</code></strong> — runs entirely on the Mac Mini. No cloud API calls for memory operations. It bundles its own PostgreSQL instance with pgvector for vector similarity search. Zero external dependencies.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">llm_provider: ollama</code> with <code class="language-plaintext highlighter-rouge">llm_model: kimi-k2.6:cloud</code></strong> — uses our “Ollama Relay” instance to power the extraction and recall LLM calls. This was important — we didn’t want memory adding per-turn API costs to a cloud provider.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">auto_recall: true</code> and <code class="language-plaintext highlighter-rouge">auto_retain: true</code></strong> — fully automatic. Every turn saves context, every new turn searches for relevant history.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">memory_mode: hybrid</code></strong> — the best of both worlds. Memories get injected into the system prompt context AND explicit <code class="language-plaintext highlighter-rouge">hindsight_recall</code> / <code class="language-plaintext highlighter-rouge">hindsight_retain</code> tools become available for targeted queries.</li>
  <li><strong><code class="language-plaintext highlighter-rouge">bank_id: hermes</code></strong> — a single shared memory bank for the default profile. Other profiles (playground, research, etc.) would later get their own isolated banks.</li>
</ul>

<p>One line in <code class="language-plaintext highlighter-rouge">~/.hermes/config.yaml</code> flipped it on:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">memory</span><span class="pi">:</span>
  <span class="na">provider</span><span class="pi">:</span> <span class="s">hindsight</span>
</code></pre></div></div>

<p>And that was it. <code class="language-plaintext highlighter-rouge">hermes memory status</code> showed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Built-in:   always active
Provider:   hindsight
Plugin:     installed ✓
Status:     available ✓
</code></pre></div></div>

<h2 id="the-rollout">The Rollout</h2>

<p>Getting it working on the default profile was step one. But we had eight other agent profiles — PMO, research, playground, development, and four F5 specialists — all needing their own isolated memory banks.</p>

<p>We created <code class="language-plaintext highlighter-rouge">hindsight/config.json</code> files under each profile directory, each with its own <code class="language-plaintext highlighter-rouge">bank_id</code>:</p>

<table>
  <thead>
    <tr>
      <th>Profile</th>
      <th>Bank ID</th>
      <th>LLM Model</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>default (hermes)</td>
      <td><code class="language-plaintext highlighter-rouge">hermes</code></td>
      <td>kimi-k2.6:cloud</td>
    </tr>
    <tr>
      <td>playground</td>
      <td><code class="language-plaintext highlighter-rouge">playground</code></td>
      <td>kimi-k2.6:cloud</td>
    </tr>
    <tr>
      <td>research</td>
      <td><code class="language-plaintext highlighter-rouge">research</code></td>
      <td>minimax-m2.7:cloud</td>
    </tr>
  </tbody>
</table>

<p>And added the memory section to each profile’s <code class="language-plaintext highlighter-rouge">config.yaml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">memory</span><span class="pi">:</span>
  <span class="na">memory_enabled</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">user_profile_enabled</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">memory_char_limit</span><span class="pi">:</span> <span class="m">2200</span>
  <span class="na">user_char_limit</span><span class="pi">:</span> <span class="m">1375</span>
  <span class="na">provider</span><span class="pi">:</span> <span class="s">hindsight</span>
  <span class="na">nudge_interval</span><span class="pi">:</span> <span class="m">10</span>
  <span class="na">flush_min_turns</span><span class="pi">:</span> <span class="m">6</span>
</code></pre></div></div>

<p>We tested isolation by retaining “Playground test message is ALPHA” under the playground bank and “Research test message is BETA” under the research bank, then recalling from each — playground got ALPHA, research got BETA, default got neither. Clean isolation. Exactly what we wanted.</p>

<h2 id="the-llm-tax">The LLM Tax</h2>

<p>Hindsight isn’t free in compute terms. Every conversation turn triggered two LLM calls:</p>

<ol>
  <li><strong>Recall</strong> — before the response, search the memory bank for relevant context and inject it</li>
  <li><strong>Retain</strong> — after the response, extract entities and facts and store them</li>
</ol>

<p>On our Ollama Relay instance with <code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code>, recall latency ranged from 0.5 to 180 seconds depending on the query complexity. That’s a significant per-turn tax. But the tradeoff was worth it: the agent could now remember across sessions, build institutional knowledge, and connect dots that flat text files never could.</p>

<p>The research profile got <code class="language-plaintext highlighter-rouge">minimax-m2.7:cloud</code> (again through our Ollama Relay) as its Hindsight LLM to avoid competing with <code class="language-plaintext highlighter-rouge">kimi-k2.6:cloud</code> and to test it out for functionality.</p>

<h2 id="what-changed">What Changed</h2>

<p>Before Hindsight, if Hermes asked “What’s the IP of the Firecrawl server?” in a new session, the agent had no idea. The answer was buried in some <code class="language-plaintext highlighter-rouge">MEMORY.md</code> file or a past conversation that required manual session search.</p>

<p>After Hindsight, the agent could recall the IP Address of the Firecrawl endpoint because it had retained that fact from a previous conversation and built a relationship between “Firecrawl” and “home lab server.”</p>

<p>The knowledge graph was the real differentiator. It didn’t just store “Firecrawl is at xxx.xxx.xxx.xxx:3002.” It connected that to “home lab,” “research agent,” “self-hosted,” “no auth required,” and every other entity that appeared alongside it. When any of those concepts came up again, the full context surfaced.</p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><summary type="html"><![CDATA[We gave Hermes a memory. A real one. Not just sticky notes on a whiteboard — a knowledge graph that builds relationships between entities, remembers what matters, and recalls it when it’s relevant.]]></summary></entry><entry><title type="html">Hermes Gets Tools and Turbocharged</title><link href="https://www.itlostandfound.xyz/blog/2026/05/15/hermes-gets-tools-and-turbocharged.html" rel="alternate" type="text/html" title="Hermes Gets Tools and Turbocharged" /><published>2026-05-15T11:30:00-05:00</published><updated>2026-05-15T11:30:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/05/15/hermes-gets-tools-and-turbocharged</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/05/15/hermes-gets-tools-and-turbocharged.html"><![CDATA[<p>Hermes got a new home today. We moved the entire agent stack off the Zimaboard2 and onto a Mac Mini M4 — and the difference isn’t incremental. It’s the kind of upgrade that changes what you can even attempt.</p>

<p>Here’s why we did it and what changed.</p>

<h2 id="the-zimaboard2-a-good-start">The Zimaboard2: A Good Start</h2>

<p>The <a href="https://shop.zimaspace.com/products/zimaboard2-single-board-server?variant=50683134312740#specification">Zimaboard2 1664</a> is a fanless x86 single-board server with an Intel N150 processor, 16 GB of RAM, dual 2.5GbE, and a PCIe 3.0 x4 slot. At $399, it’s an attractive little machine for homelab work — quiet, compact, low power. It runs ZimaOS, CasaOS, Proxmox, TrueNAS, you name it.</p>

<p>It was where Hermes was born. We installed the agent, configured the gateway, set up Ollama, got the Discord bot connected. Hermes worked on the Zimaboard2. That’s the honest truth.</p>

<p>But “worked” and “worked well” are different things.</p>

<h2 id="the-problem">The Problem</h2>

<p>The Intel N150 is a capable chip for what it is — a low-power x86 processor designed for NAS duty, media streaming, and light container orchestration. It is not designed for running local LLM inference alongside a multi-agent orchestration platform.</p>

<p>Hermes isn’t a single process. It’s a stack:</p>

<ul>
  <li>The CLI agent itself (Python, always-on)</li>
  <li>The Discord gateway (async, message-driven)</li>
  <li>Ollama for local LLM inference (GPU-dependent, memory-hungry)</li>
  <li>The API server (HTTP endpoint for external integrations)</li>
  <li>Cron jobs firing on schedules</li>
  <li>Hindsight memory extraction (at the time, 2 LLM calls per turn)</li>
  <li>Dashboard web UI</li>
</ul>

<p>On 4 cores and 16 GB of shared RAM, that stack competes with itself. Ollama loading a 4B or 8B model? The system crawls. A cron job kicks off while a Discord conversation is active? Latency spikes. Two agents talking at once? Forget it.</p>

<p>And then there was the N150’s instruction set. No AVX2, no AMX — just basic x86-64 with AES-NI. Modern LLM inference frameworks increasingly expect AVX2 as a baseline. Some operations would flat-out fail or fall back to painfully slow scalar paths.</p>

<p>The Zimaboard2 was like putting a commuter car on a race track. It’ll lap the circuit, but you won’t be setting any records, and the driver is going to be frustrated the whole time.</p>

<h2 id="the-mac-mini-m4">The Mac Mini M4</h2>

<p>The upgrade wasn’t subtle:</p>

<table>
  <thead>
    <tr>
      <th>Spec</th>
      <th>Zimaboard2 1664</th>
      <th>Mac Mini M4 Pro</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CPU</td>
      <td>Intel N150 (4 cores, no AVX2)</td>
      <td>Apple M4 Pro (20 cores, 12 GPU)</td>
    </tr>
    <tr>
      <td>RAM</td>
      <td>16 GB DDR5 (shared)</td>
      <td>64 GB unified memory</td>
    </tr>
    <tr>
      <td>GPU</td>
      <td>None</td>
      <td>16-core Metal GPU</td>
    </tr>
    <tr>
      <td>Storage</td>
      <td>2× SATA III</td>
      <td>1.7 TB NVMe</td>
    </tr>
    <tr>
      <td>Networking</td>
      <td>Dual 2.5GbE</td>
      <td>10GbE + Wi-Fi 6E</td>
    </tr>
    <tr>
      <td>Cooling</td>
      <td>Passive (aluminum chassis)</td>
      <td>Active fan (near-silent)</td>
    </tr>
    <tr>
      <td>Price</td>
      <td>$399</td>
      <td>~$1,599</td>
    </tr>
  </tbody>
</table>

<p>That’s a 5× CPU core count increase, 4× the RAM, and a GPU that doesn’t exist on the Zimaboard2. The M4 Pro’s unified memory architecture means the CPU and GPU share the same 64 GB pool — Ollama can load a 32B parameter model and still have room for everything else.</p>

<h2 id="what-changed-immediately">What Changed Immediately</h2>

<p>The first thing I did after the migration was confirm it was real. The very first message sent to Hermes on the new machine was:</p>

<blockquote>
  <p>“Say hello and confirm you’re running on the Mac Mini M4”</p>
</blockquote>

<p>It answered. And it answered fast.</p>

<p><strong>Ollama went from painful to usable.</strong> Models that took 30+ seconds to first token on the N150 responded in 2-3 seconds on the M4 Pro. We went from running one small model at a time with degraded responsiveness to loading 16 models simultaneously with headroom to spare.</p>

<p><strong>The full agent stack runs concurrently.</strong> Discord gateway, API server, cron jobs, multiple agent profiles — all running at the same time without stepping on each other’s CPU or memory. The 20-core M4 Pro parallelizes workloads that the 4-core N150 had to time-slice.</p>

<p><strong>Metal GPU acceleration for inference.</strong> Ollama on macOS uses Apple’s Metal framework for GPU offload. The <code class="language-plaintext highlighter-rouge">-ngl 99</code> flag that would crash or stall on the Zimaboard2 (no GPU!) now actually means something. We offload the entire model to the GPU and get 40-50 tokens per second on 8B-class models.</p>

<p><strong>Storage I/O stopped being a bottleneck.</strong> NVMe versus SATA III — model loading, log writes, state database queries, all of it got faster. The <code class="language-plaintext highlighter-rouge">state.db</code> that Hermes hits on every conversation turn now reads from NVMe instead of a spinning disk or slow SATA SSD.</p>

<h2 id="the-migration-itself">The Migration Itself</h2>

<p>The filesystem tells the story. Using <code class="language-plaintext highlighter-rouge">stat</code> on the key directories:</p>

<table>
  <thead>
    <tr>
      <th>Event</th>
      <th>Timestamp</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">/Users/hermes/</code> home directory created</td>
      <td>May 14, 2026 ~5:46 PM CT</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">~/.hermes/</code> directory created (install began)</td>
      <td>May 15, 2026 ~11:01 AM CT</td>
    </tr>
    <tr>
      <td>hermes-agent git repo cloned</td>
      <td>May 15, 2026 ~11:16 AM CT</td>
    </tr>
    <tr>
      <td>First conversation message</td>
      <td>May 15, 2026 ~11:36 AM CT</td>
    </tr>
  </tbody>
</table>

<p>From home directory creation to first conversation: roughly 18 hours. Most of that was macOS setup, Homebrew, Python, and configuring the environment. The actual Hermes install and configuration took about 35 minutes.</p>

<p>The hostname is <code class="language-plaintext highlighter-rouge">MacMiniM4LLM</code> — chosen deliberately. This machine’s job is to run LLMs. Everything else it does (Discord gateway, cron scheduling, dashboard, Firecrawl) is in service of that primary purpose.</p>

<h2 id="what-we-left-behind">What We Left Behind</h2>

<p>The Zimaboard2 isn’t retired. A 4-core x86 board with 16 GB of RAM and dual 2.5GbE still has plenty of uses in a homelab. It just isn’t where Hermes lives anymore. It’s been reassigned to lighter duties — the kind of workloads it was actually designed for.</p>

<p>And that’s the right place for it. The Zimaboard2 taught us what we needed in a host. It ran Hermes when we were learning what Hermes even was. But once the agent stack grew — multiple profiles, local inference, cron jobs, memory extraction, API endpoints — we needed a machine that could keep up with all of it at once.</p>

<p>The Mac Mini M4 doesn’t just keep up. It has headroom. And that headroom is what makes the difference between an agent that works and an agent that works without you noticing it’s working.</p>

<hr />

<p><em>Hermes was installed on the Mac Mini M4 on May 15, 2026. The Zimaboard2 that preceded it still serves in the homelab — just not as an LLM host. See “Hermes Gets a Brain” for the next chapter in the agent stack evolution.</em></p>]]></content><author><name>Michael Yates</name></author><category term="hermes" /><category term="ai" /><category term="self-hosted" /><category term="infrastructure" /><summary type="html"><![CDATA[Hermes got a new home today. We moved the entire agent stack off the Zimaboard2 and onto a Mac Mini M4 — and the difference isn’t incremental. It’s the kind of upgrade that changes what you can even attempt.]]></summary></entry><entry><title type="html">TMSH LLM Wiki Brain</title><link href="https://www.itlostandfound.xyz/blog/2026/04/15/tmsh-wiki-brain.html" rel="alternate" type="text/html" title="TMSH LLM Wiki Brain" /><published>2026-04-15T10:00:00-05:00</published><updated>2026-04-15T10:00:00-05:00</updated><id>https://www.itlostandfound.xyz/blog/2026/04/15/tmsh-wiki-brain</id><content type="html" xml:base="https://www.itlostandfound.xyz/blog/2026/04/15/tmsh-wiki-brain.html"><![CDATA[<p>LLMs are confident. That’s the problem.</p>

<p>Ask any LLM for TMSH syntax and you’ll get an answer. It will sound authoritative. It will be wrong. TMSH is purpose-built, highly structured, and full of nuances that general training data doesn’t capture well. Partition scoping changes behavior in ways that aren’t obvious. <code class="language-plaintext highlighter-rouge">list</code> vs <code class="language-plaintext highlighter-rouge">show</code> have distinct semantic differences that matter. <code class="language-plaintext highlighter-rouge">modify</code> list replacement behavior can silently wipe configuration. Flag combinations, module hierarchies, and object paths have exact syntax that training data garbles.</p>

<p>Rather than accept hallucinated TMSH syntax, I built a knowledge base that forces the LLM to consult vetted source material before answering.</p>

<h2 id="the-concept">The Concept</h2>

<p>The TMSH LLM Wiki Brain is a locally-hosted, compounding knowledge base for F5 BIG-IP’s Traffic Management Shell. It’s stored as an Obsidian vault of interlinked Markdown files, and it’s based on Andrej Karpathy’s “LLM Wiki” pattern — feed a curated knowledge base into a session so answers are grounded in reality, not probability.</p>

<p>The wiki enforces strict reading discipline:</p>

<ul>
  <li><strong>Start at index.md</strong> — the navigation layer, always free</li>
  <li><strong>Read at most 5 content pages per question</strong> — if more are needed, the agent stops and asks for permission</li>
  <li><strong>Fallback is mandatory silence</strong> — if the wiki has no coverage, the agent states “The wiki does not currently cover [topic]” and suggests what source could fill the gap. It does <strong>not</strong> answer from training data as a substitute</li>
</ul>

<p>The entire point is to prevent training-data guessing. Falling back silently would defeat the purpose.</p>

<h2 id="whats-in-it">What’s In It</h2>

<p>The initial ingest came from two authoritative sources:</p>

<ul>
  <li><strong>F5 KB K13225 Cheatsheet</strong> — established the <code class="language-plaintext highlighter-rouge">list</code> vs <code class="language-plaintext highlighter-rouge">show</code> semantic distinction as a foundational concept</li>
  <li><strong>BIG-IP TMSH Reference Guide v17.0.0</strong> — the 1,843-page authoritative F5 document. Full ingest created 47 pages: 27 commands, 5 modules, 9 objects, 4 concepts, 1 pattern, 2 source summaries, 1 overview, and 1 index</li>
</ul>

<p>The vault structure separates concerns cleanly: <code class="language-plaintext highlighter-rouge">raw/</code> for immutable source documents, <code class="language-plaintext highlighter-rouge">wiki/</code> for all generated content (commands, modules, objects, concepts, patterns), with every page following a consistent schema of YAML frontmatter, Obsidian wikilinks, callouts, and code blocks tagged with <code class="language-plaintext highlighter-rouge">tmsh</code> language.</p>

<h2 id="why-it-matters">Why It Matters</h2>

<p>This was the proof of concept. The pattern worked so well for a static reference that it directly led to the QKView LLM Wiki Brain — which evolved the concept from “look up syntax” to “walk a procedure against live data and get smarter after every engagement.”</p>

<p>The TMSH Wiki now serves as the gold-standard CLAUDE.md template for every other wiki brain in the system.</p>

<h2 id="see-the-full-project">See the Full Project</h2>

<p>This post is an introduction. The complete project page has the full architecture, ingest sources, current state, access rules, and enhancement opportunities:</p>

<p><strong><a href="/projects/ai/tmsh-wiki-brain/">TMSH LLM Wiki Brain →</a></strong></p>

<hr />

<p><em>Reference: <a href="https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f">Andrej Karpathy’s LLM Wiki concept</a></em></p>]]></content><author><name>Michael Yates</name></author><category term="ai" /><category term="f5" /><category term="self-hosted" /><category term="obsidian" /><category term="wiki-brain" /><summary type="html"><![CDATA[LLMs are confident. That’s the problem.]]></summary></entry></feed>