<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flux – Flux Mirror</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/</link><description>Recent content in Flux Mirror on Flux</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://fluxcd.io/flux/cli-plugins/flux-mirror/index.xml" rel="self" type="application/rss+xml"/><item><title>Flux: Flux Mirror Config</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/config/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/config/</guid><description>
&lt;p>The &lt;code>Config&lt;/code> API defines what &lt;code>flux mirror&lt;/code> mirrors and how it authenticates to
the registries involved. A single config lists the registry &lt;strong>hosts&lt;/strong> to
authenticate, the &lt;strong>OCI artifacts&lt;/strong> to copy between OCI registries, and the
&lt;strong>Helm charts&lt;/strong> to pull from HTTP/S Helm repositories and publish to an OCI
registry. Config files support &lt;code>${VAR}&lt;/code> environment substitution in field keys
and values; every referenced environment variable must be set when the config
is loaded. Use the global &lt;code>--no-envsubst&lt;/code> flag to disable substitution and parse &lt;code>${VAR}&lt;/code> strings literally.&lt;/p>
&lt;p>Sources can be OCI registries or HTTP/S Helm repositories; destinations are
always OCI registries.&lt;/p>
&lt;p>The config shape is published as a JSON Schema in
&lt;a href="https://github.com/fluxcd/flux-mirror/blob/v0.8.0/docs/config-v1beta1.json" target="_blank">&lt;code>config-v1beta1.json&lt;/code>&lt;/a> and is consumed by
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/">&lt;code>flux mirror sync&lt;/code>&lt;/a>,
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>flux mirror login&lt;/code>&lt;/a>,
and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/">&lt;code>flux mirror secret&lt;/code>&lt;/a>.&lt;/p>
&lt;h2 id="example">Example&lt;/h2>
&lt;p>The following config mirrors a container image and a Helm chart, and
authenticates to the destination registry with a per-host credential:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker.io/stefanprodan/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>quay.io/my-org/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;6.x&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">verify&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>cosign&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">minAge&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>48h&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">matchOIDCIdentity&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">issuer&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://token.actions.githubusercontent.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">subject&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>^https://github\.com/stefanprodan/.*$&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">charts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://stefanprodan.github.io/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://quay.io/my-org/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;6.x&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>quay.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;my-org+robot-user&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${QUAY_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the above example:&lt;/p>
&lt;ul>
&lt;li>The image at &lt;code>docker.io/stefanprodan/podinfo&lt;/code> is mirrored to
&lt;code>quay.io/my-org/podinfo&lt;/code>. The two highest &lt;code>6.x&lt;/code> tags are selected, their cosign
signatures, SBOMs, and attestations are mirrored alongside them, and each tag
is verified against the listed GitHub Actions OIDC identity (and required to be
at least 48h old) before it is copied.&lt;/li>
&lt;li>The &lt;code>podinfo&lt;/code> Helm chart is pulled from the HTTP/S repository at
&lt;code>https://stefanprodan.github.io/podinfo&lt;/code> and published as an OCI Helm chart to
&lt;code>quay.io/my-org/charts/podinfo&lt;/code>. The two highest &lt;code>6.x&lt;/code> versions are mirrored.&lt;/li>
&lt;li>Requests to &lt;code>quay.io&lt;/code> authenticate with the Quay robot token read from the
&lt;code>QUAY_TOKEN&lt;/code> environment variable, presented as a username/password pair
because &lt;code>username&lt;/code> is set.&lt;/li>
&lt;/ul>
&lt;p>You can run this with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>flux mirror sync ./config.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="writing-a-config-spec">Writing a Config spec&lt;/h2>
&lt;p>A config is a single YAML document with &lt;code>apiVersion: mirror.plugin.fluxcd.io/v1beta1&lt;/code>,
&lt;code>kind: Config&lt;/code>, and any of the &lt;code>artifacts&lt;/code>, &lt;code>charts&lt;/code>, and &lt;code>hosts&lt;/code> lists.&lt;/p>
&lt;p>At least one &lt;code>artifacts&lt;/code> or &lt;code>charts&lt;/code> entry is required, except for
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>flux mirror login&lt;/code>&lt;/a> and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/">&lt;code>flux mirror secret&lt;/code>&lt;/a>, which read only the &lt;code>hosts&lt;/code> section
and accept a &lt;code>hosts&lt;/code>-only config.&lt;/p>
&lt;h3 id="artifacts">Artifacts&lt;/h3>
&lt;p>&lt;code>.artifacts&lt;/code> is an optional field that lists OCI artifacts to mirror between OCI
registries. The manifest and every referenced blob are copied byte-for-byte,
preserving digests; multi-arch images are mirrored faithfully as manifest lists,
with no platform filtering.&lt;/p>
&lt;p>An entry handles any OCI-addressable content: container images, OCI Helm charts,
Flux OCI artifacts, or anything else stored in an OCI registry. To mirror a Helm
chart that is published to an HTTP/S Helm repository (and not yet in an OCI
registry), use
&lt;a href="#charts">&lt;code>.charts&lt;/code>&lt;/a> instead.&lt;/p>
&lt;p>Each entry has two required fields:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.source&lt;/code>, the OCI repository the artifact is pulled from, written without a
scheme (e.g. &lt;code>ghcr.io/fluxcd/flux-cli&lt;/code>).&lt;/li>
&lt;li>&lt;code>.destination&lt;/code>, the OCI repository the artifact is pushed to, written without a
scheme. Selected tags are mirrored to the same tag at the destination.&lt;/li>
&lt;/ul>
&lt;p>It also has optional fields documented below: &lt;code>.selector&lt;/code>
(
&lt;a href="#tag-selection">Tag selection&lt;/a>), &lt;code>.includeReferrers&lt;/code> (
&lt;a href="#referrers">Referrers&lt;/a>),
&lt;code>.verify&lt;/code> (
&lt;a href="#signature-verification">Signature verification&lt;/a>), and &lt;code>.overwrite&lt;/code>
(
&lt;a href="#overwrite-and-drift-behavior">Overwrite and drift behavior&lt;/a>).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-cli&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>quay.io/example/flux-cli&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=2.7.0 &amp;lt;3.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">10&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="tag-selection">Tag selection&lt;/h4>
&lt;p>&lt;code>.artifacts[].selector&lt;/code> is a required field that decides which tags of the
source repository are mirrored. It runs as a four-step pipeline — a regex
prefilter, a semver range filter, a sort, then a limit — always ordering highest
first and keeping the top N. The field offers the following subfields:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.regex&lt;/code>, an optional
&lt;a href="https://pkg.go.dev/regexp/syntax" target="_blank">Go regular expression&lt;/a>
prefilter applied to every tag before the sort and semver steps. It has a
required &lt;code>.pattern&lt;/code> (tags that do not match are dropped) and an optional
&lt;code>.extract&lt;/code> replacement string referencing named capture groups (e.g.
&lt;code>$version&lt;/code>). When &lt;code>.extract&lt;/code> is set, the extracted value — not the raw tag — is
used as the comparison key, so tags with prefixes or suffixes can still feed
into a semver or numerical sort.&lt;/li>
&lt;li>&lt;code>.semver&lt;/code>, an optional
&lt;a href="https://github.com/Masterminds/semver#checking-version-constraints" target="_blank">semver range&lt;/a>
(e.g. &lt;code>&amp;gt;=2.7.0 &amp;lt;3.0.0&lt;/code> or &lt;code>6.x&lt;/code>). Tags whose comparison key is not a valid
semantic version are dropped.&lt;/li>
&lt;li>&lt;code>.sortBy&lt;/code>, the sort strategy. The default is &lt;code>semver&lt;/code>.&lt;/li>
&lt;li>&lt;code>.limit&lt;/code>, how many tags to mirror, taken from the top of the sorted result. It
defaults to &lt;code>1&lt;/code>; &lt;code>0&lt;/code> disables the cap and mirrors every matching tag.&lt;/li>
&lt;/ul>
&lt;p>The supported sort strategies are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>semver&lt;/code> parses each tag as a semantic version and orders by semver
precedence. Tags that do not parse are silently dropped; use &lt;code>.regex&lt;/code> to
control what qualifies.&lt;/li>
&lt;li>&lt;code>alphabetical&lt;/code> orders tags lexicographically. This suits tags shaped like
&lt;code>RELEASE.2024-11-12T08-30-15Z&lt;/code>, where lexical order matches chronological
order.&lt;/li>
&lt;li>&lt;code>numerical&lt;/code> parses each tag as a number and orders numerically. Tags that do
not parse are silently dropped; this is typically paired with &lt;code>.regex&lt;/code> to
extract a numeric portion from a composite tag.&lt;/li>
&lt;/ul>
&lt;p>For example, to select the highest five tags by an embedded timestamp:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">regex&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pattern&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;^.+-[0-9a-f]+-(?P&amp;lt;ts&amp;gt;\d+)$&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">extract&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#39;$ts&amp;#39;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sortBy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>numerical&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="referrers">Referrers&lt;/h4>
&lt;p>&lt;code>.artifacts[].includeReferrers&lt;/code> is an optional field that, when &lt;code>true&lt;/code>, also
mirrors the referrers of each selected tag — cosign signatures, SBOMs, and
attestations attached via the
&lt;a href="https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers" target="_blank">OCI 1.1 referrers API&lt;/a>.
It defaults to &lt;code>false&lt;/code>.&lt;/p>
&lt;h4 id="signature-verification">Signature verification&lt;/h4>
&lt;p>&lt;code>.artifacts[].verify&lt;/code> is an optional field that verifies the source artifact&amp;rsquo;s
signature before any tag is copied. Every selected tag is verified at plan time;
if a selected tag has no matching valid signature, planning fails for that entry
and no copy job is scheduled. The field offers three subfields:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.provider&lt;/code>, the verification provider. It must be &lt;code>cosign&lt;/code>. Verification uses
&lt;a href="https://docs.sigstore.dev" target="_blank">Cosign keyless bundles&lt;/a> attached to the source
artifact as OCI referrers, so the signing side must publish them as referrers.&lt;/li>
&lt;li>&lt;code>.matchOIDCIdentity&lt;/code>, a list of accepted Fulcio certificate identities. Each
entry provides an &lt;code>.issuer&lt;/code> (the OIDC issuer URL, e.g.
&lt;code>https://token.actions.githubusercontent.com&lt;/code>) and a &lt;code>.subject&lt;/code> (a Go regular
expression matched against the certificate subject alternative name). The
entries are evaluated in an OR fashion: a signature is accepted when any one
identity matches.&lt;/li>
&lt;li>&lt;code>.minAge&lt;/code>, an optional
&lt;a href="https://pkg.go.dev/time#ParseDuration" target="_blank">duration&lt;/a> that
requires a signature to be at least this old, measured from its verified
transparency-log (Rekor) integrated timestamp. Tags whose signatures are valid
but too recent are reported as &lt;code>skipped&lt;/code> (reason &lt;code>signature-too-new&lt;/code>) and not
copied; signatures without an enforceable verified integrated timestamp fail
verification. Use this to let signatures settle in the transparency log before
mirroring.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/stefanprodan/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>quay.io/my-org/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;*&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">1&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">verify&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>cosign&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">minAge&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>168h&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># 7 days&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">matchOIDCIdentity&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">issuer&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://token.actions.githubusercontent.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">subject&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>^https://github\.com/stefanprodan/.*$&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="charts">Charts&lt;/h3>
&lt;p>&lt;code>.charts&lt;/code> is an optional field that lists Helm charts to mirror from an HTTP/S
Helm repository to an OCI destination. For each selected version, the chart
&lt;code>.tgz&lt;/code> is downloaded from the source and re-published to the destination as a
Helm OCI artifact (a config blob with the chart metadata and a layer with the
tarball bytes). Charts use the same outcomes and overwrite/dry-run semantics as
artifacts; drift is detected by comparing the source tarball&amp;rsquo;s content digest
against the destination chart-layer digest, so re-runs against an unchanged
source are idempotent.&lt;/p>
&lt;p>Each entry offers the following subfields:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.name&lt;/code>, the chart name within the source repository. Required.&lt;/li>
&lt;li>&lt;code>.source&lt;/code>, the HTTP/S Helm repository URL the chart is pulled from. Required;
the scheme must be &lt;code>http&lt;/code> or &lt;code>https&lt;/code>, and the repository must serve a classic
&lt;code>index.yaml&lt;/code> plus chart tarballs.&lt;/li>
&lt;li>&lt;code>.destination&lt;/code>, the OCI base URL the chart is published to. Required; the
scheme must be &lt;code>oci&lt;/code>.&lt;/li>
&lt;li>&lt;code>.version&lt;/code>, an optional
&lt;a href="https://github.com/Masterminds/semver#checking-version-constraints" target="_blank">semver range&lt;/a>
(e.g. &lt;code>&amp;gt;=2.7.0 &amp;lt;3.0.0&lt;/code>). Versions outside the range are dropped. It defaults to
&lt;code>*&lt;/code> (all versions).&lt;/li>
&lt;li>&lt;code>.limit&lt;/code>, how many matching versions to mirror, highest first. It defaults to
&lt;code>1&lt;/code>; &lt;code>0&lt;/code> disables the cap.&lt;/li>
&lt;li>&lt;code>.overwrite&lt;/code>, see
&lt;a href="#overwrite-and-drift-behavior">Overwrite and drift behavior&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Helm repository authentication is &lt;strong>not&lt;/strong> configured under &lt;code>hosts&lt;/code>. It is loaded
automatically from the ambient Helm repositories config — Helm&amp;rsquo;s default
&lt;code>repositories.yaml&lt;/code>, or &lt;code>$HELM_REPOSITORY_CONFIG&lt;/code> when set — so adding the
repository with &lt;code>helm repo add&lt;/code> is enough for &lt;code>flux mirror&lt;/code> to pick up matching
credentials. To mirror a chart that already lives in an OCI registry, list it
under
&lt;a href="#artifacts">&lt;code>.artifacts&lt;/code>&lt;/a> instead, where it is copied OCI-to-OCI as a
plain OCI artifact (authenticated through
&lt;a href="#hosts">&lt;code>hosts&lt;/code>&lt;/a>).&lt;/p>
&lt;p>The chart &lt;code>.name&lt;/code> is appended to &lt;code>.destination&lt;/code> automatically: for &lt;code>destination: oci://quay.io/example/charts&lt;/code> and &lt;code>name: nginx&lt;/code>, versions land at
&lt;code>quay.io/example/charts/nginx:&amp;lt;version&amp;gt;&lt;/code>. Because OCI tags do not accept &lt;code>+&lt;/code>,
semver build metadata (e.g. &lt;code>1.2.3+meta&lt;/code>) is encoded with &lt;code>_&lt;/code> in the destination
tag (&lt;code>1.2.3_meta&lt;/code>); listing and comparison treat both forms as the same version.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">charts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>nginx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://charts.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://quay.io/example/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=15.0.0 &amp;lt;16.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="hosts">Hosts&lt;/h3>
&lt;p>&lt;code>.hosts&lt;/code> is an optional field that configures per-host authentication for OCI
registry requests, covering both source pulls and destination pushes. A host
listed here takes priority over the ambient Docker config; requests to hosts that
are not listed fall back to the Docker config and its credential helpers
(&lt;code>~/.docker/config.json&lt;/code>, or &lt;code>$DOCKER_CONFIG&lt;/code> when set).&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> the &lt;code>hosts&lt;/code> section does not apply to HTTP/S Helm repositories
(&lt;code>.charts[].source&lt;/code>), which authenticate through the ambient Helm repositories
config.&lt;/p>
&lt;/blockquote>
&lt;p>Each entry has one required field, &lt;code>.host&lt;/code>, the registry host (with optional
port) the entry applies to (e.g. &lt;code>registry.example.com&lt;/code> or &lt;code>localhost:5000&lt;/code>),
unique across &lt;code>hosts&lt;/code>. Beyond that, an entry configures &lt;strong>either&lt;/strong> a
&lt;a href="#per-host-credential">&lt;code>.credential&lt;/code>&lt;/a> &lt;strong>or&lt;/strong> a
&lt;a href="#cloud-registry-providers">&lt;code>.provider&lt;/code>&lt;/a> — the two are mutually exclusive. A
&lt;code>credential&lt;/code> host (or a host with neither) may also set a
&lt;a href="#transport-tls">&lt;code>.tls&lt;/code>&lt;/a> block; &lt;code>tls&lt;/code> is not allowed with &lt;code>provider&lt;/code>, because a
cloud registry is managed and its transport is not customized.
&lt;a href="#blob-upload-chunking">&lt;code>.maxChunkSize&lt;/code>&lt;/a> tunes blob uploads independently of auth.
At least one of &lt;code>credential&lt;/code>, &lt;code>provider&lt;/code>, &lt;code>tls&lt;/code>, or &lt;code>maxChunkSize&lt;/code> must be set.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># A per-host credential (HTTP-layer token).&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>github&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># A cloud registry provider, via ambient workload identity.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="per-host-credential">Per-host credential&lt;/h4>
&lt;p>&lt;code>.hosts[].credential&lt;/code> configures an HTTP-layer registry credential for the host.
Exactly one &lt;strong>token source&lt;/strong> subfield selects how the credential is obtained:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.provider&lt;/code>, mints a fresh, per-request credential for the audience using a
cloud or CI identity, then caches and refreshes it on demand. One of &lt;code>github&lt;/code>,
&lt;code>forgejo&lt;/code>, &lt;code>gcp&lt;/code>, &lt;code>azure&lt;/code>, &lt;code>aws&lt;/code>, or &lt;code>jwt-svid&lt;/code> — see
&lt;a href="#token-providers">Token providers&lt;/a> for what each obtains and what the registry
must accept.&lt;/li>
&lt;li>&lt;code>.value&lt;/code>, sends the JSON Web Token configured inline as-is (e.g. a GitLab CI &lt;code>id_token&lt;/code>). Use &lt;code>${VAR}&lt;/code> to substitute it from the environment while loading the config.&lt;/li>
&lt;li>&lt;code>.fromPath&lt;/code>, sends the token read from the file at the path, with surrounding
whitespace trimmed. The file is re-read on every request, so the token can be
rotated without restarting (useful for a projected ServiceAccount token).&lt;/li>
&lt;li>&lt;code>.jwkPath&lt;/code>, signs a fresh JWT per request with the private JSON Web Key in the
file at the path. The key may be a bare JWK or a single-key JWK set
(&lt;code>{&amp;quot;keys&amp;quot;:[...]}&lt;/code>), and its &lt;code>kid&lt;/code> is carried in the JWT header. Generate a key
pair with
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/keygen/">&lt;code>flux mirror keygen&lt;/code>&lt;/a>.&lt;/li>
&lt;li>&lt;code>.jwkValue&lt;/code>, signs a fresh JWT per request with the private JSON Web Key configured inline. Use &lt;code>${VAR}&lt;/code> to substitute it from the environment while loading the config.&lt;/li>
&lt;/ul>
&lt;p>The signed and minted sources take additional claim subfields:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.iss&lt;/code> and &lt;code>.sub&lt;/code>, the issuer and subject claims. Both are &lt;strong>required&lt;/strong> with
&lt;code>.jwkPath&lt;/code> or &lt;code>.jwkValue&lt;/code>, and not allowed with the other sources.&lt;/li>
&lt;li>&lt;code>.aud&lt;/code>, the audience claim. Allowed with &lt;code>.jwkPath&lt;/code>, &lt;code>.jwkValue&lt;/code>, or &lt;code>.provider&lt;/code>,
and defaults to the host. The audience pins the credential to a specific
registry, so it must match what the registry (or the cloud identity provider)
expects.&lt;/li>
&lt;li>&lt;code>.exp&lt;/code>, the signed JWT lifetime, as a
&lt;a href="https://pkg.go.dev/time#ParseDuration" target="_blank">duration&lt;/a>. Allowed only with &lt;code>.jwkPath&lt;/code>
or &lt;code>.jwkValue&lt;/code> — the sources whose lifetime &lt;code>flux mirror&lt;/code> controls — and defaults
to &lt;code>60s&lt;/code>. A longer-lived token is cached and re-minted at half its lifetime.
Every other source&amp;rsquo;s lifetime is fixed by its issuer.&lt;/li>
&lt;li>&lt;code>.hosts[].username&lt;/code>, controls how the resolved credential is transported, and therefore
what the registry must accept — see
&lt;a href="#bearer-token-vs-usernamepassword">Bearer token vs. username/password&lt;/a>.&lt;/li>
&lt;/ul>
&lt;h5 id="token-providers">Token providers&lt;/h5>
&lt;p>When &lt;code>.credential.provider&lt;/code> is set, the credential is obtained from the
provider&amp;rsquo;s ambient identity for the audience (defaulting to the host). This is
what the registry on the other side has to accept:&lt;/p>
&lt;ul>
&lt;li>&lt;code>github&lt;/code> and &lt;code>forgejo&lt;/code> request an OIDC ID token from the CI Actions OIDC
endpoint (&lt;code>ACTIONS_ID_TOKEN_REQUEST_URL&lt;/code> / &lt;code>ACTIONS_ID_TOKEN_REQUEST_TOKEN&lt;/code>).
The registry must trust the GitHub/Forgejo Actions issuer and match the token&amp;rsquo;s
audience and subject (e.g. &lt;code>repo:my-org/my-repo:ref:refs/heads/main&lt;/code>). The two
mint tokens the same way today but are kept distinct so each platform can
diverge.&lt;/li>
&lt;li>&lt;code>gcp&lt;/code> obtains a Google ID token for the audience via Application Default
Credentials (the GKE/GCE metadata server, a service account key, or workload
identity federation). The registry must trust Google&amp;rsquo;s OIDC issuer and the
configured audience.&lt;/li>
&lt;li>&lt;code>azure&lt;/code> obtains a Microsoft Entra ID access token via the default Azure
credential chain (AKS/managed identity, workload identity federation,
environment credentials). The audience is requested as the &lt;code>&amp;lt;aud&amp;gt;/.default&lt;/code>
scope, so &lt;code>.aud&lt;/code> must be the application ID URI (or client ID) of a registered
Entra application the registry validates tokens against.&lt;/li>
&lt;li>&lt;code>aws&lt;/code> is not an OIDC token. AWS mints no JWT, so &lt;code>flux mirror&lt;/code> signs an
&lt;code>sts:GetCallerIdentity&lt;/code> request with the ambient role credentials (IRSA, EC2
instance role, environment, &amp;hellip;) and wraps it in a JWT-shaped envelope whose
header is &lt;code>{&amp;quot;alg&amp;quot;:&amp;quot;none&amp;quot;,&amp;quot;typ&amp;quot;:&amp;quot;aws-sigv4-getcalleridentity&amp;quot;}&lt;/code>. The audience is
carried as a signed &lt;code>X-Audience&lt;/code> header that pins the target registry, not as
an OIDC audience claim. The registry verifies the caller by replaying the
signed request to AWS STS and reading the returned account/ARN, so the
destination &lt;strong>must&lt;/strong> understand this scheme — a generic OIDC registry will not.&lt;/li>
&lt;li>&lt;code>jwt-svid&lt;/code> fetches a JWT-SVID for the audience from the SPIFFE Workload API
(&lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code>) and sends it as the credential. The registry must
trust the SPIFFE trust domain&amp;rsquo;s JWT bundle. This is the HTTP-layer counterpart
to the transport-layer SPIFFE X.509-SVID mTLS configured under
&lt;a href="#transport-tls">&lt;code>.tls&lt;/code>&lt;/a>; the two are independent.&lt;/li>
&lt;/ul>
&lt;h5 id="resolving-file-paths">Resolving file paths&lt;/h5>
&lt;p>&lt;code>.fromPath&lt;/code>, &lt;code>.jwkPath&lt;/code>, and the &lt;code>.tls&lt;/code> path fields are resolved relative to the
config file&amp;rsquo;s directory, and a config may only reference files under its own
directory tree. When the config is read from stdin (&lt;code>-f -&lt;/code>), the process working
directory is used as the confinement root instead.&lt;/p>
&lt;h5 id="bearer-token-vs-usernamepassword">Bearer token vs. username/password&lt;/h5>
&lt;p>&lt;code>.hosts[].username&lt;/code> controls how the resolved credential is
transported, and therefore what the registry on the other side must accept:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>&lt;code>username&lt;/code> unset (default)&lt;/strong> — the credential is treated as a &lt;strong>bearer
token&lt;/strong>. &lt;code>sync&lt;/code> sends it as an HTTP Bearer credential on every request, with no
auth challenge, and &lt;code>login&lt;/code>/&lt;code>secret&lt;/code> write it to the Docker config&amp;rsquo;s
&lt;code>registrytoken&lt;/code> field. This suits registries that natively validate an OIDC
token (or another self-contained bearer credential). &lt;code>registrytoken&lt;/code> is
understood by
&lt;a href="https://github.com/google/go-containerregistry" target="_blank">go-containerregistry&lt;/a>
(crane, and Flux&amp;rsquo;s &lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code>), but &lt;strong>not&lt;/strong> by &lt;code>kubelet&lt;/code>
image pulls. Because credential helpers only store username/secret pairs, a
&lt;code>registrytoken&lt;/code> is always written to the config file, never to a keychain
helper.&lt;/li>
&lt;li>&lt;strong>&lt;code>username&lt;/code> set&lt;/strong> — the credential becomes the &lt;strong>password&lt;/strong> of a
username/password pair. &lt;code>sync&lt;/code> goes through the standard registry auth
challenge (credentials are exchanged at the token endpoint, like the cloud
providers), and &lt;code>login&lt;/code>/&lt;code>secret&lt;/code> write &lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code>. Use this
for registries such as Docker Hub, GHCR, and Quay, which expect a
username/password login even when the username is a placeholder the registry
ignores.&lt;/li>
&lt;/ul>
&lt;p>Choose &lt;code>username&lt;/code> deliberately: set it when the registry expects a
username/password login (or the credential will be consumed by &lt;code>kubelet&lt;/code>), and
leave it unset when the registry validates a self-contained bearer token.&lt;/p>
&lt;h4 id="cloud-registry-providers">Cloud registry providers&lt;/h4>
&lt;p>&lt;code>.hosts[].provider&lt;/code> authenticates the host with a cloud registry provider&amp;rsquo;s
ambient workload identity — the same mechanism the &lt;code>flux push artifact&lt;/code> family
uses — and obtains the registry&amp;rsquo;s native credentials directly (no
&lt;code>credential&lt;/code>/JWT involved). It is mutually exclusive with &lt;code>credential&lt;/code>. The
supported values are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>ecr&lt;/code>, for Amazon ECR. It uses the AWS credential chain (IRSA, EC2 instance
role, environment, SSO), reading the region from the host.&lt;/li>
&lt;li>&lt;code>acr&lt;/code>, for Azure ACR. It uses the default Azure credential chain (managed
identity, workload identity, environment, &amp;hellip;).&lt;/li>
&lt;li>&lt;code>gar&lt;/code>, for Google GAR. It uses Google Application Default Credentials.&lt;/li>
&lt;/ul>
&lt;p>The resolved credentials are presented to the registry as a username/password
pair through the standard registry auth challenge, and written as
&lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code> by
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/">&lt;code>secret&lt;/code>&lt;/a>.&lt;/p>
&lt;h4 id="transport-tls">Transport TLS&lt;/h4>
&lt;p>&lt;code>.hosts[].tls&lt;/code> configures transport-layer TLS for the host&amp;rsquo;s registry requests,
separately from the HTTP-layer &lt;code>credential&lt;/code>. It is applied by &lt;code>sync&lt;/code> (which
connects to the registry); &lt;code>login&lt;/code> and &lt;code>secret&lt;/code> do not open registry connections
and ignore it. It is not allowed on a &lt;code>provider&lt;/code> host. The field has two
independent halves, of which at least one must be set:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.serverAuth&lt;/code>, how the registry&amp;rsquo;s server certificate is verified. Set exactly
one of &lt;code>.fromPath&lt;/code> / &lt;code>.value&lt;/code> to supply a custom CA bundle
(one or more concatenated PEM certificates), or &lt;code>.spiffe&lt;/code> to verify the
server&amp;rsquo;s X.509-SVID against the SPIFFE trust bundle. When &lt;code>.serverAuth&lt;/code> is
omitted entirely, the system trust pool is used.&lt;/li>
&lt;li>&lt;code>.clientAuth&lt;/code>, the client certificate for mTLS. Set exactly one of &lt;code>.provider: x509-svid&lt;/code> (present a SPIFFE X.509-SVID from the Workload API) or the static
&lt;code>.certificate&lt;/code> + &lt;code>.key&lt;/code> pair. The &lt;code>.certificate&lt;/code> is one of
&lt;code>.fromPath&lt;/code>/&lt;code>.value&lt;/code>; the &lt;code>.key&lt;/code> is one of &lt;code>.fromPath&lt;/code>/&lt;code>.value&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Each half chooses SPIFFE or non-SPIFFE independently, so SPIFFE can authenticate
the client while a normal or custom CA verifies the server, or vice versa. Under
&lt;code>.serverAuth.spiffe&lt;/code>, set exactly one of &lt;code>.serverID&lt;/code> (authorize one exact SPIFFE
ID), &lt;code>.trustDomain&lt;/code> (authorize any SVID in that trust domain; the value &lt;code>self&lt;/code>
means the client&amp;rsquo;s own trust domain, read from its X.509-SVID), or
&lt;code>.authorizeAny: true&lt;/code> (accept any SVID the bundle can validate — discouraged).
With SPIFFE on either side, the client SVID and trust bundle come from the
ambient Workload API socket (&lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code>) and rotate automatically.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Custom CA for server verification and a static client cert for mTLS.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tls&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serverAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fromPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./certs/ca.crt&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">certificate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fromPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./certs/client.crt&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">key&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fromPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./certs/client.key&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Full SPIFFE: X.509-SVID client cert and SPIFFE server verification.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tls&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>x509-svid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serverAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spiffe&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serverID&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe://example.org/registry&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># trustDomain: example.org # or any SVID in this trust domain&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># trustDomain: self # or any SVID in our own trust domain&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># authorizeAny: true # or any SVID at all (discouraged)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Client-only SPIFFE: SPIFFE client cert, server verified by a public/custom CA.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>public.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tls&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>x509-svid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># serverAuth omitted → system trust pool verifies the server.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="blob-upload-chunking">Blob upload chunking&lt;/h4>
&lt;p>&lt;code>.hosts[].maxChunkSize&lt;/code> is the maximum size, in KiB (1024 bytes), of a
blob-upload &lt;code>PATCH&lt;/code> request to this host; larger blobs are split into chunked
uploads. It defaults to &lt;code>0&lt;/code>, which disables chunking and sends one monolithic
&lt;code>PATCH&lt;/code> per blob. Set it for registries or proxies that cap request body sizes.&lt;/p>
&lt;h2 id="working-with-config">Working with Config&lt;/h2>
&lt;h3 id="overwrite-and-drift-behavior">Overwrite and drift behavior&lt;/h3>
&lt;p>&lt;code>.artifacts[].overwrite&lt;/code> and &lt;code>.charts[].overwrite&lt;/code> are optional fields that
control what happens when a tag or version already exists at the destination.
&lt;code>flux mirror&lt;/code> does not overwrite by default, which keeps the safe path the
default on immutable registries (ECR with &lt;code>IMMUTABLE&lt;/code> tag mutability, GAR with
tag immutability, Harbor with retention rules) and avoids redundant writes on
mutable ones.&lt;/p>
&lt;p>When a tag already exists at the destination, the source and destination digests
are compared:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Same digest&lt;/strong> — skipped silently.&lt;/li>
&lt;li>&lt;strong>Different digest, &lt;code>overwrite: false&lt;/code>&lt;/strong> — a warning is logged and the tag is
left alone, reported as &lt;code>drifted&lt;/code>. The mirror has diverged from source but is
not brought back in sync without explicit consent.&lt;/li>
&lt;li>&lt;strong>Different digest, &lt;code>overwrite: true&lt;/code>&lt;/strong> — the new digest is pushed, replacing
the destination tag. This fails if the destination registry enforces tag
immutability.&lt;/li>
&lt;/ul>
&lt;p>The drift warning is useful even on immutable registries where the divergence
cannot be resolved automatically: it surfaces in the run output and the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/#exit-codes">exit code&lt;/a>, which audit and alerting can hook
into. The
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/#flags">&lt;code>--overwrite&lt;/code>&lt;/a> CLI flag forces &lt;code>overwrite: true&lt;/code> for every entry, overriding per-entry values, for one-off resyncs.&lt;/p>
&lt;p>With &lt;code>includeReferrers: true&lt;/code>, the same rule applies to referrers. Referrers
missing at the destination are always mirrored (there is nothing to overwrite) —
the common case when an upstream signature is published after the artifact was
first mirrored. Referrers that exist with a different digest are skipped
(default) or replaced (&lt;code>overwrite: true&lt;/code>).&lt;/p>
&lt;h3 id="defaults">Defaults&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Field&lt;/th>
&lt;th>Default&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>artifacts[].selector.sortBy&lt;/code>&lt;/td>
&lt;td>&lt;code>semver&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifacts[].selector.limit&lt;/code>&lt;/td>
&lt;td>&lt;code>1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifacts[].overwrite&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifacts[].includeReferrers&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifacts[].verify&lt;/code>&lt;/td>
&lt;td>unset&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>charts[].version&lt;/code>&lt;/td>
&lt;td>&lt;code>*&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>charts[].limit&lt;/code>&lt;/td>
&lt;td>&lt;code>1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>charts[].overwrite&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].credential.aud&lt;/code>&lt;/td>
&lt;td>&lt;code>host&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].credential.exp&lt;/code>&lt;/td>
&lt;td>&lt;code>60s&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].maxChunkSize&lt;/code>&lt;/td>
&lt;td>&lt;code>0&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A &lt;code>limit&lt;/code> of &lt;code>0&lt;/code> disables the cap and mirrors every matching tag or version. Use
it with care: unrestricted mirrors can consume significant bandwidth and storage
and may trip rate limits on upstream registries.&lt;/p></description></item><item><title>Flux: Flux Mirror Sync Command</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/</guid><description>
&lt;p>The &lt;code>flux mirror sync&lt;/code> command mirrors Helm charts and OCI artifacts between
registries based on a declarative
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/">&lt;code>Config&lt;/code>&lt;/a>. It is
idempotent: re-running against the same config produces the same destination
state, copying only what is missing or drifted.&lt;/p>
&lt;h2 id="synopsis">Synopsis&lt;/h2>
&lt;pre tabindex="0">&lt;code>flux mirror sync [CONFIG|-] [flags]
&lt;/code>&lt;/pre>&lt;h2 id="configuration-source">Configuration source&lt;/h2>
&lt;p>The config path is resolved in the following order:&lt;/p>
&lt;ol>
&lt;li>The first positional argument (&lt;code>-&lt;/code> reads the config from stdin).&lt;/li>
&lt;li>The &lt;code>FLUX_MIRROR_CONFIG&lt;/code> environment variable.&lt;/li>
&lt;/ol>
&lt;p>If neither is set, the command errors. Unlike
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/">&lt;code>secret&lt;/code>&lt;/a>, &lt;code>sync&lt;/code> has no executable-relative default path.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>flux mirror sync ./flux-mirror.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync - &amp;lt; ./flux-mirror.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bb60d5">FLUX_MIRROR_CONFIG&lt;/span>&lt;span style="color:#666">=&lt;/span>./flux-mirror.yaml flux mirror sync
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="authentication">Authentication&lt;/h2>
&lt;p>&lt;code>sync&lt;/code> authenticates OCI registry requests — &lt;code>artifacts&lt;/code> source/destination and
&lt;code>charts&lt;/code> &lt;code>oci://&lt;/code> destinations — per registry host:&lt;/p>
&lt;ol>
&lt;li>Hosts listed under
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#hosts">&lt;code>hosts&lt;/code>&lt;/a> use their configured
authentication:
&lt;ul>
&lt;li>
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider: ecr|acr|gar&lt;/code>&lt;/a> obtains the
registry&amp;rsquo;s native credentials from the cloud provider&amp;rsquo;s workload identity.&lt;/li>
&lt;li>
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> resolves a per-host
token from a cloud/CI identity, a JWK signature, or an environment variable
or file. With no &lt;code>username&lt;/code> it is sent as an HTTP Bearer credential; with a
&lt;code>username&lt;/code> it is the password in the standard registry auth challenge.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Hosts not listed under &lt;code>hosts&lt;/code> use the ambient Docker config and credential
helpers (&lt;code>~/.docker/config.json&lt;/code>, or &lt;code>$DOCKER_CONFIG&lt;/code> when set).&lt;/li>
&lt;/ol>
&lt;p>A non-&lt;code>provider&lt;/code> host may also set
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#transport-tls">&lt;code>tls&lt;/code>&lt;/a> to
configure transport-layer TLS for its registry connections: a custom CA, a client
certificate (mTLS), or SPIFFE X.509-SVID mTLS.&lt;/p>
&lt;p>HTTP/S Helm repository authentication (for &lt;code>charts&lt;/code> sources) is separate from OCI
auth and always comes from the ambient Helm repositories config:&lt;/p>
&lt;ul>
&lt;li>Helm&amp;rsquo;s default &lt;code>repositories.yaml&lt;/code> path, or &lt;code>$HELM_REPOSITORY_CONFIG&lt;/code> when set.&lt;/li>
&lt;li>The &lt;code>username&lt;/code>/&lt;code>password&lt;/code>, &lt;code>certFile&lt;/code>, &lt;code>keyFile&lt;/code>, &lt;code>caFile&lt;/code>,
&lt;code>insecure_skip_tls_verify&lt;/code>, and &lt;code>pass_credentials_all&lt;/code> fields are honored.&lt;/li>
&lt;/ul>
&lt;p>Add repositories with &lt;code>helm repo add&lt;/code> and &lt;code>flux mirror&lt;/code> picks up matching HTTP/S
repository credentials automatically.&lt;/p>
&lt;h2 id="flags">Flags&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Flag&lt;/th>
&lt;th>Default&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>-o, --output text|yaml|json&lt;/code>&lt;/td>
&lt;td>&lt;code>text&lt;/code>&lt;/td>
&lt;td>Output format. &lt;code>text&lt;/code> is human-friendly; &lt;code>yaml&lt;/code> and &lt;code>json&lt;/code> print the structured
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/report/">sync report&lt;/a> to stdout.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--concurrency N&lt;/code>&lt;/td>
&lt;td>&lt;code>4&lt;/code>&lt;/td>
&lt;td>Maximum number of copy operations to run in parallel per job. Must be greater than &lt;code>0&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--retries N&lt;/code>&lt;/td>
&lt;td>&lt;code>3&lt;/code>&lt;/td>
&lt;td>Maximum number of retry attempts per job, within the &lt;code>--timeout&lt;/code> budget. Must be greater than or equal to &lt;code>0&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--timeout DURATION&lt;/code>&lt;/td>
&lt;td>&lt;code>5m&lt;/code>&lt;/td>
&lt;td>Per-job total budget covering all retry attempts. Must be greater than &lt;code>0&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--overwrite&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Force &lt;code>overwrite: true&lt;/code> on every entry, regardless of per-entry config. See
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#overwrite-and-drift-behavior">Overwrite and drift behavior&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--drift-exit-code N&lt;/code>&lt;/td>
&lt;td>&lt;code>2&lt;/code>&lt;/td>
&lt;td>Exit code to use when drift is detected without failures (0–255). Set to &lt;code>0&lt;/code> for immutable destinations that should not fail CI on drift.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--dry-run&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Run the plan and comparison pipeline without performing any writes. Reported as &lt;code>would-copy&lt;/code> / &lt;code>would-overwrite&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--verbose&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Log every operation and the involved digests on stderr. Suppresses the spinner.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--no-progress&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Disable the live progress spinner. Per-job lines and the summary still print.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--insecure&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Allow plaintext HTTP and skip TLS verification. Test/dev only.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Global flags: &lt;code>--timeout&lt;/code> sets the default operation timeout, and &lt;code>--no-envsubst&lt;/code> disables config environment substitution.&lt;/p>
&lt;h2 id="output">Output&lt;/h2>
&lt;h3 id="text-mode-default">Text mode (default)&lt;/h3>
&lt;pre tabindex="0">&lt;code>✓ ghcr.io/stefanprodan/charts/podinfo:6.10.2 (skipped)
→ localhost:5050/charts/podinfo:6.10.2
✓ ghcr.io/stefanprodan/charts/podinfo:6.11.0 (copied)
→ localhost:5050/charts/podinfo:6.11.0
✗ ghcr.io/stefanprodan/charts/podinfo2 — plan failed: NAME_UNKNOWN
Summary: 1 copied, 1 skipped, 1 failed in 4.2s.
&lt;/code>&lt;/pre>&lt;h3 id="verbose-mode">Verbose mode&lt;/h3>
&lt;p>&lt;code>--verbose&lt;/code> replaces the pretty output with a full diagnostic log stream on
stderr — every layer push, blob check, manifest digest, referrer fallback-tag
update, and registry-side warning. Reach for it when diagnosing TLS, auth,
missing-blob, or push-rejection issues.&lt;/p>
&lt;pre tabindex="0">&lt;code>2026/05/13 09:00:00 sync started entries=3 concurrency=4 retries=3 timeout=5m0s
2026/05/13 09:00:00 mirroring tag src=ghcr.io/foo/bar:1.0 dst=localhost:5050/bar:1.0
2026/05/13 09:00:00 Copying from ghcr.io/foo/bar:1.0 to localhost:5050/bar:1.0
2026/05/13 09:00:00 pushed blob: sha256:fdf53ef8e04176eedbd42713efb2d002f1741c310627b38f444c6f6d92a598f7
2026/05/13 09:00:01 tag done src=ghcr.io/foo/bar:1.0 status=copied elapsed=812ms
…
2026/05/13 09:00:04 sync complete entries=3 copied=1 overwritten=0 skipped=1 drifted=0 failed=1 duration=4.213s
&lt;/code>&lt;/pre>&lt;h3 id="structured-output">Structured output&lt;/h3>
&lt;p>&lt;code>-o yaml&lt;/code> and &lt;code>-o json&lt;/code> print a versioned report to stdout: a top-level envelope
(&lt;code>apiVersion&lt;/code>, &lt;code>kind&lt;/code>, &lt;code>$schema&lt;/code>, &lt;code>report&lt;/code>) wrapping run metadata (&lt;code>reporter&lt;/code>,
&lt;code>timestamp&lt;/code>, &lt;code>durationMs&lt;/code>), an aggregate &lt;code>summary&lt;/code> of per-status tag counts, and
&lt;code>results[]&lt;/code> where each entry carries a &lt;code>status&lt;/code> (&lt;code>completed&lt;/code>/&lt;code>failed&lt;/code>) and a
&lt;code>tags[]&lt;/code> array of per-tag rows. Every row has a &lt;code>status&lt;/code> and, when known, a
source &lt;code>digest&lt;/code>; skipped rows carry a &lt;code>reason&lt;/code>; verified rows carry a
&lt;code>verification&lt;/code> block; and, with &lt;code>includeReferrers: true&lt;/code>, a &lt;code>referrers[]&lt;/code> array.
The envelope is documented by the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/report/">sync report schema&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Status of every tag, per entry&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync config.yaml -o json | jq &lt;span style="color:#4070a0">&amp;#39;.report.results[].tags[] | {tag, status}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Tags that were copied&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync config.yaml -o json | jq &lt;span style="color:#4070a0">&amp;#39;.report.results[].tags[] | select(.status == &amp;#34;copied&amp;#34;) | .tag&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Aggregate counts&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync config.yaml -o json | jq &lt;span style="color:#4070a0">&amp;#39;.report.summary&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="status">Status&lt;/h2>
&lt;p>Each tag row (and each referrer row) lands in exactly one of these statuses:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Status&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>copied&lt;/code>&lt;/td>
&lt;td>Destination did not have the tag; mirrored from source.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>overwritten&lt;/code>&lt;/td>
&lt;td>Destination had a different digest; replaced (only with &lt;code>overwrite: true&lt;/code>).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>skipped&lt;/code>&lt;/td>
&lt;td>Nothing was copied; the row&amp;rsquo;s &lt;code>reason&lt;/code> says why.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>drifted&lt;/code>&lt;/td>
&lt;td>Destination has a different digest, &lt;code>overwrite: false&lt;/code> — left alone, surfaced in the summary.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>would-copy&lt;/code>&lt;/td>
&lt;td>Dry-run forecast: would have been copied.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>would-overwrite&lt;/code>&lt;/td>
&lt;td>Dry-run forecast: would have been overwritten.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>failed&lt;/code>&lt;/td>
&lt;td>The operation errored; the row&amp;rsquo;s &lt;code>error&lt;/code> carries the message.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A &lt;code>skipped&lt;/code> row&amp;rsquo;s &lt;code>reason&lt;/code> is either &lt;code>up-to-date&lt;/code> (the destination already has
the same digest) or &lt;code>signature-too-new&lt;/code> (a valid signature deferred by
&lt;code>verify.minAge&lt;/code>).&lt;/p>
&lt;p>A failed signature verification (bad/missing signature, OIDC mismatch) is
recorded as a &lt;code>failed&lt;/code> tag row carrying the verify error; verification runs at
plan time, so the first failure stops the entry (tags verified before it still
run) while the entry itself stays &lt;code>completed&lt;/code>. A true plan-time failure (e.g. the
source registry rejected a &lt;code>ListTags&lt;/code>) surfaces as an entry with &lt;code>status: &amp;quot;failed&amp;quot;&lt;/code>, an &lt;code>error&lt;/code>, and an empty &lt;code>tags&lt;/code> array, reported live as a &lt;code>✗ &amp;lt;entry&amp;gt; — plan failed: &amp;lt;err&amp;gt;&lt;/code> line. Both kinds count toward &lt;code>failed&lt;/code>.&lt;/p>
&lt;h3 id="referrers">Referrers&lt;/h3>
&lt;p>When an entry sets &lt;code>includeReferrers: true&lt;/code>, each tag row gains a &lt;code>referrers[]&lt;/code>
array — one row per mirrored sub-artifact (cosign signature bundle, SBOM,
attestation), each with its own &lt;code>digest&lt;/code>, &lt;code>artifactType&lt;/code>, &lt;code>status&lt;/code>, and (when
skipped) &lt;code>reason&lt;/code>. Referrers are evaluated under &lt;code>--dry-run&lt;/code> too.&lt;/p>
&lt;h2 id="exit-codes">Exit codes&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Code&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>0&lt;/code>&lt;/td>
&lt;td>Clean run — every tag was copied or skipped as expected, no drift, no failures.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>1&lt;/code>&lt;/td>
&lt;td>At least one tag job failed (network error, push rejected, retries exhausted, plan failure).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>2&lt;/code>&lt;/td>
&lt;td>No failures, but at least one tag drifted with &lt;code>overwrite: false&lt;/code>. The destination is out of date relative to source.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Failures take precedence over drift. &lt;code>--dry-run&lt;/code> does not bump the exit code for
&lt;code>would-copy&lt;/code> / &lt;code>would-overwrite&lt;/code>, but drift detection still produces &lt;code>2&lt;/code> by
default. Use &lt;code>--drift-exit-code=0&lt;/code> when the destination registry is known to be
immutable and drift should be logged without failing CI.&lt;/p>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;h3 id="mirror-https-helm-charts-into-oci-helm-charts">Mirror HTTP/S Helm charts into OCI Helm charts&lt;/h3>
&lt;p>Many common Kubernetes ecosystem components are still published only to classic
HTTP/S Helm repositories and do not offer OCI Helm charts. The
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#charts">&lt;code>charts&lt;/code>&lt;/a> section pulls those charts over HTTP/S and
re-publishes them as OCI Helm charts so a cluster can consume them from a single
OCI registry.&lt;/p>
&lt;p>This config mirrors a few such charts into GitHub Container Registry (GHCR):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># flux-mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">charts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ingress-nginx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://kubernetes.github.io/ingress-nginx&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://ghcr.io/my-org/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=4.11.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>cert-manager&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://charts.jetstack.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://ghcr.io/my-org/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.15.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>metrics-server&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://kubernetes-sigs.github.io/metrics-server&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://ghcr.io/my-org/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=3.12.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>kube-prometheus-stack&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://prometheus-community.github.io/helm-charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>oci://ghcr.io/my-org/charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">version&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=60.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># GHCR is the push destination. It expects a username/password login (the&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># username is any non-empty value; GHCR authorizes by the token), so `username`&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># is set and GITHUB_TOKEN is the password.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-org&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${GH_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each chart lands at &lt;code>ghcr.io/my-org/charts/&amp;lt;name&amp;gt;:&amp;lt;version&amp;gt;&lt;/code>. Run it from GitHub
Actions with &lt;code>packages: write&lt;/code> so the workflow&amp;rsquo;s &lt;code>GITHUB_TOKEN&lt;/code> can push:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/mirror-charts.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror-charts&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">cron&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">workflow_dispatch&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">packages&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sync&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror sync ./flux-mirror.yaml --no-progress&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GH_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mirror-oci-helm-charts-into-oci-helm-charts">Mirror OCI Helm charts into OCI Helm charts&lt;/h3>
&lt;p>A Helm chart that already lives in an OCI registry is mirrored OCI-to-OCI as a
plain OCI artifact. The
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#artifacts">&lt;code>artifacts&lt;/code>&lt;/a> section is
the only way to mirror an OCI Helm chart between OCI repositories — the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#charts">&lt;code>charts&lt;/code>&lt;/a> section is exclusively for HTTP/S sources.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/stefanprodan/charts/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/my-org/charts/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=6.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mirror-images-from-ghcr-into-a-private-registry">Mirror images from GHCR into a private registry&lt;/h3>
&lt;p>This mirrors &lt;code>ghcr.io/fluxcd&lt;/code> controller images into a private registry from
GitHub Actions. The destination authenticates with a username and password from
GitHub Actions secrets. The GHCR source is authenticated with &lt;code>GITHUB_TOKEN&lt;/code>
(rather than pulled anonymously) to avoid anonymous pull rate limits — GHCR
expects a username/password login, so &lt;code>username&lt;/code> is set there too.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># flux-mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/source-controller&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.internal.example.com/fluxcd/source-controller&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/kustomize-controller&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.internal.example.com/fluxcd/kustomize-controller&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Source: authenticate to GHCR to avoid anonymous rate limits.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-org&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${GH_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Destination: a private registry expecting a username/password login. The&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># username and password are substituted from the environment while loading the&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># config.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.internal.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${REGISTRY_USERNAME}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${REGISTRY_PASSWORD}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/mirror-images.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror-images&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">cron&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">packages&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sync&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Substitute the destination username into the config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>envsubst &amp;#39;${REGISTRY_USERNAME}&amp;#39; &amp;lt; flux-mirror.yaml &amp;gt; rendered.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">REGISTRY_USERNAME&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.REGISTRY_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror sync ./rendered.yaml --no-progress&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GH_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">REGISTRY_PASSWORD&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.REGISTRY_PASSWORD }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mirror-into-ecr-acr-or-gar-from-github-actions">Mirror into ECR, ACR, or GAR from GitHub Actions&lt;/h3>
&lt;p>To mirror container images, OCI Helm charts, and Flux OCI artifacts into a cloud
registry, authenticate to the cloud provider with its GitHub Actions OIDC login
action, then set
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>hosts[].provider&lt;/code>&lt;/a> on the
destination host so &lt;code>flux-mirror&lt;/code> reuses that ambient identity. The source here
is GHCR, authenticated with &lt;code>GITHUB_TOKEN&lt;/code>.&lt;/p>
&lt;p>The config is the same for all three clouds except for the destination host and
&lt;code>provider&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># flux-mirror.yaml — Amazon ECR destination&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/source-controller &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># container image&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com/fluxcd/source-controller&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span style="color:#062873;font-weight:bold">, limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb"> &lt;/span>}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">includeReferrers&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/stefanprodan/charts/podinfo&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># OCI Helm chart&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com/charts/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=6.0.0&amp;#34;&lt;/span>&lt;span style="color:#062873;font-weight:bold">, limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb"> &lt;/span>}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/stefanprodan/manifests/podinfo&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Flux OCI artifact&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com/manifests/podinfo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">regex&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">pattern&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;^latest$&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">}, sortBy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>alphabetical }&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>my-org&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${GH_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For ACR or GAR, change only the destination host and &lt;code>provider&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Azure ACR&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>myregistry.azurecr.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>acr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Google GAR&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>us-docker.pkg.dev&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>gar&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The workflow differs only in the cloud login step, all using GitHub Actions OIDC
(no long-lived cloud keys):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/mirror-to-ecr.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror-to-ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">on&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">cron&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">packages&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id-token&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># required for GitHub Actions OIDC&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sync&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Amazon ECR ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>aws-actions/configure-aws-credentials@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">role-to-assume&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>arn:aws:iam::123456789012:role/flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">aws-region&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>us-east-1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Azure ACR (instead of the AWS step) ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - uses: azure/login@v2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># with:&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># client-id: ${{ secrets.AZURE_CLIENT_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># tenant-id: ${{ secrets.AZURE_TENANT_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Google GAR (instead of the AWS step) ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - uses: google-github-actions/auth@v2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># with:&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># workload_identity_provider: projects/123/locations/global/workloadIdentityPools/gh/providers/gh&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># service_account: flux-mirror@my-project.iam.gserviceaccount.com&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror sync ./flux-mirror.yaml --no-progress&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GH_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mirror-into-ecr-acr-or-gar-from-a-cronjob-with-workload-identity">Mirror into ECR, ACR, or GAR from a CronJob with workload identity&lt;/h3>
&lt;p>Inside a cluster, a &lt;code>CronJob&lt;/code> can mirror on a schedule using the pod&amp;rsquo;s workload
identity. The destination cloud registry is authenticated with
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>hosts[].provider&lt;/code>&lt;/a> (which uses the ambient
cloud credential chain — IRSA, AKS Workload Identity, or GKE Workload Identity).
The source registry here accepts the cluster&amp;rsquo;s ServiceAccount OIDC tokens, so it
is authenticated with a projected ServiceAccount token sent as an HTTP Bearer
credential (no &lt;code>username&lt;/code>).&lt;/p>
&lt;p>First, the ServiceAccount, annotated for each provider&amp;rsquo;s workload identity. The
projected token&amp;rsquo;s audience must be the &lt;strong>source&lt;/strong> registry host:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># EKS with IRSA (annotation mandatory)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eks.amazonaws.com/role-arn&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>arn:aws:iam::123456789012:role/flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># EKS Pod Identity needs no annotation (the association is created via the EKS&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># API); the same ServiceAccount works.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># AKS Workload Identity (annotation mandatory; the pod also needs the&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># azure.workload.identity/use: &amp;#34;true&amp;#34; label)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">azure.workload.identity/client-id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">00000000-0000-0000-0000-000000000000&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># GKE Workload Identity Federation. With direct KSA-to-IAM bindings no&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># annotation is needed; when impersonating a GCP service account, annotate it:&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">iam.gke.io/gcp-service-account&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror@my-project.iam.gserviceaccount.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The config authenticates the destination with &lt;code>provider&lt;/code> and the source with the
projected token. Because
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#resolving-file-paths">file-path fields are confined to the config&amp;rsquo;s own
directory&lt;/a>, the config and the token
are mounted together (below) so a relative &lt;code>fromPath&lt;/code> resolves:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source-registry.example.com/library/app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com/library/app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span style="color:#062873;font-weight:bold">, limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb"> &lt;/span>}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Source: a registry that validates the cluster&amp;#39;s ServiceAccount OIDC token.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># No username → the token is sent as an HTTP Bearer credential. Its audience&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># (the source host) is set by the projected-token volume, not here.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source-registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fromPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry-token&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Destination: ECR via the pod&amp;#39;s IRSA identity (use acr/gar to switch clouds).&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>CronJob&lt;/code> runs as the annotated ServiceAccount and projects a token whose
audience is the source registry, combined with the config in one volume so the
relative &lt;code>fromPath&lt;/code> stays within the config directory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>CronJob&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">concurrencyPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Forbid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jobTemplate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">azure.workload.identity/use&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># AKS Workload Identity only&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serviceAccountName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OnFailure&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-mirror:latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;sync&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/config/flux/mirror.yaml&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;--no-progress&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumeMounts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/config/flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumes&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># One projected volume holds both the config and the registry token,&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># so /config/flux/mirror.yaml can reference ./registry-token.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">projected&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sources&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">configMap&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-config &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># holds mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">serviceAccountToken&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry-token&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">audience&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source-registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">expirationSeconds&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3600&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="mirror-with-the-aws-credential-provider-into-a-spiffe-mtls-registry">Mirror with the AWS credential provider into a SPIFFE mTLS registry&lt;/h3>
&lt;p>This &lt;code>CronJob&lt;/code> pulls container images from a registry that authenticates the
caller&amp;rsquo;s AWS identity, and pushes them to a registry that requires SPIFFE
X.509-SVID mTLS. The source host uses the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#token-providers">&lt;code>aws&lt;/code> credential provider&lt;/a>, which signs an
&lt;code>sts:GetCallerIdentity&lt;/code> request the source registry replays to AWS STS, so the
pod needs AWS credentials via IRSA. The destination host uses
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#transport-tls">&lt;code>tls&lt;/code>&lt;/a> with a SPIFFE client certificate and
SPIFFE server verification, and no HTTP credential — the registry authorizes the
client by its X.509-SVID. The SPIFFE SVIDs and trust bundle come from the
Workload API, which the SPIFFE CSI driver exposes to the pod; go-spiffe locates
the socket through &lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code>.&lt;/p>
&lt;p>The ServiceAccount is annotated for IRSA so the &lt;code>aws&lt;/code> provider can sign as the
mapped AWS role:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eks.amazonaws.com/role-arn&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>arn:aws:iam::123456789012:role/flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">artifacts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">source&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source-registry.example.com/library/app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">destination&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.org/library/app&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">selector&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>{&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">semver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;&amp;gt;=1.0.0&amp;#34;&lt;/span>&lt;span style="color:#062873;font-weight:bold">, limit&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">5&lt;/span>&lt;span style="color:#bbb"> &lt;/span>}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Source: a registry that validates the caller&amp;#39;s AWS identity. No username →&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># the signed sts:GetCallerIdentity envelope is sent as an HTTP Bearer credential.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source-registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>aws&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># Destination: SPIFFE mTLS in both directions, no HTTP credential. Our client&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># X.509-SVID authenticates us; the server&amp;#39;s SVID is verified against our own&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># trust domain.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.org&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">tls&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>x509-svid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serverAuth&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spiffe&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">trustDomain&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>self&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>CronJob&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">concurrencyPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Forbid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jobTemplate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serviceAccountName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OnFailure&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-mirror:latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;sync&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/config/flux/mirror.yaml&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;--no-progress&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># go-spiffe reads the Workload API socket from this variable.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>SPIFFE_ENDPOINT_SOCKET&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>unix:///spiffe-workload-api/spire-agent.sock&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumeMounts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/config/flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumes&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">configMap&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-config &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># holds mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># The SPIFFE CSI driver exposes the SPIRE Agent Workload API socket.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">csi&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">driver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>csi.spiffe.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="preview-resync-and-ci-invocations">Preview, resync, and CI invocations&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Preview the plan without writing to the destination.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync ./flux-mirror.yaml --dry-run -o yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Force-resync every drifted tag.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync ./flux-mirror.yaml --overwrite
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># CI-friendly: no spinner. For immutable destinations, keep CI green on drift.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror sync ./flux-mirror.yaml --no-progress --drift-exit-code&lt;span style="color:#666">=&lt;/span>&lt;span style="color:#40a070">0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Flux: Flux Mirror Login Command</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/login/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/login/</guid><description>
&lt;p>The &lt;code>flux mirror login&lt;/code> command resolves the credentials configured under
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#hosts">&lt;code>hosts&lt;/code>&lt;/a> and stores them in the Docker config, the
same way &lt;code>docker login&lt;/code> does. It mints the &lt;em>same&lt;/em> credentials
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/">&lt;code>flux mirror sync&lt;/code>&lt;/a> would attach, but once — so any tool that reads
the Docker config (&lt;code>docker&lt;/code>, &lt;code>crane&lt;/code>, &lt;code>flux push artifact&lt;/code>, &lt;code>helm&lt;/code>) can then
authenticate as those identities.&lt;/p>
&lt;h2 id="synopsis">Synopsis&lt;/h2>
&lt;pre tabindex="0">&lt;code>flux mirror login [flags]
&lt;/code>&lt;/pre>&lt;h2 id="configuration-source">Configuration source&lt;/h2>
&lt;p>The config path is resolved as: the &lt;code>--config&lt;/code>/&lt;code>-f&lt;/code> flag, else
&lt;code>$FLUX_MIRROR_CONFIG&lt;/code>, else a path next to the executable (&lt;code>&amp;lt;executable&amp;gt;.config&lt;/code>).
&lt;code>-f -&lt;/code> reads the config from stdin.&lt;/p>
&lt;p>&lt;code>login&lt;/code> reads only the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#hosts">&lt;code>hosts&lt;/code>&lt;/a> section, so — unlike
&lt;code>sync&lt;/code> — it accepts a config with no &lt;code>artifacts&lt;/code> or &lt;code>charts&lt;/code>.&lt;/p>
&lt;h2 id="flags">Flags&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Flag&lt;/th>
&lt;th>Default&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>-f, --config&lt;/code>&lt;/td>
&lt;td>&lt;code>$FLUX_MIRROR_CONFIG&lt;/code>, else &lt;code>&amp;lt;executable&amp;gt;.config&lt;/code>&lt;/td>
&lt;td>Path to the flux-mirror config, or &lt;code>-&lt;/code> for stdin.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--host&lt;/code>&lt;/td>
&lt;td>all hosts&lt;/td>
&lt;td>Registry host from the config to log in. Repeatable; defaults to all hosts.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--docker-config&lt;/code>&lt;/td>
&lt;td>&lt;code>$DOCKER_CONFIG&lt;/code>, else &lt;code>~/.docker&lt;/code>&lt;/td>
&lt;td>Docker config directory, like &lt;code>docker --config&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--plaintext&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Store the credential base64-encoded in &lt;code>config.json&lt;/code>, bypassing any OS keychain credential helper.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The global &lt;code>--timeout&lt;/code> flag (default &lt;code>1m&lt;/code>) bounds credential resolution, which
for &lt;code>provider&lt;/code> sources involves a network call to mint the token. The global
&lt;code>--no-envsubst&lt;/code> flag disables config environment substitution.&lt;/p>
&lt;h2 id="where-the-credential-goes">Where the credential goes&lt;/h2>
&lt;p>By default, the credential is written through the Docker credential store,
exactly like &lt;code>docker login&lt;/code>:&lt;/p>
&lt;ul>
&lt;li>If a credential helper is configured (&lt;code>credsStore&lt;/code>/&lt;code>credHelpers&lt;/code>) — or, on a
fresh config, one is auto-detected for the platform (a &lt;code>docker-credential-*&lt;/code>
binary on &lt;code>PATH&lt;/code>, e.g. &lt;code>osxkeychain&lt;/code>, &lt;code>secretservice&lt;/code>, &lt;code>pass&lt;/code>, &lt;code>wincred&lt;/code>) — the
secret goes to the OS keychain, not the config file.&lt;/li>
&lt;li>Otherwise, it falls back to a base64-encoded entry in &lt;code>config.json&lt;/code> (the same
plaintext fallback &lt;code>docker login&lt;/code> uses when no helper is available).&lt;/li>
&lt;/ul>
&lt;p>What gets written depends on the host:&lt;/p>
&lt;ul>
&lt;li>A cloud
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a> host, or a
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> host with &lt;code>username&lt;/code> set,
writes &lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code>.&lt;/li>
&lt;li>A &lt;code>credential&lt;/code> host without &lt;code>username&lt;/code> writes the bearer &lt;code>registrytoken&lt;/code> field
instead. Because credential helpers only store username/secret pairs, a
&lt;code>registrytoken&lt;/code> always goes to the config file (never a keychain helper). See
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#bearer-token-vs-usernamepassword">Bearer token vs. username/password&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Pass &lt;code>--plaintext&lt;/code> to force the base64 &lt;code>config.json&lt;/code> entry and bypass any
configured or auto-detected helper (this applies to the username/password case;
&lt;code>registrytoken&lt;/code> is always file-based).&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Note:&lt;/strong> a TLS-only host (only &lt;code>tls&lt;/code>, no &lt;code>credential&lt;/code>/&lt;code>provider&lt;/code>) has nothing
to store and is skipped with a &lt;code>• skipping &amp;lt;host&amp;gt;&lt;/code> message.&lt;/p>
&lt;/blockquote>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;h3 id="log-in-to-ecr-acr-or-gar-from-github-actions">Log in to ECR, ACR, or GAR from GitHub Actions&lt;/h3>
&lt;p>Authenticate to the cloud provider with its GitHub Actions OIDC login action,
then &lt;code>flux-mirror login&lt;/code> with a
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a>
host. &lt;code>login&lt;/code> mints the registry&amp;rsquo;s native credentials from that ambient identity
and writes them into the Docker config. Afterwards &lt;code>flux push artifact&lt;/code> (and any
other Docker-config-aware tool) authenticates &lt;strong>without&lt;/strong> a &lt;code>--provider&lt;/code> flag,
because the credentials are already in the config.&lt;/p>
&lt;p>A hosts-only config — switch clouds by changing the host and &lt;code>provider&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># hosts.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - host: myregistry.azurecr.io&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># provider: acr&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - host: us-docker.pkg.dev&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># provider: gar&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/push.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">id-token&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># required for GitHub Actions OIDC&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Amazon ECR ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>aws-actions/configure-aws-credentials@v4&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">with&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">role-to-assume&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>arn:aws:iam::123456789012:role/flux-push&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">aws-region&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>us-east-1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Azure ACR (instead of the AWS step) ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - uses: azure/login@v2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># with:&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># client-id: ${{ secrets.AZURE_CLIENT_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># tenant-id: ${{ secrets.AZURE_TENANT_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># --- Google GAR (instead of the AWS step) ---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - uses: google-github-actions/auth@v2&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># with:&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># workload_identity_provider: projects/123/locations/global/workloadIdentityPools/gh/providers/gh&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># service_account: flux-push@my-project.iam.gserviceaccount.com&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror login -f ./hosts.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># No --provider needed: the credential is already in the Docker config.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> flux push artifact \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> oci://123456789012.dkr.ecr.us-east-1.amazonaws.com/manifests/app:v1.0.0 \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --path=./manifests --source=&amp;#34;$GITHUB_REPOSITORY&amp;#34; --revision=&amp;#34;$GITHUB_SHA&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="log-in-to-ghcr-from-github-actions">Log in to GHCR from GitHub Actions&lt;/h3>
&lt;p>GHCR authorizes by the token and ignores the username value, but it still expects
a username/password login — so set &lt;code>username&lt;/code> to any value (for example a
&lt;code>github.*&lt;/code> context key) and pass &lt;code>GITHUB_TOKEN&lt;/code> as the password via &lt;code>value&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># hosts.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${GITHUB_REPOSITORY_OWNER} &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ignored by GHCR; any value works&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${GH_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/push.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">permissions&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">contents&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>read&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">packages&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>write&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror login -f ./hosts.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">GH_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.GITHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> flux push artifact oci://ghcr.io/$GITHUB_REPOSITORY_OWNER/manifests/app:v1.0.0 \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --path=./manifests --source=&amp;#34;$GITHUB_REPOSITORY&amp;#34; --revision=&amp;#34;$GITHUB_SHA&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="log-in-to-docker-hub-from-github-actions">Log in to Docker Hub from GitHub Actions&lt;/h3>
&lt;p>Docker Hub uses a standard username/password login. Set &lt;code>username&lt;/code> to the Docker
Hub account and pass an access token as the password via &lt;code>value&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># hosts.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>docker.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${DOCKERHUB_USERNAME}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${DOCKERHUB_TOKEN}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># .github/workflows/push.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">jobs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">push&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">runs-on&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ubuntu-latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">steps&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>actions/checkout@v6&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">uses&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>fluxcd/flux-mirror/actions/setup@main&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>envsubst &amp;#39;${DOCKERHUB_USERNAME}&amp;#39; &amp;lt; hosts.yaml &amp;gt; rendered.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">DOCKERHUB_USERNAME&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror login -f ./rendered.yaml&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">DOCKERHUB_TOKEN&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_TOKEN }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">run&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>|&lt;span style="color:#4070a0;font-style:italic">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> flux push artifact oci://docker.io/$DOCKERHUB_USERNAME/manifests/app:v1.0.0 \
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-style:italic"> --path=./manifests --source=&amp;#34;$GITHUB_REPOSITORY&amp;#34; --revision=&amp;#34;$GITHUB_SHA&amp;#34;&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">DOCKERHUB_USERNAME&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${{ secrets.DOCKERHUB_USERNAME }}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="common-invocations">Common invocations&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Log in to every host in the default config.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror login
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Specific hosts from a specific config file.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror login --host registry.example.com --host other.example.com -f ./flux-mirror.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Read the config from stdin.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror login -f - &amp;lt; flux-mirror.yaml
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Use an alternate Docker config directory, like &amp;#39;docker --config&amp;#39;.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror login --docker-config /tmp/docker
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Force a plaintext config.json entry instead of the OS keychain.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror login --plaintext
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="notes">Notes&lt;/h2>
&lt;ul>
&lt;li>The credential is short-lived (a freshly minted provider token, a signed JWT,
or whatever &lt;code>value&lt;/code>/&lt;code>fromPath&lt;/code> holds). Re-run &lt;code>login&lt;/code> before it expires; for
&lt;code>provider&lt;/code> sources the registry re-validates each request, so a stored
credential stops working once it lapses. To mint a longer-lived login token,
use a &lt;code>jwkPath&lt;/code>/&lt;code>jwkValue&lt;/code> credential with a longer &lt;code>exp&lt;/code> — see
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/keygen/">keygen&lt;/a>.&lt;/li>
&lt;li>For &lt;code>aws&lt;/code>, the credential is a JWT-shaped envelope wrapping a signed
&lt;code>sts:GetCallerIdentity&lt;/code> request, not an OIDC token. The destination registry
must understand this scheme — see
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#token-providers">Token providers&lt;/a>.&lt;/li>
&lt;/ul></description></item><item><title>Flux: Flux Mirror Secret Command</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/secret/</guid><description>
&lt;p>The &lt;code>flux mirror secret &amp;lt;name&amp;gt;&lt;/code> command resolves the credentials configured under
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#hosts">&lt;code>hosts&lt;/code>&lt;/a> for each selected host and writes them into a
Kubernetes Secret of type &lt;code>kubernetes.io/dockerconfigjson&lt;/code> — the same shape
&lt;code>kubectl create secret docker-registry&lt;/code> produces. These are the &lt;em>same&lt;/em>
credentials
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/">&lt;code>sync&lt;/code>&lt;/a> and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> resolve, so the Secret
can be referenced as an &lt;code>imagePullSecret&lt;/code>, or by a Flux
&lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code> &lt;code>.spec.secretRef&lt;/code>, to pull from a registry that
understands the configured identity.&lt;/p>
&lt;p>Its main use case is rotating short-lived pull Secrets from a &lt;code>CronJob&lt;/code>.&lt;/p>
&lt;h2 id="synopsis">Synopsis&lt;/h2>
&lt;pre tabindex="0">&lt;code>flux mirror secret &amp;lt;name&amp;gt; [flags]
&lt;/code>&lt;/pre>&lt;p>&lt;code>&amp;lt;name&amp;gt;&lt;/code> is the name of the Secret to create or replace.&lt;/p>
&lt;p>By default the Secret is &lt;strong>upserted&lt;/strong>: if one with the same name already exists it
is replaced in place. Pass &lt;code>--create&lt;/code> to instead fail if the Secret already
exists, matching &lt;code>kubectl create secret docker-registry&lt;/code>.&lt;/p>
&lt;h2 id="configuration-source">Configuration source&lt;/h2>
&lt;p>The config path is resolved as: the &lt;code>--config&lt;/code>/&lt;code>-f&lt;/code> flag, else
&lt;code>$FLUX_MIRROR_CONFIG&lt;/code>, else &lt;code>&amp;lt;executable&amp;gt;.config&lt;/code>. &lt;code>-f -&lt;/code> reads the config from
stdin. Like &lt;code>login&lt;/code>, &lt;code>secret&lt;/code> reads only the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#hosts">&lt;code>hosts&lt;/code>&lt;/a>
section and accepts a &lt;code>hosts&lt;/code>-only config.&lt;/p>
&lt;h2 id="flags">Flags&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Flag&lt;/th>
&lt;th>Default&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>-f, --config&lt;/code>&lt;/td>
&lt;td>&lt;code>$FLUX_MIRROR_CONFIG&lt;/code>, else &lt;code>&amp;lt;executable&amp;gt;.config&lt;/code>&lt;/td>
&lt;td>Path to the flux-mirror config, or &lt;code>-&lt;/code> for stdin.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--host&lt;/code>&lt;/td>
&lt;td>all hosts&lt;/td>
&lt;td>Registry host from the config to include. Repeatable; defaults to all hosts.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--create&lt;/code>&lt;/td>
&lt;td>&lt;code>false&lt;/code>&lt;/td>
&lt;td>Fail if the Secret already exists instead of replacing it.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>In addition, the command accepts the standard &lt;code>kubectl&lt;/code> connection flags and
their env vars — &lt;code>--kubeconfig&lt;/code> (&lt;code>$KUBECONFIG&lt;/code>), &lt;code>--context&lt;/code>, &lt;code>--cluster&lt;/code>,
&lt;code>-n&lt;/code>/&lt;code>--namespace&lt;/code>, &lt;code>--user&lt;/code>, &lt;code>--server&lt;/code>, &lt;code>--token&lt;/code>, &lt;code>--request-timeout&lt;/code>, &lt;code>--as&lt;/code>,
&lt;code>--certificate-authority&lt;/code>, &lt;code>--insecure-skip-tls-verify&lt;/code>, and so on. The namespace
defaults to the one from the active kubeconfig context, or the in-cluster
ServiceAccount namespace when running inside a pod.&lt;/p>
&lt;p>The global &lt;code>--timeout&lt;/code> flag (default &lt;code>1m&lt;/code>) bounds credential resolution, which
for &lt;code>provider&lt;/code> sources involves a network call to mint the token. The global
&lt;code>--no-envsubst&lt;/code> flag disables config environment substitution.&lt;/p>
&lt;h2 id="behavior">Behavior&lt;/h2>
&lt;ul>
&lt;li>Works both locally (kubeconfig) and in-cluster (falls back to the in-cluster
config and ServiceAccount namespace).&lt;/li>
&lt;li>By default the Secret is upserted (created, or replaced if it exists). With
&lt;code>--create&lt;/code>, an existing Secret of the same name is an error. On replace,
existing labels and annotations are preserved.&lt;/li>
&lt;li>With no &lt;code>--host&lt;/code>, every host in &lt;code>hosts&lt;/code> is included. A &lt;code>--host&lt;/code> that is not
present in the config is an error.&lt;/li>
&lt;li>The Secret&amp;rsquo;s &lt;code>.dockerconfigjson&lt;/code> holds one &lt;code>auths&lt;/code> entry per host. A cloud
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a> host, and a
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> host with &lt;code>username&lt;/code> set,
write &lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code> (understood by &lt;code>kubelet&lt;/code> and Flux). A
&lt;code>credential&lt;/code> host without &lt;code>username&lt;/code> writes the bearer &lt;code>registrytoken&lt;/code> field
(understood by go-containerregistry and Flux, &lt;strong>not&lt;/strong> by &lt;code>kubelet&lt;/code>). See
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#bearer-token-vs-usernamepassword">Bearer token vs. username/password&lt;/a>.&lt;/li>
&lt;li>A TLS-only host (only &lt;code>tls&lt;/code>, no &lt;code>credential&lt;/code>/&lt;code>provider&lt;/code>) has nothing to put in
the Secret and is skipped with a &lt;code>• skipping &amp;lt;host&amp;gt;&lt;/code> message.&lt;/li>
&lt;li>Credentials are short-lived (provider tokens, freshly signed JWTs). Re-run the
command to refresh the Secret before they expire; by default it replaces the
existing one in place.&lt;/li>
&lt;/ul>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;h3 id="rotate-ecr-acr-or-gar-pull-credentials-from-a-cronjob">Rotate ECR, ACR, or GAR pull credentials from a CronJob&lt;/h3>
&lt;p>A cloud registry such as ECR, ACR, or GAR issues only short-lived credentials, so
a &lt;code>CronJob&lt;/code> regenerates the pull Secret on a schedule using the pod&amp;rsquo;s workload
identity. A
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a> host mints the
registry&amp;rsquo;s native username/password from the ambient cloud identity, and &lt;code>secret&lt;/code>
writes it as a &lt;code>dockerconfigjson&lt;/code> Secret usable as an &lt;code>imagePullSecret&lt;/code>.&lt;/p>
&lt;p>The ServiceAccount carries each provider&amp;rsquo;s workload-identity annotations:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># EKS with IRSA (annotation mandatory; EKS Pod Identity needs none)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">eks.amazonaws.com/role-arn&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>arn:aws:iam::123456789012:role/flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># AKS Workload Identity (annotation mandatory; pod also needs the&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># azure.workload.identity/use: &amp;#34;true&amp;#34; label)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">azure.workload.identity/client-id&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">00000000-0000-0000-0000-000000000000&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># GKE Workload Identity Federation (annotation only when impersonating a GSA)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">annotations&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">iam.gke.io/gcp-service-account&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror@my-project.iam.gserviceaccount.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The ServiceAccount also needs RBAC to manage the Secret it writes:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>rbac.authorization.k8s.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Role&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">rules&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">apiGroups&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">resources&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;secrets&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">verbs&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;get&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;create&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;update&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#0e84b5;font-weight:bold">---&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>rbac.authorization.k8s.io/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>RoleBinding&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">subjects&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ServiceAccount&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">roleRef&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Role&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-secret&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">apiGroup&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>rbac.authorization.k8s.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A hosts-only config selecting the cloud registry (switch clouds via the host and
&lt;code>provider&lt;/code>), stored in a &lt;code>ConfigMap&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">123456789012.&lt;/span>dkr.ecr.us-east-1.amazonaws.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ecr&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - host: myregistry.azurecr.io&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># provider: acr&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># - host: us-docker.pkg.dev&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># provider: gar&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>CronJob&lt;/code> rotates the Secret in its own namespace:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>CronJob&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-regcreds&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;0 */6 * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># rotate well within the credential lifetime&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">concurrencyPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Forbid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jobTemplate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">labels&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">azure.workload.identity/use&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;true&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># AKS Workload Identity only&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serviceAccountName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OnFailure&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-mirror:latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># In-cluster: kubeconfig and namespace are auto-detected.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;secret&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;regcreds&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;-f&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/config/flux/mirror.yaml&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumeMounts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/config/flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumes&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">configMap&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-config &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># holds mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The resulting &lt;code>regcreds&lt;/code> Secret holds &lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code>, so it works
both as a pod &lt;code>imagePullSecret&lt;/code> and as a Flux &lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code>
&lt;code>.spec.secretRef&lt;/code>.&lt;/p>
&lt;h3 id="rotate-credentials-for-a-registry-that-accepts-serviceaccount-oidc-tokens">Rotate credentials for a registry that accepts ServiceAccount OIDC tokens&lt;/h3>
&lt;p>When the target registry validates the cluster&amp;rsquo;s ServiceAccount OIDC tokens
directly, project a ServiceAccount token whose audience is the registry host and
feed it to a
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> host via
&lt;code>fromPath&lt;/code>. Setting &lt;code>username&lt;/code> writes the token as the password of a
username/password pair (&lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code>), so the resulting Secret
works as a pod &lt;code>imagePullSecret&lt;/code> (which &lt;code>kubelet&lt;/code> can use) as well as a Flux
&lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code> &lt;code>.spec.secretRef&lt;/code>. The username value is
whatever the registry expects alongside the token — many token-validating
registries ignore it but require it to be present.&lt;/p>
&lt;p>Reuse the ServiceAccount and RBAC from the previous example (here the
ServiceAccount needs no cloud annotations — the registry trusts the cluster
issuer directly). The config references the token by a relative path, and because
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#resolving-file-paths">file-path fields are confined to the config&amp;rsquo;s own
directory&lt;/a>, the config and the token
are mounted together:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>sa-oidc &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># value the registry expects; often ignored&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">fromPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry-token &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># the audience is set by the projected volume&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>CronJob&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-regcreds&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;*/30 * * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># rotate ahead of the token&amp;#39;s expirationSeconds&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">concurrencyPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Forbid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jobTemplate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serviceAccountName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OnFailure&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-mirror:latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;secret&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;regcreds&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;-f&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/config/flux/mirror.yaml&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumeMounts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/config/flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumes&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># One projected volume holds both the config and the registry token,&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># so /config/flux/mirror.yaml can reference ./registry-token.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">projected&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sources&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">configMap&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-config &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># holds mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">serviceAccountToken&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry-token&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">audience&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">expirationSeconds&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#40a070">3600&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="rotate-credentials-using-a-spiffe-jwt-svid">Rotate credentials using a SPIFFE JWT-SVID&lt;/h3>
&lt;p>When the registry accepts SPIFFE JWT-SVIDs, use the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#token-providers">&lt;code>jwt-svid&lt;/code> credential provider&lt;/a>: it fetches
a JWT-SVID for the audience (the registry host) from the SPIFFE Workload API.
Setting &lt;code>username&lt;/code> writes the JWT-SVID as the password of a username/password
pair, so the resulting Secret works as a pod &lt;code>imagePullSecret&lt;/code> (which &lt;code>kubelet&lt;/code>
can use) as well as a Flux &lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code> &lt;code>.spec.secretRef&lt;/code>.
The Workload API is exposed to the pod by the SPIFFE CSI driver, and go-spiffe
locates it through the &lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code> environment variable.&lt;/p>
&lt;p>Reuse the ServiceAccount and secret-management RBAC from the first example (no
cloud annotations are needed; the registry trusts the SPIFFE trust domain).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">username&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># value the registry expects; often ignored&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>jwt-svid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># aud defaults to the host (registry.example.com)&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>batch/v1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>CronJob&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">metadata&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-regcreds&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">namespace&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-system&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">schedule&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;*/30 * * * *&amp;#34;&lt;/span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># rotate ahead of the JWT-SVID lifetime&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">concurrencyPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Forbid&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jobTemplate&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">template&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">spec&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">serviceAccountName&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">restartPolicy&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>OnFailure&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">containers&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">image&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ghcr.io/fluxcd/flux-mirror:latest&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">args&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>[&lt;span style="color:#4070a0">&amp;#34;secret&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;regcreds&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;-f&amp;#34;&lt;/span>,&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#4070a0">&amp;#34;/config/flux/mirror.yaml&amp;#34;&lt;/span>]&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">env&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># go-spiffe reads the Workload API socket from this variable.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>SPIFFE_ENDPOINT_SOCKET&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">value&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>unix:///spiffe-workload-api/spire-agent.sock&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumeMounts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/config/flux&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">mountPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>/spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">volumes&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">configMap&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>flux-mirror-config &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># holds mirror.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># The SPIFFE CSI driver exposes the SPIRE Agent Workload API socket.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">name&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe-workload-api&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">csi&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">driver&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>csi.spiffe.io&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">readOnly&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#007020;font-weight:bold">true&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The resulting Secret holds &lt;code>username&lt;/code>/&lt;code>password&lt;/code>/&lt;code>auth&lt;/code>, so it works as a pod
&lt;code>imagePullSecret&lt;/code> as well as a Flux &lt;code>OCIRepository&lt;/code>/&lt;code>HelmRepository&lt;/code>
&lt;code>.spec.secretRef&lt;/code>.&lt;/p>
&lt;h3 id="common-invocations">Common invocations&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Upsert a Secret for all hosts in the default config, in the current namespace.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror secret regcreds
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Fail if the Secret already exists, like &amp;#39;kubectl create secret docker-registry&amp;#39;.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror secret regcreds --create
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Specific hosts, a specific namespace and kubeconfig context.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror secret regcreds -n flux-system --context prod &lt;span style="color:#4070a0;font-weight:bold">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4070a0;font-weight:bold">&lt;/span> --host registry.example.com --host other.example.com -f ./flux-mirror.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Flux: Flux Mirror Keygen Command</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/keygen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/keygen/</guid><description>
&lt;p>The &lt;code>flux mirror keygen&lt;/code> command generates an EdDSA (Ed25519) JSON Web Key pair
for
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">JWK-based registry auth&lt;/a>. The output
files plug directly into the
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>hosts[].credential.jwkPath&lt;/code>&lt;/a> config field,
which
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/">&lt;code>sync&lt;/code>&lt;/a> and
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> use to sign JWTs.&lt;/p>
&lt;h2 id="synopsis">Synopsis&lt;/h2>
&lt;pre tabindex="0">&lt;code>flux mirror keygen [flags]
&lt;/code>&lt;/pre>&lt;p>It writes the key pair into a directory as two JWK sets:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>File&lt;/th>
&lt;th>Contents&lt;/th>
&lt;th>Mode&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>pubkey.json&lt;/code>&lt;/td>
&lt;td>Public JWK set — share with the registry or publish it as JWKS.&lt;/td>
&lt;td>&lt;code>0644&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>privkey.json&lt;/code>&lt;/td>
&lt;td>Private JWK set — keep it secret.&lt;/td>
&lt;td>&lt;code>0600&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Both files use the standard JWK set shape &lt;code>{&amp;quot;keys&amp;quot;:[...]}&lt;/code>. The two keys share a
UUIDv6 &lt;code>kid&lt;/code> so a signature made with the private key can be matched against the
public set.&lt;/p>
&lt;h2 id="flags">Flags&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Flag&lt;/th>
&lt;th>Default&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>-o, --output-dir&lt;/code>&lt;/td>
&lt;td>&lt;code>.&lt;/code>&lt;/td>
&lt;td>Directory to write &lt;code>pubkey.json&lt;/code> and &lt;code>privkey.json&lt;/code> into. Created if missing.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="behavior">Behavior&lt;/h2>
&lt;ul>
&lt;li>The output directory is created (&lt;code>mkdir -p&lt;/code>) if it does not exist.&lt;/li>
&lt;li>The command refuses to overwrite either file if it already exists. Delete them
or point at a fresh directory to rotate.&lt;/li>
&lt;li>The private file is written with mode &lt;code>0600&lt;/code>. If the public write fails, the
private file is rolled back, so a partial run never leaves a stray key behind.&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Write a key pair into the current directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror keygen
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># Write a key pair into ./keys/registry (created if missing).&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>flux mirror keygen -o ./keys/registry
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Output:&lt;/p>
&lt;pre tabindex="0">&lt;code>✔ private key set written to: ./keys/registry/privkey.json
✔ public key set written to: ./keys/registry/pubkey.json
&lt;/code>&lt;/pre>&lt;h2 id="example-mint-a-long-lived-login-token">Example: mint a long-lived login token&lt;/h2>
&lt;p>Unlike &lt;code>provider&lt;/code>/&lt;code>value&lt;/code> credentials — whose lifetime is fixed by an external
issuer — a &lt;code>jwkPath&lt;/code> credential is signed locally, so you control its lifetime
through
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>exp&lt;/code>&lt;/a>. This makes a key pair plus
&lt;code>login&lt;/code> a convenient way to mint a single, long-lived bearer token (for example,
a year-long token for a CI system or an air-gapped agent).&lt;/p>
&lt;p>&lt;strong>1. Generate the key pair.&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>flux mirror keygen -o ./keys/registry
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>2. Reference the private key from a host credential, with a long &lt;code>exp&lt;/code>.&lt;/strong> Set
&lt;code>iss&lt;/code>/&lt;code>sub&lt;/code> to the identity the registry expects, and &lt;code>exp&lt;/code> to the desired
lifetime (here ~1 year). With no &lt;code>username&lt;/code>, the signed JWT is stored as a bearer
&lt;code>registrytoken&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-yaml" data-lang="yaml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#60a0b0;font-style:italic"># config.yaml&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">apiVersion&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>mirror.plugin.fluxcd.io/v1beta1&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">kind&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>Config&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb">&lt;/span>&lt;span style="color:#062873;font-weight:bold">hosts&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- &lt;span style="color:#062873;font-weight:bold">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>registry.example.com&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">credential&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">jwkPath&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./keys/registry/privkey.json&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">iss&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>https://my-issuer.example&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">sub&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ci-pusher&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># aud: registry.example.com # optional, defaults to the host&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">exp&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>8760h &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># ~1 year&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>3. Log in once to mint and store the token.&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>flux mirror login -f ./config.yaml
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>login&lt;/code> signs one JWT valid for &lt;code>exp&lt;/code> and writes it to the Docker config, so
tools like &lt;code>flux push artifact&lt;/code> authenticate as that identity until it expires.&lt;/p>
&lt;p>&lt;strong>4. Grant access on the registry side.&lt;/strong> Share &lt;code>pubkey.json&lt;/code> with the registry
operator, or publish it at an HTTPS URL the registry can fetch as JWKS, so the
registry can verify tokens signed by the matching private key (matched by &lt;code>kid&lt;/code>).&lt;/p>
&lt;blockquote>
&lt;p>Re-run &lt;code>login&lt;/code> to mint a fresh token before the current one expires. Treat
&lt;code>privkey.json&lt;/code> as a secret: anyone holding it can mint tokens for &lt;code>sub&lt;/code> until
the public key is rotated out.&lt;/p>
&lt;/blockquote></description></item><item><title>Flux: Flux Mirror Report</title><link>https://fluxcd.io/flux/cli-plugins/flux-mirror/report/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://fluxcd.io/flux/cli-plugins/flux-mirror/report/</guid><description>
&lt;p>The &lt;code>flux mirror sync&lt;/code> command can emit a structured report of the mirror
results by setting &lt;code>--output&lt;/code> to &lt;code>json&lt;/code> or &lt;code>yaml&lt;/code>. The envelope shape is
versioned and documented by the JSON Schema in
&lt;a href="https://github.com/fluxcd/flux-mirror/blob/v0.8.0/docs/report-v1beta1.json" target="_blank">&lt;code>report-v1beta1.json&lt;/code>&lt;/a>.&lt;/p>
&lt;h2 id="usage">Usage&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>flux mirror sync config.yaml -o json
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Structured output always emits every entry regardless of &lt;code>--verbose&lt;/code>.
Filtering belongs downstream (&lt;code>jq&lt;/code>, &lt;code>yq&lt;/code>). The process exit code still
reflects whether any tag failed or drifted (see
&lt;a href="https://fluxcd.io/flux/cli-plugins/flux-mirror/sync/#exit-codes">exit codes&lt;/a>).&lt;/p>
&lt;h2 id="envelope">Envelope&lt;/h2>
&lt;p>Every report is wrapped in a top-level envelope:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>apiVersion&lt;/code>&lt;/td>
&lt;td>Report API version. Currently &lt;code>mirror.plugin.fluxcd.io/v1beta1&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>kind&lt;/code>&lt;/td>
&lt;td>Report API kind. Currently &lt;code>Report&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>$schema&lt;/code>&lt;/td>
&lt;td>URL of the JSON Schema describing the envelope. JSON only.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>report.reporter&lt;/code>&lt;/td>
&lt;td>Identity of the producer, e.g. &lt;code>flux-mirror/v0.1.0&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>report.timestamp&lt;/code>&lt;/td>
&lt;td>RFC 3339 UTC timestamp of the run.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>report.durationMs&lt;/code>&lt;/td>
&lt;td>Total wall time of the run, in milliseconds.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>report.summary&lt;/code>&lt;/td>
&lt;td>Aggregate counts across all entries (see below).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>report.results[]&lt;/code>&lt;/td>
&lt;td>One entry per config entry, in config order.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The &lt;code>$schema&lt;/code> key is JSON-only — it points at a JSON Schema document and
carries no meaning for YAML consumers, so it is dropped in YAML mode.&lt;/p>
&lt;h2 id="summary-fields">Summary fields&lt;/h2>
&lt;p>Every field is always present (zero when unused) so consumers can rely on a
stable key set.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>entries&lt;/code>&lt;/td>
&lt;td>Number of config entries processed.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>copied&lt;/code>&lt;/td>
&lt;td>Total tags copied across all entries.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>overwritten&lt;/code>&lt;/td>
&lt;td>Total tags overwritten (drift with &lt;code>overwrite: true&lt;/code>).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>skipped&lt;/code>&lt;/td>
&lt;td>Total tags skipped (already up to date, or deferred by &lt;code>verify.minAge&lt;/code>).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>drifted&lt;/code>&lt;/td>
&lt;td>Total tags drifted (different digest, &lt;code>overwrite: false&lt;/code>).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>wouldCopy&lt;/code>&lt;/td>
&lt;td>Dry-run forecast: tags that would have been copied.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>wouldOverwrite&lt;/code>&lt;/td>
&lt;td>Dry-run forecast: tags that would have been overwritten.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>failed&lt;/code>&lt;/td>
&lt;td>Total failures: tag-level failures plus plan-failed entries.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>exitCode&lt;/code>&lt;/td>
&lt;td>Semantic exit code: &lt;code>0&lt;/code> clean, &lt;code>1&lt;/code> failures, &lt;code>2&lt;/code> drift. Not affected by &lt;code>--drift-exit-code&lt;/code>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="result-fields">Result fields&lt;/h2>
&lt;p>Each &lt;code>results[]&lt;/code> entry describes one config entry (an artifact or chart source).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>source&lt;/code>&lt;/td>
&lt;td>Source repository/reference being mirrored.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>destination&lt;/code>&lt;/td>
&lt;td>Destination repository the entry mirrors into.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>status&lt;/code>&lt;/td>
&lt;td>Entry-level outcome: &lt;code>completed&lt;/code> or &lt;code>failed&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>error&lt;/code>&lt;/td>
&lt;td>Plan-time error message. Present only when &lt;code>status&lt;/code> is &lt;code>failed&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>tags[]&lt;/code>&lt;/td>
&lt;td>Per-tag results, in plan order (deterministic). May be empty.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The entry &lt;code>status&lt;/code> disambiguates an empty &lt;code>tags&lt;/code> array:&lt;/p>
&lt;ul>
&lt;li>&lt;code>completed&lt;/code> + &lt;code>[]&lt;/code> → the entry was planned and run, but nothing matched the selector.&lt;/li>
&lt;li>&lt;code>failed&lt;/code> + &lt;code>[]&lt;/code> (+ &lt;code>error&lt;/code>) → the entry could not be planned (e.g. tag listing failed).&lt;/li>
&lt;/ul>
&lt;p>A &lt;code>completed&lt;/code> entry can still contain tag-level &lt;code>failed&lt;/code> rows — those are per-tag
problems, not an entry-level failure.&lt;/p>
&lt;h2 id="tag-fields">Tag fields&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>tag&lt;/code>&lt;/td>
&lt;td>Tag name (artifacts) or chart version (charts).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>status&lt;/code>&lt;/td>
&lt;td>Per-tag outcome — see
&lt;a href="#status-values">Status&lt;/a>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>digest&lt;/code>&lt;/td>
&lt;td>Source artifact digest, when resolved. Absent on a verify-failed row.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>reason&lt;/code>&lt;/td>
&lt;td>Why a &lt;code>skipped&lt;/code> tag was skipped — see
&lt;a href="#reason-values">Reason&lt;/a>. Present only on &lt;code>skipped&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>error&lt;/code>&lt;/td>
&lt;td>Error message. Present only when &lt;code>status&lt;/code> is &lt;code>failed&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>referrers[]&lt;/code>&lt;/td>
&lt;td>Mirrored sub-artifacts (signatures, SBOMs, attestations). Present only with &lt;code>includeReferrers&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>verification&lt;/code>&lt;/td>
&lt;td>Signature verification metadata. Present only when a signature was confirmed (under &lt;code>verify:&lt;/code>).&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="status-values">Status values&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Status&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>copied&lt;/code>&lt;/td>
&lt;td>Destination did not have it; mirrored from source.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>overwritten&lt;/code>&lt;/td>
&lt;td>Destination had a different digest; replaced (&lt;code>overwrite: true&lt;/code>).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>skipped&lt;/code>&lt;/td>
&lt;td>Nothing copied; see &lt;code>reason&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>drifted&lt;/code>&lt;/td>
&lt;td>Destination has a different digest, &lt;code>overwrite: false&lt;/code> — left alone.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>would-copy&lt;/code>&lt;/td>
&lt;td>Dry-run: would have been copied.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>would-overwrite&lt;/code>&lt;/td>
&lt;td>Dry-run: would have been overwritten.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>failed&lt;/code>&lt;/td>
&lt;td>The operation errored; see &lt;code>error&lt;/code>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="reason-values">Reason values&lt;/h3>
&lt;p>&lt;code>reason&lt;/code> is always present on a &lt;code>skipped&lt;/code> status (tag or referrer) and absent otherwise.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Reason&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>up-to-date&lt;/code>&lt;/td>
&lt;td>Destination already has the same digest.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>signature-too-new&lt;/code>&lt;/td>
&lt;td>A valid signature was deferred because it is younger than &lt;code>verify.minAge&lt;/code>.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="referrer-fields">Referrer fields&lt;/h2>
&lt;p>Each &lt;code>referrers[]&lt;/code> entry describes one referrer (e.g. a cosign signature bundle, an
SBOM, or an attestation) mirrored alongside its parent tag.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>digest&lt;/code>&lt;/td>
&lt;td>Referrer manifest digest.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>artifactType&lt;/code>&lt;/td>
&lt;td>The referrer manifest&amp;rsquo;s &lt;code>artifactType&lt;/code>, when set.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>status&lt;/code>&lt;/td>
&lt;td>Same
&lt;a href="#status-values">Status&lt;/a> enum as a tag.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>reason&lt;/code>&lt;/td>
&lt;td>Present only on &lt;code>skipped&lt;/code> (same
&lt;a href="#reason-values">Reason&lt;/a>).&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Referrers are reported for any tag whose mirror flow reached the referrer step — every
status except &lt;code>failed&lt;/code> (the tag copy errored first) and the &lt;code>signature-too-new&lt;/code> skip
(deferred without mirroring). In &lt;code>--dry-run&lt;/code>, referrers are still evaluated and reported
with &lt;code>would-copy&lt;/code> / &lt;code>would-overwrite&lt;/code> / &lt;code>skipped&lt;/code> statuses.&lt;/p>
&lt;h2 id="verification-fields">Verification fields&lt;/h2>
&lt;p>Present only when a signature was confirmed (the entry has a &lt;code>verify:&lt;/code> block). Only
&lt;code>provider&lt;/code> is guaranteed; the rest is best-effort.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Key&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>provider&lt;/code>&lt;/td>
&lt;td>Verification provider, e.g. &lt;code>cosign&lt;/code>.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>issuer&lt;/code>&lt;/td>
&lt;td>Matched OIDC issuer from the signing certificate.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>identity&lt;/code>&lt;/td>
&lt;td>Matched certificate identity (subject alternative name).&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>integratedTime&lt;/code>&lt;/td>
&lt;td>Transparency-log integration time (RFC 3339). Present only when the bundle carries a Tlog timestamp.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>age&lt;/code>&lt;/td>
&lt;td>Signature age, as a Go duration string. Present only on a &lt;code>signature-too-new&lt;/code> skip.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>minAge&lt;/code>&lt;/td>
&lt;td>Configured &lt;code>verify.minAge&lt;/code>, as a Go duration string. Present only on a &lt;code>signature-too-new&lt;/code> skip.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>A verify &lt;em>failure&lt;/em> (bad/missing signature, OIDC mismatch) is not a &lt;code>verification&lt;/code> block:
it surfaces as a &lt;code>status: &amp;quot;failed&amp;quot;&lt;/code> tag row carrying the verify &lt;code>error&lt;/code>, with no
&lt;code>verification&lt;/code> and no &lt;code>digest&lt;/code>. Verification runs at plan time, so the first verify
failure stops the entry (tags verified before it still run); the entry itself stays
&lt;code>status: &amp;quot;completed&amp;quot;&lt;/code>. A real verify-failed row (the configured OIDC subject regex
did not match the signed identity):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;tag&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;6.13.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;failed&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;error&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;verify docker.io/stefanprodan/podinfo:6.13.0: signature verification failed: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex \&amp;#34;^https://github\\.com/example/.*$\&amp;#34;, got \&amp;#34;https://github.com/stefanprodan/podinfo/.github/workflows/release.yml@refs/tags/6.13.0\&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="example">Example&lt;/h2>
&lt;p>A report from &lt;code>flux mirror sync … -o json&lt;/code> against a config with cosign
verification (&lt;code>minAge: 48h&lt;/code>) and &lt;code>includeReferrers: true&lt;/code>. The first entry
completed: tag &lt;code>6.13.0&lt;/code> was deferred because its signature is younger than
&lt;code>minAge&lt;/code>, while &lt;code>6.12.0&lt;/code> was copied along with its cosign signature bundle. The
second entry failed at plan time because the source repository does not exist.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f0f0f0;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;apiVersion&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;mirror.plugin.fluxcd.io/v1beta1&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;kind&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;Report&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;$schema&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;https://raw.githubusercontent.com/fluxcd/flux-mirror/main/docs/report-v1beta1.json&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;report&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;reporter&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;flux-mirror/v0.1.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;timestamp&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;2026-06-06T20:12:25Z&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;durationMs&amp;#34;&lt;/span>: &lt;span style="color:#40a070">16724&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;summary&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;entries&amp;#34;&lt;/span>: &lt;span style="color:#40a070">2&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;copied&amp;#34;&lt;/span>: &lt;span style="color:#40a070">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;overwritten&amp;#34;&lt;/span>: &lt;span style="color:#40a070">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;skipped&amp;#34;&lt;/span>: &lt;span style="color:#40a070">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;drifted&amp;#34;&lt;/span>: &lt;span style="color:#40a070">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;wouldCopy&amp;#34;&lt;/span>: &lt;span style="color:#40a070">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;wouldOverwrite&amp;#34;&lt;/span>: &lt;span style="color:#40a070">0&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;failed&amp;#34;&lt;/span>: &lt;span style="color:#40a070">1&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;exitCode&amp;#34;&lt;/span>: &lt;span style="color:#40a070">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;results&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;source&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;docker.io/stefanprodan/podinfo&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;destination&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;localhost:5050/podinfo&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;completed&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;tags&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;tag&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;6.13.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;skipped&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;digest&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;sha256:7bdd8cc80b64377b05f5c0a51604c028ed6adea4beaca2e766b9325efa52916c&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;reason&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;signature-too-new&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;verification&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;provider&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;cosign&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;issuer&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;https://token.actions.githubusercontent.com&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;identity&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;https://github.com/stefanprodan/podinfo/.github/workflows/release.yml@refs/tags/6.13.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;integratedTime&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;2026-06-04T20:59:02Z&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;age&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;47h13m11s&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;minAge&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;48h0m0s&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;tag&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;6.12.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;copied&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;digest&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;sha256:453e2238f70c00dfba6803ddb99a4bb86982ee549f7273f43f90ac58f0714a81&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;referrers&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;digest&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;sha256:5892f840c5013230cd0b63fdd5076dd9fe3224a31bd3ebae5c723e3632100b0f&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;artifactType&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;application/vnd.dev.sigstore.bundle.v0.3+json&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;copied&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;verification&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;provider&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;cosign&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;issuer&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;https://token.actions.githubusercontent.com&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;identity&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;https://github.com/stefanprodan/podinfo/.github/workflows/release.yml@refs/tags/6.12.0&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;integratedTime&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;2026-05-20T10:16:39Z&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> },
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;source&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;docker.io/library/flux-mirror-nonexistent-zzz&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;destination&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;localhost:5050/nonexistent&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;status&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;failed&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;error&amp;#34;&lt;/span>: &lt;span style="color:#4070a0">&amp;#34;list tags docker.io/library/flux-mirror-nonexistent-zzz: GET https://index.docker.io/v2/library/flux-mirror-nonexistent-zzz/tags/list?n=1000: UNAUTHORIZED: authentication required; [map[Action:pull Class: Name:library/flux-mirror-nonexistent-zzz Type:repository]]&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#062873;font-weight:bold">&amp;#34;tags&amp;#34;&lt;/span>: []
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item></channel></rss>