Page Metadata
| Status: | ✅ Accepted |
|---|---|
| Date: | 2026-04-13 |
Registry Binding Credentials¶
Context and Problem Statement¶
ADR-009 splits Target into Target, Registry, RegistryBinding, and ReleaseBinding but leaves their concrete fields unspecified. ADR-008 establishes that SolAr must not handle target-side authentication.
This ADR specifies the credential semantics for Registry and RegistryBinding. It solves three concrete problems:
- Hardcoded
regcredin rendered OCIRepository templates (#165) - Single global push secret — no per-registry push credentials
- No pull secret propagation — templates cannot conditionally render pull credentials
Scope relative to ADR-009¶
ADR-009 decided the resource split, RBAC model, and rendering interaction at a conceptual level. This ADR adds:
- The two credential domains (push vs pull) and which resource carries each
targetPullSecretNameon Registry, solving #165- The render-time resolution algorithm and
ResolvedResourceAccessoutput
This ADR does not cover: Discovery integration (Discovery remains independent of the Kubernetes API resources), ReleaseBinding semantics, Profile interactions, or signature verification (out of scope — handled by Flux/Kyverno on the target cluster).
Two Credential Domains¶
| Domain | Owner | Lives on | SolAr's role |
|---|---|---|---|
| Push (SolAr-side) | Platform provider | Registry.spec.solarSecretRef |
Mounts real Secret into RenderTask Job |
| Pull (target-side) | Cluster maintainer | Registry.spec.targetPullSecretName |
Renders the name into manifests — never reads the data |
Both credential references live on Registry, not RegistryBinding. This follows from ADR-009's design: Registry is the single source of truth for a registry endpoint, and RegistryBinding is a pure link resource.
Registry¶
This ADR adds two credential fields to the Registry spec defined in ADR-009:
kind: Registry
metadata:
name: harbor-edge
spec:
hostname: harbor.edge.dmz:443
plainHTTP: false
solarSecretRef: # LocalObjectReference — real Secret in same NS
name: render-push-creds # mounted into RenderTask Job for pushing
targetPullSecretName: harbor-pull-cred # string — rendered into manifests, never read
| Field | Type | Purpose |
|---|---|---|
solarSecretRef |
LocalObjectReference |
Secret in controller NS with push credentials. Passed to RenderTask.spec.pushSecretRef. Required for registries used as render destinations. |
targetPullSecretName |
string |
Name rendered into target manifests (e.g. Flux OCIRepository.spec.secretRef.name). The actual Secret is provisioned on the target by the cluster maintainer. Omit for anonymous pull. Solves #165. |
solarSecretRef is a reference-by-name to a v1.Secret in the same namespace — the same pattern used by RenderTask.spec.pushSecretRef today.
Render-Time Resolution¶
- Collect all RegistryBindings for the Target.
- Destination: Resolve the Target's
renderRegistryRefto its Registry. Useregistry.spec.hostnameas the push URL andregistry.spec.solarSecretRefas the push credentials for the RenderTask.solarSecretRefis required: if the resolved Registry has nosolarSecretRef, the render must fail deterministically (missing push credentials are a hard render failure). - Per source resource: Match the resource's repository host against RegistryBindings. Exactly one RegistryBinding must match for each resource's repository host:
- Zero matches — fail with an error referencing the Target, the source resource, and the unmatched host.
- One match — carry the bound Registry's
targetPullSecretNameinto the resolved resource. - Multiple matches — fail with an ambiguity error referencing the Target, the source resource, and the names of the conflicting RegistryBindings.
- Validation: A missing destination registry, an unresolvable
renderRegistryRef, or an absent/unresolvableregistry.spec.solarSecretRefon the resolved Registry causes rendering to fail with an error referencing the Target and the resolved registry.
Resolved resources flow into RendererConfig as:
type ResolvedResourceAccess struct {
Repository string `json:"repository"`
Insecure bool `json:"insecure"`
Tag string `json:"tag"`
PullSecretName string `json:"pullSecretName,omitempty"`
}
Templates become:
spec:
url: oci://{{ $resource.repository }}
{{- if $resource.pullSecretName }}
secretRef:
name: {{ $resource.pullSecretName }}
{{- end }}
This replaces the hardcoded regcred (#165).
Consequences¶
Positive: Solves #165 with per-registry pull secret names. Per-registry push credentials via solarSecretRef. ADR-008 compliant — SolAr never reads target-side secrets. ADR-006 compliant — push secrets stay in controller NS. RegistryBinding stays a simple link resource.
Negative: Cluster maintainers must provision the named pull secret on each target.
Decisions¶
targetPullSecretNameis optional. Omitting it enables anonymous pull. Templates conditionally rendersecretRefonly when the field is set.- No explicit
rolefield on RegistryBinding. The destination registry is identified byTarget.spec.renderRegistryRef. RegistryBindings declare source access. Role is implicit from context. - Credentials live on Registry, not RegistryBinding. RegistryBinding is a pure link — no credential fields.
Open Questions¶
- Registry scope — namespace-scoped (current) vs cluster-scoped (
ClusterRegistry) for shared registry definitions across namespaces. Deferred to a future ADR.
Relationship to Other ADRs¶
| ADR | Relationship |
|---|---|
| 006 | solarSecretRef lives in controller NS; push secrets never copied to tenants |
| 008 | targetPullSecretName is a string — SolAr stays out of target-side auth |
| 009 | Concretizes credential fields on the resources defined there |