<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Alek&#x27;s Blog - infrastructure</title>
      <link>https://blog.none.at</link>
      <description>My Blog to share my knowledge</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://blog.none.at/categories/infrastructure/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Thu, 09 Jul 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Sovereign-Cloud-Washing: Five Questions</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereign-cloud-washing/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereign-cloud-washing/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereign-cloud-washing/">&lt;p&gt;“Sovereign cloud” is printed on marketing pages from AWS, Microsoft, Google, and a long list of
European providers alike. The label rarely comes with a definition, which makes it easy to satisfy
on paper and hard to verify in practice. I’ve put together five concrete questions, asked of the
claim itself instead of the label — enough to see, from several angles, what a provider actually
stands behind.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Who Has Access? Humans, Accounts, AI Agents</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-access-model/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-access-model/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-access-model/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereign-cloud-washing&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; split the technology-supply-chain
question into two halves: what the platform is built on, and who — or what — actually has access to
it. &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereignty-technology-stack&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; answered the first half. This
post answers the second, using systems already built and operated for this blog rather than new
research — human access, service-account access, and (increasingly relevant) AI-agent access, all
checked against the same bar: is it documented, is it audited, and is it technically enforced or
just expected to be followed.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Legally vs. Technically Enforced</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-enforcement-framework/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-enforcement-framework/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-enforcement-framework/">&lt;p&gt;The same distinction has come up three times already in this series without being named directly:
BYOK in &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereign-cloud-washing&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt;, two vendors’ own statements in
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereignty-technology-stack&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt;, and Kubernetes RBAC against a
written &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; rule in &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereignty-access-model&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt;. This post
makes it explicit, adds a third rung most sovereignty marketing doesn’t reach yet, and applies it
across everything the series has found so far — no new research, just naming a pattern that kept
repeating.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Can You Leave? Data Portability &amp; Egress</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-exit-path/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-exit-path/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-exit-path/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereign-cloud-washing&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; deliberately kept Question 4 — can you
leave — at the general level. This post is the deep-dive: concrete data-export
formats, API and query-language openness, and egress pricing already documented for AWS, GCP,
Azure, and OVH, plus new findings on how the five sovereignty offerings from
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereignty-technology-stack&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; actually hold up on exit.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Digital Sovereignty: The Complete Guide</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-guide/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-guide/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-guide/">&lt;p&gt;This is the index and reading guide for a six-part series built around one idea: “sovereign cloud”
is a marketing label until it’s tested against something specific. Part 1 sets out five concrete
questions to test it against instead of taking the label at face value; Parts 2 through 6 are the
deep-dives that answer each one in turn — grounded, wherever possible, in research or systems
already published elsewhere on this blog rather than written fresh for the series.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>What Does It Cost to Leave — or Arrive?</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-switching-cost/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-switching-cost/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-switching-cost/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereign-cloud-washing&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; deliberately kept Question 5 at the
general level too: on-prem is largely sovereign, but “engineering time re-architecting for a
new platform’s primitives, process changes… and a skills gap no one accounted for” apply whichever
direction you’re moving. This post is that deep-dive — the last of the five, using examples already
built and published rather than new claims about any provider.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Who Builds the Platform? Ownership vs. Stack</title>
          <pubDate>Thu, 09 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-09-sovereignty-technology-stack/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-09-sovereignty-technology-stack/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-09-sovereignty-technology-stack/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-09-sovereign-cloud-washing&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; raised the technology-supply-chain
question with one case: Open Telekom Cloud, legally owned by Deutsche Telekom, running on a
Huawei-licensed platform. Five more prominent, more recent cases turn out to follow the same
pattern — checked here against primary sources, not just repeated from secondary coverage —
alongside three providers where ownership and technology stack come closer to aligning, each with
its own gaps left intact rather than smoothed over.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>k8s-scale-app-rs: Scale or Restart a Kubernetes Deployment from a CronJob</title>
          <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-06-k8s-scale-app-rs/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-06-k8s-scale-app-rs/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-06-k8s-scale-app-rs/">&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;a class=&quot;zola-anchor&quot; href=&quot;#tl-dr&quot; aria-label=&quot;Anchor link for: tl-dr&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;k8s-scale-app-rs&quot;&gt;k8s-scale-app-rs&lt;&#x2F;a&gt; is a Rust CLI that does exactly one of two things and then exits:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;scale&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; — sets a Deployment’s replica count to a fixed value via the &lt;code&gt;deployments&#x2F;scale&lt;&#x2F;code&gt; subresource.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;restart&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; — patches the pod template’s &lt;code&gt;restartedAt&lt;&#x2F;code&gt; annotation, which is the same mechanism &lt;code&gt;kubectl rollout restart&lt;&#x2F;code&gt; uses.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Designed to run as a &lt;code&gt;CronJob&lt;&#x2F;code&gt;. Version 1.0.0 is published; container images and the Helm chart are on &lt;code&gt;ghcr.io&lt;&#x2F;code&gt;, signed with cosign (keyless via GitHub OIDC), and shipped with an SPDX SBOM plus SLSA build provenance.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-i-built-it&quot;&gt;Why I built it&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-i-built-it&quot; aria-label=&quot;Anchor link for: why-i-built-it&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The classic way to “run &lt;code&gt;kubectl scale&lt;&#x2F;code&gt; on a schedule in a cluster” is to put a &lt;code&gt;kubectl&lt;&#x2F;code&gt; binary into a Job image. That works, but it drags in a few things I did not want:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The full &lt;code&gt;kubectl&lt;&#x2F;code&gt; binary (about 50 MB stripped) for one API call.&lt;&#x2F;li&gt;
