<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ghpending | AkitaOnRails.com</title><link>https://akitaonrails.github.io/en/tags/ghpending/</link><description>Fabio Akita's blog — tech, career, and assorted geek topics. English edition.</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Mon, 19 Oct 2026 23:20:51 +0000</lastBuildDate><atom:link href="https://akitaonrails.github.io/en/tags/ghpending/index.xml" rel="self" type="application/rss+xml"/><item><title>Bypassing the GitHub API Block in Brazil</title><link>https://akitaonrails.github.io/en/2026/06/11/bypassing-github-api-block-brazil/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/06/11/bypassing-github-api-block-brazil/</guid><pubDate>Thu, 11 Jun 2026 20:00:00 -0300</pubDate><description>&lt;p&gt;This is a Brazil problem today. It may be your problem tomorrow if you live under a government that likes silent network blocks, copyright panic, opaque court orders, or any other flavor of &amp;ldquo;trust us, we know what we are doing.&amp;rdquo; The concrete symptom here is simple: &lt;code&gt;api.github.com&lt;/code&gt; started timing out in Brazil. The main &lt;code&gt;github.com&lt;/code&gt; site opens. SSH clones may keep working. But tools that depend on the GitHub API, like &lt;code&gt;gh&lt;/code&gt;, CI automations, personal scripts, bots, CLIs, dashboards, and anything that queries issues and pull requests, start hanging.&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a Brazil problem today. It may be your problem tomorrow if you live under a government that likes silent network blocks, copyright panic, opaque court orders, or any other flavor of &ldquo;trust us, we know what we are doing.&rdquo; The concrete symptom here is simple: <code>api.github.com</code> started timing out in Brazil. The main <code>github.com</code> site opens. SSH clones may keep working. But tools that depend on the GitHub API, like <code>gh</code>, CI automations, personal scripts, bots, CLIs, dashboards, and anything that queries issues and pull requests, start hanging.</p>
<p><a href="https://x.com/ayubio/status/2064878070394183955"target="_blank" rel="noopener">Ayub posted on X</a> that this has all the usual signs of a national block ordered by Anatel against <code>api.github.com</code>. In that same context, the tweet he quoted showed <code>api.github.com</code> resolving to <code>4.228.31.149</code>, but with no working route. The author said he tested through VIVO, Claro, and Oracle, and that it only worked through a Miami VPN. In other words: this does not look like &ldquo;my Wi-Fi is down.&rdquo; It looks like a block somewhere in the path.</p>
<p>According to Ayub, this kind of thing often happens in blocking waves, many times after Wednesday meetings with large carriers, inside the Brazilian mechanism for blocking addresses under the excuse of fighting piracy. The problem is the blast radius: instead of taking down only the target, it breaks legitimate infrastructure. Today it was the GitHub API. Tomorrow it can be anything your work depends on.</p>
<p>And yes, I confirmed it from here.</p>
<h2>How to know if you were affected<span class="hx:absolute hx:-mt-20" id="how-to-know-if-you-were-affected"></span>
    <a href="#how-to-know-if-you-were-affected" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>First, test whether the main site opens:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -4 -I --connect-timeout <span class="m">8</span> --max-time <span class="m">12</span> https://github.com/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here it worked. Then test the API:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -4 -I --connect-timeout <span class="m">8</span> --max-time <span class="m">12</span> https://api.github.com/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Here I got:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">curl: (28) Connection timed out after 8002 milliseconds</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Now check which IP your system resolves:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">getent ahostsv4 api.github.com</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In my case:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">4.228.31.149    STREAM api.github.com
</span></span><span class="line"><span class="cl">4.228.31.149    DGRAM
</span></span><span class="line"><span class="cl">4.228.31.149    RAW</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And <code>gh</code> depends on this API too. So commands like these may hang:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gh api rate_limit
</span></span><span class="line"><span class="cl">gh pr list
</span></span><span class="line"><span class="cl">gh issue list</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If you are a developer and suddenly <code>gh</code> stopped, your release scripts stopped, your PR-listing bot stopped, or some CLI that queries issues started hanging, this may be it. It is not your bug. Your route to <code>api.github.com</code> is being thrown into a hole.</p>
<h2>First try DNS 9.9.9.9<span class="hx:absolute hx:-mt-20" id="first-try-dns-9999"></span>
    <a href="#first-try-dns-9999" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Some people mentioned that changing DNS to <a href="https://www.quad9.net/"target="_blank" rel="noopener">Quad9</a>, <code>9.9.9.9</code>, could work around it. It makes sense to test, because some blocks are done through DNS: the provider returns the wrong IP, NXDOMAIN, or sends you into a sinkhole. If the problem is only your provider&rsquo;s DNS, changing DNS fixes it.</p>
<p>Test without changing anything in the system:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dig @9.9.9.9 +short api.github.com A</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>In my case, it did not help. Quad9 also returned:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">4.228.31.149</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And forcing that IP in <code>curl</code> still timed out:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -4 -I --connect-timeout <span class="m">8</span> --max-time <span class="m">12</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  --resolve api.github.com:443:4.228.31.149 <span class="se">\
</span></span></span><span class="line"><span class="cl">  https://api.github.com/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Result:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">curl: (28) Connection timed out after 8002 milliseconds</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>So, on my link, 9.9.9.9 did not fix it. But test yours. If your provider is only poisoning DNS, it may work.</p>
<p>On Arch with NetworkManager, it would be something like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S bind-tools curl
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">nmcli con show --active
</span></span><span class="line"><span class="cl">sudo nmcli con mod <span class="s2">&#34;YOUR CONNECTION NAME&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl">  ipv4.ignore-auto-dns yes <span class="se">\
</span></span></span><span class="line"><span class="cl">  ipv4.dns <span class="s2">&#34;9.9.9.9 149.112.112.112&#34;</span>
</span></span><span class="line"><span class="cl">sudo nmcli con down <span class="s2">&#34;YOUR CONNECTION NAME&#34;</span>
</span></span><span class="line"><span class="cl">sudo nmcli con up <span class="s2">&#34;YOUR CONNECTION NAME&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>On Ubuntu, same idea. Install the tools and adjust through NetworkManager:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install dnsutils curl
</span></span><span class="line"><span class="cl">nmcli con show --active</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or use the GNOME/KDE network settings screen and set manual DNS to <code>9.9.9.9</code> and <code>149.112.112.112</code> for the connection. Then disconnect and reconnect.</p>
<p>Test again:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dig +short api.github.com A
</span></span><span class="line"><span class="cl">curl -4 -I --connect-timeout <span class="m">8</span> --max-time <span class="m">12</span> https://api.github.com/</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If it works, good. You are done here.</p>
<p>If it keeps timing out, DNS was only the first layer. Then you need to route the connection through another path.</p>
<h2>Why I do not like the fixed-IP hack<span class="hx:absolute hx:-mt-20" id="why-i-do-not-like-the-fixed-ip-hack"></span>
    <a href="#why-i-do-not-like-the-fixed-ip-hack" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Another suggestion I saw was manually adding IPs to local DNS, like Pi-hole, Unbound, <code>/etc/hosts</code>, dnsmasq, that sort of thing. Example: &ldquo;resolve <code>api.github.com</code> to this other IP that works.&rdquo;</p>
