<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Projects on Backend Engineering Strategy Tools</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/</link><description>Recent content in Projects on Backend Engineering Strategy Tools</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 18 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://backend-engineering-strategy-tools.github.io/site/projects/index.xml" rel="self" type="application/rss+xml"/><item><title>Image Tooling</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/image-tooling/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/image-tooling/</guid><description>&lt;p&gt;Versioned, multi-arch Docker images for Kubernetes workflows — built with &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt;, published to Docker Hub, triggered by a version tag.&lt;/p&gt;
&lt;p&gt;The motivation is in &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/thinking/shared-tooling-images/" &gt;Shared Tooling Images&lt;/a&gt;: one image, consistent versions, three contexts — CI, local, colleagues.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="images"&gt;Images
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;GitHub repo&lt;/th&gt;
 &lt;th&gt;Docker Hub&lt;/th&gt;
 &lt;th&gt;Contents&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/image-tooling" target="_blank" rel="noopener"
 &gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;kubectl, helm, kustomize, argocd CLI, k9s, jq, yq&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s-aws&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tooling-k8s&lt;/code&gt; + AWS CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;image-tooling&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/tooling-k8s-openstack&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;tooling-k8s&lt;/code&gt; + OpenStack CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/image-buildx" target="_blank" rel="noopener"
 &gt;&lt;code&gt;image-buildx&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/buildx&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;CI builder — Docker buildx, AWS CLI, Dagger CLI&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/image-pandoc" target="_blank" rel="noopener"
 &gt;&lt;code&gt;image-pandoc&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;best-tools/pandoc&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;PDF generation — pandoc + TeX Live&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;All images publish as multi-arch manifests: &lt;code&gt;linux/amd64&lt;/code&gt; + &lt;code&gt;linux/arm64&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="quick-start"&gt;Quick start
&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Interactive shell with kubeconfig mounted:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -it --rm &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -v ~/.kube:/mnt/kube:ro &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -v &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;pwd&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;:/work &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -w /work &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker.io/best-tools/tooling-k8s:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The image entry point symlinks &lt;code&gt;/mnt/kube&lt;/code&gt; → &lt;code&gt;/root/.kube&lt;/code&gt; on startup, so &lt;code&gt;kubectl&lt;/code&gt; picks it up immediately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shell alias for daily use:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;alias k8s&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;docker run -it --rm \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; -v ~/.kube:/mnt/kube:ro \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; -v $(pwd):/work -w /work \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; docker.io/best-tools/tooling-k8s:latest&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;k8s helm lint .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;k8s kubectl get pods -n argocd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;In CI (GitHub Actions):&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;- &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Lint chart&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;run&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;docker run --rm -v ${{ github.workspace }}:/work -w /work docker.io/best-tools/tooling-k8s:latest helm lint .&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or reference the image directly as the job container — no install step needed.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="setup-contributors--maintainers"&gt;Setup (contributors / maintainers)
&lt;/h2&gt;&lt;p&gt;Credentials are set once as &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/github/" &gt;GitHub org-level secrets&lt;/a&gt; and inherited by all &lt;code&gt;image-*&lt;/code&gt; repos automatically.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Secret&lt;/th&gt;
 &lt;th&gt;Where to get it&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;DOCKERHUB_TOKEN&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;hub.docker.com → Account → Security → Access Tokens (Read, Write, Delete)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;DAGGER_CLOUD_TOKEN&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;cloud.dagger.io → Organisation → Tokens&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Path: github.com/Backend-Engineering-Strategy-Tools → Settings → Secrets and variables → Actions → New organisation secret.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="releasing"&gt;Releasing
