<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.4">Jekyll</generator><link href="http://localhost:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" /><updated>2025-10-19T23:28:37-04:00</updated><id>http://localhost:4000/feed.xml</id><title type="html">xtonyx</title><subtitle>Tony’s corner of the web — a mix of coding experiments, 3D printing, Raspberry Pi builds, and random maker notes.</subtitle><entry><title type="html">Scrapegoat - Raspberry Pi Price Tracker</title><link href="http://localhost:4000/posts/2025/10/13/scrapegoat-raspberry-pi-price-tracking/" rel="alternate" type="text/html" title="Scrapegoat - Raspberry Pi Price Tracker" /><published>2025-10-13T00:00:00-04:00</published><updated>2025-10-13T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/10/13/scrapegoat-raspberry-pi-price-tracking</id><content type="html" xml:base="http://localhost:4000/posts/2025/10/13/scrapegoat-raspberry-pi-price-tracking/"><![CDATA[<p>Back in <a href="/posts/2025/08/06/raspberry-pi-price-tracking/">August’s Raspberry Pi Price Tracking post</a>, I talked about a quick Markdown table I put together by hand to compare Micro Center’s Raspberry Pi prices. It worked fine for a snapshot, but those numbers go stale fast — and I wanted something that could keep itself up-to-date.</p>

<p><a href="/scrapegoat/">Scrapegoat</a> is a small Python + PHP project that automatically scrapes Micro Center’s Raspberry Pi listings, tracks prices over time, and publishes live tables and charts. It builds daily snapshots, flags sales and all-time lows, and outputs Reddit-ready markdown tables — all powered by static JSON data, no database needed.</p>

<p><strong><a href="/scrapegoat/">Visit the Scrapegoat project page here!</a></strong></p>

<hr />]]></content><author><name>Tony</name></author><category term="homelab" /><category term="raspberrypi" /><category term="pihole" /><category term="troubleshooting" /><category term="projects" /><category term="scrapegoat" /><category term="raspberry-pi" /><category term="microcenter" /><category term="python" /><category term="php" /><category term="github" /><category term="reddit" /><summary type="html"><![CDATA[Back in August’s Raspberry Pi Price Tracking post, I talked about a quick Markdown table I put together by hand to compare Micro Center’s Raspberry Pi prices. It worked fine for a snapshot, but those numbers go stale fast — and I wanted something that could keep itself up-to-date.]]></summary></entry><entry><title type="html">Pi‑hole Zero 2 W: Beyond NIC Flapping — The Freeze Mystery</title><link href="http://localhost:4000/posts/2025/10/02/pi-hole-followup-sd-freezes/" rel="alternate" type="text/html" title="Pi‑hole Zero 2 W: Beyond NIC Flapping — The Freeze Mystery" /><published>2025-10-02T00:00:00-04:00</published><updated>2025-10-02T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/10/02/pi-hole-followup-sd-freezes</id><content type="html" xml:base="http://localhost:4000/posts/2025/10/02/pi-hole-followup-sd-freezes/"><![CDATA[<blockquote>
  <p>TL;DR — Early on I fixed a flaky USB NIC (Realtek r8152) that caused short dropouts. Weeks later the box began <strong>hard freezing</strong>. Logs from the <strong>previous boot</strong> showed <strong>SD host timeouts</strong> (<code class="language-plaintext highlighter-rouge">sdhost-bcm2835 ... timeout waiting for hardware interrupt</code>) and ext4 read stalls. The fix: <strong>back up</strong>, move to a <strong>new endurance SD card</strong>, and restore Pi‑hole + Tailscale configs.</p>
</blockquote>

<p>If you missed the first post about the USB NIC:<br />
<a href="/posts/2025/08/24/pihole-usb-nic-quick-fix/">Quick Fix for NIC flapping</a> ·
<a href="/posts/2025/08/24/fixing-pihole-usb-nic-flapping/">Full write‑up</a></p>

<hr />

<h2 id="what-was-happening">What was happening</h2>

<ul>
  <li><strong>Pi‑hole (<code class="language-plaintext highlighter-rouge">zero.local</code>) would hard freeze</strong> every few days; only a power‑cycle revived it.</li>
  <li>Power &amp; thermals were clean (<code class="language-plaintext highlighter-rouge">vcgencmd get_throttled=0x0</code>, temps ~40 °C).</li>
  <li>The earlier <strong>NIC flapping</strong> was mitigated (100 Mb/s lock + USB autosuspend off) but freezes kept happening.</li>
</ul>

<h2 id="how-i-proved-it">How I proved it</h2>

<ol>
  <li><strong>Enable persistent logs</strong> (so “previous boot” is readable):
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo mkdir</span> <span class="nt">-p</span> /var/log/journal
<span class="nb">sudo sed</span> <span class="nt">-i</span> <span class="s1">'s/^#\?Storage=.*/Storage=persistent/'</span> /etc/systemd/journald.conf
<span class="nb">sudo </span>systemctl restart systemd-journald
</code></pre></div>    </div>
  </li>
  <li>After a freeze + reboot, check the <strong>previous boot</strong>:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>journalctl <span class="nt">--list-boots</span>
<span class="nb">sudo </span>journalctl <span class="nt">-b</span> <span class="nt">-1</span> <span class="nt">-p</span> 0..3 <span class="nt">--no-pager</span>
<span class="nb">sudo </span>journalctl <span class="nt">-b</span> <span class="nt">-1</span> <span class="nt">-k</span> <span class="nt">--no-pager</span> | <span class="se">\</span>
  <span class="nb">grep</span> <span class="nt">-Ei</span> <span class="s1">'r8152|eth0|carrier|reset high-speed|under-volt|mmc|i/o error|ext4|panic|oom|hung task|soft lockup'</span>
</code></pre></div>    </div>
  </li>
  <li>The smoking gun showed up repeatedly:
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sdhost-bcm2835 ... timeout waiting for hardware interrupt
EXT4 ... readpages / mmc_rescan backtraces
</code></pre></div>    </div>
    <p>No undervoltage, no OOM, no kernel panic → <strong>storage I/O stalls</strong>.</p>
  </li>
</ol>

<h2 id="back-up-before-it-dies">Back up before it dies</h2>

<p>Small, safe backups that matter:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Pi-hole &amp; Tailscale configs (portable)</span>
<span class="nb">sudo tar </span>czf /root/backup/pihole_etc_<span class="si">$(</span><span class="nb">date</span> +%F<span class="si">)</span>.tgz <span class="se">\</span>
  /etc/pihole /etc/dnsmasq.d /var/lib/tailscale /etc/hosts /etc/hostname

<span class="c"># Full card image, compressed, streamed to NAS over SSH</span>
<span class="nb">sudo dd </span><span class="k">if</span><span class="o">=</span>/dev/sdX <span class="nv">bs</span><span class="o">=</span>4M <span class="nv">status</span><span class="o">=</span>progress | <span class="nb">gzip</span> | <span class="se">\</span>
  ssh root@nas <span class="s1">'cat &gt; /mnt/archive/pi-backups/pihole-$(date +%F).img.gz'</span>
</code></pre></div></div>

<blockquote>
  <p>Tip: move the tarball out of <code class="language-plaintext highlighter-rouge">/root</code> so you can <code class="language-plaintext highlighter-rouge">scp</code> it as a normal user.</p>
</blockquote>

<h2 id="rebuild--restore-clean-slate">Rebuild &amp; restore (clean slate)</h2>

<ol>
  <li>Flash Raspberry Pi OS Lite onto a <strong>new A1/A2 or Endurance SD card</strong>.</li>
  <li>Install Pi‑hole + Tailscale:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-sSL</span> https://install.pi-hole.net | bash
<span class="nb">sudo </span>apt <span class="nb">install </span>tailscale <span class="nt">-y</span>
<span class="nb">sudo </span>tailscale up <span class="nt">--accept-dns</span><span class="o">=</span><span class="nb">false</span>
</code></pre></div>    </div>
  </li>
  <li>Restore configs and restart:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>scp pihole_etc_YYYY-MM-DD.tgz admin@newpi:/home/admin/
ssh admin@newpi <span class="s1">'sudo tar xzf /home/admin/pihole_etc_*.tgz -C /'</span>
ssh admin@newpi <span class="s1">'sudo systemctl restart pihole-FTL tailscaled'</span>
</code></pre></div>    </div>
  </li>
</ol>

<h2 id="hardening-nice-extras">Hardening (nice extras)</h2>

<ul>
  <li><strong>Reduce SD writes</strong>: shorter Pi‑hole query retention; consider <code class="language-plaintext highlighter-rouge">tmpfs</code> for logs.</li>
  <li><strong>Watchdogs</strong> (auto‑restart FTL if it dies) and a tiny <strong>net probe</strong> timer to log WAN/GW loss.</li>
  <li>Keep the NIC tweaks if you use a USB adapter:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Lock to 100 Mb/s and disable EEE if supported</span>
<span class="nb">sudo </span>ethtool <span class="nt">-s</span> eth0 speed 100 duplex full autoneg off <span class="o">||</span> <span class="nb">true
sudo </span>ethtool <span class="nt">--set-eee</span> eth0 eee off <span class="o">||</span> <span class="nb">true</span>
</code></pre></div>    </div>
  </li>
</ul>

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

<ul>
  <li>Two issues overlapped: <strong>NIC flaps</strong> (annoying, recoverable) and later <strong>SD I/O stalls</strong> (fatal).</li>
  <li><strong><code class="language-plaintext highlighter-rouge">journalctl -b -1</code></strong> is gold for post‑mortems.</li>
  <li>SD cards are consumables. Use <strong>high‑endurance</strong> media for 24/7 boxes like Pi‑hole.</li>
</ul>

<hr />

<h3 id="quick-fix-box-copypaste">Quick Fix box (copy/paste)</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># After reboot from a freeze</span>
<span class="nb">sudo </span>journalctl <span class="nt">-b</span> <span class="nt">-1</span> <span class="nt">-p</span> 0..3 <span class="nt">--no-pager</span>
<span class="nb">sudo </span>journalctl <span class="nt">-b</span> <span class="nt">-1</span> <span class="nt">-k</span> <span class="nt">--no-pager</span> | <span class="nb">grep</span> <span class="nt">-Ei</span> <span class="s1">'mmc|sdhost|i/o error|ext4'</span>
<span class="c"># If you see sdhost/ext4 timeouts → back up now and replace the card.</span>
</code></pre></div></div>]]></content><author><name>Tony</name></author><category term="homelab" /><category term="raspberrypi" /><category term="pihole" /><category term="troubleshooting" /><category term="raspberry-pi" /><category term="pihole" /><category term="tailscale" /><category term="sd-card" /><category term="journald" /><category term="r8152" /><summary type="html"><![CDATA[Follow‑up to the NIC flapping saga: the real cause of my Pi‑hole outages was SD card I/O stalls. Here’s how I proved it and fixed it.]]></summary></entry><entry><title type="html">Adding Comments with Utterances</title><link href="http://localhost:4000/posts/2025/08/25/adding-comments-to-site-using-utterances/" rel="alternate" type="text/html" title="Adding Comments with Utterances" /><published>2025-08-25T00:00:00-04:00</published><updated>2025-08-25T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/25/adding%20comments%20to%20site%20using%20utterances</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/25/adding-comments-to-site-using-utterances/"><![CDATA[<p>I wanted to add a lightweight way for people to interact with my Jekyll posts without running my own backend.
<a href="https://utteranc.es"><strong>Utterances</strong></a> turned out to be perfect: free, simple, and powered by GitHub Issues.</p>