<p>That can work for a few hours. I don&rsquo;t like it.</p>
<p>GitHub is large infrastructure. IPs change. Routes change. Load balancing changes. Regions change. The IP that works for somebody in Miami may be bad for you in São Paulo. The IP that works today may fail tomorrow. And if the block is a blackhole route at the provider, changing DNS to another IP may only trade one hole for another.</p>
<p>Pi-hole is great for a home network. Unbound is great. <code>/etc/hosts</code> saves you in an emergency. But pinning the IP of a large service like GitHub is how you forget an invisible hack and lose two hours next month trying to understand why only your machine is broken.</p>
<p>Use alternate DNS as a test. Use fixed IP only as a temporary bandage and document where you changed it.</p>
<h2>Fallback: SOCKS proxy only for GitHub tools<span class="hx:absolute hx:-mt-20" id="fallback-socks-proxy-only-for-github-tools"></span>
    <a href="#fallback-socks-proxy-only-for-github-tools" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>My solution was the least invasive thing I could make: a wrapper that starts a local SOCKS proxy through Tor and runs only the command I choose through it. It does not route the whole system. It does not touch the browser. It does not affect package managers, Steam, banking, nothing. Just <code>gh</code>, or whatever tool I explicitly call.</p>
<p>In my case, the wrapper lives at <code>~/.config/zsh/bin/github-proxy</code>, and the alias in <code>~/.config/zsh/aliases</code> does this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">alias</span> <span class="nv">gh</span><span class="o">=</span><span class="s1">&#39;$HOME/.config/zsh/bin/github-proxy gh&#39;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>The wrapper is this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="cp">#!/bin/bash
</span></span></span><span class="line"><span class="cl"><span class="nb">set</span> -euo pipefail
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nv">proxy_host</span><span class="o">=</span><span class="s2">&#34;127.0.0.1&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">proxy_port</span><span class="o">=</span><span class="s2">&#34;9050&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">tor_dir</span><span class="o">=</span><span class="s2">&#34;</span><span class="si">${</span><span class="nv">XDG_RUNTIME_DIR</span><span class="k">:-</span><span class="p">/tmp</span><span class="si">}</span><span class="s2">/github-proxy-tor&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">tor_rc</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$tor_dir</span><span class="s2">/torrc&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nv">tor_log</span><span class="o">=</span><span class="s2">&#34;</span><span class="nv">$tor_dir</span><span class="s2">/tor.log&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> -eq <span class="m">0</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  <span class="nb">echo</span> <span class="s2">&#34;usage: github-proxy &lt;command&gt; [args...]&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">  <span class="nb">exit</span> <span class="m">2</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> ! ss -ltn <span class="s2">&#34;sport = :</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span> <span class="p">|</span> grep -q <span class="s2">&#34;</span><span class="nv">$proxy_host</span><span class="s2">:</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">  mkdir -p <span class="s2">&#34;</span><span class="nv">$tor_dir</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  : &gt;<span class="s2">&#34;</span><span class="nv">$tor_rc</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">  tor -f <span class="s2">&#34;</span><span class="nv">$tor_rc</span><span class="s2">&#34;</span> --SocksPort <span class="s2">&#34;</span><span class="nv">$proxy_host</span><span class="s2">:</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span> --DataDirectory <span class="s2">&#34;</span><span class="nv">$tor_dir</span><span class="s2">/data&#34;</span> &gt;<span class="s2">&#34;</span><span class="nv">$tor_log</span><span class="s2">&#34;</span> 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">for</span> _ in <span class="o">{</span>1..60<span class="o">}</span><span class="p">;</span> <span class="k">do</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> grep -q <span class="s1">&#39;Bootstrapped 100%&#39;</span> <span class="s2">&#34;</span><span class="nv">$tor_log</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nb">break</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> ! <span class="nb">kill</span> -0 <span class="s2">&#34;</span><span class="nv">$!</span><span class="s2">&#34;</span> 2&gt;/dev/null<span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">      <span class="nb">echo</span> <span class="s2">&#34;github-proxy: tor exited before bootstrap; see </span><span class="nv">$tor_log</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">      <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">    <span class="k">fi</span>
</span></span><span class="line"><span class="cl">    sleep <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">done</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">if</span> ! grep -q <span class="s1">&#39;Bootstrapped 100%&#39;</span> <span class="s2">&#34;</span><span class="nv">$tor_log</span><span class="s2">&#34;</span><span class="p">;</span> <span class="k">then</span>
</span></span><span class="line"><span class="cl">    <span class="nb">echo</span> <span class="s2">&#34;github-proxy: timed out waiting for tor bootstrap; see </span><span class="nv">$tor_log</span><span class="s2">&#34;</span> &gt;<span class="p">&amp;</span><span class="m">2</span>
</span></span><span class="line"><span class="cl">    <span class="nb">exit</span> <span class="m">1</span>
</span></span><span class="line"><span class="cl">  <span class="k">fi</span>
</span></span><span class="line"><span class="cl"><span class="k">fi</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">HTTPS_PROXY</span><span class="o">=</span><span class="s2">&#34;socks5h://</span><span class="nv">$proxy_host</span><span class="s2">:</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">HTTP_PROXY</span><span class="o">=</span><span class="s2">&#34;socks5h://</span><span class="nv">$proxy_host</span><span class="s2">:</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">ALL_PROXY</span><span class="o">=</span><span class="s2">&#34;socks5h://</span><span class="nv">$proxy_host</span><span class="s2">:</span><span class="nv">$proxy_port</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">exec</span> <span class="s2">&#34;</span><span class="nv">$@</span><span class="s2">&#34;</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Notice <code>socks5h</code>. That <code>h</code> matters. It makes DNS resolution happen through the proxy, not through your local network. If you use plain <code>socks5://</code>, depending on the library, DNS may still happen locally before the proxy is used. With <code>socks5h://</code>, the idea is: &ldquo;take the hostname to the other side and resolve it there.&rdquo;</p>
<h2>Installing on Arch<span class="hx:absolute hx:-mt-20" id="installing-on-arch"></span>
    <a href="#installing-on-arch" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>On Arch:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo pacman -S tor github-cli iproute2
</span></span><span class="line"><span class="cl">mkdir -p ~/.local/bin</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Create the script:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">nano ~/.local/bin/github-proxy</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Paste the wrapper above and make it executable:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod +x ~/.local/bin/github-proxy</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Test it directly:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">~/.local/bin/github-proxy gh api rate_limit --jq .resources.core.limit</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>An authenticated account usually returns:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">5000</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>That is what happened here. Direct timed out. Through the wrapper, <code>gh api rate_limit --jq .resources.core.limit</code> returned <code>5000</code>.</p>
<p>Now add an alias to your shell.</p>
<p>ZSH:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;alias gh=&#39;</span><span class="nv">$HOME</span><span class="s2">/.local/bin/github-proxy gh&#39;&#34;</span> &gt;&gt; ~/.zshrc
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.zshrc</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Bash:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">echo</span> <span class="s2">&#34;alias gh=&#39;</span><span class="nv">$HOME</span><span class="s2">/.local/bin/github-proxy gh&#39;&#34;</span> &gt;&gt; ~/.bashrc
</span></span><span class="line"><span class="cl"><span class="nb">source</span> ~/.bashrc</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Test:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">gh api rate_limit --jq .resources.core.limit
</span></span><span class="line"><span class="cl">gh pr list</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Ubuntu<span class="hx:absolute hx:-mt-20" id="ubuntu"></span>
    <a href="#ubuntu" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>On Ubuntu, the Tor package is easy:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install tor iproute2 curl</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>For GitHub CLI, if the <code>gh</code> package is available in your version:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install gh</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>If it is not, follow the official GitHub CLI installation for Ubuntu/Debian. The rest is the same: create <code>~/.local/bin/github-proxy</code>, run <code>chmod +x</code>, and put the alias in <code>.bashrc</code> or <code>.zshrc</code>.</p>