&lt;/h2&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git tag -a v1.0.0 -m &lt;span style="color:#e6db74"&gt;&amp;#34;Release v1.0.0&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git push origin v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The GitHub Actions workflow triggers on &lt;code&gt;v*.*.*&lt;/code&gt; tags, calls &lt;code&gt;dagger call publish-multi-arch&lt;/code&gt;, and pushes both &lt;code&gt;best-tools/&amp;lt;image&amp;gt;:v1.0.0&lt;/code&gt; and &lt;code&gt;best-tools/&amp;lt;image&amp;gt;:latest&lt;/code&gt; to Docker Hub. Pipeline trace at &lt;a class="link" href="https://dagger.cloud/" target="_blank" rel="noopener"
 &gt;cloud.dagger.io&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="links"&gt;Links
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools" target="_blank" rel="noopener"
 &gt;Backend-Engineering-Strategy-Tools org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://hub.docker.com/u/best-tools" target="_blank" rel="noopener"
 &gt;best-tools on Docker Hub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger pipelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>This Site</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/best-site/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/best-site/</guid><description>&lt;p&gt;This started as an effort to clean up and publish existing notes — things accumulated over years of work that were sitting in various raw forms and never properly written up. The act of publishing forces a level of clarity that private notes rarely get: you have to explain the context, fill the gaps, and decide what actually matters.&lt;/p&gt;
&lt;p&gt;It doubles as deliberate practice in documentation. Getting into the habit of writing things up properly — not just as a record of what was done, but as something useful to come back to — is a skill that compounds. The site is both the output and the exercise.&lt;/p&gt;
&lt;p&gt;Practically it also serves as a personal reference: a place to look things up that I have actually done and know the details of, rather than re-deriving them from scratch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stack:&lt;/strong&gt; Hugo static site using the &lt;a class="link" href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener"
 &gt;Stack theme&lt;/a&gt; v4 via Go modules. Deployed to &lt;a class="link" href="https://pages.github.com/" target="_blank" rel="noopener"
 &gt;GitHub Pages&lt;/a&gt; via GitHub Actions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/site" target="_blank" rel="noopener"
 &gt;Backend-Engineering-Strategy-Tools/site&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="why-hugo"&gt;Why Hugo