<hr />

<h3 id="setup-steps">Setup Steps</h3>

<ol>
  <li><strong>Create a repo</strong> just for blog comments (or reuse an existing one).
    <ul>
      <li>I made a public repo, separate from my sites private repo.</li>
      <li>Installed the Utterances GitHub App on that repo.</li>
    </ul>
  </li>
  <li><strong>Generate the config</strong> on <a href="https://utteranc.es">utteranc.es</a>
    <ul>
      <li>Fill in your user/repo</li>
      <li>Choose your mapping
        <ul>
          <li>I went with <code class="language-plaintext highlighter-rouge">pathname</code> so each post maps to a unique issue automatically. It seemed the best choice for my Jekyll generated site.</li>
        </ul>
      </li>
      <li>Add a label <em>(optional)</em></li>
      <li>Choose a Theme</li>
      <li>Click copy and move to step 3.</li>
    </ul>
  </li>
  <li>
    <p><strong>Add the embed</strong><br />
I made an include file from the generated config plus added some noscript text because I’m old.</p>

    <div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">&lt;!-- _includes/comments-utterances.html --&gt;</span>
<span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">"comments"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;script
    </span><span class="na">src=</span><span class="s">"https://utteranc.es/client.js"</span>
    <span class="na">repo=</span><span class="s">"USER/REPO"</span>
    <span class="na">issue-term=</span><span class="s">"pathname"</span>
    <span class="na">label=</span><span class="s">"blog-comments"</span>
    <span class="na">theme=</span><span class="s">"github-dark"</span>
    <span class="na">crossorigin=</span><span class="s">"anonymous"</span>
    <span class="na">async</span><span class="nt">&gt;&lt;/script&gt;</span>
  <span class="nt">&lt;noscript&gt;</span>Please enable JavaScript to view the comments powered by Utterances.<span class="nt">&lt;/noscript&gt;</span>
<span class="nt">&lt;/div&gt;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Wire it into my layout</strong><br />
In <code class="language-plaintext highlighter-rouge">_layouts/post.html</code>:</p>

    <div class="language-liquid highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">{%</span><span class="w"> </span><span class="nt">if</span><span class="w"> </span><span class="nv">page</span><span class="p">.</span><span class="nv">comments</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">false</span><span class="w"> </span><span class="cp">%}</span>
  <span class="cp">{%</span><span class="w"> </span><span class="nt">include</span><span class="w"> </span>comments-utterances.html<span class="w"> </span><span class="cp">%}</span>
<span class="cp">{%</span><span class="w"> </span><span class="nt">endif</span><span class="w"> </span><span class="cp">%}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Toggle per post</strong><br />
By default comments show up. If I don’t want them:</p>

    <div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">comments</span><span class="pi">:</span> <span class="kc">false</span>
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h3 id="notes">Notes</h3>

<ul>
  <li>Issues get auto-created in the repo on first comment.</li>
  <li>Comments support Markdown + reactions.</li>
  <li>Users need a GitHub account to comment, which should keep spam low.. and I imagine if I have any audience they probably use Github.</li>
  <li>I used the ‘label’ setting just to try it out. I could use this to label comments based on the post type at some point.</li>
  <li>You can customize the layout and theme.</li>
</ul>

<hr />

<p>That’s it. I finished the implementation in less than ten minutes. It seriously took me longer to write this page out. I imagine if I wanted to customize it a bit, that would add some time, but I just wanted to see how it worked.</p>

<p>Leave a comment below if this helped you I guess…</p>

<hr />

<h3 id="update">Update</h3>

<p>I added a specific comment box for 404 pages so that bad links could be reported. Since the default method uses the pathname to generate each issue for comments, I needed to change <code class="language-plaintext highlighter-rouge">issue-term="pathname"</code> to <code class="language-plaintext highlighter-rouge">issue-number="1"</code>. This ties is to issue number 1 on the github repo no matter the link it comes from.</p>]]></content><author><name>Tony</name></author><category term="jekyll" /><category term="comments" /><category term="utterances" /><category term="github" /><summary type="html"><![CDATA[I wanted to add a lightweight way for people to interact with my Jekyll posts without running my own backend. Utterances turned out to be perfect: free, simple, and powered by GitHub Issues.]]></summary></entry><entry><title type="html">Quick Fix: Stabilizing Pi-hole USB NIC Flapping (Realtek r8152)</title><link href="http://localhost:4000/posts/2025/08/24/pihole-usb-nic-quick-fix/" rel="alternate" type="text/html" title="Quick Fix: Stabilizing Pi-hole USB NIC Flapping (Realtek r8152)" /><published>2025-08-24T00:00:00-04:00</published><updated>2025-08-24T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/24/pihole-usb-nic-quick-fix</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/24/pihole-usb-nic-quick-fix/"><![CDATA[<p>This is the TL;DR version.<br />
For the full background and debugging steps, check out <a href="/posts/2025/08/24/fixing-pihole-usb-nic-flapping/">Fixing PiHole USB NIC Flapping</a>.</p>