<h2>Better alternatives than Tor<span class="hx:absolute hx:-mt-20" id="better-alternatives-than-tor"></span>
    <a href="#better-alternatives-than-tor" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Tor worked and it is convenient, but it is not the only option. Sometimes it is slow. Sometimes a service dislikes Tor exits. For <code>gh api</code> and listing PRs, fine. For heavier use, you may want something more predictable.</p>
<p>Reasonable options:</p>
<ul>
<li>Normal VPN: Proton, Mullvad, IVPN, your own WireGuard. Route everything or use split tunneling if you know how to configure it.</li>
<li>Tailscale with an exit node outside Brazil: clean, if you have a trusted machine outside the country or a VPS.</li>
<li>SSH SOCKS to a VPS: <code>ssh -N -D 127.0.0.1:9050 user@your-vps</code>. Then use the same <code>HTTPS_PROXY=socks5h://127.0.0.1:9050</code>.</li>
<li>Corporate proxy: if your company has egress outside Brazil, configure only the tools that need it.</li>
</ul>
<p>The care here is not changing everything at the same time. First prove that direct <code>api.github.com</code> fails. Then prove that another path works. Only then automate.</p>
<h2>Adjusting your own tools: ghpending<span class="hx:absolute hx:-mt-20" id="adjusting-your-own-tools-ghpending"></span>
    <a href="#adjusting-your-own-tools-ghpending" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This block also hit one of my utilities: <a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener">ghpending</a>. I already wrote about it in <a href="/en/2026/05/23/i-built-a-cli-to-check-my-github-pending-stuff-ghpending/">&ldquo;I Built a CLI to Check My GitHub Pending Stuff: ghpending&rdquo;</a>. It lists open issues and pull requests across my repositories, so I know where somebody is waiting for an answer.</p>
<p>The problem is obvious: it uses the GitHub API. If <code>api.github.com</code> falls into a hole, it falls with it.</p>
<p>So I added a proxy fallback. <code>ghpending</code> now reads <code>GITHUB_TOKEN</code> like before, but also tries to use a SOCKS proxy in this order:</p>
<ol>
<li><code>GHPENDING_GITHUB_PROXY</code>, if you want to force a specific proxy.</li>
<li><code>HTTPS_PROXY</code>, <code>https_proxy</code>, <code>ALL_PROXY</code>, or <code>all_proxy</code>, if they are <code>socks5://</code> or <code>socks5h://</code>.</li>
<li>A local SOCKS proxy at <code>127.0.0.1:9050</code>, if one is listening.</li>
<li>If none of that exists, it tries direct access.</li>
</ol>
<p>In the README, it looks like this:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GITHUB_TOKEN</span><span class="o">=</span><span class="k">$(</span>gh auth token<span class="k">)</span> ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>And for proxy:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nv">GHPENDING_GITHUB_PROXY</span><span class="o">=</span>socks5h://127.0.0.1:9050 ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Or just run it through the wrapper:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">~/.local/bin/github-proxy ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>My wrapper starts Tor on <code>127.0.0.1:9050</code>; <code>ghpending</code> detects that proxy and uses it for API calls. If you are not blocked, it keeps working directly. If you are blocked, it has an escape route.</p>
<p>This is the kind of fallback every Brazilian dev should now consider for tools that depend on foreign APIs. It is ugly, but the Brazilian internet is getting unpredictable.</p>
<h2>The political problem<span class="hx:absolute hx:-mt-20" id="the-political-problem"></span>
    <a href="#the-political-problem" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>At the end of the thread, <a href="https://x.com/ayubio/status/2064880160893968643"target="_blank" rel="noopener">Ayub reminded everyone</a> that, because of these secret collateral blocks and their side effects, there would already be around 250 million addresses blocked on the Brazilian internet. He also linked a presentation about the state of blocking in Brazil. In another tweet in the same thread, he says authorities defend secrecy as the only alternative left to fight crime.</p>
<p>I do not buy that excuse.</p>
<p>Secret blocking of critical infrastructure, with no transparency, no public adversarial process, no auditable list, no explanation of scope, is operational censorship. In my read, it is illegal precisely because it dodges the public, auditable process that should exist when the State orders internet access to be broken. Maybe it started with pirate TV boxes. Maybe the speech sounds noble. But the mechanism is there: somebody presses a button, carriers apply the block, legitimate users only find out when a work tool stops.</p>
<p>Today, the collateral damage hit honest developers trying to work with GitHub. Tomorrow it hits a Linux package, a container registry, a CDN, a payment API, an authentication service. Then everybody runs to VPNs, Tor, proxies, alternate DNS, <code>/etc/hosts</code>, Pi-hole. Congratulations: little by little, we are rebuilding the survival manual for a censored internet.</p>
<p>No need to overdo the metaphor. Brazil is not China yet. But the smell is the same: centralized opaque blocking, collateral damage, and ordinary users having to learn bypasses to work.</p>
<p>If you were affected, document it. Run the commands. Save outputs. Open an issue with your provider. Complain with data. Share workarounds, but do not normalize this. A bypass is a bandage. The real problem is a State giving itself the power to silently break pieces of the internet.</p>
<p>And if all you wanted was to run <code>gh pr list</code> to review a pull request: welcome to Brazilian infrastructure in 2026.</p>
]]></content:encoded><category>ghpending</category><category>networking</category><category>security</category><category>linux</category></item><item><title>Akita's AI Tips and Toolkit: ai-jail, ai-memory, ai-usagebar</title><link>https://akitaonrails.github.io/en/2026/05/24/akita-ai-tips-toolkit-ai-jail-ai-memory-ai-usagebar/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/05/24/akita-ai-tips-toolkit-ai-jail-ai-memory-ai-usagebar/</guid><pubDate>Sun, 24 May 2026 10:30:00 -0300</pubDate><description>&lt;p&gt;In last week&amp;rsquo;s post, &lt;a href="https://akitaonrails.github.io/en/2026/05/14/wrapping-up-my-ai-marathon-success-or-failure/"&gt;&amp;ldquo;Wrapping Up My AI Marathon: Success or Failure?&amp;rdquo;&lt;/a&gt;, I closed the first phase of my marathon. More than 600 hours of heavy coding-agent use, dozens of projects, hundreds of thousands of lines of code and documentation, and a pile of practical conclusions. Not marketing. Practical.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll repeat something important before someone starts cloning all my repositories thinking they are 1.0 products: most of the &lt;code&gt;frank-*&lt;/code&gt; projects on my GitHub are proofs of concept. Experiments. Some work well for me, in my environment, in my workflow. That does not mean they are ready to become a critical dependency in your company tomorrow morning. Want to play, contribute, study? Great. Just don&amp;rsquo;t confuse &amp;ldquo;I published it on GitHub&amp;rdquo; with &amp;ldquo;this is production ready.&amp;rdquo;&lt;/p&gt;</description><content:encoded><![CDATA[<p>In last week&rsquo;s post, <a href="/en/2026/05/14/wrapping-up-my-ai-marathon-success-or-failure/">&ldquo;Wrapping Up My AI Marathon: Success or Failure?&rdquo;</a>, I closed the first phase of my marathon. More than 600 hours of heavy coding-agent use, dozens of projects, hundreds of thousands of lines of code and documentation, and a pile of practical conclusions. Not marketing. Practical.</p>
<p>I&rsquo;ll repeat something important before someone starts cloning all my repositories thinking they are 1.0 products: most of the <code>frank-*</code> projects on my GitHub are proofs of concept. Experiments. Some work well for me, in my environment, in my workflow. That does not mean they are ready to become a critical dependency in your company tomorrow morning. Want to play, contribute, study? Great. Just don&rsquo;t confuse &ldquo;I published it on GitHub&rdquo; with &ldquo;this is production ready.&rdquo;</p>
<p>There are exceptions. <a href="https://github.com/akitaonrails/frankmd"target="_blank" rel="noopener">FrankMD</a>, for example, received a decent amount of contributions, tests, polish, real bug fixes, real use. Today it is much more stable than it was in the first post. Still, the point stands: these projects were born as a vibe-coding lab, not as a 3-year product plan with roadmap, enterprise support, and SLA.</p>
<p>This post is a roundup of what I learned in that lab. A few of my tools, a few habits, a few recommendations. Nothing mystical. No &ldquo;revolutionary new way to program.&rdquo; Old engineering with new tooling.</p>
<h2>First: stop collecting agents<span class="hx:absolute hx:-mt-20" id="first-stop-collecting-agents"></span>
    <a href="#first-stop-collecting-agents" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>If you are a normal developer, you do not need 12 agents, 40 wrappers, 7 orchestrators, and a Kubernetes diagram to choose who gets to write an <code>if</code>.</p>