&lt;/h2&gt;&lt;p&gt;Static is the right default for content that does not need dynamic rendering. Hugo is fast — full site builds in milliseconds — and its template model maps well to a Go-centric mindset. The theme ecosystem is good and the Stack theme in particular supports the layout and content model needed here without significant customisation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="build-pipeline"&gt;Build Pipeline
&lt;/h2&gt;&lt;p&gt;The GHA workflow runs on push to &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Fetch private assets&lt;/strong&gt; — CV and cover letter PDFs are stored in a private release on a separate repo. The workflow fetches them via GitHub API using a scoped &lt;code&gt;BEST_SITE_PAT&lt;/code&gt; secret and writes them into &lt;code&gt;static/cv/&lt;/code&gt; before the build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hugo build&lt;/strong&gt; — &lt;code&gt;hugo --minify&lt;/code&gt; via the official &lt;code&gt;ghcr.io/gohugoio/hugo&lt;/code&gt; Docker image, producing &lt;code&gt;public/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy&lt;/strong&gt; — &lt;code&gt;actions/deploy-pages&lt;/code&gt; pushes &lt;code&gt;public/&lt;/code&gt; to GitHub Pages.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This keeps CV content separate from site code — the PDF repo can be updated independently without touching the site repo, and a site redeploy picks up the latest version.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="theme-customisation"&gt;Theme Customisation
&lt;/h2&gt;&lt;p&gt;Stack theme v4 is imported via &lt;code&gt;go.mod&lt;/code&gt;. Hugo&amp;rsquo;s lookup order means local files in &lt;code&gt;layouts/&lt;/code&gt; or &lt;code&gt;assets/&lt;/code&gt; override the theme without forking it.&lt;/p&gt;
&lt;p&gt;The main override is &lt;code&gt;layouts/_default/single.html&lt;/code&gt; — the site uses &lt;code&gt;layout: single&lt;/code&gt; on all content pages to get full-width rendering (no sidebar). All pages also set &lt;code&gt;showReadingTime: false&lt;/code&gt; in front matter.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="domain"&gt;Domain
&lt;/h2&gt;&lt;p&gt;Currently served at &lt;code&gt;backend-engineering-strategy-tools.github.io/site/&lt;/code&gt;. A redirect from &lt;code&gt;best.mjnet.info&lt;/code&gt; is set up via an S3 static website bucket — no files in the bucket, just a redirect rule. DNS managed in Route53.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;S3 bucket setup&lt;/strong&gt; (bucket name must match domain):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws s3api create-bucket &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --bucket best.mjnet.info &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --region eu-central-1 &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --create-bucket-configuration LocationConstraint&lt;span style="color:#f92672"&gt;=&lt;/span&gt;eu-central-1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws s3api put-public-access-block &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --bucket best.mjnet.info &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --public-access-block-configuration BlockPublicAcls&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false,IgnorePublicAcls&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false,BlockPublicPolicy&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false,RestrictPublicBuckets&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws s3api put-bucket-website &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --bucket best.mjnet.info &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --website-configuration &lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;IndexDocument&amp;#34;:{&amp;#34;Suffix&amp;#34;:&amp;#34;index.html&amp;#34;},&amp;#34;RoutingRules&amp;#34;:[{&amp;#34;Redirect&amp;#34;:{&amp;#34;Protocol&amp;#34;:&amp;#34;https&amp;#34;,&amp;#34;HostName&amp;#34;:&amp;#34;backend-engineering-strategy-tools.github.io&amp;#34;,&amp;#34;ReplaceKeyPrefixWith&amp;#34;:&amp;#34;site/&amp;#34;}}]}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Route53 ALIAS record&lt;/strong&gt; pointing &lt;code&gt;best.mjnet.info&lt;/code&gt; at the S3 website endpoint:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;aws route53 change-resource-record-sets &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --hosted-zone-id &amp;lt;ZONE_ID&amp;gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --change-batch &lt;span style="color:#e6db74"&gt;&amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;Changes&amp;#34;: [{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;Action&amp;#34;: &amp;#34;CREATE&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;ResourceRecordSet&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;Name&amp;#34;: &amp;#34;best.mjnet.info&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;Type&amp;#34;: &amp;#34;A&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;AliasTarget&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;HostedZoneId&amp;#34;: &amp;#34;Z21DNDUVLTQW6Q&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;DNSName&amp;#34;: &amp;#34;s3-website.eu-central-1.amazonaws.com&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;EvaluateTargetHealth&amp;#34;: false
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Z21DNDUVLTQW6Q&lt;/code&gt; is the fixed AWS hosted zone ID for S3 website endpoints in &lt;code&gt;eu-central-1&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="content-model"&gt;Content Model
&lt;/h2&gt;&lt;p&gt;Two sections with distinct purposes:&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Section&lt;/th&gt;
 &lt;th&gt;Tone&lt;/th&gt;
 &lt;th&gt;What goes here&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;homelab/&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Narrative, first-person&lt;/td&gt;
 &lt;td&gt;What actually happened — the sequence, dead ends, workarounds&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;&lt;code&gt;public-notes/&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;Reference, factual&lt;/td&gt;
 &lt;td&gt;How things work — distilled, structured, without the personal journey&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The separation keeps the narrative and reference writing from muddying each other. A reader looking for a quick reference does not want to read about what went wrong first; a reader following the homelab story wants the context.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;projects/&lt;/code&gt; and &lt;code&gt;thinking/&lt;/code&gt; round out the structure — projects are documented outcomes, thinking is longer-form analysis.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="whats-next"&gt;What&amp;rsquo;s Next
&lt;/h2&gt;&lt;p&gt;The current setup — Hugo on GitHub Pages — is the right foundation for a content-heavy site. A few things are planned or in progress:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Custom domain&lt;/strong&gt; — &lt;code&gt;best.mjnet.info&lt;/code&gt; redirect via S3 + Route53 (see Domain section above). Proper custom domain with HTTPS as a follow-up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better build pipeline&lt;/strong&gt; — replacing the Makefile with a &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/cicd/dagger/" &gt;Dagger&lt;/a&gt; Go module for local/CI parity and pinned Hugo versions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;More content&lt;/strong&gt; — the backlog of notes and projects is long. The site grows as things get cleaned up and published.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Longer term, the intent is to grow this into a more multi-faceted site — login-protected sections, dynamic content, interactive tooling. Hugo handles the static part well; the approach is to add separate paths alongside it for authentication and dynamic content rather than replacing the static foundation. The static site stays fast and low-cost, dynamic parts layer in where they are actually needed.&lt;/p&gt;</description></item><item><title>Gardener on Cleura</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/gardener/</link><pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/gardener/</guid><description>&lt;p&gt;Getting hands-on with &lt;a class="link" href="https://gardener.cloud/" target="_blank" rel="noopener"
 &gt;Gardener&lt;/a&gt; on &lt;a class="link" href="https://cleura.com/" target="_blank" rel="noopener"
 &gt;Cleura&lt;/a&gt; — a European OpenStack cloud — ahead of using it professionally. The focus is on the networking and traffic ingress side: how does a Gardener shoot cluster on OpenStack expose services, what does the LoadBalancer path actually look like, and when does ingress apply versus when it does not.&lt;/p&gt;