<p>If your <strong>Pi-hole</strong> randomly drops off the network and you see this in <code class="language-plaintext highlighter-rouge">dmesg</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>r8152 1-1.4:1.0 eth0: carrier off
r8152 1-1.4:1.0 eth0: carrier on
</code></pre></div></div>

<p>…it could mean your <strong>Realtek r8152</strong> USB NIC is flapping. These steps make it persistent and stable.</p>

<p><em>This is based on my trouble with a Pi Zero 2 W with Waveshare Eth/USB Hub Hat, but may apply to any sbc using an r8152 nic.</em></p>

<hr />

<h2 id="step-1--lock-link-speed-with-networkmanager"><strong>Step 1 — Lock Link Speed with NetworkManager</strong></h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli connection modify <span class="s2">"Wired connection 1"</span> <span class="se">\</span>
  ethernet.auto-negotiate no <span class="se">\</span>
  ethernet.speed 100 <span class="se">\</span>
  ethernet.duplex full

<span class="nb">sudo </span>nmcli connection down <span class="s2">"Wired connection 1"</span>
<span class="nb">sudo </span>nmcli connection up <span class="s2">"Wired connection 1"</span>
</code></pre></div></div>

<p>Check:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ethtool eth0 | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'Speed|Duplex|Auto-negotiation'</span>
</code></pre></div></div>

<hr />

<h2 id="step-2--disable-energy-efficient-ethernet-eee"><strong>Step 2 — Disable Energy Efficient Ethernet (EEE)</strong></h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ethtool <span class="nt">--set-eee</span> eth0 eee off
ethtool <span class="nt">--show-eee</span> eth0
</code></pre></div></div>

<p>Make it permanent:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'options r8152 eee_enable=0'</span> | <span class="nb">sudo tee</span> /etc/modprobe.d/r8152.conf
<span class="nb">sudo </span>update-initramfs <span class="nt">-u</span>
</code></pre></div></div>

<hr />

<h2 id="step-3--disable-usb-autosuspend"><strong>Step 3 — Disable USB Autosuspend</strong></h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">printf</span> <span class="s1">'%s\n'</span> <span class="s1">'ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8152", \
  TEST=="power/control", ATTR{power/control}="on"'</span> | <span class="se">\</span>
  <span class="nb">sudo tee</span> /etc/udev/rules.d/99-r8152-no-autosuspend.rules

<span class="nb">sudo </span>udevadm control <span class="nt">--reload</span>
<span class="nb">sudo </span>udevadm trigger
</code></pre></div></div>

<hr />

<h2 id="step-4--monitor-stability"><strong>Step 4 — Monitor Stability</strong></h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dmesg | <span class="nb">grep</span> <span class="nt">-c</span> <span class="s2">"r8152 .* eth0: carrier"</span>
<span class="nb">sudo </span>dmesg <span class="nt">--follow</span> | <span class="nb">grep </span>r8152
</code></pre></div></div>

<hr />

<h2 id="summary"><strong>Summary</strong></h2>

<p>If you’re seeing <strong>random dropouts</strong> on a Pi-hole or Tailscale box with a <strong>Realtek r8152</strong> USB NIC, the quick fix is:</p>

<ol>
  <li>Force 100 Mbps full-duplex and disable autonegotiation.</li>
  <li>Disable <strong>EEE</strong>.</li>
  <li>Turn off <strong>USB autosuspend</strong>.</li>
</ol>

<p>This stabilizes the NIC and keeps DNS + Tailscale online without random interruptions.</p>]]></content><author><name>Tony</name></author><category term="Pi-hole" /><category term="Networking" /><category term="Quick Fix" /><category term="pihole" /><category term="tailscale" /><category term="raspberry-pi" /><category term="usb-nic" /><category term="r8152" /><category term="fix" /><summary type="html"><![CDATA[A quick reference for fixing random USB NIC disconnects on Pi-hole + Tailscale setups using Realtek r8152 adapters.]]></summary></entry><entry><title type="html">Automating Jekyll Commits with Gitkwik</title><link href="http://localhost:4000/posts/2025/08/24/gitkwik-for-jekyll/" rel="alternate" type="text/html" title="Automating Jekyll Commits with Gitkwik" /><published>2025-08-24T00:00:00-04:00</published><updated>2025-08-24T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/24/gitkwik-for-jekyll</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/24/gitkwik-for-jekyll/"><![CDATA[<p>After creating <a href="/posts/2025/07/27/scripting-git-updates/">gitkwik</a> to handle versioning and commits on my Node based project, <a href="/dashatron/">Dashatron</a>, I wanted a simpler version for my Jekyll site. This version removes all the NPM logic, supports versioning directly inside <code class="language-plaintext highlighter-rouge">_config.yml</code>, and auto-builds the site with <code class="language-plaintext highlighter-rouge">jekyll build</code> so I never forget.</p>

<hr />

<h2 id="what-it-does">What It Does</h2>

<ol>
  <li>Checks for uncommitted changes</li>
  <li>Prompts for a commit message</li>
  <li>Offers to bump the <code class="language-plaintext highlighter-rouge">version:</code> in <code class="language-plaintext highlighter-rouge">_config.yml</code> (<code class="language-plaintext highlighter-rouge">patch</code>, <code class="language-plaintext highlighter-rouge">minor</code>, or <code class="language-plaintext highlighter-rouge">major</code>)</li>
  <li>Commits the version bump (if any) separately</li>
  <li>Builds the site with <code class="language-plaintext highlighter-rouge">bundle exec jekyll build</code></li>
  <li>Optionally commits <code class="language-plaintext highlighter-rouge">_site/</code> if tracked</li>
</ol>

<p>It skips the push step — I like to eyeball everything one last time before sending it up.</p>

<hr />

<h2 id="jekyllkwiksh">jekyllkwik.sh</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>

<span class="nb">set</span> <span class="nt">-e</span>

<span class="nb">echo</span> <span class="s2">"Checking for changes..."</span>
<span class="k">if </span>git diff <span class="nt">--quiet</span> <span class="o">&amp;&amp;</span> git diff <span class="nt">--cached</span> <span class="nt">--quiet</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Working directory clean. No changes to commit. Exiting."</span>
  <span class="nb">exit </span>0
<span class="k">fi

</span><span class="nb">echo</span> <span class="s2">"Detected changes:"</span>
git status <span class="nt">-s</span>

<span class="nb">echo</span> <span class="s2">""</span>
<span class="nb">read</span> <span class="nt">-p</span> <span class="s2">"Enter your commit message: "</span> commit_message

<span class="nb">echo</span> <span class="s2">"Staging changes..."</span>
git add <span class="nb">.</span>

<span class="nb">echo</span> <span class="s2">"Committing..."</span>
git commit <span class="nt">-m</span> <span class="s2">"</span><span class="nv">$commit_message</span><span class="s2">"</span>