<p>In practice, focus on <strong>Claude Code</strong>, <strong>Codex</strong>, or <strong>opencode</strong>. That&rsquo;s it.</p>
<p>Yes, Gemini CLI exists. Yes, Cursor exists. Yes, Windsurf, Crush, OpenClaw, Pi, Aider exist, and tomorrow another half-dozen will show up. I tested a bunch. Read my LLM benchmark posts, especially <a href="/en/2026/04/24/llm-benchmarks-parte-3-deepseek-kimi-mimo/">&ldquo;LLM Benchmarks Part 3: DeepSeek, Kimi, MiMo&rdquo;</a>. The conclusion is boring and obvious: today, the best cost-benefit options for heavy programming are <strong>Opus 4.7</strong> and <strong>GPT 5.5</strong> on subsidized Pro/Plus/Max plans. Nothing comes close when the job is spending several hours a day with a real coding agent.</p>
<p>Paying directly for tokens is expensive. Paying for a subsidized monthly plan is much cheaper. That&rsquo;s why I keep alternating between Claude Code and Codex: not because I want &ldquo;multi-agent architecture,&rdquo; but because the plans have limits and I work many hours in a row.</p>
<p>Are DeepSeek v4 and Kimi 2.6 cheaper? Yes. Are they usable? Yes. I like them for several cases. But for <strong>Agile Vibe Coding</strong> the way I&rsquo;ve been advocating since <a href="/en/2026/02/20/zero-to-post-production-in-1-week-using-ai-on-real-projects-behind-the-m-akita-chronicles/">&ldquo;From Zero to Post-Production in 1 Week&rdquo;</a>, they are still lacking. They stumble more on continuity, long refactors, tests, understanding the whole project. Can you use them? Sure. But if the goal is consistent productivity, Opus and GPT are still the bar.</p>
<p>Running an open-source model locally is neat. I played with that a lot in the <a href="/en/2026/03/31/minisforum-ms-s1-max-amd-ai-max-395-review/">Minisforum MS-S1 Max with AMD AI Max+ 395 review</a>. For one-shots, studying, playing with local inference, it&rsquo;s fun. For Agile Vibe Coding all day, every day, it is still not practical. Latency, context, quality, setup maintenance, VRAM, all of it collects tax.</p>
<p>Running multiple LLMs in parallel is not practical either, the whole &ldquo;planner + executor + reviewer + judge&rdquo; routine. I tested that in <a href="/en/2026/04/25/llm-benchmarks-vale-a-pena-misturar-2-modelos/">&ldquo;LLM Benchmarks: Is It Worth Mixing 2 Models?&rdquo;</a>. The idea looks nice on paper. In practice, it increases cost, latency, error surface, and the gain does not pay for the complexity.</p>
<p>The secret is not having a bunch of agents. Forget OpenClaw or Pi for day-to-day programming. The secret is engineering process. XP. Tests. <a href="/en/2026/04/20/clean-code-for-ai-agents/">Clean Code for AI Agents</a>. CI. Pair programming. Constant refactoring. Small code. Automated deploy. Fast feedback.</p>
<p>Tools help. Engineering wins.</p>
<h2>ai-jail: autonomy with a fence<span class="hx:absolute hx:-mt-20" id="ai-jail-autonomy-with-a-fence"></span>
    <a href="#ai-jail-autonomy-with-a-fence" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The first project I want to highlight is <a href="https://github.com/akitaonrails/ai-jail"target="_blank" rel="noopener">ai-jail</a>. It is a sandbox wrapper to run AI agents with restricted system access. On Linux it uses <code>bubblewrap</code> (<code>bwrap</code>) and Landlock. On macOS it uses <code>sandbox-exec</code>. The idea is simple: the agent sees the environment it needs to work, but does not go rummaging through your whole system.</p>
<p>Do I use it every day? Yes. Do I use it always? No.</p>
<p>When I know I am not doing anything risky, I open <code>claude</code> or <code>codex</code> directly. If I am touching a simple project, just running tests, editing markdown, refactoring local stuff, often I do not bother. In more than 600 hours of intense Agile Vibe Coding, more than half a million lines of code and documentation, I never had one of those folklore cases of an LLM destroying my host system in some unrecoverable way.</p>
<p>And look, I explicitly used agents to configure my Omarchy. ZSH, Hyprland, Waybar, terminal configs, login scripts, all of it. They never did anything I did not ask for.</p>
<p>Those stories of &ldquo;the agent deleted my whole home by itself&rdquo; make me raise an eyebrow. In my experience, when you ask it to delete something dangerous, Claude and Codex ask for confirmation. You have to say yes. If you said yes without reading, that is not LLM black magic. That is user in zombie mode.</p>
<p>Nothing is guaranteed, of course. Hence ai-jail.</p>
<p>My favorite use is combining <code>ai-jail</code> with the harness&rsquo; no-brakes mode:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ai-jail claude --dangerously-allow-permissions
</span></span><span class="line"><span class="cl">ai-jail codex --dangerously-bypass-approvals-and-sandbox</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It sounds contradictory, but it makes sense. I remove the confirmation friction inside the agent, but put a fence at the operating-system level. The agent can work fast inside the project, but the host filesystem is essentially read-only. The current directory is read-write. Whatever I explicitly map with <code>--rw-map</code> is also read-write. The rest is not to be touched.</p>
<p>The ai-jail README sums it up well: it is a sandbox wrapper for agents like Claude Code, GPT Codex, OpenCode, and Crush. On the first run it creates a <code>.ai-jail</code> in the project, which can be committed. It supports <code>--dry-run</code> to see mounts before executing, <code>--lockdown</code> for a stricter mode, <code>--private-home</code> to hide real dotfiles, <code>--mask .env</code> to hide sensitive files inside the project itself, and isolated browser profiles for Chromium/Firefox.</p>
<p>Do not sell this as absolute security. Process sandboxing is not a VM. Kernel bugs exist. Side channels exist. macOS has limitations. The README itself is clear: if you need to isolate a truly hostile workload, use a disposable VM. I do not use ai-jail as &ldquo;military-grade armor.&rdquo; I use it as another layer, good enough for a development workflow.</p>
<p>And even inside the project, everything is Git. I push frequently to GitHub or to my private Gitea. So the realistic worst case is the agent destroying the project directory. Annoying? Yes. Catastrophic? No. I clone again. I come back from the remote. I redo what was local. If <code>.git</code> gets corrupted, I delete everything and clone again.</p>
<p>Secrets also have a copy outside. <code>.env</code>, tokens, keys, everything that matters sits in Bitwarden. <a href="/en/2025/09/10/omarchy-2-0-bitwarden-self-hosted-vaultwarden/">Self-hosted in Vaultwarden</a>, of course. So if I lose the local directory, I did not lose the keys to the kingdom.</p>
<p>The most common risk is not the agent deleting everything. It is you leaking a secret to Git. That happened to me more than once. You run <code>git add .</code>, forget a <code>.env</code>, push to the remote, congratulations. It happens. That&rsquo;s why you must look at <code>git status</code>, <code>git diff --cached</code>, and think before pushing.</p>
<p>Even when it happens, LLMs are very competent at helping rewrite history. I ask them to clean the secret from Git, force-push, review that it disappeared, and then I rotate the key. It is not pretty, but it is manageable.</p>
<p>So yes, I recommend using Claude/Codex with dangerous permissions <strong>inside</strong> ai-jail. Is there a 0.001% chance of an unrecoverable accident? There is. But if you are a good engineer, that should not kill you. I have layers: automatic Btrfs snapshots, restic to my NAS, NAS archiving offsite to AWS Glacier, remote Git, Bitwarden. No tool replaces backups.</p>
<h2>ai-memory: context that survives the harness<span class="hx:absolute hx:-mt-20" id="ai-memory-context-that-survives-the-harness"></span>
    <a href="#ai-memory-context-that-survives-the-harness" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The second is <a href="https://github.com/akitaonrails/ai-memory"target="_blank" rel="noopener">ai-memory</a>, which I explained in detail in <a href="/en/2026/05/23/i-built-memory-system-for-coding-agents-ai-memory/">&ldquo;I Built a Memory System for Coding Agents: ai-memory&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ai-memory/ai-memory-logo.png" alt="ai-memory project logo: circular pictogram with a stylized chip opening “wings” like libraries/pages on each side, next to the italic ai-memory text."  loading="lazy" /></p>