&lt;p&gt;The test application is a &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/projects/minecraft/" &gt;Minecraft server with Velocity proxy&lt;/a&gt; — useful precisely because it is raw TCP rather than HTTP, which forces the full LoadBalancer path rather than an ingress shortcut.&lt;/p&gt;
&lt;p&gt;→ &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/gardener/" &gt;Gardener on Cleura — technical notes&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="steps"&gt;Steps
&lt;/h2&gt;&lt;h3 id="1--shoot-cluster"&gt;1 — Shoot cluster
&lt;/h3&gt;&lt;p&gt;Provision a Gardener shoot cluster on Cleura. Cleura wraps Gardener behind their own REST API — &lt;code&gt;gardenctl&lt;/code&gt; and the Gardener Terraform provider require the garden cluster kubeconfig, which Cleura does not expose. Cluster lifecycle goes through their REST API instead.&lt;/p&gt;
&lt;p&gt;→ &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/kubernetes/gardener/#provisioning-a-shoot-cluster-on-cleura" &gt;Provisioning via Cleura REST API&lt;/a&gt;&lt;br&gt;
→ &lt;a class="link" href="https://github.com/cleura/docs/issues/533" target="_blank" rel="noopener"
 &gt;Cleura docs issue #533 — IaC and gardenctl access&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="2--minecraft-via-standard-loadbalancer"&gt;2 — Minecraft via standard LoadBalancer
&lt;/h3&gt;&lt;p&gt;Deploy &lt;a class="link" href="https://github.com/itzg/docker-minecraft-server" target="_blank" rel="noopener"
 &gt;&lt;code&gt;itzg/minecraft-server&lt;/code&gt;&lt;/a&gt; as a StatefulSet with a plain &lt;code&gt;LoadBalancer&lt;/code&gt; service for TCP 25565 — the direct Octavia path, no Gateway involved. Gets the server running quickly and confirms TCP exposure works on Cleura independently.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Internet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;TCP 25565
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Octavia LB (direct LoadBalancer service)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Minecraft Pod (itzg/minecraft-server)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PVC (Cinder)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Manifests: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/scripts/mc/stateful_set.yaml" &gt;stateful_set.yaml&lt;/a&gt; · &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/scripts/mc/service.yaml" &gt;service.yaml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Apply directly from this repo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -k &lt;span style="color:#e6db74"&gt;&amp;#34;https://github.com/Backend-Engineering-Strategy-Tools/site//static/scripts/mc?ref=main&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check the rollout and grab the external IP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get all -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mc-example
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get svc mc-example &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}:{.spec.ports[0].port}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="25--migrate-to-helm-chart"&gt;2.5 — Migrate to Helm chart
&lt;/h3&gt;&lt;p&gt;Swap the raw manifests for the &lt;a class="link" href="https://github.com/itzg/minecraft-server-charts" target="_blank" rel="noopener"
 &gt;&lt;code&gt;itzg/minecraft-server-charts&lt;/code&gt;&lt;/a&gt; Helm chart — actively maintained, covers server type, persistence, RCON, backups, and extra ports (BlueMap, Dynmap). The raw YAML stays useful as a reference for the underlying shape.&lt;/p&gt;