<span class="nb">echo</span> <span class="s2">""</span>
<span class="nb">echo</span> <span class="s2">"Do you want to bump the version number in _config.yml?"</span>
<span class="k">select </span>bump <span class="k">in</span> <span class="s2">"patch"</span> <span class="s2">"minor"</span> <span class="s2">"major"</span> <span class="s2">"no version change"</span> <span class="s2">"cancel"</span><span class="p">;</span> <span class="k">do
  case</span> <span class="nv">$bump</span> <span class="k">in
    </span>patch|minor|major<span class="p">)</span>
      <span class="nb">echo</span> <span class="s2">"Bumping </span><span class="nv">$bump</span><span class="s2"> version in _config.yml..."</span>

      <span class="k">if </span><span class="nb">grep</span> <span class="nt">-q</span> <span class="s2">"^[[:space:]]*version:"</span> _config.yml<span class="p">;</span> <span class="k">then
        </span><span class="nv">old_version</span><span class="o">=</span><span class="si">$(</span><span class="nb">grep</span> <span class="s2">"^[[:space:]]*version:"</span> _config.yml | <span class="nb">sed</span> <span class="nt">-E</span> <span class="s1">'s/^[[:space:]]*version:[[:space:]]*["]?([^"]+)["]?/\1/'</span><span class="si">)</span>
        <span class="nv">IFS</span><span class="o">=</span><span class="s1">'.'</span> <span class="nb">read</span> <span class="nt">-r</span> major minor patch <span class="o">&lt;&lt;&lt;</span> <span class="s2">"</span><span class="nv">$old_version</span><span class="s2">"</span>

        <span class="k">case</span> <span class="nv">$bump</span> <span class="k">in
          </span>patch<span class="p">)</span> <span class="nv">patch</span><span class="o">=</span><span class="k">$((</span>patch <span class="o">+</span> <span class="m">1</span><span class="k">))</span> <span class="p">;;</span>
          minor<span class="p">)</span> <span class="nv">minor</span><span class="o">=</span><span class="k">$((</span>minor <span class="o">+</span> <span class="m">1</span><span class="k">))</span><span class="p">;</span> <span class="nv">patch</span><span class="o">=</span>0 <span class="p">;;</span>
          major<span class="p">)</span> <span class="nv">major</span><span class="o">=</span><span class="k">$((</span>major <span class="o">+</span> <span class="m">1</span><span class="k">))</span><span class="p">;</span> <span class="nv">minor</span><span class="o">=</span>0<span class="p">;</span> <span class="nv">patch</span><span class="o">=</span>0 <span class="p">;;</span>
        <span class="k">esac</span>

        <span class="nv">new_version</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">major</span><span class="k">}</span><span class="s2">.</span><span class="k">${</span><span class="nv">minor</span><span class="k">}</span><span class="s2">.</span><span class="k">${</span><span class="nv">patch</span><span class="k">}</span><span class="s2">"</span>
        <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">''</span> <span class="nt">-E</span> <span class="s2">"s/^[[:space:]]*version:[[:space:]]*</span><span class="se">\"</span><span class="s2">?[^</span><span class="se">\"</span><span class="s2">]+</span><span class="se">\"</span><span class="s2">?/version: </span><span class="se">\"</span><span class="nv">$new_version</span><span class="se">\"</span><span class="s2">/"</span> _config.yml

        <span class="nb">echo</span> <span class="s2">"version: </span><span class="nv">$old_version</span><span class="s2"> → </span><span class="nv">$new_version</span><span class="s2">"</span>
        git add _config.yml
        git commit <span class="nt">-m</span> <span class="s2">"bump version to </span><span class="nv">$new_version</span><span class="s2">"</span>
      <span class="k">else
        </span><span class="nb">echo</span> <span class="s2">"No version: field found in _config.yml — skipping."</span>
      <span class="k">fi
      </span><span class="nb">break</span>
      <span class="p">;;</span>
    <span class="s2">"no version change"</span><span class="p">)</span>
      <span class="nb">echo</span> <span class="s2">"Skipping version bump..."</span>
      <span class="nb">break</span>
      <span class="p">;;</span>
    cancel<span class="p">)</span>
      <span class="nb">echo</span> <span class="s2">"Cancelled."</span>
      <span class="nb">exit </span>0
      <span class="p">;;</span>
    <span class="k">*</span><span class="p">)</span>
      <span class="nb">echo</span> <span class="s2">"Invalid selection."</span>
      <span class="p">;;</span>
  <span class="k">esac</span>
<span class="k">done

</span><span class="nb">echo</span> <span class="s2">"Building Jekyll site..."</span>
bundle <span class="nb">exec </span>jekyll build <span class="o">||</span> <span class="o">{</span> <span class="nb">echo</span> <span class="s2">"Jekyll build failed."</span><span class="p">;</span> <span class="nb">exit </span>1<span class="p">;</span> <span class="o">}</span>

<span class="k">if</span> <span class="o">[</span> <span class="nt">-d</span> <span class="s2">"_site"</span> <span class="o">]</span><span class="p">;</span> <span class="k">then
  </span><span class="nb">echo</span> <span class="s2">"Checking for changes in _site..."</span>
  <span class="k">if</span> <span class="o">!</span> git diff <span class="nt">--quiet</span> _site<span class="p">;</span> <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"Staging updated _site output..."</span>
    git add _site
    git commit <span class="nt">-m</span> <span class="s2">"update _site build output"</span>
  <span class="k">else
    </span><span class="nb">echo</span> <span class="s2">"No changes in _site."</span>
  <span class="k">fi
fi

</span><span class="nb">echo</span> <span class="s2">"Done! You can now push your changes."</span>
</code></pre></div></div>

<hr />

<h2 id="adding-version-to-_configyml">Adding <code class="language-plaintext highlighter-rouge">version:</code> to <code class="language-plaintext highlighter-rouge">_config.yml</code></h2>

<p>Part of this scripts function is to handle versioning, so you need to add this line near the top of your <code class="language-plaintext highlighter-rouge">_config.yml</code>:</p>

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

<p>You can then use it inside your Jekyll templates using:</p>

<div class="language-liquid highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{ '{ site.version }' }
</code></pre></div></div>

<hr />]]></content><author><name>Tony</name></author><category term="jekyll" /><category term="bash" /><category term="git" /><category term="automation" /><category term="workflow" /><summary type="html"><![CDATA[A simplified version of my gitkwik script, adapted for Jekyll projects — with support for version bumps in `_config.yml`, clean commits, and automatic site builds.]]></summary></entry><entry><title type="html">Fixing Pi-hole Dropouts on Zero: Debugging the USB NIC Flapping Issue</title><link href="http://localhost:4000/posts/2025/08/24/fixing-pihole-usb-nic-flapping/" rel="alternate" type="text/html" title="Fixing Pi-hole Dropouts on Zero: Debugging the USB NIC Flapping Issue" /><published>2025-08-24T00:00:00-04:00</published><updated>2025-08-24T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/24/fixing-pihole-usb-nic-flapping</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/24/fixing-pihole-usb-nic-flapping/"><![CDATA[<p><strong>*Update</strong>: I found another issue with this Pi-Hole install. <a href="/posts/2025/10/02/pi-hole-followup-sd-freezes/">Read about it here.</a>*</p>

<p>For a while, my <strong>zero.local</strong> Pi-hole / Tailscale box would randomly go offline. DNS queries failed, Tailscale would scream about DERP disconnects, and sometimes the only fix was a reboot. This was on a Raspberry Pi Zero 2 W with a <a href="https://www.waveshare.com/wiki/ETH/USB_HUB_HAT_(B)">Waveshare ETH/USB Hub Hat</a> using a Realtek R8152 NIC.</p>

<p>The real culprit turned out to be the NIC flapping its link — dropping carrier and renegotiating constantly. Here’s the final clean fix that made it stable.</p>

<p>If you just want the commands, check out the <a href="/notes/pihole-usb-nic-quick-fix/">Quick Fix Guide</a></p>

<hr />

<h2 id="symptoms"><strong>Symptoms</strong></h2>

<ul>
  <li><strong>Random DNS failures</strong> on the network, even though Pi-hole and the system were running.</li>
  <li><strong>Tailscale logs</strong> showed repeated DERP disconnects and timeouts.</li>
  <li>Device sometimes stayed up but was unreachable; other times it locked until reboot.</li>
  <li>Power and thermal checks showed no undervoltage or overheating — this was strictly a NIC problem.</li>
</ul>

<hr />

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

<h3 id="1-lock-speedduplex-with-networkmanager">1. Lock speed/duplex with NetworkManager</h3>

<p>Force the NIC to 100 Mbps full-duplex, with autonegotiation off. This lowers heat and avoids flaky gigabit negotiation.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nmcli connection modify <span class="s2">"Wired connection 1"</span> <span class="se">\</span>
  ethernet.auto-negotiate no <span class="se">\</span>
  ethernet.speed 100 <span class="se">\</span>
  ethernet.duplex full