<p>The motivation is simple: harnesses lose context. Claude Code compacts. Codex compacts. opencode compacts. The session ends. You open another agent and it does not know what happened in the last 4 hours. If you only use one harness, maybe it does not bother you much. If you alternate between Claude and Codex like I do, it gets annoying fast.</p>
<p>I wanted a long-term memory consolidator that worked across different harnesses. Something that allowed handoff between Claude, Codex, and opencode without me manually writing <code>HANDOFF.md</code> every time. Something that captured prompts, tool calls, and session events through hooks, consolidated them into markdown pages, indexed with SQLite/FTS5, and exposed everything through MCP when the agent needed to query it.</p>
<p>Another motivator was what I explained in <a href="/en/2026/04/06/rag-is-dead-long-context/">&ldquo;Is RAG Dead? Long Context, Grep, and the End of the Mandatory Vector DB&rdquo;</a>. For code projects, simple text almost always solves more than fancy vector DB architecture. Markdown, <code>grep</code>, SQLite FTS5, long context, and good files in <code>docs/</code> already take you far. ai-memory follows that line: compile memory into text, leave it on disk, index it simply, and only call an LLM when consolidation is worth it.</p>
<p>ai-memory was not made to be a user-browsable knowledge base. It has a read-only web UI, yes, but that is for auditing and inspection. The memory is optimized for LLM consumption. It is an internal wiki the harness can query when it thinks it needs to. It is a place where I can explicitly ask &ldquo;save this for the next session&rdquo; and expect it to survive longer than Claude or Codex&rsquo;s default compaction.</p>
<p><img src="https://raw.githubusercontent.com/akitaonrails/ai-memory/main/docs/web-projects-home.png" alt="ai-memory web UI homepage showing detected projects, page counts, last activity, and the warning that memory is optimized for LLM consumption, not human-curated documentation."  loading="lazy" /></p>
<p>The README sums it up well: coding agents lose all context when the session ends; ai-memory gives them a shared persistent wiki. Every prompt, tool call, and decision can be captured automatically. At the end of the session, relevant pages are rewritten as coherent narrative. In the next session, another agent can receive a handoff already prepended into the context.</p>
<p>This is useful when you close Claude at 6pm, open Codex the next day in the same directory, and want it to know you were testing session cookies because JWT rotation had run into trouble. Or when you return to a project six weeks later and do not remember why you discarded Redis.</p>
<p>But do not confuse it with canonical documentation. If you did important research you do not want to lose, ask the agent to write markdown in <code>docs/</code> inside the project. Architecture decision, benchmark, library analysis, deploy runbook, anything that must survive with guarantees should become an explicit file in the repo. That is simpler, auditable, and versioned.</p>
<p>ai-memory covers the middle band: transient information that might matter later. A weird bug that appeared. The reasoning that led to a decision. An environment gotcha. A session summary. Things you do not want to turn into official docs yet, but also do not want to throw away.</p>
<p>It is still beta. I use it in personal production, but it needs many more users beating on it. The most polished integrations today are Claude Code, Codex, and opencode. Gemini CLI, OpenClaw, and exotic MCP clients still need more testing. PRs are welcome. Bug reports too. &ldquo;I installed it and it broke on my setup&rdquo; is exactly the kind of feedback that reveals a wrong assumption.</p>
<h2>ai-usagebar: knowing when to switch horses<span class="hx:absolute hx:-mt-20" id="ai-usagebar-knowing-when-to-switch-horses"></span>
    <a href="#ai-usagebar-knowing-when-to-switch-horses" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>As I said above, I prefer using Anthropic and OpenAI&rsquo;s subsidized plans. The problem is that if you do real Agile Vibe Coding, you hit limits. Daily limit. Weekly limit. Session limit. Limit that changes. Limit that does not show up clearly anywhere.</p>
<p>I was using <a href="https://github.com/mryll/claudebar"target="_blank" rel="noopener">claudebar</a>, which is good, but only covers Claude. I wanted to see the main vendors in one place. That became <a href="https://github.com/akitaonrails/ai-usagebar"target="_blank" rel="noopener">ai-usagebar</a>, which I introduced in <a href="/en/2026/05/24/i-built-a-waybar-widget-for-omarchy-to-monitor-llm-usage-ai-usagebar/">&ldquo;I Built a Waybar Widget for Omarchy to Monitor LLM Plan Usage: ai-usagebar&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-usagebar/ai-usagebar-waybar.png" alt="Waybar widget showing “cld 29% · 1h 12m” in the top-right corner, with the tooltip open showing the Claude Max 20x plan: Session 29%, Weekly 47%, Sonnet only 0%, Extra usage $0.00 against a $1100 limit, and the last-updated time."  loading="lazy" /></p>
<p>It is a Waybar widget and Rust TUI to monitor Anthropic Claude, OpenAI Codex/ChatGPT, Z.AI GLM, and OpenRouter usage. For Claude and OpenAI, it reads the OAuth credentials the official CLIs already wrote. For Z.AI and OpenRouter, it uses API keys. It shows session percentage, reset, limits, balance, and lets you switch the primary vendor.</p>
<p>The value is banal and therefore useful: I glance at the bar and know whether I should continue in Claude, hand off to Codex, or top up OpenRouter credit. I do not want to discover I hit the weekly limit in the middle of a large refactor.</p>
<p>There is also a standalone TUI:</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-usagebar/ai-usagebar-tui-openai.png" alt="ai-usagebar-tui on the OpenAI tab running in a terminal: Codex 5h and weekly gauges, Credits block with message-count ranges, Claude / OpenAI / GLM (Z.AI) / OpenRouter tabs at the top, key hints in the footer."  loading="lazy" /></p>
<p>If you do not use Omarchy, Hyprland, or Waybar, you can still run <code>ai-usagebar-tui</code> in any terminal. SSH, tmux, normal terminal, whatever. The bar is convenience. The TUI is the universal form.</p>
<h2>ghpending: GitHub pending stuff without the ritual<span class="hx:absolute hx:-mt-20" id="ghpending-github-pending-stuff-without-the-ritual"></span>
    <a href="#ghpending-github-pending-stuff-without-the-ritual" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>After the marathon, I ended up with dozens of <code>ai-*</code> and <code>frank-*</code> repos on GitHub. That creates an annoying problem: issues and pull requests start appearing. A contributor opens a PR. Someone reports a bug. Someone else sends a suggestion. If I have to open the browser, enter repo by repo, filter tab by tab, I will postpone it. And if I postpone it, I go weeks without answering contributors.</p>