&lt;p&gt;Manifests: &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/scripts/mc-helm/values.yaml" &gt;values.yaml&lt;/a&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add minecraft-server-charts https://itzg.github.io/minecraft-server-charts/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm upgrade --install mc minecraft-server-charts/minecraft &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; -f https://backend-engineering-strategy-tools.github.io/site/scripts/mc-helm/values.yaml
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Check the rollout and grab the external IP:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get all -l app.kubernetes.io/instance&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get svc mc-example -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}:{.spec.ports[0].port}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="3--envoy-gateway"&gt;3 — Envoy Gateway
&lt;/h3&gt;&lt;p&gt;Deploy &lt;a class="link" href="https://gateway.envoyproxy.io/" target="_blank" rel="noopener"
 &gt;Envoy Gateway&lt;/a&gt; into the shoot cluster — the CNCF implementation of the &lt;a class="link" href="https://gateway-api.sigs.k8s.io/" target="_blank" rel="noopener"
 &gt;Kubernetes Gateway API&lt;/a&gt;. The NGINX Ingress Controller is deprecated; Gateway API is the forward path with a standardised spec for both HTTP and TCP.&lt;/p&gt;
&lt;p&gt;Envoy Gateway exposes a single &lt;code&gt;LoadBalancer&lt;/code&gt; service via Octavia. Everything routes through it.&lt;/p&gt;
&lt;h3 id="4--httproute-certificates-and-bluemap"&gt;4 — HTTPRoute, certificates, and BlueMap
&lt;/h3&gt;&lt;p&gt;Deploy &lt;a class="link" href="https://bluemap.bluecolored.de/" target="_blank" rel="noopener"
 &gt;BlueMap&lt;/a&gt; — a Minecraft mod that renders the world as a live 3D web map served over HTTP. Route it through the Gateway with a &lt;code&gt;HTTPRoute&lt;/code&gt; and wire &lt;a class="link" href="https://cert-manager.io/" target="_blank" rel="noopener"
 &gt;cert-manager&lt;/a&gt; to provision a Let&amp;rsquo;s Encrypt certificate.&lt;/p&gt;
&lt;p&gt;A real HTTP service with a real use, not a throwaway test page. Validates the full HTTP + TLS path before touching the game server.&lt;/p&gt;
&lt;h3 id="5--migrate-to-tcproute"&gt;5 — Migrate to TCPRoute
&lt;/h3&gt;&lt;p&gt;Migrate the TCP service to a &lt;code&gt;TCPRoute&lt;/code&gt; through Envoy Gateway. &lt;code&gt;TCPRoute&lt;/code&gt; is in the Gateway API experimental channel — this step validates that a single Gateway handles both HTTP and raw TCP.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Internet
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Octavia LB (one Gateway LoadBalancer)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Envoy Gateway
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;+------------------------------+------------------------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;| |
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTPRoute → BlueMap TCPRoute → Minecraft
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="6--velocity-if-needed"&gt;6 — Velocity (if needed)
&lt;/h3&gt;&lt;p&gt;Add &lt;a class="link" href="https://papermc.io/software/velocity" target="_blank" rel="noopener"
 &gt;Velocity&lt;/a&gt; as a TCP proxy in front of the Minecraft server if multi-server routing becomes relevant — lobby, modded, survival as separate backends. Skip if a single server is enough.&lt;/p&gt;
&lt;p&gt;→ &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/projects/minecraft/" &gt;Minecraft project&lt;/a&gt;&lt;/p&gt;
&lt;h3 id="7--plugin-pipeline"&gt;7 — Plugin pipeline
&lt;/h3&gt;&lt;p&gt;A colleague is building a Minecraft plugin. The goal is a &lt;a class="link" href="https://dagger.io/" target="_blank" rel="noopener"
 &gt;Dagger&lt;/a&gt; pipeline with GitHub Actions — the same build running locally and in CI, covering the JVM toolchain and packaging steps.&lt;/p&gt;
&lt;h3 id="8--ai"&gt;8 — AI
&lt;/h3&gt;&lt;p&gt;Something with NPC behaviour, a bot, or plugin-side automation. Low priority, high fun.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="iac-gap"&gt;IaC gap
&lt;/h2&gt;&lt;p&gt;Cleura does not expose the garden cluster kubeconfig. That one limitation closes off the entire Gardener tooling ecosystem: &lt;code&gt;gardenctl&lt;/code&gt; requires it, the &lt;a class="link" href="https://registry.terraform.io/providers/gardener/gardener" target="_blank" rel="noopener"
 &gt;Gardener Terraform provider&lt;/a&gt; requires it, and any Crossplane provider built on the Gardener API would require it too. There is no HCL path here.&lt;/p&gt;
