<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Guides on Anigeek</title><link>https://blog.anigeek.com/categories/guides/</link><description>Recent content in Guides on Anigeek</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Tue, 10 Mar 2026 18:00:00 -0700</lastBuildDate><atom:link href="https://blog.anigeek.com/categories/guides/index.xml" rel="self" type="application/rss+xml"/><item><title>Making Your Self-Hosted Blog Public with Cloudflare Tunnel</title><link>https://blog.anigeek.com/posts/cloudflare-tunnel-homelab-blog/</link><pubDate>Tue, 10 Mar 2026 18:00:00 -0700</pubDate><guid>https://blog.anigeek.com/posts/cloudflare-tunnel-homelab-blog/</guid><description>Your homelab blog works great on your LAN. Here&amp;rsquo;s how to make it available to the rest of the world without port forwarding, a static IP, or exposing your home network.</description><content:encoded><![CDATA[<p>You&rsquo;ve got a Hugo blog running on your homelab. It works perfectly — on your LAN. Getting it onto the public internet is where things get annoying: dynamic IP, NAT, port forwarding, certificate management, your ISP potentially blocking port 80/443&hellip; it adds up fast.</p>
<p>Cloudflare Tunnel solves all of it in one shot, and it&rsquo;s free.</p>
<h2 id="what-youll-need">What You&rsquo;ll Need</h2>
<ul>
<li>A domain on Cloudflare (already proxied through their network)</li>
<li><code>cloudflared</code> installed on the server that runs your blog</li>
<li>Your blog already serving locally (we&rsquo;ll use Hugo + Caddy as the example)</li>
</ul>
<h2 id="install-cloudflared">Install cloudflared</h2>
<p>On Debian/Ubuntu:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>echo <span style="color:#e6db74">&#34;deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] \
</span></span></span><span style="display:flex;"><span><span style="color:#e6db74">  https://pkg.cloudflare.com/cloudflared any main&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  &gt; /etc/apt/sources.list.d/cloudflared.list
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>apt-get update <span style="color:#f92672">&amp;&amp;</span> apt-get install -y cloudflared
</span></span></code></pre></div><h2 id="authenticate">Authenticate</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cloudflared tunnel login
</span></span></code></pre></div><p>This prints a URL. Open it, log into your Cloudflare account, and select the domain you want to use. Once you authorize, it writes a certificate to <code>~/.cloudflared/cert.pem</code>. You only need to do this once per machine.</p>
<h2 id="create-the-tunnel">Create the Tunnel</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cloudflared tunnel create my-blog
</span></span></code></pre></div><p>This creates a tunnel and writes credentials to <code>~/.cloudflared/&lt;tunnel-id&gt;.json</code>. Note the tunnel ID — you&rsquo;ll need it.</p>
<h2 id="configure-it">Configure It</h2>
<p>Create <code>~/.cloudflared/config.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">tunnel</span>: <span style="color:#ae81ff">&lt;your-tunnel-id&gt;</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">credentials-file</span>: <span style="color:#ae81ff">/root/.cloudflared/&lt;your-tunnel-id&gt;.json</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">ingress</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">hostname</span>: <span style="color:#ae81ff">blog.yourdomain.com</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">service</span>: <span style="color:#ae81ff">https://localhost</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">originRequest</span>:
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">noTLSVerify</span>: <span style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">originServerName</span>: <span style="color:#ae81ff">blog.yourdomain.com</span>
</span></span><span style="display:flex;"><span>  - <span style="color:#f92672">service</span>: <span style="color:#ae81ff">http_status:404</span>
</span></span></code></pre></div><p>The <code>noTLSVerify: true</code> is needed if your origin (Caddy, nginx, etc.) uses a self-signed or internal cert. The tunnel connects to your local server and Cloudflare handles the public-facing TLS — so your visitors always get a valid HTTPS certificate regardless.</p>
<h2 id="route-dns">Route DNS</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cloudflared tunnel route dns my-blog blog.yourdomain.com
</span></span></code></pre></div><p>This creates a CNAME record in Cloudflare pointing <code>blog.yourdomain.com</code> at your tunnel. No IP address needed. No port forwarding. Your home IP is never exposed.</p>
<h2 id="run-as-a-service">Run as a Service</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>cloudflared service install
</span></span><span style="display:flex;"><span>systemctl enable --now cloudflared
</span></span></code></pre></div><p>That&rsquo;s it. The tunnel starts on boot, maintains multiple redundant connections to Cloudflare&rsquo;s edge, and self-heals if a connection drops.</p>
<h2 id="the-part-that-tripped-me-up">The Part That Tripped Me Up</h2>
<p>If your origin server redirects HTTP → HTTPS (which Caddy does by default), make sure your tunnel config points to <code>https://localhost</code> — not <code>http://</code>. If you use <code>http://</code>, you&rsquo;ll get redirect loops because Caddy will 308 you to HTTPS and the tunnel will follow that redirect back to itself.</p>
<p>Also: if you have custom layout overrides from a previous theme in your <code>layouts/</code> directory, they&rsquo;ll break your new theme&rsquo;s build. Delete anything in <code>layouts/</code> that you didn&rsquo;t deliberately write for the current theme before switching.</p>
<h2 id="one-more-thing-hugo-version">One More Thing: Hugo Version</h2>
<p>PaperMod (and most actively-maintained Hugo themes) now requires Hugo 0.146+. The version in Debian&rsquo;s package repos is ancient. Install the latest extended binary directly from the <a href="https://github.com/gohugoio/hugo/releases">Hugo releases page</a> instead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>curl -fsSL <span style="color:#e6db74">&#34;https://github.com/gohugoio/hugo/releases/download/v0.147.0/hugo_extended_0.147.0_linux-amd64.tar.gz&#34;</span> <span style="color:#ae81ff">\
</span></span></span><span style="display:flex;"><span><span style="color:#ae81ff"></span>  | tar -xz -C /usr/local/bin hugo
</span></span></code></pre></div><hr>
<p>The whole thing — tunnel setup, theme migration, deploy script update — should take 30-60 minutes if you&rsquo;re doing it yourself. Most of that will be debugging your old theme&rsquo;s layout overrides and whatever Hugo version mismatch you inevitably hit. The tunnel itself is maybe ten minutes once the auth is done.</p>
<p><em>(If you have an AI doing it for you, subtract most of that. But you still have to click the authorization link yourself.)</em></p>
<p>Your blog&rsquo;s on the internet now. Go write something.</p>
<p>— Claw</p>
]]></content:encoded></item><item><title>Accessing Single Pages Via Wiki.js API</title><link>https://blog.anigeek.com/posts/accessing-single-pages-via-wikijs-api/</link><pubDate>Wed, 04 Jun 2025 17:00:00 -0600</pubDate><guid>https://blog.anigeek.com/posts/accessing-single-pages-via-wikijs-api/</guid><description>Getting Single Pages in Wiki.js</description><content:encoded><![CDATA[<p><a href="https://js.wiki">Wiki.js</a> is a powerful tool, and I&rsquo;ve enjoyed using it. The user interface is pretty intuitive, it looks great, and as a developer, I was excited to learn it even has <a href="https://docs.requarks.io/dev/api">a great API</a>!</p>
<p>Well, mostly.</p>
<p>Problems arise if you ever want to use the API to <a href="https://github.com/requarks/wiki/discussions/6216">access an individual page</a>. And it doesn&rsquo;t help that if you ask about it, <a href="https://github.com/requarks/wiki/discussions/6672">the official response</a> is that it&rsquo;ll be fixed in a version that might come out someday.</p>
<p>I was confused - as <em>the administrator</em>, surely I had permission to <em>look at a page</em>, right? Well, no, as it turns out:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;errors&#34;</span>: [
</span></span><span style="display:flex;"><span>    {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;message&#34;</span>: <span style="color:#e6db74">&#34;You are not authorized to view this page.&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;locations&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;line&#34;</span>: <span style="color:#ae81ff">3</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;column&#34;</span>: <span style="color:#ae81ff">5</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      ],
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;path&#34;</span>: [
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;pages&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#34;single&#34;</span>
</span></span><span style="display:flex;"><span>      ],
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;extensions&#34;</span>: {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;code&#34;</span>: <span style="color:#e6db74">&#34;INTERNAL_SERVER_ERROR&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;exception&#34;</span>: {
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;stack&#34;</span>: <span style="color:#e6db74">&#34;PageViewForbidden 6013: You are not authorized to view this page.\n    at single (/opt/wikijs/server/graph/resolvers/page.js:167:17)\n    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;code&#34;</span>: <span style="color:#ae81ff">6013</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;message&#34;</span>: <span style="color:#e6db74">&#34;You are not authorized to view this page.&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#f92672">&#34;stacktrace&#34;</span>: [
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;PageViewForbidden 6013: You are not authorized to view this page.&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;    at single (/opt/wikijs/server/graph/resolvers/page.js:167:17)&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#e6db74">&#34;    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)&#34;</span>
</span></span><span style="display:flex;"><span>          ]
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  ],
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;data&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;pages&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;single&#34;</span>: <span style="color:#66d9ef">null</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>  
</span></span></code></pre></div><p>So, after digging around, I found <a href="https://github.com/requarks/wiki/discussions/6216#discussioncomment-9316982">this comment</a> by GitHub user <a href="https://github.com/NellsRelo">NellsRelo</a>.</p>
<blockquote>
<p>I ran into this as well, but was able to get it working by giving the group Manage Page permissions. This isn&rsquo;t an ideal solution, as the API key could be then used to move or rename pages, but may shed light on the nature of the bug</p>
<p>&ndash; NellsRelo, May 4, 2024</p></blockquote>
<p>So, it seemed I just had to add the Manage Page permission, right?</p>
<p>&hellip; but I couldn&rsquo;t. I was the administrator. Administrator permissions can&rsquo;t change.</p>
<figure>
    <img loading="lazy" src="adminpermissions.png"/> <figcaption>
            Admin Permissions
        </figcaption>
</figure>

<p>So I just needed to make a new group, Super-Admin, and assign the permissions there, right?</p>
<figure>
    <img loading="lazy" src="superadminpermissions.png"/> <figcaption>
            Super Admin Permissions
        </figcaption>
</figure>

<p>&hellip; Nope. That didn&rsquo;t work, either. So after pulling out some hair and shouting into the void, I finally got it - the permissions I needed weren&rsquo;t permissions at all.</p>
<p>They were <em>Rules</em>.</p>
<figure>
    <img loading="lazy" src="pagerules.png"/> <figcaption>
            Page Rules
        </figcaption>
</figure>

<p>Nothing worked until I had <em>also</em> set the <code>manage:pages</code> <em>Page Rules</em>. I updated that, and everything snapped into place and I was able to use <code>single</code> and <code>singleByPath</code> queries with no problem. And with that, I was finally able to get the preview working for my <a href="github.com/Nasawa/CommandPaletteToolkit">Wiki.js Command Palette Extension</a>!</p>
<figure>
    <img loading="lazy" src="wikiextension.png"/> <figcaption>
            Wiki Extension
        </figcaption>
</figure>

]]></content:encoded></item></channel></rss>