<p>Yes, I could use <code>gh</code>, the official GitHub CLI. I do use it. But I wanted something dumber and faster: a short command to print a digest of everything pending in the repos I chose to monitor. That became <a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener">ghpending</a>, which I described in <a href="/en/2026/05/23/i-built-a-cli-to-check-my-github-pending-stuff-ghpending/">&ldquo;I Built a CLI to Check My GitHub Pending Stuff: ghpending&rdquo;</a>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ghpending/ghpending-screenshot.png" alt="ghpending terminal output: compact table listing each monitored repo with open issue and pull request counts, latest activity author, and how long ago it happened."  loading="lazy" /></p>
<p>That is it: <code>ghpending add</code> to choose repos, <code>ghpending</code> to see the digest, <code>ghpending list</code>, <code>ghpending rm</code>. Without auth it works with GitHub&rsquo;s 60 req/h anonymous limit. With <code>GITHUB_TOKEN=$(gh auth token) ghpending</code>, it goes up to 5,000 req/h.</p>
<p>The point is not replacing GitHub. It is reducing friction. If I can run one command in the terminal and see &ldquo;there are 3 PRs and 2 issues to look at,&rdquo; I look. If it depends on browser ritual, notifications, and tabs, it waits.</p>
<p>And Claude/Codex are very good at this kind of work. You can ask:</p>
<blockquote>
  <p>&ldquo;Check the open pull requests, review the code, see if it compiles, run the tests, add tests if missing, rebase from master if needed, merge and close.&rdquo;</p>

</blockquote>
<p>Or:</p>
<blockquote>
  <p>&ldquo;Check the open issues, evaluate whether there is a real bug, reproduce it if possible, propose a fix, answer the user.&rdquo;</p>

</blockquote>
<p>They read the PR, understand the diff, run tests, rebase, solve simple conflicts, write the contributor response. You still keep your brain on. You are delegating mechanical work you already know how to do.</p>
<h2>Refactoring: ask explicitly, several times<span class="hx:absolute hx:-mt-20" id="refactoring-ask-explicitly-several-times"></span>
    <a href="#refactoring-ask-explicitly-several-times" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Claude and Codex are very good at refactoring, but do not assume they will do it automatically the way you would. I usually start projects or features with explicit instructions:</p>
<blockquote>
  <p>&ldquo;Use Rust best practices. Avoid unnecessary duplication. Modularize only where it makes sense. Add unit tests for every new feature. Follow <a href="/en/2026/04/20/clean-code-for-ai-agents/">Clean Code</a> principles. Do not create an abstraction before it is needed.&rdquo;</p>

</blockquote>
<p>After a large feature, a large fix, or a few hours of development, I stop and ask:</p>
<blockquote>
  <p>&ldquo;Now that we touched a lot of code, re-evaluate it carefully. Delete dead code. Refactor obvious duplication. Add missing tests. Remove magic numbers. Check if any modules are too large. Do not change behavior without tests.&rdquo;</p>

</blockquote>
<p>Before release:</p>
<blockquote>
  <p>&ldquo;Before publishing this version, do a sanity audit. Look for secret leaks, dangerous permissions, destructive commands, path traversal, unnecessary unwraps, missing validation, and anything that looks like an obvious vulnerability.&rdquo;</p>

</blockquote>
<p>Does that guarantee security? Of course not. Not even close. But it removes a lot of dumb mistakes. It is another layer. I like running this audit with Claude and then with Codex, because each one finds different things. Bugs still pass. Security is process, not a magic ritual.</p>
<h2>Disposable experiments: code got cheap<span class="hx:absolute hx:-mt-20" id="disposable-experiments-code-got-cheap"></span>
    <a href="#disposable-experiments-code-got-cheap" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Before choosing a library, framework, architecture, model, or service, I like creating <code>experiments/</code>.</p>
<p>Example:</p>
<blockquote>
  <p>&ldquo;Before choosing between Qwen and DeepSeek, create a disposable prototype in <code>experiments/</code> for an A/B test. Document the analysis in <code>docs/</code> with result, cost, latency, quality, and recommendation.&rdquo;</p>

</blockquote>
<p>This changed how I decide. Before, many engineering decisions became opinion, feeling, faith in somebody else&rsquo;s benchmark. Now code is cheap. I ask the agent to make two or three versions, run tests, measure, document, throw away what does not work.</p>
<p>Prototyping got genuinely fast. <a href="/en/2026/04/05/turning-youtube-into-a-karaoke-app-frank-karaoke/">Frank Karaoke</a> is a good example: turning YouTube into a karaoke app was the kind of idea I would maybe leave in a mental list for months, waiting for &ldquo;a free weekend&rdquo; that never arrives. Now I can open <code>experiments/</code>, ask for a rough prototype, test the technical path, and decide whether it deserves to become a project. Look at my other 2026 posts about <code>frank-*</code> and <code>ai-*</code> projects: almost all of them started like this, as disposable code that proved something before earning a name.</p>
<p>But cheap prototypes do not mean one-shot. I already wrote in <a href="/en/2026/04/15/how-to-talk-to-claude-code-effectively/">&ldquo;Why LLMs Aren&rsquo;t Giving You the Result You Expect&rdquo;</a>: a good prompt is an iterative conversation. You provide context, show references, point out constraints, read the result, correct direction, ask for a rewrite. If you throw in a vague sentence and expect a finished product, that&rsquo;s on you.</p>
<p>I learned this by taking a beating too. In the <a href="/en/2026/03/05/my-first-vibe-code-failure-frank-yomik/">Frank Yomik</a> post I described my first real vibe-coding failure: when the direction is wrong, insisting on the same prompt just digs the hole deeper. Sometimes you need to reduce scope, isolate the problem, create a test, change strategy. LLM does not turn a bad plan into good engineering.</p>
<p>And there are domains where you need to study along with it. In the <a href="/en/2026/04/23/driveclub-shadps4-e-ia-como-aprender/">DriveClub and shadPS4</a> post, AI almost sent me in every wrong direction because the problem required understanding rendering, gamma, shaders, buffers. The tool helps you explore, but you still decide whether the explanation makes sense. If you do not understand enough to verify, you are just accepting a pretty guess.</p>
<p>That is also why I keep repeating the idea from <a href="/en/2026/03/01/software-is-never-done-4-projects-life-after-deploy-one-shot-prompt-myth/">&ldquo;Software Is Never Done&rdquo;</a>: deploy is not the finish line. The prototype becomes a product when it handles bug reports, maintenance, refactoring, docs, releases, rollbacks, users complaining. LLM helps a lot with that boring cycle. But the cycle still exists.</p>
<p>If it is testable, test it. If it can be measured, measure it. If it cannot be measured, at least write down the assumptions. The important thing is not choosing just because it &ldquo;seems better.&rdquo;</p>
<p>Cheap code also means detachment. Make code to throw away. Make an ugly prototype. Make a disposable benchmark. Ask the agent to document what it learned in <code>docs/decision-qwen-vs-deepseek.md</code>, then delete <code>experiments/</code> if you want. The document stays. The lesson stays.</p>
<p>LLMs are also very good at documentation. Use that. Ask it to document everything. Why we chose PostgreSQL instead of SQLite in this case. Why the deploy uses a multi-stage Docker image. Why the service runs behind Cloudflare Tunnel. Why that sysctl exists. Even if you delete half of it later, the cost of writing fell near zero.</p>
<h2>Automated deploy or you will stop maintaining it<span class="hx:absolute hx:-mt-20" id="automated-deploy-or-you-will-stop-maintaining-it"></span>
    <a href="#automated-deploy-or-you-will-stop-maintaining-it" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Every project of mine that matters has automated build. Many run in my homelab, as I described in <a href="/en/2026/03/31/migrating-my-home-server-with-claude-code/">&ldquo;Migrating my Home Server with Claude Code&rdquo;</a>. Almost every project has a <code>bin/deploy</code> that builds a Docker image, pushes it, rotates it on the server, applies migration if needed, and validates healthcheck.</p>
