<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitOps |</title><link>https://example.com/tags/gitops/</link><atom:link href="https://example.com/tags/gitops/index.xml" rel="self" type="application/rss+xml"/><description>GitOps</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sat, 16 May 2026 00:00:00 +0000</lastBuildDate><image><url>https://example.com/media/icon_hu_da05098ef60dc2e7.png</url><title>GitOps</title><link>https://example.com/tags/gitops/</link></image><item><title>Stage II: CI/CD Pipeline, GitOps &amp; Ephemeral Secrets</title><link>https://example.com/blog/stage-2-automation-zero-trust/</link><pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate><guid>https://example.com/blog/stage-2-automation-zero-trust/</guid><description>&lt;p&gt;With a highly available K3s foundation established on my KVM environment in Stage I, the cluster was functional but not yet &amp;ldquo;enterprise-grade.&amp;rdquo; Manually applying manifests leads to configuration drift, and standard Kubernetes Secrets violate zero-trust security principles.&lt;/p&gt;
&lt;p&gt;This post covers &lt;strong&gt;Stage II&lt;/strong&gt; of the roadmap: establishing a strict CI/CD GitOps pipeline and securing the cluster&amp;rsquo;s sensitive credentials for my stateful workloads (specifically my n8n workflow engine and PostgreSQL database).&lt;/p&gt;
&lt;h2 id="table-of-contents"&gt;Table of Contents&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="github-actions"&gt;1. Continuous Integration with GitHub Actions&lt;/h2&gt;
&lt;p&gt;Before deployment can happen, the code must be built into an immutable artifact. I utilized &lt;strong&gt;GitHub Actions&lt;/strong&gt; to automatically build my application code into Docker images and push them to the container registry on every main branch commit. This ensures that if a deployment breaks, I can instantly roll back to a previously tagged, working image.&lt;/p&gt;
&lt;p&gt;Here is a view of the automated pipeline successfully building and pushing the containerized application upon a new commit:&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="GitHub Actions CI Pipeline Success"
srcset="https://example.com/blog/stage-2-automation-zero-trust/github-action_hu_6946e54d37ded239.webp 320w, https://example.com/blog/stage-2-automation-zero-trust/github-action_hu_aaa7ff0cdc704c77.webp 480w, https://example.com/blog/stage-2-automation-zero-trust/github-action_hu_258ec8fe3ef742a2.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://example.com/blog/stage-2-automation-zero-trust/github-action_hu_6946e54d37ded239.webp"
width="760"
height="453"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="argocd"&gt;2. The GitOps Philosophy: ArgoCD &amp;amp; Helm&lt;/h2&gt;
&lt;p&gt;The core rule of GitOps is simple: &lt;strong&gt;The Git repository is the single source of truth.&lt;/strong&gt; I deployed &lt;strong&gt;ArgoCD&lt;/strong&gt; to operate as a continuous reconciliation loop, tracking my GitHub repository. Rather than syncing raw YAML, ArgoCD dynamically renders my deployments using &lt;strong&gt;Helm&lt;/strong&gt; and &lt;strong&gt;Kustomize&lt;/strong&gt; before applying them to the K3s cluster.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argoproj.io/v1alpha1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Application&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;whatsapp-bot-sync&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;argocd&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;project&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;default&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;repoURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://github.com/danack10/k3s-whatsapp-chatbot.git&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;targetRevision&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;HEAD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;apps/whatsapp-bot/base&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;https://kubernetes.default.svc&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;whatsapp-bot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;automated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;prune&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selfHeal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;syncOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;CreateNamespace=True&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once applied, the GitOps synchronization kicks in. The following dashboard view shows ArgoCD mapping and deploying the entire application stack:&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="ArgoCD Application Sync Spiderweb"
srcset="https://example.com/blog/stage-2-automation-zero-trust/argocd-tree_hu_6caeb52c84f0dc87.webp 320w, https://example.com/blog/stage-2-automation-zero-trust/argocd-tree_hu_4678e766810a09bc.webp 480w, https://example.com/blog/stage-2-automation-zero-trust/argocd-tree_hu_e9fa480fd7716cc8.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://example.com/blog/stage-2-automation-zero-trust/argocd-tree_hu_6caeb52c84f0dc87.webp"
width="760"
height="492"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt; Setting up secure authentication for ArgoCD to pull from my private repository was tricky. I wanted to use a dynamic GitHub App rather than a static Personal Access Token (PAT). Ensuring the GitHub App had the correct permissions scoped strictly to the &lt;code&gt;k3s-whatsapp-chatbot&lt;/code&gt; repo took some trial and error. Additionally, before my edge ingress was fully established, I had to utilize &lt;code&gt;kubectl port-forward&lt;/code&gt; routed entirely through my Tailscale mesh to access the ArgoCD UI, keeping the interface completely hidden from the public internet.&lt;/p&gt;
&lt;h2 id="secrets-problem"&gt;3. The Problem with K8s Secrets&lt;/h2&gt;
&lt;p&gt;Deploying standard applications via GitOps is straightforward, but deploying secrets presents a major security flaw. You cannot push raw or base64-encoded secrets into a Git repository.&lt;/p&gt;
&lt;p&gt;To solve this, I deployed &lt;strong&gt;HashiCorp Vault&lt;/strong&gt; as the centralized, encrypted credential store inside the cluster.&lt;/p&gt;
&lt;h2 id="vault"&gt;4. Declarative Security with Vault &amp;amp; OpenTofu&lt;/h2&gt;
&lt;p&gt;Many engineers deploy Vault and then manually configure it using CLI commands (&lt;code&gt;vault write auth/kubernetes...&lt;/code&gt;). To maintain my declarative infrastructure standards, I bypassed the CLI entirely and used the &lt;strong&gt;OpenTofu Vault Provider&lt;/strong&gt; to configure Vault&amp;rsquo;s state as code. This allowed me to declaratively define my Kubernetes authentication roles, policies, and secret engines.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Challenge:&lt;/strong&gt; Learning to manage persistent state in Kubernetes was a huge learning curve. Stateful workloads like Vault, n8n, and PostgreSQL require robust Persistent Volume Claims (PVCs). At one point, my &lt;code&gt;vault-0&lt;/code&gt; pod entered a failure state. The &amp;ldquo;junior developer&amp;rdquo; instinct is to simply delete the PVC, tear the Vault down, and rebuild it from scratch. However, in an enterprise environment with thousands of production secrets, deleting the database is catastrophic! Instead of wiping it, I architected a proper recovery process: safely restarting the pod, maintaining the PVC integrity, and gracefully unsealing the vault using my Shamir key shares.&lt;/p&gt;
&lt;h2 id="eso"&gt;5. Bridging the Gap: External Secrets Operator (ESO)&lt;/h2&gt;
&lt;p&gt;While Vault securely stores the credentials, my stateful workloads (n8n and Postgres) still need a way to read them without hardcoding Vault API logic into the application containers.&lt;/p&gt;
&lt;p&gt;I implemented the &lt;strong&gt;External Secrets Operator (ESO)&lt;/strong&gt; to act as an automated courier. It authenticates with Vault, reads the secure payload, and dynamically injects it into a standard native Kubernetes Secret.&lt;/p&gt;
&lt;p&gt;Here we define two distinct &lt;code&gt;ExternalSecret&lt;/code&gt; manifests in a single multi-document YAML file. The first manifest pulls static database credentials, while the second pulls an ephemeral GitHub token:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 1. Manifest for static n8n database credentials&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;external-secrets.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ExternalSecret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;n8n-creds-sync&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;whatsapp-bot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;refreshInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1h&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secretStoreRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vault-backend-global&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ClusterSecretStore&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bot-secrets&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DB_POSTGRESDB_USER&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remoteRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;n8n/config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;DB_POSTGRESDB_PASSWORD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remoteRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;n8n/config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;N8N_ENCRYPTION_KEY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remoteRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;n8n/config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ENCRYPTION_KEY&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# 2. Manifest for dynamic GitHub Token&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;external-secrets.io/v1beta1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ExternalSecret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github-token-sync&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;whatsapp-bot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;refreshInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;45m&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;secretStoreRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;vault-github-backend&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;SecretStore&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github-auth-secret&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;remoteRef&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github-app/token/whatsapp-bot&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;property&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;token&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Because this &lt;code&gt;ExternalSecret&lt;/code&gt; manifest contains no actual passwords (only reference pointers to Vault), it is perfectly safe to commit to GitHub and deploy via ArgoCD. The n8n and Postgres pods then consume &lt;code&gt;pg-secret-live&lt;/code&gt; natively.&lt;/p&gt;
&lt;p&gt;Here is the verification from the cluster showing the ExternalSecret successfully synchronized with Vault, proving the ephemeral credential pipeline is fully functional:&lt;/p&gt;
&lt;p&gt;
&lt;figure &gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="ESO SecretSynced Status"
srcset="https://example.com/blog/stage-2-automation-zero-trust/eso-proof_hu_547b9c035fee72b5.webp 320w, https://example.com/blog/stage-2-automation-zero-trust/eso-proof_hu_5571c2a63559a7fd.webp 480w, https://example.com/blog/stage-2-automation-zero-trust/eso-proof_hu_d0498f824edfd9cf.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://example.com/blog/stage-2-automation-zero-trust/eso-proof_hu_547b9c035fee72b5.webp"
width="760"
height="235"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id="outcomes"&gt;6. Stage II Outcomes&lt;/h2&gt;
&lt;p&gt;By the end of Phase 6, the cluster achieved a true DevSecOps deployment pipeline:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero Manual Intervention:&lt;/strong&gt; Code pushed to Git is built by Actions, and automatically synchronized by ArgoCD.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Persistent &amp;amp; Stateful:&lt;/strong&gt; n8n, PostgreSQL, and Vault are securely backed by PVCs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero-Trust Credentials:&lt;/strong&gt; No sensitive data exists in the repository. Configuration is handled declaratively via OpenTofu, and credentials are injected ephemerally by Vault and ESO.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With the deployment engine automated and secured, the cluster is ready to be exposed to the internet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Next up:&lt;/strong&gt; In
, I cover how I secured the edge perimeter using &lt;strong&gt;Cloudflare Tunnels&lt;/strong&gt; and established full-stack observability with &lt;strong&gt;Prometheus and Grafana&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Cloud Native Architecture: Zero-Trust Bare Metal Kubernetes</title><link>https://example.com/projects/whatsapp-chatbot/</link><pubDate>Wed, 28 Jan 2026 00:00:00 +0000</pubDate><guid>https://example.com/projects/whatsapp-chatbot/</guid><description>&lt;p&gt;An ongoing, 10-phase infrastructure build demonstrating modern DevSecOps principles. This active laboratory project is transforming bare-metal hardware into a highly available, self-healing, and secure Kubernetes environment using GitOps methodologies.&lt;/p&gt;
&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;I wanted to move beyond simple cloud provider tutorials and understand how the underlying compute layers actually work. I engineered this cluster to enforce zero-trust security and eliminate manual configuration drift, proving that enterprise-grade automation can be built from bare metal using virtualization, secure tunneling, and GitOps.&lt;/p&gt;
&lt;h2 id="infrastructure-capabilities"&gt;Infrastructure Capabilities&lt;/h2&gt;
&lt;h3 id="1-virtualized-compute--zero-trust-access"&gt;1. Virtualized Compute &amp;amp; Zero-Trust Access&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;KVM Hypervisor&lt;/strong&gt; - Ubuntu guest virtual machine running efficiently on a bare-metal Kali Linux host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tailscale Mesh VPN&lt;/strong&gt; - Hardened, zero-trust SSH access tunnel completely isolating the host from the public internet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Declarative Provisioning&lt;/strong&gt; - Utilizing OpenTofu to dynamically provision and manage the infrastructure state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-container-orchestration--networking"&gt;2. Container Orchestration &amp;amp; Networking&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;K3s Orchestration&lt;/strong&gt; - Lightweight, highly available Kubernetes deployment.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dynamic Ingress&lt;/strong&gt; - Traefik configured as the primary ingress controller to manage robust routing and load balancing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-cicd--gitops"&gt;3. CI/CD &amp;amp; GitOps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Continuous Integration&lt;/strong&gt; - GitHub Actions automates the building and pushing of Docker images for immutable rollbacks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ArgoCD Synchronization&lt;/strong&gt; - Cluster state is bound directly to the Git repository using Helm and Kustomize.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero Manual Drift&lt;/strong&gt; - ArgoCD automatically detects and overwrites any manual changes, enforcing strict GitOps compliance.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-secret-management"&gt;4. Secret Management&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HashiCorp Vault&lt;/strong&gt; - Centralized, encrypted storage for all sensitive credentials and API keys.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External Secrets Operator (ESO)&lt;/strong&gt; - Dynamically injects Vault secrets directly into Kubernetes pods.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="system-architecture"&gt;System Architecture&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;┌─────────────────┐ (Builds Docker Image) ┌───────────────────────┐
│ GitHub Actions │────────────────────────────▶│ Container Registry │
│ (CI Pipeline) │ │ (GHCR / Hub) │
└─────────────────┘ └───────────────────────┘
│ │
│ (Updates Manifests) │ (Pulls Image)
▼ ▼
┌──────────────┐ ┌───────────────┐ ┌───────────────────────┐
│ │ │ │ │ Kubernetes (K3s) │
│ Git Repo │────▶│ ArgoCD │────▶│ ┌───────────────────┐ │
│ (Manifests) │ │ (Controller) │ │ │ Traefik Ingress │ │
│ │ │ │ │ └───────────────────┘ │
└──────────────┘ └───────────────┘ │ ┌───────────────────┐ │
│ │ k3s-whatsapp-bot │ │
┌──────────────┐ ┌───────────────┐ │ │ (n8n + Postgres) │ │
│ │ │ External │ │ └───────────────────┘ │
│ HashiCorp │◀────│ Secrets │◀────│ ┌───────────────────┐ │
│ Vault │ │ Operator │ │ │ ESO Injector │ │
│ │ │ │ │ └───────────────────┘ │
└──────────────┘ └───────────────┘ └───────────────────────┘
[ Infrastructure: KVM Ubuntu Guest on Kali Metal | Secured via Tailscale ]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="engineering-outcomes"&gt;Engineering Outcomes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;🚀 &lt;strong&gt;Full CI/CD&lt;/strong&gt;: 100% automated pipeline from code push (GitHub Actions) to cluster synchronization (ArgoCD).&lt;/li&gt;
&lt;li&gt;🔒 &lt;strong&gt;Security&lt;/strong&gt;: Host isolated via Tailscale; zero hardcoded secrets via Vault and ESO.&lt;/li&gt;
&lt;li&gt;📉 &lt;strong&gt;Configuration Drift&lt;/strong&gt;: Reduced to 0% through strict ArgoCD reconciliation loops.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="technical-deep-dives-architecture-series"&gt;Technical Deep Dives (Architecture Series)&lt;/h2&gt;
&lt;p&gt;To explore the raw code, YAML manifests, and how I solved specific architectural challenges across the lifecycle, read my detailed engineering write-ups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📝 &lt;strong&gt;
&lt;/strong&gt; - &lt;em&gt;Deep dive into Phases 1-4: Virtualizing Ubuntu on Kali via KVM, Tailscale SSH tunnels, and OpenTofu provisioning.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;📝 &lt;strong&gt;
&lt;/strong&gt; - &lt;em&gt;Deep dive into Phases 5-6: Docker builds via GitHub Actions, Helm/ArgoCD drift elimination, and Vault/ESO injection.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;📝 &lt;strong&gt;
&lt;/strong&gt; (Coming Soon) - &lt;em&gt;Deep dive into Phases 7-8: Cloudflare Tunnels and Prometheus/Grafana telemetry.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;📝 &lt;strong&gt;
&lt;/strong&gt; (Coming Soon) - &lt;em&gt;Deep dive into Phases 9-10: Executing automated attack paths against the cluster and Building a SIEM alerting pipeline.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-10-phase-engineering-roadmap"&gt;The 10-Phase Engineering Roadmap&lt;/h2&gt;
&lt;p&gt;This cluster is designed as a living DevSecOps laboratory. I am currently executing Phase 7 of a comprehensive, capability-driven lifecycle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stage I: The Compute Foundation (✅ Completed)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 1: Base Hypervisor&lt;/strong&gt; - Bare-metal Kali Linux hosting KVM.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 2: Virtualization &amp;amp; Access&lt;/strong&gt; - OpenTofu provisioning of the Ubuntu guest and zero-trust Tailscale SSH tunneling.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 3: Container Orchestration&lt;/strong&gt; - High-availability K3s cluster bootstrapping.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 4: Edge Routing&lt;/strong&gt; - Dynamic ingress and load balancing via Traefik.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Stage II: Automation &amp;amp; Zero-Trust (✅ Completed)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 5: CI/CD Pipeline&lt;/strong&gt; - GitHub Actions building Docker images and ArgoCD/Helm synchronizing the GitOps state.&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Phase 6: Ephemeral Secrets&lt;/strong&gt; - Zero-trust credential injection via HashiCorp Vault and ESO.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Stage III: Perimeter Defense &amp;amp; Observability (⏳ In Progress)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⏳ &lt;strong&gt;Phase 7: Zero-Trust Perimeter&lt;/strong&gt; - Integrating Cloudflare Tunnels for unexposed, secure ingress.&lt;/li&gt;
&lt;li&gt;⏳ &lt;strong&gt;Phase 8: Full-Stack Telemetry&lt;/strong&gt; - Deploying Prometheus &amp;amp; Grafana for cluster observability.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Stage IV: Purple Teaming Laboratory (📅 Planned)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;📅 &lt;strong&gt;Phase 9: Offensive Simulation (Red)&lt;/strong&gt; - Executing automated attack paths against the cluster to validate resilience.&lt;/li&gt;
&lt;li&gt;📅 &lt;strong&gt;Phase 10: Threat Detection (Blue)&lt;/strong&gt; - Building a SIEM alerting pipeline to capture the offensive testing telemetry.&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>