<span class="nb">sudo </span>nmcli connection down <span class="s2">"Wired connection 1"</span>
<span class="nb">sudo </span>nmcli connection up <span class="s2">"Wired connection 1"</span>
</code></pre></div></div>

<p>Verify:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ethtool eth0 | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'Speed|Duplex|Auto-negotiation'</span>
</code></pre></div></div>

<hr />

<h3 id="2-disable-energy-efficient-ethernet-eee">2. Disable Energy Efficient Ethernet (EEE)</h3>

<p>The r8152 driver tries to use EEE, which causes instability.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ethtool <span class="nt">--set-eee</span> eth0 eee off
ethtool <span class="nt">--show-eee</span> eth0
</code></pre></div></div>

<p>Make it permanent:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">echo</span> <span class="s1">'options r8152 eee_enable=0'</span> | <span class="nb">sudo tee</span> /etc/modprobe.d/r8152.conf
<span class="nb">sudo </span>update-initramfs <span class="nt">-u</span>
</code></pre></div></div>

<hr />

<h3 id="3-disable-usb-autosuspend">3. Disable USB Autosuspend</h3>

<p>Keep the NIC from being powered down mid-traffic.</p>

<p><code class="language-plaintext highlighter-rouge">/etc/udev/rules.d/99-r8152-no-autosuspend.rules</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="0bda", ATTR{idProduct}=="8152", \
  TEST=="power/control", ATTR{power/control}="on"
</code></pre></div></div>

<p>Reload rules:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>udevadm control <span class="nt">--reload</span>
<span class="nb">sudo </span>udevadm trigger
</code></pre></div></div>

<hr />

<h2 id="sanity-check"><strong>Sanity Check</strong></h2>

<ul>
  <li><code class="language-plaintext highlighter-rouge">ethtool eth0</code> shows <strong>100Mb/s</strong>, <strong>Full Duplex</strong>, <strong>Auto-negotiation off</strong>.</li>
  <li><code class="language-plaintext highlighter-rouge">ethtool --show-eee eth0</code> shows <strong>EEE disabled</strong>.</li>
  <li><code class="language-plaintext highlighter-rouge">dmesg | grep -c "r8152 .* eth0: carrier"</code> stays low — no constant flaps.</li>
</ul>

<hr />

<h2 id="future-hardening"><strong>Future Hardening</strong></h2>

<ul>
  <li>Add a small heatsink to the Realtek chip (they run hot).</li>
  <li>Use a NIC with a better chipset (ASIX AX88179) if you want true gigabit.</li>
  <li>Add a watchdog to auto-restart networking if the link ever dies completely.</li>
</ul>

<hr />

<h2 id="final-verdict"><strong>Final Verdict</strong></h2>

<p>The outages weren’t Pi-hole’s fault, weren’t Tailscale’s fault, and weren’t caused by the Pi itself. The <strong>Realtek r8152 NIC</strong> was flapping due to autonegotiation quirks and power-saving features.</p>

<p>Locking the link at <strong>100 Mbps full duplex</strong>, disabling <strong>EEE</strong>, and turning off <strong>USB autosuspend</strong> solved it. The box has been stable since.</p>]]></content><author><name>Tony</name></author><category term="pihole" /><category term="tailscale" /><category term="raspberry-pi" /><category term="usb-nic" /><category term="r8152" /><category term="troubleshooting" /><summary type="html"><![CDATA[How I debugged and fixed random DNS outages on my Pi-hole + Tailscale box caused by a flaky Realtek USB NIC.]]></summary></entry><entry><title type="html">Remotely Updating from Debian 12 Bookworm to Debian 13 Trixie</title><link href="http://localhost:4000/posts/2025/08/23/remotely-updating-macmini-to-debian-13/" rel="alternate" type="text/html" title="Remotely Updating from Debian 12 Bookworm to Debian 13 Trixie" /><published>2025-08-23T00:00:00-04:00</published><updated>2025-08-23T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/23/remotely-updating-macmini-to-debian-13</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/23/remotely-updating-macmini-to-debian-13/"><![CDATA[<h4 id="update-20250905-there-were-display-issues-following-this-update-i-will-post-a-followup-once-ive-corrected-them"><em>Update 2025/09/05:</em> There were display issues following this update. I will post a followup once I’ve corrected them.</h4>

<h4 id="update-20251014-i-still-havent-figured-this-out-but-i-havent-spend-much-time-on-it"><em>Update 2025/10/14:</em> I still haven’t figured this out, but I haven’t spend much time on it.</h4>

<hr />

<p>I’m traveling at the moment but have an idle morning so I’m geeking around and I see Debian 13 <code class="language-plaintext highlighter-rouge">Trixie</code> was release a week or so ago. Being a glutton for punishment, I’ve decided to see if I can force my 3D print server to complete the upgrade without needing physical intervention. This would be the second full kernel upgrade that install has seen.</p>

<table>
  <thead>
    <tr>
      <th>“MacMini”</th>
      <th>Mid 2011 Mac Mini</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>CPU</td>
      <td>Intel Core i5-2415M @ 2.30 GHz</td>
    </tr>
    <tr>
      <td>Architecture</td>
      <td>2 cores / 4 threads (Sandy Bridge)</td>
    </tr>
    <tr>
      <td>RAM</td>
      <td>16 GB - 1600Mhz DDR3</td>
    </tr>
  </tbody>
</table>

<p>Since I repurposed this as a server for my 3D printers, it has run Debian. I initially installed Raspberry Desktop on it so that it was something familiar to me as my initial Linux knowledge was all Raspberry Pi based. Raspberry Desktop has not been updated since 2022 and it’s currently based on Debian 11 (Bullseye). I was running into issues with some things being out of date so I pushed Debian 12 (Bookworm) to it without much fuss and it’s been running fairly well besides stumbling into an issue with upgrading Crowsnest, where <code class="language-plaintext highlighter-rouge">camera-streamer</code> was looking to see if I was running a Raspberry Pi. It checked for an old system file, and removing this remnant fixed this issue with no side effect.</p>

<hr />

<h2 id="beginning-the-process">Beginning the Process</h2>

<p>The first thing I’ve got to do is backup my important things. Since this server is just for Klipper &amp; Mainsail, I simply exported out backups from there and continued on.</p>

<p>Next thing to do was make sure it was fully updated. I ran the following.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt upgrade
<span class="nb">sudo </span>apt full-upgrade
<span class="nb">sudo </span>apt <span class="nt">--purge</span> autoremove
</code></pre></div></div>

<hr />

<h2 id="update-the-apt-sources">Update the APT Sources</h2>

<p>The next step is swapping the <code class="language-plaintext highlighter-rouge">apt</code> sources. You could do this manually, or you can do it the quick and dirty way. I went quick and dirty, and just checked the files after the fact.</p>

<p>You’ll need to edit <code class="language-plaintext highlighter-rouge">/etc/apt/sources.list</code> and any files under <code class="language-plaintext highlighter-rouge">/etc/apt/sources.list.d/</code>. Change every occurrence of <code class="language-plaintext highlighter-rouge">bookworm</code> to <code class="language-plaintext highlighter-rouge">trixie</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo sed</span> <span class="nt">-i</span> <span class="s1">'s/bookworm/trixie/g'</span> /etc/apt/sources.list
<span class="nb">sudo sed</span> <span class="nt">-i</span> <span class="s1">'s/bookworm/trixie/g'</span> /etc/apt/sources.list.d/<span class="k">*</span>.list
</code></pre></div></div>

<p><em>Double-check your source files!</em></p>

<hr />

<h2 id="and-so-it-begins">And so it begins…</h2>

<p>First I updated the package list with <code class="language-plaintext highlighter-rouge">apt update</code> and then began upgrades with <code class="language-plaintext highlighter-rouge">apt upgrade</code>. That’s when I ran into my first hiccup.</p>

<p>insert screenshot here</p>

<p>This is a DKMS build failing against an old kernel (5.10.0‑34-amd64) that’s still installed on the system. DKMS tries to (re)build the Broadcom “wl” module for every installed kernel; headers for 5.10 aren’t present, so it bombs and blocks dpkg.</p>

