<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flux – Flux Mirror</title><link>https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/index.xml" rel="self" type="application/rss+xml"/><item><title>Flux: Flux Mirror Config</title><link>https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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.14.1/docs/config-v1beta2.json" target="_blank">&lt;code>config-v1beta2.json&lt;/code>&lt;/a>; the deprecated
&lt;a href="https://github.com/fluxcd/flux-mirror/blob/v0.14.1/docs/config-v1beta1.json" target="_blank">&lt;code>config-v1beta1.json&lt;/code>&lt;/a> remains published for existing
configs. It is consumed by
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/sync/">&lt;code>flux mirror sync&lt;/code>&lt;/a>,
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/login/">&lt;code>flux mirror login&lt;/code>&lt;/a>,
and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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">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>credential.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/v1beta2&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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/login/">&lt;code>flux mirror login&lt;/code>&lt;/a> and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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="api-version">API version&lt;/h3>
&lt;p>&lt;code>mirror.plugin.fluxcd.io/v1beta2&lt;/code> is the current &lt;code>apiVersion&lt;/code>. The previous
&lt;code>mirror.plugin.fluxcd.io/v1beta1&lt;/code> is deprecated: a config that still declares it
still loads (it is converted to v1beta2 in memory), but prints a migration
warning. v1beta2 is a breaking change that adds an optional &lt;code>credential.type&lt;/code>
(defaults to &lt;code>jwt&lt;/code>), renames the credential claims (&lt;code>aud&lt;/code>→&lt;code>audiences&lt;/code>,
&lt;code>iss&lt;/code>→&lt;code>issuer&lt;/code>, &lt;code>sub&lt;/code>→&lt;code>subject&lt;/code>, &lt;code>exp&lt;/code>→&lt;code>expiration&lt;/code>), moves
&lt;code>hosts[].username&lt;/code> under &lt;code>credential&lt;/code>, renames the SPIFFE credential provider
(&lt;code>jwt-svid&lt;/code>→&lt;code>spiffe&lt;/code>), and replaces the &lt;code>.tls&lt;/code> serverAuth/clientAuth blocks with
&lt;code>.tls.path&lt;/code> (a directory),
inline &lt;code>.ca.crt&lt;/code>, &lt;code>.tls.crt&lt;/code>, and &lt;code>.tls.key&lt;/code> values, or &lt;code>.tls.spiffe.serverIDs&lt;/code>
(SPIFFE mutual TLS). Update the &lt;code>apiVersion&lt;/code> and the
renamed fields to silence the warning. Inline v1beta1 &lt;code>.tls&lt;/code> values and SPIFFE
&lt;code>serverAuth&lt;/code> with &lt;code>clientAuth: x509-svid&lt;/code> convert automatically; per-file paths,
one-sided SPIFFE, and &lt;code>authorizeAny&lt;/code> fail with a migration error and must be
rewritten as &lt;code>.tls.path&lt;/code>, inline certificate fields, or &lt;code>.tls.spiffe&lt;/code>.&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>), &lt;code>.overwrite&lt;/code> and
&lt;code>.ignoreDriftForExitCode&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;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">ignoreDriftForExitCode&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>).
A host may appear more than once only when the entries are split by
&lt;a href="#host-direction">&lt;code>.direction&lt;/code>&lt;/a>; overlapping entries are rejected. Beyond that,
an entry configures a
&lt;a href="#per-host-credential">&lt;code>.credential&lt;/code>&lt;/a> and/or a
&lt;a href="#transport-tls">&lt;code>.tls&lt;/code>&lt;/a> block, or
a cloud
&lt;a href="#cloud-registry-providers">&lt;code>.provider&lt;/code>&lt;/a>. A cloud &lt;code>provider&lt;/code> is mutually
exclusive with &lt;code>tls&lt;/code>, because a cloud registry is managed and its transport is
not customized; the default &lt;code>generic&lt;/code> provider is an ordinary registry and
composes with both. A cloud &lt;code>provider&lt;/code> may also carry a &lt;code>credential&lt;/code>: its
identity is then exchanged for the cloud provider&amp;rsquo;s registry credentials
instead of using the ambient cloud identity.
&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>, a cloud
&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;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 authenticated with a GitHub 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>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:#062873;font-weight:bold">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">tenantID&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${AZURE_TENANT_ID}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientID&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${AZURE_CLIENT_ID}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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:#062873;font-weight:bold">audiences&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- api://AzureADTokenExchange&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h4 id="host-direction">Host direction&lt;/h4>
&lt;p>&lt;code>.hosts[].direction&lt;/code> limits an entry to one side of a mirror:&lt;/p>
&lt;ul>
&lt;li>&lt;code>both&lt;/code> (default) applies the entry to source pulls and destination pushes.&lt;/li>
&lt;li>&lt;code>source&lt;/code> applies it only to source pulls.&lt;/li>
&lt;li>&lt;code>destination&lt;/code> applies it only to destination pushes.&lt;/li>
&lt;/ul>
&lt;p>This is what lets the same registry host carry different credentials for the
source and destination, for example when copying between two repositories on
&lt;code>ghcr.io&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:#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">direction&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>source&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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>${LEGACY_GHCR_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>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">direction&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>destination&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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;/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.
&lt;code>.type&lt;/code> is optional and selects the credential material. It defaults to &lt;code>jwt&lt;/code>;
only &lt;code>jwt&lt;/code> is supported today, with &lt;code>x509&lt;/code> reserved for a future release.
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 audiences using a
cloud, CI, or SPIFFE 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>spiffe&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/CD &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://deploy-preview-2713--fluxcd.netlify.app/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>.issuer&lt;/code> and &lt;code>.subject&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>.audiences&lt;/code>, the audience claim as a list. 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. On a cloud &lt;code>provider&lt;/code> host, an unset
&lt;code>.audiences&lt;/code> uses the cloud provider&amp;rsquo;s own default (&lt;code>sts.amazonaws.com&lt;/code> for
ECR, &lt;code>api://AzureADTokenExchange&lt;/code> for ACR) instead of the host. Every source
accepts multiple values except the &lt;code>gcp&lt;/code> and &lt;code>azure&lt;/code> providers, which mint a
token with a single &lt;code>aud&lt;/code> and therefore reject a list longer than one.&lt;/li>
&lt;li>&lt;code>.expiration&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;/ul>
&lt;p>Two subfields apply to every token source:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.envelope&lt;/code>, an optional Go template applied to the resolved credential just
before it is sent to the registry, for registries that expect the token in a
specific serialized form. The template data is a single &lt;code>.Token&lt;/code> field holding
the credential, and two functions are available: &lt;code>base64&lt;/code> (standard base64)
and &lt;code>hex&lt;/code> (lowercase hexadecimal). For example,
&lt;code>envelope: &amp;quot;token-{{ hex .Token }}&amp;quot;&lt;/code> sends the hex-encoded credential with a
&lt;code>token-&lt;/code> prefix. The envelope applies to both
&lt;a href="#bearer-token-vs-usernamepassword">transport styles&lt;/a>: it transforms the bearer
token, or the password when &lt;code>.credential.username&lt;/code> is set. An unset envelope
sends the credential unchanged.&lt;/li>
&lt;li>&lt;code>.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 audiences (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>). On a
cloud &lt;code>provider&lt;/code> host (&lt;code>ecr&lt;/code>, &lt;code>acr&lt;/code>, or &lt;code>gar&lt;/code>), the same token is instead
exchanged with the cloud provider for registry credentials, so the cloud
identity provider must trust the GitHub/Forgejo Actions issuer. The two mint
tokens the same way today but are kept distinct so each platform can diverge.
Each audience is sent as a repeated &lt;code>audience&lt;/code> query parameter; how the
endpoint folds them into the token is up to the provider.&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. User credentials (&lt;code>authorized_user&lt;/code>, e.g. from
&lt;code>gcloud auth application-default login&lt;/code>) cannot mint a token for a custom
audience: with &lt;code>.audiences&lt;/code> set the run fails, and with &lt;code>.audiences&lt;/code> unset it
falls back to the default-audience ID token carried in the ADC response, whose
audience is the gcloud OAuth client ID and whose identity is the signed-in
user.&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>.audiences&lt;/code> must contain a single 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 audiences
are carried as signed &lt;code>X-Audience&lt;/code> header values, not as an OIDC audience
claim: the registry accepts the request only if the signed list contains an
identity it recognizes, so a single audience ties the request to that one
registry while several authorize each registry listed. 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 plain OIDC
registry will not.&lt;/li>
&lt;li>&lt;code>spiffe&lt;/code> fetches a JWT-SVID from the SPIFFE Workload API
(&lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code>) for the audiences and sends it as the credential.
The extra audiences are carried as additional JWT-SVID audiences. The registry
must trust the SPIFFE trust domain&amp;rsquo;s JWT bundle. On a cloud provider host, the
JWT-SVID is exchanged with the cloud provider, which must trust the SPIFFE
trust domain. 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.path&lt;/code> directory 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>.credential.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>credential.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>credential.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>credential.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> selects how the host&amp;rsquo;s registry credentials are obtained. It
defaults to &lt;code>generic&lt;/code>, an ordinary registry for which &lt;code>credential&lt;/code> and/or &lt;code>tls&lt;/code>
configure the connection directly. The other values authenticate the host with a
cloud registry provider&amp;rsquo;s workload identity — the same mechanism the
&lt;code>flux push artifact&lt;/code> family uses — and obtain the registry&amp;rsquo;s native credentials
directly. A cloud provider is mutually exclusive with &lt;code>tls&lt;/code>; it composes with a
&lt;code>credential&lt;/code>, whose identity is exchanged for the cloud provider&amp;rsquo;s registry
credentials instead of using the ambient cloud identity. The supported values
are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>generic&lt;/code> (default), an ordinary registry whose authentication is configured
through &lt;code>credential&lt;/code> and/or &lt;code>tls&lt;/code>. It is the implicit value when &lt;code>provider&lt;/code> is
omitted, and composes with both.&lt;/li>
&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. With a
&lt;code>credential&lt;/code>, the identity is exchanged via &lt;code>AssumeRoleWithWebIdentity&lt;/code> (set
&lt;code>.audiences&lt;/code> to the role&amp;rsquo;s expected audience, typically &lt;code>sts.amazonaws.com&lt;/code>,
and grant the role trust to the credential issuer).&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;). With a &lt;code>credential&lt;/code>, the
identity is exchanged as a client assertion, typically with the
&lt;code>api://AzureADTokenExchange&lt;/code> audience.&lt;/li>
&lt;li>&lt;code>gar&lt;/code>, for Google GAR. It uses Google Application Default Credentials. With a
&lt;code>credential&lt;/code>, the identity is exchanged through Google Workload Identity
Federation.&lt;/li>
&lt;/ul>
&lt;p>When a cloud &lt;code>provider&lt;/code> carries a &lt;code>credential&lt;/code>, the matching &lt;code>.ecr&lt;/code>, &lt;code>.acr&lt;/code>, or
&lt;code>.gar&lt;/code> block is required:&lt;/p>
&lt;ul>
&lt;li>&lt;code>.ecr.roleARN&lt;/code> is the IAM role assumed with the credential identity via
&lt;code>AssumeRoleWithWebIdentity&lt;/code>. The region is derived from the ECR host, as in
the rest of Flux. &lt;code>.ecr.stsEndpoint&lt;/code> optionally overrides the STS endpoint.&lt;/li>
&lt;li>&lt;code>.acr.tenantID&lt;/code> and &lt;code>.acr.clientID&lt;/code> identify the Microsoft Entra application
used as the client assertion.&lt;/li>
&lt;li>&lt;code>.gar.workloadIdentityProvider&lt;/code> is the workload identity pool provider
resource name; &lt;code>.gar.gcpServiceAccount&lt;/code> optionally impersonates a service
account. The built-in GKE workload identity pool only trusts GKE
ServiceAccount tokens, so an external credential provider always needs an
explicit &lt;code>.gar&lt;/code> block.&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">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:#062873;font-weight:bold">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">roleARN&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"># stsEndpoint: https://sts.us-east-1.amazonaws.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">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:#062873;font-weight:bold">audiences&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- sts.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">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:#062873;font-weight:bold">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">tenantID&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${AZURE_TENANT_ID}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">clientID&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${AZURE_CLIENT_ID}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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>forgejo&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#062873;font-weight:bold">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">workloadIdentityProvider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>projects/123/locations/global/workloadIdentityPools/gh/providers/gh&lt;span style="color:#bbb">
&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"># gcpServiceAccount: 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">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;/code>&lt;/pre>&lt;/div>&lt;p>The cloud providers&amp;rsquo; 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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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 cloud &lt;code>provider&lt;/code> host. Exactly one of the
following must be set:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;code>.path&lt;/code>, a directory laid out like a mounted Flux &lt;code>certSecretRef&lt;/code> Secret:&lt;/p>
&lt;ul>
&lt;li>&lt;code>ca.crt&lt;/code> supplies the CA bundle used to verify the registry&amp;rsquo;s server
certificate (one or more concatenated PEM certificates).&lt;/li>
&lt;li>&lt;code>tls.crt&lt;/code> and &lt;code>tls.key&lt;/code> supply a client certificate and key for mTLS.&lt;/li>
&lt;/ul>
&lt;p>At least one of &lt;code>ca.crt&lt;/code> or the &lt;code>tls.crt&lt;/code>/&lt;code>tls.key&lt;/code> pair must be present, and
the two files of the pair must be set together. Legacy Flux key names
(&lt;code>caFile&lt;/code>, &lt;code>certFile&lt;/code>, &lt;code>keyFile&lt;/code>) are accepted as fallbacks, with the standard
names taking precedence. The directory is read with the same key names and
validation as the Flux controllers; relative paths are resolved against the
config file&amp;rsquo;s directory and confined within it, exactly like the credential
file paths.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>inline PEM values, set directly as &lt;code>.ca.crt&lt;/code>, &lt;code>.tls.crt&lt;/code>, and &lt;code>.tls.key&lt;/code>.
Use these when the PEM material is already available to the config, for
example through &lt;code>${VAR}&lt;/code> substitution, and cannot be mounted as a directory.
&lt;code>tls.crt&lt;/code> and &lt;code>tls.key&lt;/code> must be set together, and at least one of &lt;code>ca.crt&lt;/code> or
the pair must be present.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;code>.spiffe&lt;/code>, SPIFFE mutual TLS. &lt;code>.spiffe.serverIDs&lt;/code> is required and lists the
registry server identities to authorize. The client X.509-SVID and trust
bundle come from the ambient Workload API socket (&lt;code>SPIFFE_ENDPOINT_SOCKET&lt;/code>) and
rotate automatically.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>SPIFFE is always mutual TLS: when &lt;code>.spiffe&lt;/code> is set, flux-mirror presents its own
X.509-SVID as the client certificate as part of the handshake. The independent
server/client halves of earlier revisions are gone, as is client-only SPIFFE.&lt;/p>
&lt;p>Each &lt;code>.spiffe.serverIDs&lt;/code> entry accepts the
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/integrations/spiffe-mtls/">SPIFFE ID formats defined by Flux RFC-0014&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">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">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./certs&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># contains ca.crt, tls.crt, and tls.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>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># CA-only: verify the server with a custom CA, no client 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">host&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>ca-only.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">path&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>./certs/ca-only&lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># contains ca.crt&lt;/span>&lt;span style="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"># Inline PEM values, e.g. injected from CI secrets.&lt;/span>&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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>inline.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">ca.crt&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${REGISTRY_CA_PEM}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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.crt&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${REGISTRY_CLIENT_CERT_PEM}&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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.key&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>${REGISTRY_CLIENT_KEY_PEM}&lt;span style="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"># SPIFFE mutual TLS: 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">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">serverIDs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- spiffe://example.org/registry &lt;span style="color:#bbb"> &lt;/span>&lt;span style="color:#60a0b0;font-style:italic"># exact SPIFFE 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"># - spiffe://example.org/registry/ # any ID under this prefix&lt;/span>&lt;span style="color:#bbb">
&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"># - spiffe://example.org # 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"># - spiffe://self # any SVID in our own trust domain&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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/sync/#exit-codes">exit code&lt;/a>, which audit and alerting can hook
into. The
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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>&lt;code>.artifacts[].ignoreDriftForExitCode&lt;/code> is an optional boolean, &lt;code>false&lt;/code> by default. When
&lt;code>true&lt;/code>, drifted tags for that entry are still logged and reported as &lt;code>drifted&lt;/code>,
but they do not contribute to the drift exit code. If every drifted tag belongs
to an entry with &lt;code>ignoreDriftForExitCode: true&lt;/code>, the run exits &lt;code>0&lt;/code> and
&lt;code>--drift-exit-code&lt;/code> is not applied. Use it for an artifact whose destination is
expected to diverge, without muting drift for the rest of the config.&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[].ignoreDriftForExitCode&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[].direction&lt;/code>&lt;/td>
&lt;td>&lt;code>both&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].provider&lt;/code>&lt;/td>
&lt;td>&lt;code>generic&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].ecr&lt;/code>&lt;/td>
&lt;td>unset&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].acr&lt;/code>&lt;/td>
&lt;td>unset&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].gar&lt;/code>&lt;/td>
&lt;td>unset&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].credential.audiences&lt;/code>&lt;/td>
&lt;td>&lt;code>host&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].credential.expiration&lt;/code>&lt;/td>
&lt;td>&lt;code>60s&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>hosts[].credential.envelope&lt;/code>&lt;/td>
&lt;td>unset&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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/sync/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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. OCI artifacts are copied by the
source digest used for comparison, even if the source tag changes during the
operation.&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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/login/">&lt;code>login&lt;/code>&lt;/a> and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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.
Combined with a &lt;code>credential&lt;/code>, it exchanges the credential identity for
those credentials instead.&lt;/li>
&lt;li>
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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>credential.username&lt;/code> it is sent as an HTTP Bearer
credential; with &lt;code>credential.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 host whose &lt;code>provider&lt;/code> is unset or &lt;code>generic&lt;/code> may also set
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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 directory or inline PEM values with a custom CA and/or client
certificate, or SPIFFE mutual TLS.&lt;/p>
&lt;p>With the default registry transport, &lt;code>--insecure&lt;/code> skips TLS verification for OCI
requests, including chart destinations and referrer lookups.&lt;/p>
&lt;p>Provider credentials obtained from a cloud registry provider or a &lt;code>credential&lt;/code>
token source are cached for the duration of the sync run. This avoids minting a
new cloud/CI identity token and exchanging it for registry credentials on every
request. Use &lt;code>--token-cache-max-size&lt;/code> and &lt;code>--token-cache-max-duration&lt;/code> to tune the
cache, or set the size to &lt;code>0&lt;/code> to disable it.&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://deploy-preview-2713--fluxcd.netlify.app/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 and tag-planning operations to run in parallel. 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://deploy-preview-2713--fluxcd.netlify.app/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. Drift on entries with &lt;code>ignoreDriftForExitCode: true&lt;/code> does not trigger it.&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;tr>
&lt;td>&lt;code>--token-cache-max-size N&lt;/code>&lt;/td>
&lt;td>&lt;code>100&lt;/code>&lt;/td>
&lt;td>Maximum number of provider credentials cached. Set to &lt;code>0&lt;/code> to disable the cache.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>--token-cache-max-duration DURATION&lt;/code>&lt;/td>
&lt;td>&lt;code>1h&lt;/code>&lt;/td>
&lt;td>Maximum duration a cached provider token is reused before it is refreshed.&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://deploy-preview-2713--fluxcd.netlify.app/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;p>Drift on an artifact entry with
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#overwrite-and-drift-behavior">&lt;code>ignoreDriftForExitCode: true&lt;/code>&lt;/a> is still logged and
reported as &lt;code>drifted&lt;/code>, but does not contribute to this exit code. A run where all
drifted tags belong to such entries exits &lt;code>0&lt;/code>, and &lt;code>--drift-exit-code&lt;/code> is not
applied.&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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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&lt;/span>&lt;span style="color:#bbb">
&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"># `credential.username` 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">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">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">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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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>credential.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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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">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">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">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">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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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">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://deploy-preview-2713--fluxcd.netlify.app/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>credential.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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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 credential.username → the token is sent as an HTTP Bearer credential. Its&lt;/span>&lt;span style="color:#bbb">
&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"># audience (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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#transport-tls">&lt;code>tls.spiffe&lt;/code>&lt;/a> and no HTTP credential — the registry
authorizes the client by its X.509-SVID and the server is verified by its SPIFFE
identity. 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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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.&lt;/span>&lt;span style="color:#bbb">
&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 credential.username → the signed sts:GetCallerIdentity envelope is sent&lt;/span>&lt;span style="color:#bbb">
&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"># 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">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">serverIDs&lt;/span>:&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#bbb"> &lt;/span>- spiffe://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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/login/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a> host, or a
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> host with &lt;code>credential.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>credential.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://deploy-preview-2713--fluxcd.netlify.app/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> and no cloud &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://deploy-preview-2713--fluxcd.netlify.app/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>Instead of running the cloud provider&amp;rsquo;s login action, set
&lt;code>credential.provider&lt;/code> to &lt;code>github&lt;/code> or &lt;code>forgejo&lt;/code> on the host. &lt;code>flux-mirror&lt;/code>
fetches the Actions OIDC token and exchanges it with the cloud provider. Set
&lt;code>credential.audiences&lt;/code> to the audience the cloud identity provider expects
(e.g. &lt;code>sts.amazonaws.com&lt;/code> or &lt;code>api://AzureADTokenExchange&lt;/code>). Add the required
&lt;code>ecr.roleARN&lt;/code>, &lt;code>acr.tenantID&lt;/code>/&lt;code>acr.clientID&lt;/code>, or
&lt;code>gar.workloadIdentityProvider&lt;/code> block.&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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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>credential.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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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">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>credential.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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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">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>expiration&lt;/code> — see
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/secret/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/sync/">&lt;code>sync&lt;/code>&lt;/a> and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#cloud-registry-providers">&lt;code>provider&lt;/code>&lt;/a> host, and a
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>credential&lt;/code>&lt;/a> host with &lt;code>credential.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>credential.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://deploy-preview-2713--fluxcd.netlify.app/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> and no cloud &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://deploy-preview-2713--fluxcd.netlify.app/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>When running in GitHub or Forgejo Actions, the host can instead set
&lt;code>credential.provider&lt;/code> to &lt;code>github&lt;/code> or &lt;code>forgejo&lt;/code>; &lt;code>flux-mirror&lt;/code> exchanges the
Actions OIDC token with the cloud provider, so the pod does not need ambient
cloud credentials. Add the required &lt;code>ecr.roleARN&lt;/code>,
&lt;code>acr.tenantID&lt;/code>/&lt;code>acr.clientID&lt;/code>, or &lt;code>gar.workloadIdentityProvider&lt;/code> block.&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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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://deploy-preview-2713--fluxcd.netlify.app/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>credential.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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">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">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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#token-providers">&lt;code>spiffe&lt;/code> credential provider&lt;/a>: it fetches
a JWT-SVID for the audiences (defaulting to the registry host) from the SPIFFE
Workload API. Setting &lt;code>credential.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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">provider&lt;/span>:&lt;span style="color:#bbb"> &lt;/span>spiffe&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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:#60a0b0;font-style:italic"># audiences default 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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/keygen/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/sync/">&lt;code>sync&lt;/code>&lt;/a> and
&lt;a href="https://deploy-preview-2713--fluxcd.netlify.app/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://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/config/#per-host-credential">&lt;code>expiration&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>expiration&lt;/code>.&lt;/strong>
Set &lt;code>issuer&lt;/code>/&lt;code>subject&lt;/code> to the identity the registry expects, and &lt;code>expiration&lt;/code> to
the desired lifetime (here ~1 year). With no &lt;code>credential.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/v1beta2&lt;span style="color:#bbb">
&lt;/span>&lt;/span>&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">issuer&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">subject&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"># audiences: # 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:#60a0b0;font-style:italic"># - registry.example.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">expiration&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 the configured &lt;code>expiration&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 the
configured &lt;code>subject&lt;/code> until the public key is rotated out.&lt;/p>
&lt;/blockquote></description></item><item><title>Flux: Flux Mirror Report</title><link>https://deploy-preview-2713--fluxcd.netlify.app/flux/cli-plugins/flux-mirror/report/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://deploy-preview-2713--fluxcd.netlify.app/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.14.1/docs/report-v1beta2.json" target="_blank">&lt;code>report-v1beta2.json&lt;/code>&lt;/a>; the deprecated
&lt;a href="https://github.com/fluxcd/flux-mirror/blob/v0.14.1/docs/report-v1beta1.json" target="_blank">&lt;code>report-v1beta1.json&lt;/code>&lt;/a> remains published for existing
reports.&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://deploy-preview-2713--fluxcd.netlify.app/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/v1beta2&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>), including entries with &lt;code>ignoreDriftForExitCode: true&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. Drift on entries with &lt;code>ignoreDriftForExitCode: true&lt;/code> is excluded. 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>ignoreDriftForExitCode&lt;/code>&lt;/td>
&lt;td>&lt;code>true&lt;/code> when the entry&amp;rsquo;s drifted tags were excluded from the exit 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/v1beta2&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-v1beta2.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>