<p>Claude and Codex are excellent at deploy. They read <code>bin/deploy</code>, understand Dockerfile, edit GitHub Actions, adjust release workflows, fix binary signing, update Homebrew taps, generate checksums. And yes, I ask Claude/Codex to SSH into my homelab and redeploy Docker containers. They are very good at that. ai-jail, for example, has GitHub Actions to build signed binaries for Linux and macOS.</p>
<p>If you have dozens of projects, the more automated the better. I rarely type admin commands in the terminal by myself. I ask the LLM to do it, observe, approve what is dangerous, and then ask:</p>
<blockquote>
  <p>&ldquo;Document what was done and why it was done.&rdquo;</p>

</blockquote>
<p>This applies to the homelab and my workstation. My Omarchy is customized through Claude: ZSH, Hyprland, Waybar, scripts, shortcuts, themes, integrations. Even my gaming/emulation setups fall into this, as I showed in the <a href="/en/2026/04/19/my-favorite-retro-racing-games-on-distrobox/">retro racing games running on Distrobox</a> post. This is a case where I normally do not use ai-jail, because I actually want to touch the host. So I look at commands, read diffs, approve slowly. Even then, I never had the agent break my system in an unrecoverable way. Quite the opposite. My PC has never been more stable and documented.</p>
<p>Days later, when I forget why I configured something that way, the context is in markdown, Git, ai-memory, or the project history itself. I can ask and the agent has something to pull from.</p>
<h2>Even this post was written this way<span class="hx:absolute hx:-mt-20" id="even-this-post-was-written-this-way"></span>
    <a href="#even-this-post-was-written-this-way" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>This is how I write these posts now. I dump a huge messy block from my brain into Claude. I ask it to read recent posts to calibrate style. I ask it to check the projects directly and elaborate the technical parts. I ask it to use the latest screenshots, upload to S3 when needed, link previous posts, review the tone.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/24/ai-toolkit/writing-this-post.png" alt="Terminal screenshot with the raw prompt used to write this post: a huge block of instructions, links, opinions, and technical details dumped into the agent before final editing."  loading="lazy" /></p>
<p>Then I read and edit. I cut what sounds generic. I replace sentences that sound like release notes. I put my rants back in. The result is still my text; I just spend energy on what matters: deciding what to say, correcting nuance, cutting nonsense.</p>
<h2>&ldquo;But did you test Pi?&rdquo;<span class="hx:absolute hx:-mt-20" id="but-did-you-test-pi"></span>
    <a href="#but-did-you-test-pi" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Every time I mention an LLM or LLM tool, someone shows up in the comments with the same question: &ldquo;but did you test Pi?&rdquo;, &ldquo;but did you test DeepSeek?&rdquo;, &ldquo;but what about Qwen?&rdquo;, &ldquo;but what about the new model that came out yesterday?&rdquo; Every. Single. Time.</p>
<p>It is annoying as fuck. And almost always it comes from three kinds of people: someone who tested nothing and wants to outsource the work, someone who already chose a tool and wants me to validate their choice, or a fanboy treating an LLM vendor like a soccer team. They all sound the same. NPC repeating tutorial dialogue.</p>
<p>Software engineers should not decide based on other people&rsquo;s opinions. Not even mine. If you choose a tool because &ldquo;Akita said so,&rdquo; you understood it wrong. I advocate Claude Code and Codex because I used them for hundreds of hours, in real projects, publishing code and post-mortems of what worked and what failed. And even then I did not stop at opinion.</p>
<p>I built my own automated benchmark: <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">llm-coding-benchmark</a>. I update it when a popular vendor releases a new version. I run comparable scenarios. I publish results. Then I adjust my opinion based on the data. That gives me enough ground to say that, in my workflow, Opus and GPT keep winning. Not because I woke up wanting to defend some billionaire company&rsquo;s logo.</p>
<p>If your first reaction was &ldquo;but did you test Qwen?&rdquo;, great. Test it yourself. Run the benchmark. Create a better scenario. Send a PR. Show numbers. Show cost. Show latency. Show where it failed. Show where it won. Bring new data.</p>
<p>Loose opinion is irrelevant. &ldquo;It works for me&rdquo; does not scale. &ldquo;I like it better&rdquo; does not matter. &ldquo;In my one-shot prompt it was better&rdquo; is an anecdote. Engineering starts when something can be reproduced, compared, and acted on.</p>
<p>So it is simple: bring actionable data or shut the hell up. I do not need vendor fan clubs in the comments. I need people testing, measuring, and contributing new results.</p>
<h2>Final checklist<span class="hx:absolute hx:-mt-20" id="final-checklist"></span>
    <a href="#final-checklist" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Short summary, because this post is already long:</p>