<p>So let’s just remove the driver and fix packages…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nt">-y</span> purge broadcom-sta-dkms
<span class="nb">sudo </span>apt <span class="nt">-f</span> <span class="nb">install
sudo </span>apt autoremove <span class="nt">--purge</span>
</code></pre></div></div>

<p>That fixed it and let me finish the basic updates. Now it’s time to run the big upgrades. Cross your fingers and pray because I’m doing this as I type…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt full-upgrade
</code></pre></div></div>

<p>…. lets see what happens.</p>

<p>First issue doing <code class="language-plaintext highlighter-rouge">full-upgrade</code> was this</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Errors were encountered <span class="k">while </span>processing:
 /tmp/apt-dpkg-install-c5gq5O/02-libgtk-3-0t64_3.24.49-3_i386.deb
E: Sub-process /usr/bin/dpkg returned an error code <span class="o">(</span>1<span class="o">)</span>
</code></pre></div></div>

<p>This error is coming from a 32‑bit (i386) GTK package that’s part of the Debian 13 t64 transition:<br />
libgtk-3-0t64_…_i386.deb. On Trixie, many libs got new …t64 names. Since I don’t <em>think</em> I need 32-bit anymore, I’ll just nuke it.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check if i386 is enabled and what packages are installed</span>
dpkg <span class="nt">--print-foreign-architectures</span>
dpkg <span class="nt">-l</span> | <span class="nb">awk</span> <span class="s1">'/:i386/{print $2}'</span>
</code></pre></div></div>

<p>Nothing in here looked important to me, so I’ll continue on.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Purge all installed i386 packages</span>
dpkg <span class="nt">-l</span> | <span class="nb">awk</span> <span class="s1">'/:i386/{print $2}'</span> | <span class="nb">sudo </span>xargs <span class="nt">-r</span> apt <span class="nt">-y</span> purge
</code></pre></div></div>

<p>This failed. Continuing to run into dpkg errors.. but I’m too stupid to try and sort that out so fuck it.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nt">-o</span> Dpkg::Options::<span class="o">=</span><span class="s2">"--force-overwrite"</span> <span class="nt">-f</span> <span class="nb">install</span>
</code></pre></div></div>

<p>This looks like it succesfully fixed the dpkg errors so ran <code class="language-plaintext highlighter-rouge">sudo apt update</code> and <code class="language-plaintext highlighter-rouge">sudo apt upgrade</code> again. At 60% as I type this…</p>

<p>I ran a few passes of the following until everything resolved…</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt <span class="nt">-f</span> <span class="nb">install
sudo </span>apt autoremove <span class="nt">--purge</span> <span class="nt">-y</span>
<span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt full-upgrade
</code></pre></div></div>

<p>I was left with one straggler, <code class="language-plaintext highlighter-rouge">dpkg: warning: cannot remove non-foreign architecture 'i386'</code>, so I ran the following commands..</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>dpkg <span class="nt">--configure</span> <span class="nt">-a</span>
<span class="nb">sudo </span>apt <span class="nt">-f</span> <span class="nb">install</span> <span class="o">||</span> <span class="nb">true
sudo </span>apt clean
<span class="nb">sudo rm</span> <span class="nt">-f</span> /var/cache/apt/archives/<span class="k">*</span>i386.deb
</code></pre></div></div>

<p>And here were are..</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
Hit:1 http://deb.debian.org/debian trixie InRelease
Hit:2 http://deb.debian.org/debian-security trixie-security InRelease
Hit:3 http://deb.debian.org/debian trixie-updates InRelease
Get:4 https://pkgs.tailscale.com/stable/debian trixie InRelease
Hit:5 https://packagecloud.io/dcommander/turbovnc/any any InRelease
Fetched 6,579 B <span class="k">in </span>1s <span class="o">(</span>7,485 B/s<span class="o">)</span>
All packages are up to date.
</code></pre></div></div>

<p>I guess all there is left to do is reboot and see if it comes back online.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>reboot
</code></pre></div></div>

<p>Now we wait…</p>

<p>So it took a bit longer than usual to reboot, or at least it felt like it, but it’s back online. It’s up. It’s running. I did this from 300 miles away. I feel like I accomplished something. I know a clean install is the right way to do this, but I like a challenge. Doing things like this is an exercise, letting me learn how to troubleshoot things I wouldn’t typically run into in the day to day.</p>]]></content><author><name>Tony</name></author><category term="bash" /><category term="linux" /><category term="debian" /><category term="macmini" /><category term="trixie" /><category term="bullseye" /><category term="bookworm" /><category term="vintage mac" /><summary type="html"><![CDATA[Update 2025/09/05: There were display issues following this update. I will post a followup once I’ve corrected them.]]></summary></entry><entry><title type="html">The burnt out husk of a lawn tractor…</title><link href="http://localhost:4000/posts/2025/08/15/the-burnt-out-husk-of-a-lawn-tractor/" rel="alternate" type="text/html" title="The burnt out husk of a lawn tractor…" /><published>2025-08-15T00:00:00-04:00</published><updated>2025-08-15T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/15/the%20burnt%20out%20husk%20of%20a%20lawn%20tractor</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/15/the-burnt-out-husk-of-a-lawn-tractor/"><![CDATA[<p>When I bought my house about five years ago my mother in law gave me her old lawn tractor. It was around 18 years old and had plenty of hours on it but it was in good shape. It my was first lawn tractor and a point of suburban pride for an idiot like me. I spent that last five years puttering around the back yard on it, usually with a beer in one hand and my headphones on listening to a book or podcast. It was a bit of a running joke about being a stereotypical dad. My wife got me a <a href="https://www.dudedad.com/products/hot-dad-summer-shirt">t-shirt</a> from <em>Dude Dad</em> that said “Hot Dad Summer” with a silhouette of a lawn tractor on it. I’ve got a small model of my tractor on my desk at work, and another larger model of it in my garage. It really has been my summer “hobby” ever since we’ve into this house.</p>

<p>Unfortunately time had taken its toll on the old girl as it does. I had rebuilt the deck a couple years ago; new spindles, bearings, refreshed the paint, etc. I had changed the bearing on the front wheels, though the really needed new axles. It had a few oil leaks as well, so I fixed the ones I could without disassembling the entire motor but they continued. Oil was leaking on the exhaust and it would begin to smoke badly once it was up to temperature. Last season there was an incident and a small fire happened below the power take off and melted a belt and some wires. That was an easy fix and I just told myself to be better at clearing the debris from around the motor.</p>

<p>This season comes around and I’m doing my best and not expecting any issues with my spotless, though a bit leaky tractor, when disaster struck. I had finished up most of the yard and hopped off to go use the weed whacker. I finish up trimming the weeds around the other shed and as I turn around to head back to the mower I see an inferno erupting under the hood. I jogged over to it and pulled the trans disconnect and popped the brake and pulled it away from the shed and out from under a tree. Resigned to the fact that it was probably already too far gone, I grabbed my beer and phone from their spots next to the seat and walked back to the house, turned on the hose, and made my way back to the mower.</p>

<p>There, beer in hand and headphones in my ear, I stood gently spraying the first lawn tractor I had ever had. Mourning its last breath and thinking about what was next and how I was going to maintain my stupid yard, it was a melancholy moment. The peaceful moment was broken suddenly when through my headphones I heard “HEY!” and turned to see one of my neighbors shouting at me from across the fence while waving a little red fire extinguisher in the air. At first I raised my beer to them as I continued to spray and then told them, “I’ve got it. It’s dead anyway, no need to waste your extinguisher but thank you.”</p>

<p>I realize it’s a bit silly to reminisce about something as silly as a lawn tractor, but I miss the simple delight of it.</p>

<h2 id="what-comes-next">What comes next…</h2>

<p>Being a quite lucky person, and a bit of a masochist, I’ve taken on the project of rebuilding her.</p>

