<?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>Self-Hosting on blog.taikiito.com</title><link>https://blog.taikiito.com/en/tags/self-hosting/</link><description>Recent content in Self-Hosting on blog.taikiito.com</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Sat, 22 Aug 2026 08:00:00 +0800</lastBuildDate><atom:link href="https://blog.taikiito.com/en/tags/self-hosting/index.xml" rel="self" type="application/rss+xml"/><item><title>Before Writing a Single Post, I Renamed My GitHub Account</title><link>https://blog.taikiito.com/en/posts/2026-08-22-building-this-blog/</link><pubDate>Sat, 22 Aug 2026 08:00:00 +0800</pubDate><guid>https://blog.taikiito.com/en/posts/2026-08-22-building-this-blog/</guid><description>The decision to start publishing this blog: Hugo + PaperMod, GitHub Actions deploying to GitHub Pages under a custom domain, a private source repo force-pushing built output to a separate public repo, and a GitHub username rename along the way.</description><content:encoded><![CDATA[<p>Last post was about building an RSS reader and tearing it down the same day. Around the same time, I made a bigger call: to start writing down what I&rsquo;d been doing, in public. This blog is the result, and I started it that same day.</p>
<h2 id="why">Why</h2>
<p>The same newsletter that had introduced me to MulmoClaude in the first place also carried some advice from its author: if you want to move toward being an engineer, publish real work on GitHub. The domain setup, self-hosting, and email troubleshooting I&rsquo;d been doing seemed like the kind of thing that could be genuinely useful to someone hitting the same problem via search, so I decided to write it up.</p>
<h2 id="choosing-a-diary-format">Choosing a diary format</h2>
<p>I first considered topic-organized how-to posts, then switched partway through to a chronological diary - writing things in the order they actually happened, mistakes and detours included. It&rsquo;s closer to how it actually went.</p>
<h2 id="picking-the-stack">Picking the stack</h2>
<p>I went with Hugo as the static site generator, mainly because multi-language support (i18n) is built in. Theme: PaperMod. Deploy is GitHub Actions building the site and pushing it to GitHub Pages, with a custom domain (<code>blog.taikiito.com</code>) pointed at it via a Cloudflare CNAME.</p>
<p>I split it into two repos: the actual Hugo source I edit (<code>blog-source</code>) stays private, and a single Actions job builds it and force-pushes only the built static output to a separate public repo (<code>blog</code>). Editing history and drafts never leave the private repo - only the finished output is public, which I like.</p>
<h2 id="renamed-my-username-while-i-was-at-it">Renamed my username while I was at it</h2>
<p>I also renamed my GitHub username at this point. The old one had a string based on my birthdate in it, added without much thought when I first signed up, and I&rsquo;d wanted to change it for a while. My first choice was already taken, so I landed on a <code>-dev</code> suffix instead. With zero public repos at the time, the switching cost was basically nothing.</p>
<p>I also set up a GitHub profile README - creating a repo with the exact same name as your username makes it auto-display on your profile page. Kept it to two lines:</p>
<blockquote>
<p>Tinkering with computers on the side. Hoping it helps someone.</p>
</blockquote>
<h2 id="where-things-stand">Where things stand</h2>
<p>The blog itself and a public GitHub account are both in place now. The actual posts are what comes next, written up after the fact as things happen.</p>
<p>To be continued.</p>
]]></content:encoded></item><item><title>With the Domain Working, I Built Myself a Digital Business Card the Same Day</title><link>https://blog.taikiito.com/en/posts/2026-08-19-digital-business-card/</link><pubDate>Wed, 19 Aug 2026 10:00:00 +0800</pubDate><guid>https://blog.taikiito.com/en/posts/2026-08-19-digital-business-card/</guid><description>A same-day side quest after getting the domain working: a static digital business card at a new subdomain, served entirely separately from the main app, with a vCard-download button, a WhatsApp click-to-chat link, and a small run-in with Cloudflare&amp;rsquo;s email obfuscation.</description><content:encoded><![CDATA[<p>Last time, I got <code>taikiito.com</code> working and could reach my own MulmoClaude from a browser. Now that I could carve out subdomains under it, I built one more thing the same day: a digital business card.</p>
<h2 id="one-subdomain-one-static-page">One subdomain, one static page</h2>
<p>I created a new subdomain, <code>card.taikiito.com</code>, and added a second nginx server block on the same Lightsail box. The important part: it&rsquo;s completely separate from the main Node.js app. No <code>proxy_pass</code> - just a single static HTML file being served - so traffic to the card page has zero effect on the main app&rsquo;s load.</p>
<p>The content is lifted straight from the business card I actually use day to day. I kept it down to two buttons:</p>
<ul>
<li>&ldquo;Save Contact&rdquo; - generates a <code>.vcf</code> file on the spot via a JS Blob and opens the phone&rsquo;s add-to-contacts sheet</li>
<li>&ldquo;WhatsApp&rdquo; - a <code>wa.me</code> click-to-chat deep link</li>
</ul>
<p>I originally had a third button for sending an email too, but later cut it down to just an info row with a <code>mailto:</code> link. Fewer buttons turned out to be more usable.</p>
<h2 id="a-small-color-detour">A small color detour</h2>
<p>I&rsquo;d built it in navy from the start, but made a black-and-white version too, just to compare side by side. Navy won, and the black-and-white draft got deleted.</p>
<h2 id="a-small-gotcha">A small gotcha</h2>
<p>Right after launch, previewing the card inside this app&rsquo;s own embedded iframe, the email address showed up as a garbled placeholder string instead of the real thing. My first thought was I&rsquo;d broken something. Turned out to be Cloudflare&rsquo;s &ldquo;Email Address Obfuscation&rdquo; (Scrape Shield) - it automatically encodes email addresses on the page to deter spam scrapers, and a small decoding script is supposed to restore the real address in a normal browser. That decoder just doesn&rsquo;t run inside this app&rsquo;s preview iframe. It&rsquo;s a zone-wide setting, so I left it on and confirmed the real address renders fine in an actual browser tab.</p>
<h2 id="what-a-name-domain-unlocks">What a name-domain unlocks</h2>
<p>The moment my own name became a domain, spinning up small subdomains like this got trivial. A few more of the same pattern would follow.</p>
<p>To be continued.</p>
]]></content:encoded></item><item><title>Getting My Own Domain, and Email On It</title><link>https://blog.taikiito.com/en/posts/2026-08-19-domain-and-email/</link><pubDate>Wed, 19 Aug 2026 09:00:00 +0800</pubDate><guid>https://blog.taikiito.com/en/posts/2026-08-19-domain-and-email/</guid><description>Got a domain with my own name on it, found a backdoor I&amp;rsquo;d accidentally left open while reviewing access settings, and hit silent delivery failures even with SPF/DKIM/DMARC all correctly configured.</description><content:encoded><![CDATA[<p>Last time I wrote about moving to an always-on server. Next: getting my own domain, and email on top of it.</p>
<h2 id="why-bother-with-a-domain">Why bother with a domain</h2>
<p>The server itself was running fine, but the address you&rsquo;d use to reach it wasn&rsquo;t exactly memorable, and I wanted something with my own name in it. I registered <code>taikiito.com</code> via Cloudflare Registrar.</p>
<p>I also learned that a brand-new domain tends to get treated with suspicion for a while by various automated network filters, as &ldquo;uncategorized&rdquo; and unclassified. There&rsquo;s basically no way around this except time - letting the domain accumulate age and a sending history until it earns a trustworthy classification. I registered it now, well before I&rsquo;d actually rely on it, and I&rsquo;m letting that trust build up patiently.</p>
<h2 id="a-backdoor-id-left-open-myself">A backdoor I&rsquo;d left open myself</h2>
<p>While reviewing my access setup, I tried reaching the Lightsail origin&rsquo;s raw IP directly instead of the domain - and it loaded fine.</p>
<p>That meant the authentication gate I&rsquo;d set up via Cloudflare Access, which protects access through the domain, could be completely bypassed by hitting the origin IP directly. The lock I&rsquo;d built for the front door meant nothing if you came in through the back.</p>
<p>The same day, I set up <code>ufw</code> to only allow inbound traffic on ports 80/443 <strong>from Cloudflare&rsquo;s published IP ranges</strong> (<code>https://www.cloudflare.com/ips-v4</code>), denying everything else by default. I made sure to run <code>ufw allow OpenSSH</code> explicitly <em>before</em> enabling ufw (get that order wrong and you can lock yourself out of SSH the moment the firewall activates). After applying it, domain access kept working fine, and direct origin-IP access was cut off entirely.</p>
<h2 id="a-522-error-when-adding-a-second-origin">A 522 error when adding a second origin</h2>
<p>Under this domain, I added a second entry point for MulmoClaude (<code>mulmoclaude.taikiito.com</code>) and a subdomain for a digital business card (<code>card.taikiito.com</code>).</p>
<p>Right after setting up the DNS (A record) and the Cloudflare Access policy for the second entry point, hitting it returned Cloudflare&rsquo;s <strong>522 error (connection timed out to origin)</strong>. The cause: SSL/TLS encryption mode is managed per zone (per domain), not globally. The new <code>taikiito.com</code> zone had defaulted to <strong>Full</strong> mode, so Cloudflare tried HTTPS to the origin on port 443, but nginx there was only listening on port 80. Switching it to <strong>Flexible</strong> mode, matching the existing domain, fixed it immediately.</p>
<h2 id="went-ahead-and-set-up-spfdkimdmarc-for-email-too">Went ahead and set up SPF/DKIM/DMARC for email too</h2>
<p>Since I had the domain, I used iCloud+&rsquo;s <strong>Custom Email Domain</strong> feature to set up <code>me@taikiito.com</code>. No extra subscription - just three DNS records, exactly as instructed:</p>
<ul>
<li><strong>MX</strong>: <code>mx01.mail.icloud.com</code> / <code>mx02.mail.icloud.com</code></li>
<li><strong>SPF</strong> (TXT): <code>v=spf1 include:icloud.com ~all</code></li>
<li><strong>DKIM</strong> (CNAME): <code>sig1._domainkey</code> → <code>sig1.dkim.taikiito.com.at.icloudmailadmin.com</code></li>
</ul>
<p>These prove, through a few different mechanisms, that mail is genuinely coming from my domain and not being spoofed. Simple copy-paste work.</p>
<h2 id="still-didnt-reach-one-particular-inbox">Still didn&rsquo;t reach one particular inbox</h2>
<p>The setup was correct, but a test email to one of my other addresses never arrived. No bounce either - it just silently disappeared.</p>
<p>Just in case, I added <strong>DMARC</strong> too (TXT, <code>v=DMARC1; p=none; rua=mailto:(report address)</code>) and confirmed via an external DNS checker that it had propagated correctly. Still nothing. The cause was a sender-reputation problem that SPF/DKIM/DMARC being perfectly correct simply can&rsquo;t fix - the domain has no sending history yet. Mail servers with strict filtering seem especially sensitive to this.</p>
<p>I&rsquo;d run out of technical levers to pull, so I&rsquo;m letting time solve this one too. Even with SPF/DKIM/DMARC configured flawlessly, a domain&rsquo;s &ldquo;trust&rdquo; has a component that correct technical settings alone can&rsquo;t fix - learned twice now, once for web access and once for email.</p>
<p>To be continued.</p>
]]></content:encoded></item></channel></rss>