<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Heir Blog</title>
        <link>https://tardigradeproj.github.io/docs/blog</link>
        <description>Heir Blog</description>
        <lastBuildDate>Tue, 13 Oct 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Heir v1.36.5-heirv0.2.0]]></title>
            <link>https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0</link>
            <guid>https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0</guid>
            <pubDate>Tue, 13 Oct 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Heir v1.36.5-heirv0.2.0 is out, tracking Kubernetes v1.36.5. The headline change is how addons work: CoreDNS and the Flannel CNI are no longer]]></description>
            <content:encoded><![CDATA[<p>Heir v1.36.5-heirv0.2.0 is out, tracking Kubernetes v1.36.5. The headline change is how addons work: CoreDNS and the Flannel CNI are no longer
rendered from bespoke <code>Runtime</code> fields, they're Helm charts you can disable, override, or replace, alongside any other chart you want installed
at bootstrap. Backing that is a new component, <code>clusteragent</code>, which now owns everything that has to act against the cluster itself.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="helm-based-cluster-addons">Helm-based cluster addons<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#helm-based-cluster-addons" class="hash-link" aria-label="Direct link to Helm-based cluster addons" title="Direct link to Helm-based cluster addons" translate="no">​</a></h2>
<p>Up to this release, addons like CoreDNS and the CNI plugin were provisioned from manifests rendered by the control plane, which meant the
<code>Runtime</code> CRD grew a bespoke, typed field for every setting each one needed. That doesn't scale past a handful of addons, so it's been replaced
with a <a class="" href="https://tardigradeproj.github.io/docs/addon/helm-controller">Helm controller</a> instead. <code>spec.cluster.addons.helm</code> now lets a <code>Runtime</code> manage its own addons:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">cluster</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">addons</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token key atrule" style="color:#00a4db">helm</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token key atrule" style="color:#00a4db">charts</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> coredns</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">enabled</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cilium</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">install</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">chart</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> cilium</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">repo</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> https</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain">//helm.cilium.io/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">version</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.15.0"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> metrics</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">server</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token key atrule" style="color:#00a4db">override</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">              </span><span class="token key atrule" style="color:#00a4db">values</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token key atrule" style="color:#00a4db">content</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c">                  replicas: 2</span><br></div></code></pre></div></div>
<p>Use <code>enabled: false</code> to drop one of Heir's built-in addons, <code>install</code> to add an entirely new chart to the bootstrap phase, or <code>override</code> to
tweak a built-in addon's Helm values without touching its chart source. Each entry is keyed by <code>name</code>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-dedicated-clusteragent">A dedicated clusteragent<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#a-dedicated-clusteragent" class="hash-link" aria-label="Direct link to A dedicated clusteragent" title="Direct link to A dedicated clusteragent" translate="no">​</a></h2>
<p>Everything that needs to reach into the <em>tenant</em> cluster, rather than just manage the management cluster's view of it, now lives in its own
component: <code>clusteragent</code>. That's worker join tokens, kubelet-serving CSR approval, Kubernetes Service <code>EndpointSlice</code> sync, and the new Helm
addon/<code>HelmChart</code> reconciler described above. Splitting it out keeps the management-cluster controller manager focused on the management cluster (guarantee that the cluster is healthy),
and gives the tenant-facing half its own image, CRDs, RBAC, and release cadence.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workerjointoken-and-helmchart-move-clusters">WorkerJoinToken and HelmChart move clusters<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#workerjointoken-and-helmchart-move-clusters" class="hash-link" aria-label="Direct link to WorkerJoinToken and HelmChart move clusters" title="Direct link to WorkerJoinToken and HelmChart move clusters" translate="no">​</a></h2>
<p>This is the release's breaking change: <code>WorkerJoinToken</code> and <code>HelmChart</code> moved out of the <code>controlplane</code> API group into a new one,
<code>clusteragent.tardigrade.runtime.io/v1alpha1</code>, and are now reconciled against the <em>tenant</em> cluster, not the management cluster. <code>WorkerJoinToken</code>
is also cluster-scoped now, and no longer carries a <code>runtimeRef</code>, it's always bound to the cluster it's created against, so there's nothing left
to point at:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> clusteragent.tardigrade.runtime.io/v1alpha1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> WorkerJoinToken</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> new</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">node</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ttl</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 3h</span><br></div></code></pre></div></div>
<p>If you have any tooling that applies or reads either resource under the old <code>controlplane</code> group, or against the management cluster's
kubeconfig, it needs to target the new group and the tenant's own kubeconfig instead. See the updated
<a class="" href="https://tardigradeproj.github.io/docs/configuration-options/worker-join-token">WorkerJoinToken</a> reference and <a class="" href="https://tardigradeproj.github.io/docs/installation/quickstart">Quickstart</a> for the new shape end-to-end.</p>
<p>The typed <code>cluster.network.coredns</code> and <code>cluster.network.cni</code> <code>Runtime</code> fields are gone as a consequence, both are provisioned exclusively
through <code>spec.cluster.addons.helm.charts</code> now, and if you were using the previous map-keyed <code>spec.cluster.addons</code> shape, it's been restructured
into the <code>charts</code> list shown above.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cli-cleanup">CLI cleanup<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#cli-cleanup" class="hash-link" aria-label="Direct link to CLI cleanup" title="Direct link to CLI cleanup" translate="no">​</a></h2>
<p><code>heir build</code> and <code>heir provision controlplane</code> have been removed. They were superseded a while ago by the goreleaser/Dockerfile image pipeline
and the <code>Runtime</code> CRD controller respectively, and had no remaining callers. <code>heir delete controlplane</code> is unaffected. Separately, if you import
Heir's Go packages directly: <code>api/v1alpha1</code> and <code>internal/controller</code> moved to <code>api/controlplane/v1alpha1</code> and
<code>internal/controller/controlplane</code> as part of migrating to kubebuilder's multi-group layout, which is what unblocked adding the <code>clusteragent</code>
group in the first place.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="fixes-worth-knowing-about">Fixes worth knowing about<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#fixes-worth-knowing-about" class="hash-link" aria-label="Direct link to Fixes worth knowing about" title="Direct link to Fixes worth knowing about" translate="no">​</a></h2>
<ul>
<li class=""><strong>PKI SAN rotation now rolls control-plane and plane-tunnel pods.</strong> Previously only the PKI Secret was regenerated; running pods kept serving
stale certificates until something else happened to restart them.</li>
<li class=""><strong><code>WorkerJoinToken</code> no longer rewrites its status on every reconcile</strong>, which had been bumping <code>resourceVersion</code> and re-triggering its own
watch in a tight loop.</li>
<li class=""><strong>containerd and its containers now survive a <code>heir</code> restart.</strong> The generated systemd unit used <code>KillMode=control-group</code>, which signalled
every process in heir's cgroup, containerd, shims, every running container, on stop or restart. It's now <code>KillMode=process</code> with
<code>Delegate=yes</code>, so only heir's own PID is signalled and kubelet/containerd keep managing their own per-pod cgroups.</li>
<li class="">Removed kubelet's <code>--runtime-cgroups</code> flag, which pointed at a <code>containerd.service</code> cgroup that never exists under heir's process model.</li>
<li class=""><code>heir provision worker</code> no longer fails with <code>open ... text file busy</code> when re-run against an already-joined node.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="containerd-config-schema-v4">containerd config schema v4<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#containerd-config-schema-v4" class="hash-link" aria-label="Direct link to containerd config schema v4" title="Direct link to containerd config schema v4" translate="no">​</a></h2>
<p>The generated containerd config migrated to schema v4, a requirement from containerd 2.3+, which splits the CRI plugin into separate
images/runtime plugins. The generated <code>config.toml</code> also now imports <code>/etc/lib/heir/containerd/conf.d/*.toml</code>, so you can layer extra config,
a registry mirror, for example, in a separate file that survives <code>heir provision worker</code> regenerating the main file on every run.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-development-with-docker-compose">Local development with docker-compose<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.36.5-heirv0.2.0#local-development-with-docker-compose" class="hash-link" aria-label="Direct link to Local development with docker-compose" title="Direct link to Local development with docker-compose" translate="no">​</a></h2>
<p>Local worker nodes are now <code>docker-compose</code> bootloose containers instead of Vagrant VMs, mirroring the same image, privilege, and cgroup setup
the e2e test helper already used. A new <code>.local/run-locally.sh</code> script drives the whole walkthrough end to end: build, stand up the management
cluster, deploy the controller manager, provision a tenant <code>Runtime</code>, wait for <code>clusteragent</code> to come up, mint and deliver join tokens, join both
<code>dev-worker0</code> and <code>dev-worker1</code>, and confirm both reach <code>Ready</code>.</p>]]></content:encoded>
            <category>Heir</category>
            <category>Kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Heir v1.35.7-heirv0.1.0]]></title>
            <link>https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0</link>
            <guid>https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0</guid>
            <pubDate>Mon, 10 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Heir v1.35.7-heirv0.1.0 is out. It tracks Kubernetes v1.35.7 and is the first release you can install with Helm: the control-plane]]></description>
            <content:encoded><![CDATA[<p>Heir v1.35.7-heirv0.1.0 is out. It tracks Kubernetes v1.35.7 and is the first release you can install with Helm: the control-plane
Operator now ships as a proper chart, worker join tokens are a Kubernetes resource instead of a CLI command, and Kubernetes Konnectivity
has been replaced end-to-end by Heir's own implementation (plane tunnel).</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="install-the-operator-with-helm">Install the Operator with Helm<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#install-the-operator-with-helm" class="hash-link" aria-label="Direct link to Install the Operator with Helm" title="Direct link to Install the Operator with Helm" translate="no">​</a></h2>
<p>Heir is moving from a CLI-driven workflow to an Operator-driven one, and this release is the first concrete step, not just a change in how you
happen to invoke commands. The wider vision is for every piece of cluster lifecycle, certificate renewal, control plane upgrades, worker node
upgrades, installing addons, and more, to be driven declaratively: you describe desired state in a CRD, and a controller reconciles it, the same
model Kubernetes already uses for everything else it manages. That's also the direction that leads toward adopting
<a href="https://cluster-api.sigs.k8s.io/" target="_blank" rel="noopener noreferrer" class="">Cluster API</a> (CAPI) as the machinery behind that lifecycle management, rather than Heir maintaining its own
bespoke imperative path alongside a growing declarative one.</p>
<p>This release's slice of that shift: previously, standing up a Heir cluster meant running the CLI directly against a management cluster. As of this
release, the recommended path is to install the Operator chart and let it reconcile <code>Runtime</code> and <code>WorkerJoinToken</code> resources you apply with
<code>kubectl</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">helm install heir oci://ghcr.io/tardigradeproj/charts/heir --version 1.35.7-heirv0.1.0 \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -n heir-system --create-namespace \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --set manager.image.repository=ghcr.io/tardigradeproj/heir-controller-manager \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --set manager.image.tag=v1.35.7-heirv0.1.0</span><br></div></code></pre></div></div>
<p>The chart covers the controller Deployment, RBAC helper roles, CRD install/keep behavior, the metrics endpoint, and optional cert-manager and Prometheus
ServiceMonitor integration. See the <a class="" href="https://tardigradeproj.github.io/docs/installation/quickstart">updated Quickstart</a> for the full walkthrough, and the
<a class="" href="https://tardigradeproj.github.io/docs/configuration-options/chart">Helm Chart reference</a> for every value it accepts.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="plane-tunnel-replaces-konnectivity">Plane Tunnel replaces Konnectivity<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#plane-tunnel-replaces-konnectivity" class="hash-link" aria-label="Direct link to Plane Tunnel replaces Konnectivity" title="Direct link to Plane Tunnel replaces Konnectivity" translate="no">​</a></h2>
<p>Heir no longer depends on the Kubernetes Konnectivity proxy to reach worker nodes. Konnectivity assumes the API server is a fixed,
singleton process: each <code>konnectivity-agent</code> holds a persistent tunnel to every <code>konnectivity-server</code> replica, so total connections grow
as N×M, adding a replica forces every existing agent to reconnect before traffic can reach it, and <code>--server-count</code> has to be kept in
sync with the actual replica count by hand. Heir runs the API server as a replicated, horizontally-scalable Deployment, an assumption
Konnectivity was never built for.</p>
<p>The plane tunnel keeps the same N×M connection shape, one multiplexed TCP tunnel per worker per replica, but removes the coordination
problems around it:</p>
<ul>
<li class=""><strong>Every agent dials every replica</strong>, not a routing table's worth. The agent resolves the plane tunnel's headless Service via DNS
(<code>--replica-discovery-dns</code>), learns the current replica count, and keeps that many tunnels open, reconnecting as replicas come and go.</li>
<li class=""><strong>No wrong-replica scenario.</strong> Because every replica already holds a tunnel to every worker, any replica can serve any request.</li>
<li class=""><strong>Scaling out doesn't drop existing tunnels.</strong> A new replica just adds a DNS record; agents pick it up on their next reconnect and
open one more tunnel without disturbing the others. There's no <code>--server-count</code> to keep in sync.</li>
</ul>
<p>Each worker's tunnel is authenticated with mutual TLS, using the node's <code>system:node:&lt;name&gt;</code> client certificate, verified against the
cluster CA, so the plane tunnel knows exactly which worker owns a tunnel with no separate identity protocol. Routing a request
(<code>kubectl exec</code>, log streaming, or the API server's own reach into a kubelet) works the same way it does today with Konnectivity's
egress selector: the API server issues an HTTP <code>CONNECT worker2:10250</code> over its own mTLS leg to the plane tunnel Service, any replica
accepts it, looks up the already-open agent tunnel for <code>worker2</code>, and dials the kubelet through it. The plane tunnel never terminates
the <em>inner</em> TLS session between the API server and the kubelet, it just splices bytes between the two legs, so certificate verification
on both ends behaves exactly as it would over a direct connection. Full design details, including the egress selector configuration,
live in <code>docs/plane-tunnel.md</code> in the controller repo.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-this-exists-beyond-replacing-konnectivity">Why this exists beyond replacing Konnectivity<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#why-this-exists-beyond-replacing-konnectivity" class="hash-link" aria-label="Direct link to Why this exists beyond replacing Konnectivity" title="Direct link to Why this exists beyond replacing Konnectivity" translate="no">​</a></h3>
<p>Konnectivity only ever solved one problem: letting the API server reach a kubelet. The plane tunnel is built to solve that same class of
problem for everything else Heir wants to run centrally, not just <code>kube-apiserver</code>. Every worker already holds one multiplexed
connection into the management cluster; anything that needs to talk to something on that worker can register itself as another upstream
on the same connection instead of asking for its own inbound port.</p>
<p>That's the actual reason it changed: it's the foundation for keeping Heir's "run control plane components centrally" philosophy
consistent.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="multiplexing-with-outbound-not-http2">Multiplexing with <code>outbound</code>, not HTTP/2<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#multiplexing-with-outbound-not-http2" class="hash-link" aria-label="Direct link to multiplexing-with-outbound-not-http2" title="Direct link to multiplexing-with-outbound-not-http2" translate="no">​</a></h3>
<p>The plane tunnel's transport is <a href="https://github.com/tardigradeproj/outbound" target="_blank" rel="noopener noreferrer" class=""><code>outbound</code></a>, a Go fork of HashiCorp's <code>yamux</code> that Heir
maintains. <code>outbound</code> multiplexes streams at the transport layer, framing them directly over a raw TCP connection with a 13-byte
header, instead of layering multiplexing inside HTTP/2's request/response semantics. That makes it cheaper than HTTP/2 for this job: no
HTTP stack to run on either end, no request/response framing to pay for, just streams over a socket.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="closing-a-real-allocation-leak-in-the-tunnels-copy-path">Closing a real allocation leak in the tunnel's copy path<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#closing-a-real-allocation-leak-in-the-tunnels-copy-path" class="hash-link" aria-label="Direct link to Closing a real allocation leak in the tunnel's copy path" title="Direct link to Closing a real allocation leak in the tunnel's copy path" translate="no">​</a></h3>
<p><code>outbound</code>'s <code>Tunnel</code> proxies data between an agent stream and the real upstream connection with a small bidirectional pipe that used to
call <code>io.Copy(dst, src)</code>. <code>net.TCPConn</code> implements <code>io.ReaderFrom</code>/<code>io.WriterTo</code>, so <code>io.Copy</code> silently skipped its own pooled buffer and
allocated a fresh scratch buffer per direction on every proxied connection. The old <code>io.Copy</code> path was using 2.4x more memory than the
pooled version.</p>
<p>The fix, <a href="https://github.com/tardigradeproj/outbound" target="_blank" rel="noopener noreferrer" class=""><code>outbound</code></a> commit <code>4522a0b</code>, pools those buffers in a <code>sync.Pool</code> and hides
<code>net.TCPConn</code>'s fast-path interfaces so <code>io.CopyBuffer</code> is forced to actually use them:</p>
<div class="language-go codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-go codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> copyBufferPool </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> sync</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Pool</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	New</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">func</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> any </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> buf </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">make</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token builtin">byte</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32</span><span class="token operator" style="color:#393A34">*</span><span class="token number" style="color:#36acaa">1024</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;</span><span class="token plain">buf </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">func</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">tunnelPipe</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">a</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> b net</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Conn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	pipe </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">func</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">dst</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> src net</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Conn</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		bufp </span><span class="token operator" style="color:#393A34">:=</span><span class="token plain"> copyBufferPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">*</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token builtin">byte</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">defer</span><span class="token plain"> copyBufferPool</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">Put</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bufp</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token boolean" style="color:#36acaa">_</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">_</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> io</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">CopyBuffer</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">onlyWriter</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">dst</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> onlyReader</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">src</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain">bufp</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><code>go tool pprof -top mem.out</code> confirms it: <code>io.copyBuffer</code> now shows up going through the pool, and the rest of the profile is ordinary
<code>Session</code>/<code>Stream</code>/TLS bookkeeping, not a scratch buffer reallocated on every connection.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-declarative-way-to-join-workers">A declarative way to join workers<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#a-declarative-way-to-join-workers" class="hash-link" aria-label="Direct link to A declarative way to join workers" title="Direct link to A declarative way to join workers" translate="no">​</a></h2>
<p><code>heir token generate</code> is now backed by a <code>WorkerJoinToken</code> custom resource. Apply one scoped to a <code>Runtime</code>, and the controller mints a
one-shot bootstrap token and kubeconfig, publishes it as an owned <code>&lt;name&gt;-jointoken</code> Secret, revokes it on deletion, and re-issues it if
the Secret is ever edited or deleted out from under it:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apiVersion</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> controlplane.tardigrade.runtime.io/v1alpha1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">kind</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> WorkerJoinToken</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">metadata</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> new</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">node</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">namespace</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> default</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">spec</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">runtimeRef</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token key atrule" style="color:#00a4db">name</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> heir</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">quickstart</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token key atrule" style="color:#00a4db">ttl</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> 3h</span><br></div></code></pre></div></div>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">JOIN_TOKEN=$(kubectl get secret new-node-jointoken -o jsonpath='{.data.jointoken}')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo heir provision worker --token="$JOIN_TOKEN"</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-redesigned-runtime-api">A redesigned Runtime API<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#a-redesigned-runtime-api" class="hash-link" aria-label="Direct link to A redesigned Runtime API" title="Direct link to A redesigned Runtime API" translate="no">​</a></h2>
<p>The <code>Runtime</code> spec picked up a round of cleanup this release, all reflected in the <a class="" href="https://tardigradeproj.github.io/docs/configuration-options/runtime">Configuration Options</a>
reference and the Quickstart:</p>
<ul>
<li class=""><code>upstreamCluster</code> is now <code>cluster</code></li>
<li class="">The shared <code>controlPlaneEndpoint.addresses</code> list is gone. The API server and plane tunnel each get their own <code>host</code> under
<code>cluster.controlPlaneExternalEndpoint</code>, since they can now sit behind different IPs</li>
<li class=""><code>ControlPlaneEndpointSpec</code> is renamed <code>ControlPlaneExternalEndpointSpec</code></li>
<li class="">The plane tunnel's NodePort moved out of the shared service spec into <code>spec.controlPlane.planeTunnel.service</code></li>
<li class=""><code>RegisterSetting</code> is renamed <code>RegistrySettings</code> on the CoreDNS and kube-proxy specs</li>
<li class=""><code>ComponentEndpoint.Host</code> is now validated with a CEL rule requiring a bare hostname or IPv4 address, since it's formatted directly into
a cert SAN</li>
</ul>
<p>This is a breaking change to the <code>Runtime</code> spec. If you're upgrading an existing cluster, update your manifests before applying this
version's CRDs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sharper-certificate-and-status-visibility">Sharper certificate and status visibility<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#sharper-certificate-and-status-visibility" class="hash-link" aria-label="Direct link to Sharper certificate and status visibility" title="Direct link to Sharper certificate and status visibility" translate="no">​</a></h2>
<p><code>Runtime.status</code> reports per-certificate expiry now, one entry per PKI cert (<code>ca</code>, <code>apiserver</code>, <code>serviceaccount</code>, <code>planetunnel</code>,
<code>apiserver-planetunnel</code>, plus the scheduler and controller-manager client certs) instead of a single <code>certificatesExpireAt</code> field, and
carries <code>observedGeneration</code> so you can tell whether status reflects the latest spec. <code>WorkerJoinToken</code> also gained printer columns, so
<code>kubectl get workerjointoken</code> gives you the owning <code>Runtime</code>, readiness, reason, and expiry at a glance.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="under-the-hood">Under the hood<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#under-the-hood" class="hash-link" aria-label="Direct link to Under the hood" title="Direct link to Under the hood" translate="no">​</a></h2>
<ul>
<li class="">The <code>heir-controller-manager</code> image is now built by goreleaser, the same pipeline that builds <code>heir</code> and <code>heir-tunnel</code>, instead of a
one-off Dockerfile</li>
<li class="">Dependency versions (Kubernetes, containerd, runc, crictl, CNI, Calico) are centralized in <code>.dependencies-version.yaml</code>; this release
bumps Kubernetes to v1.35.7 and crictl to v1.35.0 to match</li>
<li class="">goreleaser now supports <code>-rc</code> prerelease tags, which is how <code>v1.35.7-heirv0.1.0-rc1</code> shipped ahead of this stable cut</li>
<li class="">End-to-end tests build real binaries and provision an actual Runtime and worker join, run in CI via shared, de-duplicated
build-toolchain GitHub Actions</li>
<li class=""><code>docs/running-locally.md</code> was rewritten as a full local-dev walkthrough and linked from the README and contributing guide</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="upgrading">Upgrading<a href="https://tardigradeproj.github.io/docs/blog/heir-v1.35.7-heirv0.1.0#upgrading" class="hash-link" aria-label="Direct link to Upgrading" title="Direct link to Upgrading" translate="no">​</a></h2>
<p>Because the <code>Runtime</code> API changed shape, treat this as a breaking release: re-apply CRDs, update any <code>Runtime</code> manifests using the old
field names, and switch worker provisioning over to <code>WorkerJoinToken</code>. The <a class="" href="https://tardigradeproj.github.io/docs/installation/quickstart">Quickstart</a> and
<a class="" href="https://tardigradeproj.github.io/docs/configuration-options/runtime">Configuration Options</a> docs are fully updated for v1.35.7-heirv0.1.0 and are the fastest way to see the
new shape end-to-end.</p>]]></content:encoded>
            <category>Heir</category>
            <category>Kubernetes</category>
        </item>
        <item>
            <title><![CDATA[Why HEIR?]]></title>
            <link>https://tardigradeproj.github.io/docs/blog/why-heir</link>
            <guid>https://tardigradeproj.github.io/docs/blog/why-heir</guid>
            <pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[I can bet you are reading this article from a device with a bright display, memory, a processor, and an]]></description>
            <content:encoded><![CDATA[<p>I can bet you are reading this article from a device with a bright display, memory, a processor, and an
energy source. That device is connected to the internet, a global web of undersea cables, data centers,
routing protocols, and radio frequencies working in concert so that these words could travel from a server
somewhere in the world and render, pixel by pixel, on your screen. Before you even read the first sentence, a
browser had to interpret code, an operating system had to manage resources, and hardware built from silicon mined
from the earth had to execute billions of operations per second.</p>
<!-- -->
<p>If you try to do an exercise of understanding the details of each piece that makes reading this article possible, you
would need to rediscover the whole world, and it would take you more than 1,000 lifetimes. This means one thing:
we don't understand 99.999% of the things that we interact with on a daily basis, but still, we trust them with our lives.
Some are so well-designed that there are ones who may think that they are features that the world came with from the
start. And there's nothing wrong with that, quite the opposite, that's how communities are supposed to function.</p>
<p>The interesting point is that these technologies didn't appear out of nowhere; they started from nothing, from
imprecise beginnings, and over time became reliable because groups of people worked and designed them in a way that
made them trustworthy. And if there hadn't been other groups of people to pick up where the last group left off,
the technology would have died, and we would have collapsed as a society.</p>
<p>This is what we want to celebrate with Heir: a world in which you don't need to know everything, a society where
each generation doesn't need to rediscover the world, a world of heritage.</p>
<p>Heir is not the last Kubernetes distribution. Kubernetes is not the last container orchestration tool.
Containers are not the last Linux namespace abstraction. Linux is not the last operating system. x86 and ARM
are not the last CPU architectures. Transistors are not the last switching mechanism. Silicon is not the
last material. Nothing is good enough to be considered final, everything is just a bridge to what comes next,
something bigger and better.</p>]]></content:encoded>
            <category>Heir</category>
            <category>Open Source</category>
        </item>
    </channel>
</rss>