&lt;li&gt;A kubeconfig &#x2F; ServiceAccount plumbing that is broader than the one operation actually needs.&lt;&#x2F;li&gt;
&lt;li&gt;Enough YAML around the pod spec to hide the actual behavior behind a template.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;A purpose-built tool is a natural fit for Rust here: a single self-contained binary — no OpenSSL, no runtime dependency beyond glibc and the CA trust store — and a ServiceAccount that only needs &lt;code&gt;deployments&#x2F;scale: patch&lt;&#x2F;code&gt;. That is what k8s-scale-app-rs is.&lt;&#x2F;p&gt;
&lt;p&gt;KEDA and Argo Workflows solve a related but different problem — reacting to events or orchestrating multi-step pipelines — not “flip a replica count or a restart annotation on a fixed schedule.” KEDA in particular has its own well-known scale-from-zero gap: a Prometheus-metric trigger can scale 1→N but not 0→1, because a Deployment at zero replicas emits no metric to react to (see &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-observability&#x2F;#keda-autoscaling&quot;&gt;the LLM inference series&lt;&#x2F;a&gt; for the concrete failure mode). None of that machinery applies here — a CronJob calling a purpose-built binary on a fixed schedule has nothing to react to.&lt;&#x2F;p&gt;
&lt;p&gt;The same binary also covers restart. &lt;code&gt;kubectl rollout restart deployment&#x2F;foo&lt;&#x2F;code&gt; under the hood is one patch on &lt;code&gt;spec.template.metadata.annotations[&quot;kubectl.kubernetes.io&#x2F;restartedAt&quot;]&lt;&#x2F;code&gt;. Two subcommands, same binary, same image.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-it-does&quot; aria-label=&quot;Anchor link for: what-it-does&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The CLI is two subcommands sharing common arguments:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;k8s-scale-app-rs scale   --deployment &amp;lt;NAME&amp;gt; --replicas &amp;lt;N&amp;gt; [--dry-run] [--extra-ca-bundle &amp;lt;PATH&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;k8s-scale-app-rs restart --deployment &amp;lt;NAME&amp;gt;                [--dry-run] [--extra-ca-bundle &amp;lt;PATH&amp;gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Configuration is via environment variables, overridable by CLI flags:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Flag&lt;&#x2F;th&gt;&lt;th&gt;ENV&lt;&#x2F;th&gt;&lt;th&gt;Subcommands&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--deployment&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;K8S_SCALE_DEPLOYMENT&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;both&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--namespace&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;K8S_SCALE_NAMESPACE&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;both&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--replicas&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;K8S_SCALE_REPLICAS&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;scale&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--dry-run&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;K8S_SCALE_DRY_RUN&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;both&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;--extra-ca-bundle&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;K8S_SCALE_EXTRA_CA_BUNDLE&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;both&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The &lt;code&gt;--namespace&lt;&#x2F;code&gt; flag exists, but the CronJob does not hardcode it. Instead, the pod reads its own namespace from the Downward API and passes it through as an environment variable. That keeps the ServiceAccount’s Role safely namespace-scoped: the tool can only ever act on Deployments in the namespace where the CronJob itself runs. Not a general-purpose “scale anything anywhere” tool.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;design-highlights&quot;&gt;Design highlights&lt;a class=&quot;zola-anchor&quot; href=&quot;#design-highlights&quot; aria-label=&quot;Anchor link for: design-highlights&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;minimal-rbac-via-the-scale-subresource&quot;&gt;Minimal RBAC via the &lt;code&gt;scale&lt;&#x2F;code&gt; subresource&lt;a class=&quot;zola-anchor&quot; href=&quot;#minimal-rbac-via-the-scale-subresource&quot; aria-label=&quot;Anchor link for: minimal-rbac-via-the-scale-subresource&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Setting replicas does not need &lt;code&gt;patch&lt;&#x2F;code&gt; on the full Deployment. Kubernetes exposes &lt;code&gt;deployments&#x2F;scale&lt;&#x2F;code&gt; as a dedicated subresource:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ules&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;piGroups&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;apps&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;esources&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;deployments&#x2F;scale&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;erbs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;update&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That is all the scale mode needs. Restart mode does need &lt;code&gt;patch&lt;&#x2F;code&gt; on &lt;code&gt;deployments&lt;&#x2F;code&gt; itself (to update the pod template annotation), so the shipped Role covers both:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt; a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;piGroups&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;apps&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    r&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;esources&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;deployments&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;    v&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;erbs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;get&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator&quot;&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;patch&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;patch&lt;&#x2F;code&gt; on the full Deployment is broader than &lt;code&gt;patch&lt;&#x2F;code&gt; on &lt;code&gt;deployments&#x2F;scale&lt;&#x2F;code&gt; — RBAC review at deploy time should factor that in when the CronJob is configured for restart mode.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;extra-ca-bundle-merged-into-the-trust-store&quot;&gt;Extra-CA bundle merged into the trust store&lt;a class=&quot;zola-anchor&quot; href=&quot;#extra-ca-bundle-merged-into-the-trust-store&quot; aria-label=&quot;Anchor link for: extra-ca-bundle-merged-into-the-trust-store&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The kube client uses the in-cluster CA from the ServiceAccount token secret automatically. For clusters whose API server certificate is signed by a corporate CA that is not in the SA-mounted &lt;code&gt;ca.crt&lt;&#x2F;code&gt;, &lt;code&gt;--extra-ca-bundle &amp;lt;path&amp;gt;&lt;&#x2F;code&gt; reads a PEM file (or a full chain of them) and appends every &lt;code&gt;CERTIFICATE&lt;&#x2F;code&gt; block to &lt;code&gt;kube::Config.root_cert&lt;&#x2F;code&gt;. Non-certificate blocks in the file are filtered out.&lt;&#x2F;p&gt;
&lt;p&gt;Rendered in Helm as an optional ConfigMap mount; in Kustomize as an opt-in Component. Neither path forces the mount when there is no corporate CA to add.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-rustls-0-23-cryptoprovider-gotcha&quot;&gt;The rustls 0.23 CryptoProvider gotcha&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-rustls-0-23-cryptoprovider-gotcha&quot; aria-label=&quot;Anchor link for: the-rustls-0-23-cryptoprovider-gotcha&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;If you build against &lt;code&gt;kube-rs&lt;&#x2F;code&gt; with the &lt;code&gt;rustls-tls&lt;&#x2F;code&gt; feature on the current release, the binary compiles cleanly — and then panics at the first TLS handshake with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Could not automatically determine the process-level CryptoProvider from Rustls crate features&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;rustls&lt;&#x2F;code&gt; 0.23 no longer picks a crypto backend based on downstream feature flags. The application itself must call &lt;code&gt;rustls::crypto::ring::default_provider().install_default()&lt;&#x2F;code&gt; before any TLS work happens. In k8s-scale-app-rs this runs as the first step in &lt;code&gt;main()&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Two notes for anyone hitting the same wall: the panic never fires at compile time, and no test caught it locally because integration tests without a cluster do not open a TLS connection. It only surfaces when the CronJob actually tries to reach the API server.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;two-deploy-paths-independent&quot;&gt;Two deploy paths, independent&lt;a class=&quot;zola-anchor&quot; href=&quot;#two-deploy-paths-independent&quot; aria-label=&quot;Anchor link for: two-deploy-paths-independent&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Helm and Kustomize both ship in the repo, and either can be used on its own:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Helm chart&lt;&#x2F;strong&gt; with &lt;code&gt;values-{dev,preprod,prod}.yaml&lt;&#x2F;code&gt;, a &lt;code&gt;mode: scale | restart&lt;&#x2F;code&gt; toggle that swaps the subcommand argument and drops the unused replicas env, and &lt;code&gt;serviceAccount.create&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;rbac.create&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;extraCA.enabled&lt;&#x2F;code&gt; toggles for the pieces that a given cluster may or may not need.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Kustomize&lt;&#x2F;strong&gt; with a base that has only the CronJob, plus three opt-in Components (&lt;code&gt;serviceaccount&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;rbac&#x2F;&lt;&#x2F;code&gt;, &lt;code&gt;extra-ca&#x2F;&lt;&#x2F;code&gt;) that overlays include as needed. Overlays under &lt;code&gt;overlays&#x2F;{dev,preprod,prod}&#x2F;&lt;&#x2F;code&gt; set namespace and image tag and a patch that touches only the fields that vary per stage.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;No Helm-inside-Kustomize glue, no Kustomize-on-Helm-render. Whichever tool matches an existing pipeline is the one to use.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;two-container-images&quot;&gt;Two container images&lt;a class=&quot;zola-anchor&quot; href=&quot;#two-container-images&quot; aria-label=&quot;Anchor link for: two-container-images&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The default image and a &lt;code&gt;-debug&lt;&#x2F;code&gt; variant, both built in the same GitHub Actions matrix — the
production&#x2F;&lt;code&gt;-debug&lt;&#x2F;code&gt; split itself is a general pattern, covered in more depth in
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-01-k8s-openshift-bp-workloads&#x2F;#choose-your-base-image-carefully&quot;&gt;Part 2 of the K8s&#x2F;OpenShift Best Practices series&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;File&lt;&#x2F;th&gt;&lt;th&gt;Base&lt;&#x2F;th&gt;&lt;th&gt;Contents&lt;&#x2F;th&gt;&lt;th&gt;Approx. size&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Containerfile&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;registry.access.redhat.com&#x2F;ubi10&#x2F;ubi-micro&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Binary + CA trust store + &lt;code&gt;&#x2F;licenses&#x2F;&lt;&#x2F;code&gt;, no package manager&lt;&#x2F;td&gt;&lt;td&gt;≈35 MB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Containerfile.debug&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;registry.access.redhat.com&#x2F;ubi10&#x2F;ubi-minimal&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Everything above + &lt;code&gt;bash&lt;&#x2F;code&gt;, &lt;code&gt;curl&lt;&#x2F;code&gt;, &lt;code&gt;bind-utils&lt;&#x2F;code&gt; (&lt;code&gt;dig&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;≈115 MB&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;code&gt;ubi10-micro&lt;&#x2F;code&gt; is not fully distroless in the Google sense — bash and coreutils are still there — but it has no package manager and no network tools, which fits the pod’s actual operational surface for a scheduled scale or restart call. The &lt;code&gt;-debug&lt;&#x2F;code&gt; variant exists for the day a pod loops in &lt;code&gt;ImagePullBackOff&lt;&#x2F;code&gt; or a network policy silently drops the API-server traffic and someone needs to &lt;code&gt;kubectl exec&lt;&#x2F;code&gt; in with tools to check.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-ubi10&quot;&gt;Why UBI10&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-ubi10&quot; aria-label=&quot;Anchor link for: why-ubi10&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;The base itself, not just the two variants built from it, was a deliberate choice. I went with
Red Hat’s UBI over an Alpine or a generic Docker Hub base image for one reason: it gives the
image a level of maintenance stability I don’t have to police myself — CVE scanning and rebuilds
are Red Hat’s job, not something I track on my own. That comes with a tradeoff worth stating
plainly: UBI is not “digitally sovereign” in the sense the upcoming Digital Sovereignty in
Practice series covers — it is a US vendor’s supply chain, the same as pulling from Docker Hub
or almost any other public registry would be. I did not find a base that solved that concern
without giving up the maintenance guarantees I wanted, so UBI10 here is a compromise I made
deliberately, not a claim that the underlying problem is solved.&lt;&#x2F;p&gt;
&lt;p&gt;Both images ship an aggregated &lt;code&gt;&#x2F;licenses&#x2F;LICENSES.txt&lt;&#x2F;code&gt; bundled with &lt;code&gt;cargo-about&lt;&#x2F;code&gt; during the container build, generated from a whitelist of accepted SPDX identifiers. A new dependency that pulls in an unfamiliar license fails the container build on purpose.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;supply-chain&quot;&gt;Supply chain&lt;a class=&quot;zola-anchor&quot; href=&quot;#supply-chain&quot; aria-label=&quot;Anchor link for: supply-chain&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;k8s-scale-app-rs&#x2F;blob&#x2F;main&#x2F;.github&#x2F;workflows&#x2F;build-publish.yaml&quot;&gt;build-publish workflow&lt;&#x2F;a&gt; runs three jobs on every push, plus a release job on tag pushes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;test&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; — &lt;code&gt;cargo fmt --check&lt;&#x2F;code&gt;, &lt;code&gt;cargo clippy -D warnings&lt;&#x2F;code&gt;, &lt;code&gt;cargo test --release&lt;&#x2F;code&gt;. Cluster tests auto-skip in CI (no &lt;code&gt;KUBECONFIG&lt;&#x2F;code&gt;, no in-cluster SA token).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;build-image&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; — matrix over &lt;code&gt;Containerfile&lt;&#x2F;code&gt; and &lt;code&gt;Containerfile.debug&lt;&#x2F;code&gt;. Each variant is built with buildx, pushed to &lt;code&gt;ghcr.io&#x2F;git001&#x2F;k8s-scale-app-rs&lt;&#x2F;code&gt;, smoke-tested (&lt;code&gt;--version&lt;&#x2F;code&gt;, &lt;code&gt;scale --help&lt;&#x2F;code&gt;, &lt;code&gt;restart --help&lt;&#x2F;code&gt;), then:
&lt;ul&gt;
&lt;li&gt;signed with &lt;strong&gt;cosign keyless&lt;&#x2F;strong&gt; via the GitHub OIDC token (no key management — Fulcio issues a short-lived certificate, the signature is logged to Rekor);&lt;&#x2F;li&gt;
&lt;li&gt;has an &lt;strong&gt;SPDX-JSON SBOM&lt;&#x2F;strong&gt; generated with syft and attached via &lt;code&gt;cosign attest --type spdxjson&lt;&#x2F;code&gt;;&lt;&#x2F;li&gt;
&lt;li&gt;has &lt;strong&gt;SLSA build provenance&lt;&#x2F;strong&gt; pushed with &lt;code&gt;actions&#x2F;attest-build-provenance&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;publish-chart&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; — &lt;code&gt;helm lint&lt;&#x2F;code&gt; and &lt;code&gt;helm package&lt;&#x2F;code&gt; on every push; OCI push to &lt;code&gt;oci:&#x2F;&#x2F;ghcr.io&#x2F;git001&#x2F;charts&#x2F;k8s-scale-app-rs&lt;&#x2F;code&gt;, plus cosign sign and SLSA provenance only on tag events (avoids overwrite conflicts on GHCR-immutable tags).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;release&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; (tag events only) — creates the GitHub Release with auto-generated notes from commits since the previous tag, attaches the packaged chart &lt;code&gt;.tgz&lt;&#x2F;code&gt; as an asset, and prepends the release body with pull and verify snippets.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;All of that runs with &lt;code&gt;permissions: contents:read, packages:write, id-token:write, attestations:write&lt;&#x2F;code&gt; — the minimum needed for OIDC-based signing plus attestation upload.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;verifying-a-published-image&quot;&gt;Verifying a published image&lt;a class=&quot;zola-anchor&quot; href=&quot;#verifying-a-published-image&quot; aria-label=&quot;Anchor link for: verifying-a-published-image&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A consumer that wants to check what came out of that pipeline needs cosign v2:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IMG&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;8&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;l&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;v&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IDENTITY&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;^https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;k8s-scale-app-rs&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;ISSUER&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword z-keyword z-operator&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;p&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;g&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;b&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;u&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;s&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;r&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;e&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;n&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;t&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;c&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;o&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cosign&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; verify&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-identity-regexp&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IDENTITY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-oidc-issuer&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;ISSUER&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IMG&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cosign&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; verify-attestation&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; spdxjson&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-identity-regexp&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IDENTITY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-oidc-issuer&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;ISSUER&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IMG&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cosign&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; verify-attestation&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-type&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; slsaprovenance&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-identity-regexp&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IDENTITY&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-certificate-oidc-issuer&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;ISSUER&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-other z-variable&quot;&gt;IMG&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For enforcement inside the cluster, a cosign-aware admission controller — &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;kyverno.io&#x2F;&quot;&gt;Kyverno&lt;&#x2F;a&gt;, &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigstore&#x2F;policy-controller&quot;&gt;Sigstore policy-controller&lt;&#x2F;a&gt;, or &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sse-secure-systems.github.io&#x2F;connaisseur&#x2F;&quot;&gt;Connaisseur&lt;&#x2F;a&gt; — can turn the signature into a hard gate that rejects any pod trying to pull an image that was not produced by exactly this workflow. That is a separate deployment concern; the tool repo does not ship the policy itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mirroring-the-image-into-another-registry&quot;&gt;Mirroring the image into another registry&lt;a class=&quot;zola-anchor&quot; href=&quot;#mirroring-the-image-into-another-registry&quot; aria-label=&quot;Anchor link for: mirroring-the-image-into-another-registry&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Verifying a signature only works if the signature actually travels with the image. Not every &lt;code&gt;docker pull &amp;amp;&amp;amp; docker push&lt;&#x2F;code&gt;-style mirroring tool preserves it — some tools do, some quietly drop it, and one (&lt;code&gt;oc-mirror v2&lt;&#x2F;code&gt;) solves a related but entirely different signature problem. I wrote up a full, sourced comparison — &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-06-mirroring-signed-images-private-registry&#x2F;&quot;&gt;Which Tool Mirrors a Cosign-Signed Image into a Private Registry?&lt;&#x2F;a&gt; — with &lt;code&gt;regctl&lt;&#x2F;code&gt; coming out as the most complete option for this specific image.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code-and-repository&quot;&gt;Code and repository&lt;a class=&quot;zola-anchor&quot; href=&quot;#code-and-repository&quot; aria-label=&quot;Anchor link for: code-and-repository&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Repository: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;k8s-scale-app-rs&quot;&gt;k8s-scale-app-rs on GitHub&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Container images: &lt;code&gt;ghcr.io&#x2F;git001&#x2F;k8s-scale-app-rs:v1.0.0&lt;&#x2F;code&gt; and &lt;code&gt;ghcr.io&#x2F;git001&#x2F;k8s-scale-app-rs:v1.0.0-debug&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Helm chart: &lt;code&gt;oci:&#x2F;&#x2F;ghcr.io&#x2F;git001&#x2F;charts&#x2F;k8s-scale-app-rs:1.0.0&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Feedback and PRs welcome.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Which Tool Mirrors a Cosign-Signed Image into a Private Registry?</title>
          <pubDate>Mon, 06 Jul 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-07-06-mirroring-signed-images-private-registry/</link>
          <guid>https://blog.none.at/blog/2026/2026-07-06-mirroring-signed-images-private-registry/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-07-06-mirroring-signed-images-private-registry/">&lt;h2 id=&quot;tl-dr&quot;&gt;TL;DR&lt;a class=&quot;zola-anchor&quot; href=&quot;#tl-dr&quot; aria-label=&quot;Anchor link for: tl-dr&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;regctl&lt;&#x2F;code&gt; is the most complete option — it exposes the legacy tag-based signature scheme and the
modern OCI 1.1 referrers API as two separate, explicit flags. &lt;code&gt;oras cp -r&lt;&#x2F;code&gt; is the second choice,
and the tool cosign’s own project points to now that &lt;code&gt;cosign copy&lt;&#x2F;code&gt; is deprecated. &lt;code&gt;oc-mirror v2&lt;&#x2F;code&gt;
looks like it should be on this list but actually solves a different, unrelated signature problem.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem&quot;&gt;The problem&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-problem&quot; aria-label=&quot;Anchor link for: the-problem&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Verifying a signature only works if the signature actually travels with the image. Anyone mirroring
a Cosign-signed image into a private or air-gapped registry needs the copy step itself to carry the
signature along, not just the layers — and that is not automatic. Some tools drop it silently, some
need a config flag turned on first, and one widely-used tool solves a related but entirely different
problem. &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-06-k8s-scale-app-rs&#x2F;&quot;&gt;k8s-scale-app-rs&lt;&#x2F;a&gt; is the worked example
throughout this post — its own container images are signed keylessly with cosign, carry an SBOM and
SLSA build provenance, and its &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-07-06-k8s-scale-app-rs&#x2F;#verifying-a-published-image&quot;&gt;“Verifying a published image”&lt;&#x2F;a&gt;
section shows what verification actually checks. This post is about what happens &lt;em&gt;before&lt;&#x2F;em&gt;
verification: getting the signature into the registry the verifier will actually query.&lt;&#x2F;p&gt;
&lt;p&gt;A short, sourced comparison, checked directly against each project’s docs&#x2F;source (as of July 2026):&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-naming-trap-worth-knowing-first&quot;&gt;A naming trap worth knowing first&lt;a class=&quot;zola-anchor&quot; href=&quot;#a-naming-trap-worth-knowing-first&quot; aria-label=&quot;Anchor link for: a-naming-trap-worth-knowing-first&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Red Hat’s &lt;code&gt;oc-mirror v2&lt;&#x2F;code&gt; — the tool for mirroring into disconnected OpenShift installs — has its
own &lt;code&gt;--secure-policy&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;--remove-signatures&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;--registries.d&lt;&#x2F;code&gt; flags, but they implement Red Hat’s
older “simple signing” scheme: a &lt;code&gt;policy.json&lt;&#x2F;code&gt; plus GPG keys plus an external &lt;code&gt;sigstore:&lt;&#x2F;code&gt; URL (e.g.
&lt;code&gt;mirror.openshift.com&#x2F;pub&#x2F;openshift-v4&#x2F;signatures&lt;&#x2F;code&gt; for OpenShift release images). That predates,
and has nothing to do with, the CNCF Sigstore project this post’s cosign workflow uses — same word,
unrelated mechanism. &lt;code&gt;oc-mirror v2&lt;&#x2F;code&gt; went GA in OpenShift 4.18 (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;openshift_container_platform&#x2F;4.18&#x2F;html&#x2F;release_notes&#x2F;ocp-4-18-release-notes&quot;&gt;release
notes&lt;&#x2F;a&gt;),
but its &lt;code&gt;ImageSetConfiguration&lt;&#x2F;code&gt; API is still &lt;code&gt;v2alpha1&lt;&#x2F;code&gt;, and none of Red Hat’s own walkthroughs for
it touch Cosign-style signatures at all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;tool-comparison&quot;&gt;Tool comparison&lt;a class=&quot;zola-anchor&quot; href=&quot;#tool-comparison&quot; aria-label=&quot;Anchor link for: tool-comparison&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;&#x2F;th&gt;&lt;th&gt;Legacy tag-based signature (&lt;code&gt;sha256-&amp;lt;digest&amp;gt;.sig&lt;&#x2F;code&gt;)&lt;&#x2F;th&gt;&lt;th&gt;OCI 1.1 referrers (modern Cosign&#x2F;SBOM&#x2F;attestations)&lt;&#x2F;th&gt;&lt;th&gt;Note&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;regctl.regclient.org&#x2F;cli&#x2F;regctl&#x2F;image&#x2F;copy&#x2F;&quot;&gt;&lt;code&gt;regctl image copy&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;--digest-tags&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;--referrers&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;only tool here with separate, explicit flags for both schemes&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;oras.land&#x2F;docs&#x2F;commands&#x2F;oras_cp&quot;&gt;&lt;code&gt;oras cp -r&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;td&gt;&lt;td&gt;via &lt;code&gt;-r&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;-r&lt;&#x2F;code&gt; (marked “Preview”)&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;--from&#x2F;to-distribution-spec&lt;&#x2F;code&gt; handles referrers-tag vs. referrers-API mismatches between registries&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;skopeo copy&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;opt-in&lt;&#x2F;td&gt;&lt;td&gt;not supported&lt;&#x2F;td&gt;&lt;td&gt;needs &lt;code&gt;use-sigstore-attachments: true&lt;&#x2F;code&gt; under the &lt;code&gt;default-docker&lt;&#x2F;code&gt; key in &lt;code&gt;registries.d&lt;&#x2F;code&gt;, on both source and destination&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Zot registry &lt;code&gt;sync&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;syncLegacyCosignTags&lt;&#x2F;code&gt; (default &lt;code&gt;true&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;only with &lt;code&gt;preserveDigest: true&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;without &lt;code&gt;preserveDigest&lt;&#x2F;code&gt;, Zot re-encodes Docker-format images to OCI on sync, which changes the digest and detaches the signature&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;cosign copy&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;yes&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;deprecated&lt;&#x2F;strong&gt; as of &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigstore&#x2F;cosign&#x2F;pull&#x2F;4681&quot;&gt;sigstore&#x2F;cosign#4681&lt;&#x2F;a&gt; (merged 2026-02-04) — the tool itself now points to &lt;code&gt;oras copy -r&lt;&#x2F;code&gt; instead&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;crane copy&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;only incidentally, via &lt;code&gt;--all-tags&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;no&lt;&#x2F;td&gt;&lt;td&gt;plain manifest&#x2F;layer copier, no signature awareness at all&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Harbor replication&lt;&#x2F;td&gt;&lt;td&gt;per Harbor’s own docs, yes&lt;&#x2F;td&gt;&lt;td&gt;open gap (&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;goharbor&#x2F;harbor&#x2F;issues&#x2F;23210&quot;&gt;goharbor&#x2F;harbor#23210&lt;&#x2F;a&gt;)&lt;&#x2F;td&gt;&lt;td&gt;works for the legacy scheme; OCI 1.1 referrers replication is not yet reliable&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;code&gt;regctl&lt;&#x2F;code&gt; is the only tool in this comparison that exposes both signature schemes as distinct,
intentional flags rather than as a side effect of copying “everything.” For a registry pair that
already speaks the OCI 1.1 referrers API end to end, &lt;code&gt;oras cp -r&lt;&#x2F;code&gt; is the other option worth
checking — it’s what the cosign project’s own deprecation notice for &lt;code&gt;cosign copy&lt;&#x2F;code&gt; points to.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;example-mirroring-with-regctl&quot;&gt;Example: mirroring with regctl&lt;a class=&quot;zola-anchor&quot; href=&quot;#example-mirroring-with-regctl&quot; aria-label=&quot;Anchor link for: example-mirroring-with-regctl&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here is an example command to mirror &lt;code&gt;k8s-scale-app-rs&lt;&#x2F;code&gt; itself, signature included, using &lt;code&gt;regctl&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;regctl&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; image&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; copy&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-digest-tags&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-referrers&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  ghcr.io&#x2F;git001&#x2F;k8s-scale-app-rs:v1.0.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-character&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-string&quot;&gt;  my-private-registry.example.com&#x2F;k8s-scale-app-rs:v1.0.0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;a class=&quot;zola-anchor&quot; href=&quot;#sources&quot; aria-label=&quot;Anchor link for: sources&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigstore&#x2F;cosign&#x2F;pull&#x2F;4681&quot;&gt;sigstore&#x2F;cosign PR #4681 — “Deprecate cosign copy”&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigstore&#x2F;cosign&#x2F;issues&#x2F;4335&quot;&gt;sigstore&#x2F;cosign#4335 — Complete OCI 1.1 Referrers API Support Across All Cosign Commands&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigstore&#x2F;cosign&#x2F;issues&#x2F;4564&quot;&gt;sigstore&#x2F;cosign#4564 — cosign verify fails after cosign copy (JFrog Artifactory)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;podman-container-tools&#x2F;skopeo&#x2F;issues&#x2F;2061&quot;&gt;podman-container-tools&#x2F;skopeo#2061 — sigstore signature copying and &lt;code&gt;use-sigstore-attachments&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;regctl.regclient.org&#x2F;cli&#x2F;regctl&#x2F;image&#x2F;copy&#x2F;&quot;&gt;regctl &lt;code&gt;image copy&lt;&#x2F;code&gt; reference&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;oras.land&#x2F;docs&#x2F;commands&#x2F;oras_cp&quot;&gt;oras &lt;code&gt;cp&lt;&#x2F;code&gt; command reference&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;goharbor&#x2F;harbor&#x2F;issues&#x2F;23210&quot;&gt;goharbor&#x2F;harbor#23210 — OCI 1.1 referrers not replicated&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;zotregistry.dev&#x2F;v2.1.14&#x2F;articles&#x2F;mirroring&#x2F;&quot;&gt;Zot registry: mirroring&#x2F;sync documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.redhat.com&#x2F;en&#x2F;documentation&#x2F;openshift_container_platform&#x2F;4.18&#x2F;html&#x2F;release_notes&#x2F;ocp-4-18-release-notes&quot;&gt;OpenShift Container Platform 4.18 release notes&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;openshift&#x2F;oc-mirror&#x2F;blob&#x2F;main&#x2F;docs&#x2F;features&#x2F;signature-verification.md&quot;&gt;openshift&#x2F;oc-mirror — signature-verification.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>What is a (D)DoS - technical</title>
          <pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-19-ddos-technical/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-19-ddos-technical/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-19-ddos-technical/">&lt;p&gt;The technical follow-up to the &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-19-ddos&#x2F;&quot;&gt;non-technical (D)DoS overview&lt;&#x2F;a&gt;. Covers Layer 3&#x2F;4 network floods (SYN, UDP, amplification), BGP hijacking, Layer 7 application DDoS, and operational resilience — all focused on availability.&lt;&#x2F;p&gt;
&lt;p&gt;Originally published at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.opensourcerers.org&#x2F;2024&#x2F;01&#x2F;22&#x2F;whats-a-ddos-distributed-denial-of-service-and-how-to-protect-against-such-an-attack-technical&#x2F;&quot;&gt;opensourcerers.org&lt;&#x2F;a&gt;; updated with current attack vectors and technologies.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>What is a (D)DoS</title>
          <pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-19-ddos/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-19-ddos/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-19-ddos/">&lt;p&gt;(D)DoS attacks are not just a technical problem — they operate at the Business, Social, and Informational level as well. This post covers the non-technical dimensions; the &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-19-ddos-technical&#x2F;&quot;&gt;technical follow-up&lt;&#x2F;a&gt; covers Layer 3&#x2F;4 network floods, BGP hijacking, TLS handshake attacks, Layer 7 application DDoS, and operational resilience.&lt;&#x2F;p&gt;
&lt;p&gt;Originally published at &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.opensourcerers.org&#x2F;2023&#x2F;11&#x2F;27&#x2F;whats-a-ddos-and-how-to-protect-against-such-an-attack-non-technical&#x2F;&quot;&gt;opensourcerers.org&lt;&#x2F;a&gt;; updated with current examples and expanded framing.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>(D)DoS and Application Security: The Complete Guide</title>
          <pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-19-ddos-guide/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-19-ddos-guide/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-19-ddos-guide/">&lt;p&gt;This is the index and reading guide for a three-part series on denial-of-service and application
security. The first two parts cover &lt;strong&gt;availability&lt;&#x2F;strong&gt; attacks — a service being made unreachable or
unusable — at levels most people don’t associate with the term “DDoS”: the business level (losing
the people who run the service), the social level (manipulating the humans behind it), the
informational level (reputational disruption), and finally the technical level most readers expect
(network floods, BGP hijacking, Layer 7 exhaustion). The third part is a deliberate step sideways,
into application-layer attacks — SQL Injection, Log4Shell, the OWASP Top 10 — that threaten
&lt;strong&gt;confidentiality and integrity&lt;&#x2F;strong&gt; instead of availability.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>How I work with Claude Code</title>
          <pubDate>Wed, 10 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-10-how-i-work-with-claude/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-10-how-i-work-with-claude/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-10-how-i-work-with-claude/">&lt;p&gt;Since 2025, I have been using AI tools as part of my daily work. Not as a replacement for
thinking — as a productivity aid. An AI coding agent has become a regular part of how I work on infrastructure, blog posts,
and software projects. Most of what follows likely applies to other agents as well —
Cursor, Copilot, or whatever comes next. I happen to use Claude.&lt;&#x2F;p&gt;
&lt;p&gt;This post is not a tutorial and not a pitch. It is a reflection on what actually works — for me, on my projects —
across months of real use, across 40+ projects, and across months of agent use.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: Connect IDEs and Web UIs</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-agents/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-agents/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-agents/">&lt;p&gt;The first four parts of this series deployed a vLLM inference endpoint at
&lt;code&gt;https:&#x2F;&#x2F;llm.YOUR_DOMAIN&#x2F;v1&lt;&#x2F;code&gt;, protected by a Bearer token, running on an OVH RTX5000-28 GPU node.
This part shows how to connect coding assistants, web UIs, and other OpenAI-compatible clients
to that endpoint.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: Terraform, Ansible, and Deployment</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-deployment/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-deployment/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-deployment/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-introduction&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; covered the architecture and use cases. This post walks through the complete Terraform and Ansible setup and a first deployment.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: LiteLLM API Gateway</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-gateway/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-gateway/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-gateway/">&lt;p&gt;Parts 1–5 deployed a functional self-hosted LLM endpoint. This part adds &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.litellm.ai&#x2F;&quot;&gt;LiteLLM&lt;&#x2F;a&gt; — an open-source proxy that exposes a unified OpenAI-compatible API across multiple LLM backends — in front of vLLM. The gateway layer brings per-user API keys, budget enforcement, and automatic fallback to commercial APIs when the local model is unavailable.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: The Complete Guide</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-guide/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-guide/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-guide/">&lt;p&gt;This is the index and reading guide for a six-part series on self-hosting LLM inference on a
GPU-enabled Kubernetes node pool, using OVH Managed Kubernetes Service (MKS) as the concrete
platform throughout. The series runs end to end: the decision of whether to self-host at all,
provisioning the GPU infrastructure, serving a model behind an OpenAI-compatible API, wiring up
observability and autoscaling, connecting real client tools, and finally putting a multi-user
gateway in front of the whole thing.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: Introduction</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-introduction/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-introduction/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-introduction/">&lt;p&gt;This post covers the decision context for self-hosting LLM inference on OVH MKS: when it makes sense, what the stack looks like, and what the costs are. &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-deployment&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; walks through the Terraform and Ansible setup.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: Prometheus, Grafana, and KEDA</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-observability/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-observability/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-observability/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-introduction&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; covered the architecture and use cases.
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-deployment&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; walked through Terraform and Ansible.
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-serving&#x2F;&quot;&gt;Part 3&lt;&#x2F;a&gt; covered models and the OpenAI API.
This part adds observability (Prometheus + Grafana) and scale-to-zero autoscaling via KEDA.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>LLM Inference on OVH MKS: Models, AWQ, and OpenAI API</title>
          <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-serving/</link>
          <guid>https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-serving/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-06-02-llm-inference-on-ovh-serving/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-introduction&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; covered the architecture and use cases.
&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-06-02-llm-inference-on-ovh-deployment&#x2F;&quot;&gt;Part 2&lt;&#x2F;a&gt; walked through the Terraform and Ansible setup.
This post covers which models fit on the RTX5000-28’s 16 GB GPU VRAM, why AWQ quantization is required for 7B+ models,
and how to use the OpenAI-compatible endpoint from your own code.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>SigNoz on OVH MKS: Access Log Reports with Vector and ClickHouse</title>
          <pubDate>Mon, 18 May 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-05-18-signoz-on-ovh-usecase-access-logs/</link>
          <guid>https://blog.none.at/blog/2026/2026-05-18-signoz-on-ovh-usecase-access-logs/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-05-18-signoz-on-ovh-usecase-access-logs/">&lt;p&gt;Posts &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-05-16-signoz-on-ovh-infrastructure&#x2F;&quot;&gt;1&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-05-16-signoz-on-ovh-metrics-traces-logs&#x2F;&quot;&gt;2&lt;&#x2F;a&gt; of this series set up the SigNoz observability stack on OVH MKS and demonstrated metrics, traces, and Kubernetes pod log collection. This post adds a practical use case: structured Envoy access log collection via Vector, archiving to OVH Object Storage in Combined Log Format, and monthly HTML reports via awffull — accessible at &lt;code&gt;https:&#x2F;&#x2F;reports.&amp;lt;your-domain&amp;gt;&lt;&#x2F;code&gt; through the existing Istio gateway.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>SigNoz on OVH MKS: The Complete Guide</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-guide/</link>
          <guid>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-guide/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-guide/">&lt;p&gt;This is the index and reading guide for a three-part series that turns storage-engine theory into a
fully reproducible, sovereign observability stack: &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;signoz.io&quot;&gt;SigNoz&lt;&#x2F;a&gt; Community Edition on
OVH Managed Kubernetes Service (MKS), with metrics, distributed traces, and logs all stored in a
single ClickHouse cluster and cold-tiered to OVH Object Storage Infrequent Access. It is an
alternative to hyperscaler-locked observability SaaS, built as a working example repository rather
than a slide deck — clone it, fill in credentials, run Terraform and Ansible. Unlike a stack that
bolts together Prometheus, Jaeger or Tempo, and Loki or Elasticsearch as three separately-operated
databases, SigNoz stores metrics, traces, and logs together in ClickHouse from the start — one
retention policy, one S3 tiering config, one SQL interface, instead of three.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>SigNoz on OVH MKS: Infrastructure with Terraform + Ansible</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-infrastructure/</link>
          <guid>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-infrastructure/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-infrastructure/">&lt;p&gt;The previous two series covered the theory: &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-05-14-es-os-loki-quickwit-clickhouse&#x2F;&quot;&gt;which storage engine fits 7-year log archiving&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-05-14-aws-gcp-azure-ovh-log-archiving&#x2F;&quot;&gt;what managed cloud services cost&lt;&#x2F;a&gt;. This post turns that analysis into a fully reproducible sovereign observability stack. The goal is not just lower cost, but operational control and EU-hosted observability without hyperscaler lock-in — &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;signoz.io&quot;&gt;SigNoz&lt;&#x2F;a&gt; Community Edition on OVH, with metrics, distributed traces, and logs all stored in ClickHouse with S3 cold-tier tiering to OVH Object Storage Infrequent Access.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>SigNoz on OVH MKS: Metrics, Traces &amp; Logs with Istio Ambient</title>
          <pubDate>Sat, 16 May 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-metrics-traces-logs/</link>
          <guid>https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-metrics-traces-logs/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-05-16-signoz-on-ovh-metrics-traces-logs/">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;blog.none.at&#x2F;blog&#x2F;2026&#x2F;2026-05-16-signoz-on-ovh-infrastructure&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; set up the infrastructure: OVH MKS, vRack, Istio Ambient Mode, and SigNoz itself via Terraform + Ansible. This post covers what to do after the cluster is running — sending telemetry data, verifying the S3 cold tier is active, and building the first dashboards.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Running pdns_recursor as a root-independent validating resolver</title>
          <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-04-05-pdns-recursor-root-resolver/</link>
          <guid>https://blog.none.at/blog/2026/2026-04-05-pdns-recursor-root-resolver/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-04-05-pdns-recursor-root-resolver/">&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;doc.powerdns.com&#x2F;recursor&#x2F;&quot;&gt;PowerDNS Recursor&lt;&#x2F;a&gt; is a mature, production-grade recursive DNS resolver. This post documents how to configure it to bootstrap directly from a local &lt;code&gt;root.zone&lt;&#x2F;code&gt; file — so it never needs to reach the root name servers at runtime — with full DNSSEC validation producing the &lt;code&gt;ad&lt;&#x2F;code&gt; flag in responses.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Running a validating DNS recursor from the root zone with Hickory DNS</title>
          <pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-04-04-hickory-dns-root-resolver/</link>
          <guid>https://blog.none.at/blog/2026/2026-04-04-hickory-dns-root-resolver/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-04-04-hickory-dns-root-resolver/">&lt;p&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hickory-dns&#x2F;hickory-dns&quot;&gt;Hickory DNS&lt;&#x2F;a&gt; is a Rust-based DNS implementation that covers the full stack: resolver, recursor, authoritative server, and DNSSEC. This post documents how to run it as a &lt;strong&gt;full recursive resolver&lt;&#x2F;strong&gt; — starting directly from the root zone, with DNSSEC validation and encrypted upstream transport — based on the changes developed in the &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;hickory-dns&#x2F;tree&#x2F;recurser-from-root-zone&quot;&gt;&lt;code&gt;recurser-from-root-zone&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; branch.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>vigil-log-relay: Kubernetes Log Collection Without a DaemonSet</title>
          <pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-03-22-vigil-log-relay/</link>
          <guid>https://blog.none.at/blog/2026/2026-03-22-vigil-log-relay/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-03-22-vigil-log-relay/">&lt;p&gt;Every team running workloads on Kubernetes eventually faces the same question:
how do you get logs out of your pods and into your log aggregation stack?
The standard answer is a DaemonSet. It works, but it comes with a cost.
&lt;strong&gt;&lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;vigil-rs&#x2F;tree&#x2F;main&#x2F;crates&#x2F;vigil-log-relay&quot;&gt;vigil-log-relay&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; is a different answer.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>vigil-rs: A Rust Service Supervisor for Containers</title>
          <pubDate>Sat, 21 Mar 2026 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2026/2026-03-21-virgil/</link>
          <guid>https://blog.none.at/blog/2026/2026-03-21-virgil/</guid>
          <description xml:base="https://blog.none.at/blog/2026/2026-03-21-virgil/">&lt;p&gt;Every container that runs more than one process needs a supervisor. Most reach for
&lt;code&gt;dumb-init&lt;&#x2F;code&gt;, &lt;code&gt;tini&lt;&#x2F;code&gt;, &lt;code&gt;s6-overlay&lt;&#x2F;code&gt;, or &lt;code&gt;supervisord&lt;&#x2F;code&gt;. None of them felt quite right
for what we needed, so we wrote &lt;strong&gt;vigil-rs&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This post walks through what &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;vigil-rs&quot;&gt;github.com&#x2F;git001&#x2F;vigil-rs&lt;&#x2F;a&gt; is, why we built it, and how it works internally.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>tls-proxy-tunnel: Transparent TLS Tunnelling Through Corporate HTTP Proxies</title>
          <pubDate>Tue, 02 Jul 2024 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2024/2024-07-02-tls-proxy-tunnel/</link>
          <guid>https://blog.none.at/blog/2024/2024-07-02-tls-proxy-tunnel/</guid>
          <description xml:base="https://blog.none.at/blog/2024/2024-07-02-tls-proxy-tunnel/">&lt;p&gt;Many enterprise networks sit behind a corporate HTTP CONNECT proxy. Applications
that speak TLS natively — think &lt;code&gt;git&lt;&#x2F;code&gt;, &lt;code&gt;curl&lt;&#x2F;code&gt;, SSH-over-HTTPS, or any custom
binary — often have no built-in proxy support. Configuring every single tool is
tedious, fragile, and sometimes impossible when you don’t control the binary.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;tls-proxy-tunnel&lt;&#x2F;code&gt; (&lt;code&gt;tpt&lt;&#x2F;code&gt;) solves this at layer 4: it sits between your
application and the outside world, intercepts the TLS connection, extracts the
Server Name Indication (SNI) from the ClientHello, and tunnels the raw bytes
through your corporate HTTP CONNECT proxy — without ever terminating TLS.&lt;&#x2F;p&gt;</description>
      </item>
      <item>
          <title>Building a native file upload handler for Caddy v2</title>
          <pubDate>Tue, 01 Mar 2022 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2022/2022-03-01-caddy-upload/</link>
          <guid>https://blog.none.at/blog/2022/2022-03-01-caddy-upload/</guid>
          <description xml:base="https://blog.none.at/blog/2022/2022-03-01-caddy-upload/">&lt;p&gt;When &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caddyserver&#x2F;caddy&#x2F;releases&#x2F;tag&#x2F;v2.0.0&quot;&gt;Caddy v2&lt;&#x2F;a&gt; was released in 2020, it introduced a new concept of handlers that use Go’s native module features.&lt;&#x2F;p&gt;
&lt;p&gt;In 2022 one of our customers needed to upload files, and at that time none of the available web servers could handle this natively without an extra program. So I decided to create a new handler for Caddy v2 — &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;caddyv2-upload&quot;&gt;caddyv2-upload&lt;&#x2F;a&gt; was born.&lt;&#x2F;p&gt;
&lt;p&gt;I was not aware of any other tool which can handle file uploads natively in just one binary which can be used in Kubernetes or OpenShift, or I have not searched hard enough 😁. I also wanted to learn go, so I decided to create a new handler for the go based webserver &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;caddyserver.com&#x2F;&quot;&gt;caddyserver&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In May 2022 &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sigoden&#x2F;dufs&quot;&gt;dufs&lt;&#x2F;a&gt;, a Rust-based server, was created — so that might be another option worth considering. 🤷&lt;&#x2F;p&gt;
&lt;p&gt;Any how I wanted to learn go in the pre AI Area and at that time I looked into the docs of go modules, into the source of caddy servers own handlers and created step by step a new handler.&lt;&#x2F;p&gt;
&lt;p&gt;Luckily there was already podman and buildah, so I was able to create container images to run and test locally.&lt;&#x2F;p&gt;
&lt;p&gt;Building something real turned out to be the most effective way to learn Go. Diving into Caddy’s own handler source code, understanding the module system, and iterating with containers made the concepts stick far better than any tutorial would have.&lt;&#x2F;p&gt;
&lt;p&gt;A minimal Caddyfile configuration looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    order upload before file_server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;:2015 {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    root .&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    file_server browse&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    @mypost method POST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    upload @mypost {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        dest_dir &#x2F;var&#x2F;uploads&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        max_filesize 4MB&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Build with &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;caddyserver&#x2F;xcaddy&quot;&gt;xcaddy&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;xcaddy&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; build&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant z-constant z-other&quot;&gt;-with&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; github.com&#x2F;git001&#x2F;caddyv2-upload&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Today &lt;a rel=&quot;noopener external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;git001&#x2F;caddyv2-upload&quot;&gt;caddyv2-upload&lt;&#x2F;a&gt; (v0.20.0) supports file uploads via HTTP multipart form, configurable destination directories, UUID-based subdirectory creation per upload, fixed filenames, pass-through mode to continue to the next Caddy handler after upload, configurable file size limits, an HTTP notification callback after successful uploads, and a &lt;code&gt;&#x2F;health&lt;&#x2F;code&gt; endpoint. It integrates with Caddy’s variable system and works in Kubernetes and OpenShift environments. Authentication is handled by Caddy’s own security layer rather than the module itself.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How does SNI Routing work in HAProxy</title>
          <pubDate>Fri, 17 May 2019 00:00:00 +0000</pubDate>
          <author>aleks</author>
          <link>https://blog.none.at/blog/2019/2019-05-17-haproxy-sni-routing/</link>
          <guid>https://blog.none.at/blog/2019/2019-05-17-haproxy-sni-routing/</guid>
          <description xml:base="https://blog.none.at/blog/2019/2019-05-17-haproxy-sni-routing/">&lt;p&gt;As I travel a lot I faced the problem that in some Wi-Fi networks certain ports are blocked for outgoing communication 😱. The solution is to use software that can handle TCP and HTTP via port &lt;strong&gt;443&lt;&#x2F;strong&gt; so that I can use my Nextcloud and my XMPP client on the same port.&lt;&#x2F;p&gt;</description>
      </item>
    </channel>
</rss>