<ul>
<li>Focus on Claude Code, Codex, or opencode. Stop collecting agents.</li>
<li>Use Opus 4.7 and GPT 5.5 through Pro/Plus/Max plans when daily productivity matters.</li>
<li>DeepSeek, Kimi, and local models are useful, but still do not replace the main workflow.</li>
<li>Agile Vibe Coding is XP with LLM: tests, <a href="/en/2026/04/20/clean-code-for-ai-agents/">Clean Code</a>, CI, pair programming, and deploy.</li>
<li>Use <code>ai-jail</code> when you want agent autonomy with a filesystem fence.</li>
<li>Combine <code>ai-jail</code> with dangerous Claude/Codex permissions when the project is protected by Git.</li>
<li>Have real backup: Btrfs snapshots, restic, NAS, offsite, Git, Bitwarden.</li>
<li>Review <code>git add</code>, <code>git diff --cached</code>, and pushes so you do not leak secrets.</li>
<li>Use <code>ai-memory</code> for transient context and handoff between agents.</li>
<li>Write canonical documentation in <code>docs/</code> when information needs to survive with guarantees.</li>
<li>Use <code>ai-usagebar</code> to know when to switch from Claude to Codex, or when to top up OpenRouter.</li>
<li>Use <code>ghpending</code> so you do not abandon issues and PRs.</li>
<li>Ask for refactoring explicitly after large features.</li>
<li>Run a security audit before release, preferably with more than one model.</li>
<li>Create disposable prototypes in <code>experiments/</code> before deciding by faith.</li>
<li>If you disagree with a benchmark, run <a href="https://github.com/akitaonrails/llm-coding-benchmark"target="_blank" rel="noopener">llm-coding-benchmark</a> and bring new data.</li>
<li>Document decisions in markdown. Code got cheap. Documentation too.</li>
<li>Automate deploy. If it depends on a manual ritual, you will stop doing it.</li>
</ul>
<p>In the end, it is the same advice as always: tools change, engineering does not. LLM increases leverage. If you already had good habits, you will produce more. If you did not, you will just produce mess faster.</p>
<p>And yes, this changes the interface of the work. I already wrote about that in <a href="/en/2026/04/11/vs-code-is-the-new-punch-card/">&ldquo;VS Code Is the New Punch Card&rdquo;</a>: polishing pixels in an editor is now old-programmer nostalgia. The hard part is writing intent correctly, reviewing execution without being lazy, measuring results, throwing away cheap code, and not confusing opinion with data.</p>
]]></content:encoded><category>ai-jail</category><category>ai-memory</category><category>ai-usagebar</category><category>ghpending</category></item><item><title>I Built a CLI to Check My GitHub Pending Stuff: ghpending</title><link>https://akitaonrails.github.io/en/2026/05/23/i-built-a-cli-to-check-my-github-pending-stuff-ghpending/</link><guid isPermaLink="true">https://akitaonrails.github.io/en/2026/05/23/i-built-a-cli-to-check-my-github-pending-stuff-ghpending/</guid><pubDate>Sat, 23 May 2026 22:00:00 -0300</pubDate><description>&lt;p&gt;After my &lt;a href="https://akitaonrails.github.io/en/2026/05/14/wrapping-up-my-ai-marathon-success-or-failure/"&gt;AI marathon&lt;/a&gt;, I&amp;rsquo;m sitting on dozens of public GitHub repos to look after. A bunch of them have contributors filing issues and sending pull requests. The problem is, opening a browser, walking into each repo, checking whether anything new is waiting, becomes a chore I end up dodging. And when I dodge it, I go weeks without responding to collaborators.&lt;/p&gt;
&lt;p&gt;I know there are plenty of tools that solve this. But I wanted something dead simple and fast to run in the terminal so I can see pending work across all my projects at once. So I built it: &lt;strong&gt;&lt;a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener"&gt;ghpending&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>After my <a href="/en/2026/05/14/wrapping-up-my-ai-marathon-success-or-failure/">AI marathon</a>, I&rsquo;m sitting on dozens of public GitHub repos to look after. A bunch of them have contributors filing issues and sending pull requests. The problem is, opening a browser, walking into each repo, checking whether anything new is waiting, becomes a chore I end up dodging. And when I dodge it, I go weeks without responding to collaborators.</p>
<p>I know there are plenty of tools that solve this. But I wanted something dead simple and fast to run in the terminal so I can see pending work across all my projects at once. So I built it: <strong><a href="https://github.com/akitaonrails/ghpending"target="_blank" rel="noopener">ghpending</a></strong>.</p>
<p><img src="https://new-uploads-akitaonrails.s3.us-east-2.amazonaws.com/2026/05/23/ghpending/ghpending-screenshot.png" alt="ghpending output in the terminal: compact table listing each tracked repo with counts of open issues and pull requests, latest activity author, and how long ago."  loading="lazy" /></p>
<p>Runs on Linux and macOS. I tested on Linux (Arch + Omarchy) but haven&rsquo;t run it on macOS yet. If you try it there and hit a bug, file an issue at <a href="https://github.com/akitaonrails/ghpending/issues"target="_blank" rel="noopener">github.com/akitaonrails/ghpending/issues</a>.</p>
<h2>Install<span class="hx:absolute hx:-mt-20" id="install"></span>
    <a href="#install" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Three paths, pick whichever fits your setup.</p>
<p>Via Homebrew (macOS or Linux):</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">brew tap akitaonrails/tap <span class="o">&amp;&amp;</span> brew install ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Via Cargo:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">cargo install ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>Via mise:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">mise use -g github:akitaonrails/ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Upgrading<span class="hx:absolute hx:-mt-20" id="upgrading"></span>
    <a href="#upgrading" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="c1"># Homebrew</span>
</span></span><span class="line"><span class="cl">brew upgrade ghpending
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># Cargo</span>
</span></span><span class="line"><span class="cl">cargo install ghpending --force
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># mise</span>
</span></span><span class="line"><span class="cl">mise upgrade ghpending
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># From source</span>
</span></span><span class="line"><span class="cl"><span class="nb">cd</span> ghpending <span class="o">&amp;&amp;</span> git pull <span class="o">&amp;&amp;</span> cargo install --path .</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Usage<span class="hx:absolute hx:-mt-20" id="usage"></span>
    <a href="#usage" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>Four commands. The normal flow is to run <code>ghpending add</code> once to set up which repos you want to track, then just <code>ghpending</code> whenever you want to see the digest.</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">ghpending add    <span class="c1"># pick repos from a GitHub user or org to track</span>
</span></span><span class="line"><span class="cl">ghpending        <span class="c1"># print the digest of open issues and PRs</span>
</span></span><span class="line"><span class="cl">ghpending list   <span class="c1"># show tracked repos</span>
</span></span><span class="line"><span class="cl">ghpending rm     <span class="c1"># remove repos from tracking via interactive menu</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p><code>add</code> asks for a user or organization, lists their public repos, and lets you check off the ones you want to follow. The username gets saved, so re-running <code>add</code> skips that prompt.</p>
<p>The main command (<code>ghpending</code> with no args) fetches every tracked repo concurrently and prints the table with open issues and PRs.</p>
<h2>GitHub token (optional)<span class="hx:absolute hx:-mt-20" id="github-token-optional"></span>
    <a href="#github-token-optional" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>It works without auth, but GitHub caps anonymous traffic at 60 requests per hour. To raise that ceiling to 5,000 req/h, export a token:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl"><span class="nv">GITHUB_TOKEN</span><span class="o">=</span><span class="k">$(</span>gh auth token<span class="k">)</span> ghpending</span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<p>It&rsquo;s read silently at startup, no config needed.</p>
<h2>Where the config lives<span class="hx:absolute hx:-mt-20" id="where-the-config-lives"></span>
    <a href="#where-the-config-lives" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><ul>
<li>Linux: <code>~/.config/ghpending/config.toml</code></li>
<li>macOS: <code>~/Library/Application Support/ghpending/config.toml</code></li>
</ul>
<p>You can edit it directly if you want to reorder repos or change the <code>user</code>. Shape:</p>
<div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code">

<div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="nx">user</span> <span class="p">=</span> <span class="s2">&#34;akitaonrails&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nx">repos</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;akitaonrails/ai-memory&#34;</span><span class="p">,</span> <span class="s2">&#34;akitaonrails/ai-jail&#34;</span><span class="p">]</span></span></span></code></pre></div></div><div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0">
  <button
    class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
    title="Copy code"
  >
    <div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"></div>
<div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"></div>
  </button>
</div>
</div>
<h2>Why this CLI exists<span class="hx:absolute hx:-mt-20" id="why-this-cli-exists"></span>
    <a href="#why-this-cli-exists" class="subheading-anchor" aria-label="Permalink for this section"></a></h2><p>The honest answer: tools I build for myself, I use. Tools written by other people always come with some friction (web login, a different architectural opinion, a feature I wanted missing or one I didn&rsquo;t want included). Since the goal was to reduce the friction of checking 30 repos a week, 200 lines of Rust on a Saturday afternoon beat learning a new stack.</p>
<p>Now I&rsquo;m hoping I can respond to contributors faster. If you find it useful, it&rsquo;s MIT licensed, PRs are welcome.</p>
]]></content:encoded><category>ghpending</category><category>automation</category><category>developer-tools</category><category>version-control</category></item></channel></rss>