<p><em>to be continued…</em></p>]]></content><author><name>Tony</name></author><category term="home" /><category term="home" /><category term="lawncare" /><category term="engines" /><category term="john deere" /><summary type="html"><![CDATA[When I bought my house about five years ago my mother in law gave me her old lawn tractor.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://localhost:4000/assets/img/posts/tractor/tractor-toy.jpeg" /><media:content medium="image" url="http://localhost:4000/assets/img/posts/tractor/tractor-toy.jpeg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Installing Rayhunter on Orbic Hotspot</title><link href="http://localhost:4000/posts/2025/08/14/rayhunter-on-orbic/" rel="alternate" type="text/html" title="Installing Rayhunter on Orbic Hotspot" /><published>2025-08-14T00:00:00-04:00</published><updated>2025-08-14T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/14/rayhunter-on-orbic</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/14/rayhunter-on-orbic/"><![CDATA[<h2 id="rayhunter-project">Rayhunter Project</h2>

<p>Several months back I saw an article from the <a href="https://www.eff.org/deeplinks/2025/03/meet-rayhunter-new-open-source-tool-eff-detect-cellular-spying">Electronic Frontier Foundation</a> that talked about using cheap mobile hotspots to detect cellphone intercepting Stingrays. Although I have no use for such a device, I thought it was a cool project. The hardware only cost me $11 on eBay. It seemed like a simple enough project, but I ran into some issues when trying to patch the device from my M2 macbook, so I thought I’d share what I did to get around the issues. Hopefully it helps someone else in the same situation.</p>

<hr />

<h2 id="installing-effs-rayhunter-on-an-orbic-mobile-hotspot">Installing EFF’s Rayhunter on an Orbic Mobile Hotspot</h2>

<p>Here’s a step-by-step of how I got <a href="https://github.com/EFForg/rayhunter">EFF’s Rayhunter</a> working on an <strong>Orbic Speed RC400L (Verizon) 4G LTE mobile hotspot</strong>, including all the weird quirks I had to fix.</p>

<hr />

<h2 id="prerequisites">Prerequisites</h2>

<ul>
  <li>An Orbic Speed RC400L 4g LTE hotspot</li>
  <li><code class="language-plaintext highlighter-rouge">platform-tools</code> (ADB binaries).</li>
  <li>Rayhunter binaries from EFF: <a href="https://github.com/EFForg/rayhunter">github.com/EFForg/rayhunter</a>.</li>
  <li>A host machine running macOS (in my case, Apple Silicon).</li>
</ul>

<figure class="gallery not-prose my-6 grid gap-4 grid-cols-1" data-gallery="orbic"><a href="/assets/img/posts/rayhunter/orbic.png" class="gallery__item group relative block overflow-hidden rounded-xl border border-[var(--line)] bg-[var(--panel-2)] shadow-sm transition duration-200 hover:-translate-y-1 hover:border-[var(--accent)] focus:outline-none focus-visible:ring focus-visible:ring-[var(--accent)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--panel)]" data-gallery="orbic" data-src="/assets/img/posts/rayhunter/orbic.png">
        <img src="/assets/img/posts/rayhunter/orbic.png" alt="orbic.png" class="gallery__img h-full w-full object-cover transition-transform duration-300 transform-gpu group-hover:scale-[1.03]" srcset="/assets/resized/256/orbic.png 256w, /assets/resized/360/orbic.png 360w, /assets/resized/480/orbic.png 480w, /assets/resized/800/orbic.png 800w, /assets/resized/1200/orbic.png 1200w, /assets/img/posts/rayhunter/orbic.png 1600w" sizes="min(80ch, 100vw)" width="1600" height="1200" loading="lazy" decoding="async" fetchpriority="low" />

      </a><figcaption class="col-span-full mt-3 text-center text-xs uppercase tracking-[0.2em] text-[var(--muted)]">
      Orbic Speed RC400L
    </figcaption></figure>

<hr />

<h2 id="enabling-adb">Enabling ADB</h2>

<ol>
  <li>Power on the hotspot and plug it into USB.</li>
  <li>
    <p>Ensure ADB is enabled on the device:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./platform-tools/adb devices
</code></pre></div>    </div>

    <p>You should see a real device listed (e.g. <code class="language-plaintext highlighter-rouge">b258a7ae</code>).</p>
  </li>
  <li>
    <p>If multiple devices show up (like an emulator), use:</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./platform-tools/adb <span class="nt">-s</span> b258a7ae ...
</code></pre></div>    </div>

    <p>for every command.</p>
  </li>
  <li>I updated the <code class="language-plaintext highlighter-rouge">install.sh</code> script to lock ADB to that serial using:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">ADB</span><span class="o">=(</span>./platform-tools/adb <span class="nt">-s</span> b258a7ae<span class="o">)</span>
</code></pre></div>    </div>
    <p>And used <code class="language-plaintext highlighter-rouge">${{ADB[@]}}</code> to avoid errors like:</p>
    <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./platform-tools/adb -s b258a7ae: No such file or directory
</code></pre></div>    </div>
  </li>
</ol>

<hr />

<h2 id="patching-installsh">Patching <code class="language-plaintext highlighter-rouge">install.sh</code></h2>

<p>The original <code class="language-plaintext highlighter-rouge">install.sh</code> assumes ADB is globally accessible and doesn’t handle multiple devices. I made these changes:</p>

<ul>
  <li>Defined ADB with arguments as an array:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">ADB</span><span class="o">=(</span>./platform-tools/adb <span class="nt">-s</span> b258a7ae<span class="o">)</span>
</code></pre></div>    </div>
  </li>
  <li>Replaced all calls to <code class="language-plaintext highlighter-rouge">"$ADB"</code> with <code class="language-plaintext highlighter-rouge">"${{ADB[@]}}"</code>.</li>
  <li>Wrapped critical commands (like checking for <code class="language-plaintext highlighter-rouge">atfwd_daemon</code>) in timeout logic so the script doesn’t hang forever.</li>
</ul>

<p>Example change:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wait_for_atfwd_daemon<span class="o">()</span> <span class="o">{</span>
    <span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"waiting for atfwd_daemon to startup..."</span>
    <span class="k">for </span>i <span class="k">in</span> <span class="o">{{</span>1..60<span class="o">}}</span><span class="p">;</span> <span class="k">do
        if </span>_adb_shell pgrep atfwd_daemon <span class="o">&gt;</span> /dev/null 2&gt;&amp;1<span class="p">;</span> <span class="k">then
            </span><span class="nb">echo</span> <span class="s2">" found!"</span>
            <span class="k">return
        fi
        </span><span class="nb">sleep </span>1
    <span class="k">done
    </span><span class="nb">echo</span> <span class="s2">" timeout waiting for atfwd_daemon"</span>
    <span class="nb">exit </span>1
<span class="o">}</span>
</code></pre></div></div>

<hr />

<h2 id="deploying-rayhunter">Deploying Rayhunter</h2>

<p>Once patched, the install went like this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./install.sh
</code></pre></div></div>

<p>The script:</p>

<ul>
  <li>Pushed the <code class="language-plaintext highlighter-rouge">rootshell</code> binary to <code class="language-plaintext highlighter-rouge">/data/local/tmp/</code></li>
  <li>Waited for <code class="language-plaintext highlighter-rouge">atfwd_daemon</code> to come online</li>
  <li>Installed and forwarded the Rayhunter server</li>
  <li>Set up TCP port forwarding with:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./platform-tools/adb <span class="nt">-s</span> b258a7ae forward tcp:8080 tcp:8080
</code></pre></div>    </div>
  </li>
</ul>

<p>Then, from the host machine, I accessed the Rayhunter web UI at:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>http://localhost:8080
</code></pre></div></div>

<hr />

<h2 id="testing-rayhunter-manually">Testing Rayhunter (Manually)</h2>

<p>Here’s the <code class="language-plaintext highlighter-rouge">test_rayhunter()</code> function I used:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>test_rayhunter<span class="o">()</span> <span class="o">{</span>
    <span class="nv">URL</span><span class="o">=</span><span class="s2">"http://localhost:8080"</span>
    <span class="s2">"</span><span class="k">${</span><span class="p">{ADB[@]</span><span class="k">}</span><span class="s2">}"</span> forward tcp:8080 tcp:8080 <span class="o">&gt;</span> /dev/null
    <span class="nb">echo</span> <span class="nt">-n</span> <span class="s2">"checking for rayhunter server..."</span>

    <span class="nv">SECONDS</span><span class="o">=</span>0
    <span class="k">while</span> <span class="o">((</span> SECONDS &lt; 30 <span class="o">))</span><span class="p">;</span> <span class="k">do
        if </span>curl <span class="nt">-L</span> <span class="nt">--fail-with-body</span> <span class="s2">"</span><span class="nv">$URL</span><span class="s2">"</span> <span class="nt">-o</span> /dev/null <span class="nt">-s</span><span class="p">;</span> <span class="k">then
            </span><span class="nb">echo</span> <span class="s2">"success!"</span>
            <span class="nb">echo</span> <span class="s2">"you can access rayhunter at </span><span class="nv">$URL</span><span class="s2">"</span>
            <span class="k">return
        fi
        </span><span class="nb">sleep </span>1
    <span class="k">done
    </span><span class="nb">echo</span> <span class="s2">"timeout reached! failed to reach rayhunter url </span><span class="nv">$URL</span><span class="s2">, something went wrong :("</span>
<span class="o">}</span>
</code></pre></div></div>

<hr />

<h2 id="pro-tips">Pro Tips</h2>

<ul>
  <li>If ADB gets stuck on “more than one device/emulator,” kill the emulator:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb <span class="nt">-s</span> emulator-XXXX emu <span class="nb">kill</span>
</code></pre></div>    </div>
  </li>
  <li>You can always forcibly reset:
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>adb kill-server <span class="o">&amp;&amp;</span> adb start-server
</code></pre></div>    </div>
  </li>
  <li>Make sure <code class="language-plaintext highlighter-rouge">pgrep</code> works on the device; if not, use <code class="language-plaintext highlighter-rouge">ps | grep</code> fallback.</li>
</ul>

<hr />]]></content><author><name>Tony</name></author><category term="rayhunter" /><category term="orbic" /><category term="eff" /><category term="adb" /><category term="hotspot" /><category term="root" /><category term="install" /><summary type="html"><![CDATA[Steps to install EFF's Rayhunter software on an Orbic-branded mobile hotspot]]></summary></entry><entry><title type="html">Raspberry Pi Price Tracking</title><link href="http://localhost:4000/posts/2025/08/06/raspberry-pi-price-tracking/" rel="alternate" type="text/html" title="Raspberry Pi Price Tracking" /><published>2025-08-06T00:00:00-04:00</published><updated>2025-08-06T00:00:00-04:00</updated><id>http://localhost:4000/posts/2025/08/06/Raspberry-Pi-Price-Tracking</id><content type="html" xml:base="http://localhost:4000/posts/2025/08/06/raspberry-pi-price-tracking/"><![CDATA[<blockquote>
  <p><strong>Update — Oct 2025:</strong><br />
This project has grown into a full automated tracker called <strong><a href="https://xtonyx.org/scrapegoat/">Scrapegoat</a></strong> — it scrapes Micro Center’s Raspberry Pi listings daily, tracks prices over time, and publishes interactive tables and charts.<br />
Read the follow-up post: <a href="/2025/10/13/scrapegoat-raspberry-pi-price-tracking/">Scrapegoat – Raspberry Pi Price Tracker</a></p>
</blockquote>

<h2 id="why">Why…</h2>

<p>This started off as an answer to a Reddit post.  Essentially, the poster asked if a 3 or 4 was good enough to run something. The memory requirements were discussed and it came down to a 4 or 5 and I dug into the prices to show it’s only $5 more to get an 5 with the same memory as a 4. A big part of me hates the apathetic way we all check Amazon for pricing first, when historically I’ve found Microcenter has the best prices. They’ve always had decent prices on Pi’s and so I generally use them as the baseline when costing projects.  I like to share my little markdown pricing table when people are trying to debate which device to choose now.. and I’d like to figure out a good way to keep an up-to-date record of these prices.</p>

<h2 id="raspberry-pi-devices">Raspberry Pi Devices</h2>

<p>These are the prices at MicroCenter as of Aug 8 2025</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">Memory</th>
      <th style="text-align: right">Zero W <sup>1.</sup></th>
      <th style="text-align: right">Zero 2 W <sup>1.</sup></th>
      <th style="text-align: right">Pi 3A+</th>
      <th style="text-align: right">Pi 3B+</th>
      <th style="text-align: right">Pi 4B</th>
      <th style="text-align: right">Pi 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">512mb</td>
      <td style="text-align: right">$14.99</td>
      <td style="text-align: right">$14.99</td>
      <td style="text-align: right">$24.99</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
    </tr>
    <tr>
      <td style="text-align: left">1GB</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">$34.99</td>
      <td style="text-align: right">$34.99</td>
      <td style="text-align: right">x</td>
    </tr>
    <tr>
      <td style="text-align: left">4GB</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">$54.99</td>
      <td style="text-align: right">$59.99</td>
    </tr>
    <tr>
      <td style="text-align: left">8GB</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">$74.99</td>
      <td style="text-align: right">$79.99</td>
    </tr>
    <tr>
      <td style="text-align: left">16GB</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">x</td>
      <td style="text-align: right">$99.99</td>
    </tr>
  </tbody>
</table>

<p><sup>1.</sup> Add $1.00 for a Zero w/ presoldered headers.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: left">Power</th>
      <th style="text-align: left">Connector</th>
      <th style="text-align: right">Price</th>
      <th style="text-align: center">Link</th>
      <th style="text-align: left">Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: left">12.5W</td>
      <td style="text-align: left">Micro USB</td>
      <td style="text-align: right">$7.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/643086/raspberry-pi-125-watt-power-supply">Link</a></td>
      <td style="text-align: left"> </td>
    </tr>
    <tr>
      <td style="text-align: left">15W</td>
      <td style="text-align: left">USB-C</td>
      <td style="text-align: right">$7.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/608169/raspberry-pi-4-official-15w-power-supply-us-white">Link</a></td>
      <td style="text-align: left">Raspberry Pi 4 Official 15W Power Supply US - White</td>
    </tr>
    <tr>
      <td style="text-align: left">15W</td>
      <td style="text-align: left">USB-C</td>
      <td style="text-align: right">$7.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/608170/raspberry-pi-4-official-15w-power-supply-us-black">Link</a></td>
      <td style="text-align: left">Raspberry Pi 4 Official 15W Power Supply US - Black</td>
    </tr>
    <tr>
      <td style="text-align: left">27W</td>
      <td style="text-align: left">USB-C</td>
      <td style="text-align: right">$11.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/671926/raspberry-pi-27w-usb-c-psu-white">Link</a></td>
      <td style="text-align: left">Raspberry Pi 27W USB-C PSU - White</td>
    </tr>
    <tr>
      <td style="text-align: left">27W</td>
      <td style="text-align: left">USB-C</td>
      <td style="text-align: right">$11.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/671927/raspberry-pi-27w-usb-c-psu-black">Link</a></td>
      <td style="text-align: left">Raspberry Pi 27W USB-C PSU - Black</td>
    </tr>
    <tr>
      <td style="text-align: left">30W</td>
      <td style="text-align: left">RJ45 POE</td>
      <td style="text-align: right">$39.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/692332/raspberry-pi-poe-injector">Link</a></td>
      <td style="text-align: left">Raspberry Pi PoE Injector</td>
    </tr>
    <tr>
      <td style="text-align: left">45W</td>
      <td style="text-align: left">USB-C</td>
      <td style="text-align: right">$14.99</td>
      <td style="text-align: center"><a href="https://www.microcenter.com/product/692335/raspberry-pi-45w-usb-c-power-supply">Link</a></td>
      <td style="text-align: left">Raspberry Pi 45W USB-C Power Supply</td>
    </tr>
  </tbody>
</table>

<p>Pi 5
2GB $50
4GB $60
8GB $80
16GB $120</p>

<p>Pi 4
1GB $35
2GB $45
4GB $55
8GB $75</p>]]></content><author><name>Tony</name></author><category term="microcenter" /><category term="raspberry pi" /><summary type="html"><![CDATA[Tracking the price of Raspberry Pi products at Microcenter so I don't have to keep looking them up..]]></summary></entry></feed>