&lt;p&gt;What remains is Cleura&amp;rsquo;s own REST API — which is fine for interactive use but falls short the moment you want to drive cluster lifecycle from a pipeline. A bash script wrapping &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt; works, and that is what &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/scripts/cleura-shoot.sh" &gt;cleura-shoot.sh&lt;/a&gt; does, but it is a workaround rather than a solution. No state, no plan, no diff — just imperative API calls.&lt;/p&gt;
&lt;p&gt;Options if this needs to graduate beyond a script:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Crossplane &lt;code&gt;provider-http&lt;/code&gt;&lt;/strong&gt; — can wrap the REST API declaratively, but has no native polling or deletion hooks, so the reconciliation story is awkward&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom Terraform provider&lt;/strong&gt; — full &lt;code&gt;plan&lt;/code&gt;/&lt;code&gt;apply&lt;/code&gt; semantics, but requires writing a Go provider from scratch&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pulumi dynamic provider&lt;/strong&gt; — similar effort, Python or TypeScript&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A feature request for gardenctl access or a native IaC provider has been filed with Cleura (→ &lt;a class="link" href="https://github.com/cleura/docs/issues/533" target="_blank" rel="noopener"
 &gt;cleura/docs#533&lt;/a&gt;). Until something changes there, the bash script is as good as it gets.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="status"&gt;Status
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Step&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;1 — Shoot cluster on Cleura&lt;/td&gt;
 &lt;td&gt;done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2 — Minecraft via LoadBalancer (itzg)&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2.5 — Migrate to Helm chart&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3 — Envoy Gateway&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4 — HTTPRoute + cert-manager + BlueMap&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5 — Migrate to TCPRoute&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6 — Velocity&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;7 — Plugin pipeline (Dagger)&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;8 — AI&lt;/td&gt;
 &lt;td&gt;planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Building this out — notes will expand as each step lands.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Procedural Mesh — Blender Python &amp; AI-Assisted Geometry</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/</link><pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/procedural-mesh/</guid><description>&lt;p&gt;Using Python scripts inside Blender to generate, manipulate, and export geometry — rather than modelling by hand. The config block is the model. Changing a dimension means editing a constant and re-running, not touching a mesh.&lt;/p&gt;
&lt;p&gt;Two related but distinct approaches:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parametric generation&lt;/strong&gt; — the script builds geometry from scratch. Holes, plates, text engravings, export to STL, automated renders. The shape lives entirely in code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mesh manipulation&lt;/strong&gt; — an existing STL is imported and cut up. Different problem: no parametric handle, working with whatever the downloaded mesh gives you.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both involve AI in the loop — either iterating on the Python with Claude, or using AI to determine cut planes on existing geometry.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="stages"&gt;Stages
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Step&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;th&gt;Status&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;0&lt;/td&gt;
 &lt;td&gt;Manual Blender modelling — &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/scout-badges-resin/" &gt;Packat &amp;amp; Klart badge&lt;/a&gt; and &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/casting-badges/" &gt;scout emblem casting master&lt;/a&gt;; learning the tool before scripting&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;1&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/public-notes/frameworks-tools/blender-python/" &gt;Approach documented&lt;/a&gt; — Python scripted geometry, config-block iteration pattern&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;2&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/rack-support-brace/" &gt;Rack Support Brace&lt;/a&gt; — first implementation; flat plate, boolean hole grid, engraved text, automated renders&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;3&lt;/td&gt;
 &lt;td&gt;Headless render — Blender 5.1.2 + Dagger pipeline; STL → multi-angle PNGs without opening Blender; smoke-tested and running in CI&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;4&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/scout-buckle/" &gt;Scout Buckle&lt;/a&gt; — complex geometry; compound curves, functional tongue mechanism, tolerances that matter&lt;/td&gt;
 &lt;td&gt;In progress&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;5&lt;/td&gt;
 &lt;td&gt;CI/CD pipeline — STL or script change → GitHub Actions → Dagger → PNG artifacts; &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/procedural-mesh-pipeline" target="_blank" rel="noopener"
 &gt;procedural-mesh-pipeline&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Done&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;6&lt;/td&gt;
 &lt;td&gt;AI feedback loop — describe correction → updated script → re-run, without opening Blender&lt;/td&gt;
 &lt;td&gt;Planned&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;7&lt;/td&gt;
 &lt;td&gt;&lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/garage/dragon-split/" &gt;Dragon Split&lt;/a&gt; — mesh manipulation; cut an existing articulated dragon STL into printable segments with connectors&lt;/td&gt;
 &lt;td&gt;Not started&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="notes"&gt;Notes
&lt;/h2&gt;&lt;p&gt;The rack brace (step 2) was easier than expected — straightforward geometry, minimal iteration. That is why the pipeline steps did not get built yet: the manual workflow was fast enough that the overhead of automating it was not justified for one part.&lt;/p&gt;
&lt;p&gt;The scout buckle (step 4) is the harder test. Compound curves and functional constraints mean the script has needed partial rebuilds rather than config-block edits. That is the case that justified building the render pipeline properly.&lt;/p&gt;
&lt;p&gt;Steps 3 and 5 ended up built together rather than sequentially. The pipeline lives in a separate repo — &lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/procedural-mesh-pipeline" target="_blank" rel="noopener"
 &gt;procedural-mesh-pipeline&lt;/a&gt; — Dagger + GitHub Actions, Blender 5.1.2 headless, Cycles CPU. Renders run natively on the CI runner (amd64); locally the pipeline is used for smoke-testing the image only. Blender has no official ARM64 Linux binary so local renders under emulation are impractical.&lt;/p&gt;
&lt;p&gt;The dragon split (step 7) is a different class of problem — remixing rather than generating. Worth keeping in the same project because the tooling overlaps (Blender Python, STL export) even if the approach does not.&lt;/p&gt;</description></item><item><title>Kubernetes Across the Stack</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/kubernetes-stack/</link><pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/kubernetes-stack/</guid><description>&lt;p&gt;A documented comparison of running Kubernetes across every major hosting model — cloud managed, self-managed on cloud, private cloud, and bare metal at home. The goal is a honest, practical reference for each environment: what it costs you in time and money, where the rough edges are, and how the networking story differs between them.&lt;/p&gt;
&lt;p&gt;The thread running through all of it is &lt;a class="link" href="https://www.talos.dev/" target="_blank" rel="noopener"
 &gt;Talos Linux&lt;/a&gt; — an immutable, API-driven OS built specifically for Kubernetes. No SSH, no shell, no config drift. The same OS everywhere means the operational model stays consistent regardless of what is running underneath.&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;Environment&lt;/th&gt;
 &lt;th&gt;Approach&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack — &lt;a class="link" href="https://cleura.com/" target="_blank" rel="noopener"
 &gt;Cleura&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Talos &amp;amp; Terraform&lt;/td&gt;
 &lt;td&gt;draft exists&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack — &lt;a class="link" href="https://cleura.com/" target="_blank" rel="noopener"
 &gt;Cleura&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Talos, with Omni&lt;/td&gt;
 &lt;td&gt;maybe ?&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack — &lt;a class="link" href="https://elastx.se/" target="_blank" rel="noopener"
 &gt;ElastX&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Talos &amp;amp; Terraform&lt;/td&gt;
 &lt;td&gt;draft exists&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;OpenStack — &lt;a class="link" href="https://elastx.se/" target="_blank" rel="noopener"
 &gt;ElastX&lt;/a&gt;&lt;/td&gt;
 &lt;td&gt;Talos, with Omni&lt;/td&gt;
 &lt;td&gt;maybe ?&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Homelab — bare metal&lt;/td&gt;
 &lt;td&gt;Talos + Pixieboot + Omni&lt;/td&gt;
 &lt;td&gt;draft exists&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Homelab — bare metal&lt;/td&gt;
 &lt;td&gt;Talos + Pixieboot without Omni&lt;/td&gt;
 &lt;td&gt;maybe ?&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Homelab — OpenStack&lt;/td&gt;
 &lt;td&gt;OpenStack on bare metal, Talos running on top&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;(stretch)&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Homelab — OpenStack&lt;/td&gt;
 &lt;td&gt;Talos on bare metal, OpenStack inside cluster&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;(stretch)&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;AWS&lt;/td&gt;
 &lt;td&gt;Talos on EC2&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;(stretch)&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Azure&lt;/td&gt;
 &lt;td&gt;Talos on VMs&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;(stretch)&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GCP&lt;/td&gt;
 &lt;td&gt;Talos on Compute Engine&lt;/td&gt;
 &lt;td&gt;&lt;em&gt;(stretch)&lt;/em&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Stretch goals&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;AWS, Azure, GCP — same Talos approach, different underlying infrastructure. Interesting eventually, but not the priority.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Omni&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://omni.siderolabs.com/" target="_blank" rel="noopener"
 &gt;Omni&lt;/a&gt; is Sidero&amp;rsquo;s managed control plane for Talos clusters — worth documenting both with and without it. Without Omni gives you the full picture of what Talos management looks like manually; with Omni shows what the managed layer buys you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Homelab provisioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nodes provisioned via Pixieboot — no USB sticks, no manual installations. A node powers on, boots from the network, and registers. The goal is a fully reproducible cluster from scratch with minimal human steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cluster provisioning and bootstrap for each environment&lt;/li&gt;
&lt;li&gt;Networking — CNI choices, ingress, cross-cluster connectivity&lt;/li&gt;
&lt;li&gt;Storage — what you get managed vs what you have to bring yourself&lt;/li&gt;
&lt;li&gt;Operational differences — upgrades, node management, observability&lt;/li&gt;
&lt;li&gt;Cost and trade-off summary across environments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Making it usable&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Getting a cluster running is the easy part. Making it usable is where environments diverge. Each environment needs an answer for ingress, DNS, and storage — and the answer varies significantly depending on what the underlying platform provides.&lt;/p&gt;
&lt;p&gt;On managed cloud you can lean on load balancers and block storage from the provider. On OpenStack you have those options if the provider exposes them. On bare metal at home you are on your own — MetalLB or similar for load balancer IPs, a local DNS solution, and either local storage or something like Rook/Ceph. Same Kubernetes, very different operational story underneath.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Notes exist in various states — pulling them together, testing, and documenting properly is the work.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Minecraft Server</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/minecraft/</link><pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/minecraft/</guid><description>&lt;p&gt;Building and running a Minecraft server with the kids — hosted in the &lt;a class="link" href="https://backend-engineering-strategy-tools.github.io/site/homelab/" &gt;homelab&lt;/a&gt; on bare metal rather than paying for a managed service. Part infrastructure project, part excuse to learn together.&lt;/p&gt;
&lt;p&gt;The longer-term goal is a proper setup: automated backups, world persistence across restarts, maybe some automation around starting and stopping the server on demand.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Notes and repo to follow.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;More to come.&lt;/em&gt;&lt;/p&gt;</description></item><item><title>Touchscreen HUD Build</title><link>https://backend-engineering-strategy-tools.github.io/site/projects/touchscreen-hud/</link><pubDate>Mon, 16 Mar 2026 00:00:00 +0000</pubDate><guid>https://backend-engineering-strategy-tools.github.io/site/projects/touchscreen-hud/</guid><description>&lt;p&gt;A small batch of fanless Atom-based machines with touchscreens — picked up as a hardware experiment, now being packaged up to hand out to colleagues at nerd night.&lt;/p&gt;
&lt;p&gt;The goal: a fully reproducible setup. Boot from a Debian preseed image, drivers configured, sensible defaults in place, and a demo running out of the box. Each machine goes out with a link back to the repo so anyone who wants to dig in or rebuild from scratch can do so.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repos&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/touchdemo" target="_blank" rel="noopener"
 &gt;touchdemo&lt;/a&gt; — demo application running on the devices &lt;em&gt;(work in progress)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/Backend-Engineering-Strategy-Tools/debian-preseed-demo" target="_blank" rel="noopener"
 &gt;debian-preseed-demo&lt;/a&gt; — automated Debian install via preseed &lt;em&gt;(in progress)&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Still to figure out&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Power adapter specs and sourcing&lt;/li&gt;
&lt;li&gt;Preseed configuration — touch input, display drivers, auto-login&lt;/li&gt;
&lt;li&gt;First-boot experience and documentation&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>