catalog

package
v0.37.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: Apache-2.0 Imports: 39 Imported by: 0

Documentation

Overview

Package catalog provides artifact storage and retrieval for scafctl.

The catalog system manages solutions, providers, and auth handlers as OCI artifacts, enabling versioned storage, distribution, and dependency management.

Catalog Types

There are two types of catalogs:

  • Local catalog: Built-in catalog stored at XDG data path, always available
  • Remote catalog: OCI registry-based catalog for distribution

Built-in Local Catalog

The local catalog is always available and is first in resolution order. It uses the XDG-compliant path from paths.CatalogDir():

  • Linux: ~/.local/share/scafctl/catalog/
  • macOS: ~/.local/share/scafctl/catalog/
  • Windows: %LOCALAPPDATA%\scafctl\catalog\

The local catalog stores artifacts as OCI artifacts in OCI Image Layout format, providing content-addressable storage with full OCI compatibility.

Registry

The Registry manages multiple catalogs with a defined resolution order:

  1. Built-in local catalog (always first)
  2. Configured remote catalogs (in config order)

This enables seamless artifact resolution across local and remote sources.

Artifact Types

Currently supported artifact types:

  • Solutions: YAML configuration files (application/vnd.scafctl.solution.v1+yaml)
  • Providers: go-plugin binaries exposing providers (application/vnd.scafctl.provider.v1+binary)
  • Auth Handlers: go-plugin binaries exposing auth handlers (application/vnd.scafctl.auth-handler.v1+binary)

Example Usage

// Create a registry with the built-in local catalog
reg, err := catalog.NewRegistry(logger)
if err != nil {
    return err
}

// Build a solution into the local catalog
ref := catalog.Reference{
    Kind:    catalog.ArtifactKindSolution,
    Name:    "my-solution",
    Version: semver.MustParse("1.0.0"),
}
info, err := reg.Local().Store(ctx, ref, content, nil, annotations, false)

// Resolve an artifact (checks local first, then configured catalogs)
content, info, err := reg.Fetch(ctx, ref)

Index

Constants

View Source
const (

	// AnnotationTitle is the human-readable title.
	AnnotationTitle = "org.opencontainers.image.title"

	// AnnotationDescription is a longer description.
	AnnotationDescription = "org.opencontainers.image.description"

	// AnnotationVersion is the version (semver).
	AnnotationVersion = "org.opencontainers.image.version"

	// AnnotationCreated is the creation timestamp (RFC 3339).
	AnnotationCreated = "org.opencontainers.image.created"

	// AnnotationAuthors is the contact for the image.
	AnnotationAuthors = "org.opencontainers.image.authors"

	// AnnotationSource is the URL to get source code.
	AnnotationSource = "org.opencontainers.image.source"

	// AnnotationDocumentation is the URL for documentation.
	AnnotationDocumentation = "org.opencontainers.image.documentation"

	// AnnotationVendor is the vendor name.
	AnnotationVendor = "org.opencontainers.image.vendor"

	// AnnotationArtifactType is the scafctl artifact type ("solution", "provider", or "auth-handler").
	AnnotationArtifactType = "dev.scafctl.artifact.type"

	// AnnotationArtifactName is the artifact name.
	AnnotationArtifactName = "dev.scafctl.artifact.name"

	// AnnotationCategory is the solution category.
	AnnotationCategory = "dev.scafctl.solution.category"

	// AnnotationTags is a comma-separated list of tags.
	AnnotationTags = "dev.scafctl.solution.tags"

	// AnnotationMaintainers is a JSON array of maintainer objects.
	AnnotationMaintainers = "dev.scafctl.solution.maintainers"

	// AnnotationRequires is the dependency specifications (future).
	AnnotationRequires = "dev.scafctl.solution.requires"

	// AnnotationDisplayName is the human-friendly display name.
	AnnotationDisplayName = "dev.scafctl.solution.displayName"

	// AnnotationProviders is a comma-separated list of provider names (provider artifacts only).
	AnnotationProviders = "dev.scafctl.plugin.providers"

	// AnnotationPlatform is the target platform (provider/auth-handler artifacts only, e.g., "linux/amd64").
	AnnotationPlatform = "dev.scafctl.plugin.platform"

	// AnnotationOrigin records how a local artifact was obtained.
	// Values: "built", "pulled from <catalog>", "auto-cached from <catalog>".
	// This key carries provenance metadata only. Its exact storage location
	// depends on the catalog implementation, so it must not be assumed to be
	// descriptor-only or digest-stable.
	AnnotationOrigin = "dev.scafctl.artifact.origin"

	// AnnotationBuildCommit is the short git commit SHA at build time.
	AnnotationBuildCommit = "dev.scafctl.build.commit"

	// AnnotationBuildDirty indicates the working tree had uncommitted changes at build time.
	AnnotationBuildDirty = "dev.scafctl.build.dirty"
)

OCI annotation keys.

View Source
const (
	// MediaTypeSolutionManifest is the manifest media type for solution artifacts.
	MediaTypeSolutionManifest = "application/vnd.oci.image.manifest.v1+json"

	// MediaTypeSolutionContent is the content layer media type for solution YAML.
	MediaTypeSolutionContent = "application/vnd.scafctl.solution.v1+yaml"

	// MediaTypeSolutionConfig is the config blob media type for solution metadata.
	MediaTypeSolutionConfig = "application/vnd.scafctl.solution.config.v1+json"

	// MediaTypeSolutionBundle is the content layer media type for solution bundle tar archives.
	MediaTypeSolutionBundle = "application/vnd.scafctl.solution.bundle.v1+tar"

	// MediaTypeSolutionBundleManifest is the media type for deduplicated bundle manifests (v2).
	MediaTypeSolutionBundleManifest = "application/vnd.scafctl.solution.bundle-manifest.v2+json"

	// MediaTypeSolutionBundleBlob is the media type for individual file blobs in deduplicated bundles (v2).
	MediaTypeSolutionBundleBlob = "application/vnd.scafctl.solution.bundle-blob.v2+octet-stream"

	// MediaTypeSolutionBundleSmallTar is the media type for grouped small files in deduplicated bundles (v2).
	MediaTypeSolutionBundleSmallTar = "application/vnd.scafctl.solution.bundle-small.v2+tar"

	// MediaTypeProviderManifest is the manifest media type for provider artifacts.
	MediaTypeProviderManifest = "application/vnd.oci.image.manifest.v1+json"

	// MediaTypeProviderBinary is the content layer media type for provider binaries.
	MediaTypeProviderBinary = "application/vnd.scafctl.provider.v1+binary"

	// MediaTypeProviderConfig is the config blob media type for provider metadata.
	MediaTypeProviderConfig = "application/vnd.scafctl.provider.config.v1+json"

	// MediaTypeAuthHandlerManifest is the manifest media type for auth handler artifacts.
	MediaTypeAuthHandlerManifest = "application/vnd.oci.image.manifest.v1+json"

	// MediaTypeAuthHandlerBinary is the content layer media type for auth handler binaries.
	MediaTypeAuthHandlerBinary = "application/vnd.scafctl.auth-handler.v1+binary"

	// MediaTypeAuthHandlerConfig is the config blob media type for auth handler metadata.
	MediaTypeAuthHandlerConfig = "application/vnd.scafctl.auth-handler.config.v1+json"
)

OCI media types for scafctl artifacts.

View Source
const DefaultMaxLayerSize int64 = 1 << 30 // 1 GiB

DefaultMaxLayerSize bounds how large a content layer scafctl will read into memory when fetching a plugin/solution blob. It guards against a hostile or misconfigured registry advertising an enormous blob. Plugin binaries are typically tens of MiB, so 1 GiB is a generous ceiling that still prevents unbounded allocation.

Note: oras-go's content.FetchAll / content.ReadAll impose their own 32 MiB cap, which is appropriate for small JSON manifests/configs but far too small for binary layers. fetchLayerContent deliberately bypasses that cap (while preserving size + digest verification) for content layers.

View Source
const (
	// LocalCatalogName is the name of the built-in local catalog.
	LocalCatalogName = "local"
)
View Source
const OCILayoutVersion = "1.0.0"

OCILayoutVersion is the OCI image layout version.

View Source
const RegistryUsernameACR = "00000000-0000-0000-0000-000000000000"

RegistryUsernameACR is the Azure Container Registry username for token auth. ACR uses a zero-GUID as the username when authenticating with an Entra token.

View Source
const RegistryUsernameDefault = "oauth2accesstoken"

RegistryUsernameDefault is the default username for OAuth2 token-based registry auth.

Variables

View Source
var ErrArtifactExists = errors.New("artifact already exists")

ErrArtifactExists is returned when storing an artifact that already exists.

View Source
var ErrArtifactNotFound = errors.New("artifact not found")

ErrArtifactNotFound is returned when an artifact cannot be found.

View Source
var ErrEnumerationNotSupported = errors.New("registry does not support repository enumeration")

ErrEnumerationNotSupported is returned when a registry does not support the _catalog endpoint for repository enumeration.

View Source
var ErrInvalidReference = errors.New("invalid reference")

ErrInvalidReference is returned when a reference is malformed.

View Source
var ErrPlatformNotFound = errors.New("platform not found")

ErrPlatformNotFound is returned when no matching platform is found in an image index.

View Source
var SupportedPluginPlatforms = []string{
	"linux/amd64",
	"linux/arm64",
	"darwin/amd64",
	"darwin/arm64",
	"windows/amd64",
	"windows/arm64",
}

SupportedPluginPlatforms is the list of platforms supported for plugin artifacts.

Functions

func BridgeAuthToRegistry added in v0.7.0

func BridgeAuthToRegistry(ctx context.Context, handler auth.Handler, registryHost, scope string) (string, string, error)

BridgeAuthToRegistry converts an auth handler's token into OCI registry credentials. Each registry type expects a specific username/password convention:

  • GitHub (ghcr.io): username=<github-username>, password=<access-token>
  • GCP (gcr.io, *.pkg.dev): username=oauth2accesstoken, password=<access-token>
  • Entra (*.azurecr.io): username=00000000-0000-0000-0000-000000000000, password=<access-token>
  • Generic OAuth2: username=oauth2accesstoken (or custom registryUsername), password=<access-token>

func ConfigMediaTypeForKind

func ConfigMediaTypeForKind(kind ArtifactKind) string

ConfigMediaTypeForKind returns the config media type for an artifact kind.

func GetTags

func GetTags(annotations map[string]string) []string

GetTags parses the tags annotation into a slice.

func IncludePreReleaseFromContext added in v0.9.0

func IncludePreReleaseFromContext(ctx context.Context) bool

IncludePreReleaseFromContext returns true if pre-release versions should be included when resolving the latest version.

func IndexPlatforms added in v0.6.0

func IndexPlatforms(index *ocispec.Index) []string

IndexPlatforms returns the list of platforms in an OCI image index.

func InferAuthHandler added in v0.7.0

func InferAuthHandler(registryHost string, customHandlers []config.CustomOAuth2Config) string

InferAuthHandler maps a registry host to a built-in or custom auth handler name. Returns empty string if no handler can be inferred for the registry.

func InferDefaultScope added in v0.8.0

func InferDefaultScope(registryHost string) string

InferDefaultScope returns the default OAuth scope for a known registry host. Returns empty string if no default scope is known for the registry.

func IsArtifactNotFoundError

func IsArtifactNotFoundError(err error) bool

IsArtifactNotFoundError is an alias for IsNotFound.

func IsEnumerationNotSupported added in v0.10.1

func IsEnumerationNotSupported(err error) bool

IsEnumerationNotSupported returns true if the error indicates the registry does not support listing all repositories.

func IsExists

func IsExists(err error) bool

IsExists returns true if the error indicates an artifact already exists.

func IsImageIndex added in v0.6.0

func IsImageIndex(desc ocispec.Descriptor) bool

IsImageIndex returns true if the descriptor has image index media type.

func IsInvalidReference

func IsInvalidReference(err error) bool

IsInvalidReference returns true if the error indicates an invalid reference.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound returns true if the error indicates an artifact was not found.

func IsPlatformNotFound added in v0.6.0

func IsPlatformNotFound(err error) bool

IsPlatformNotFound returns true if the error indicates a missing platform.

func IsPreRelease added in v0.9.0

func IsPreRelease(v *semver.Version) bool

IsPreRelease returns true if the semver version has a pre-release suffix (e.g., 1.0.0-beta.1, 2.0.0-alpha).

func IsSupportedPlatform added in v0.6.0

func IsSupportedPlatform(platform string) bool

IsSupportedPlatform returns true if the platform string is in the supported list.

func IsValidDigest

func IsValidDigest(digest string) bool

IsValidDigest checks if a string is a valid OCI digest.

func IsValidName

func IsValidName(name string) bool

IsValidName checks if a name follows the naming convention. Names must be lowercase alphanumeric with hyphens, starting with a letter.

func IsValidTagChar added in v0.6.0

func IsValidTagChar(ch rune) bool

IsValidTagChar returns true if the rune is valid for an OCI tag.

func LooksLikeCatalogURL added in v0.6.0

func LooksLikeCatalogURL(s string) bool

LooksLikeCatalogURL returns true if the string looks like a URL/registry rather than a simple catalog name. A URL contains "." (domain separator) or ":" (port separator), or starts with a scheme prefix.

func LooksLikeRemoteReference added in v0.8.0

func LooksLikeRemoteReference(ref string) bool

LooksLikeRemoteReference returns true if the reference appears to be a remote registry reference. Remote references contain a registry host with a dot (e.g., "ghcr.io"), a port (e.g., "localhost:5000"), or start with "oci://". Plain paths like "configs/solution" return false.

func MatchPlatform added in v0.6.0

func MatchPlatform(index *ocispec.Index, platform string) (*ocispec.Descriptor, error)

MatchPlatform selects the manifest descriptor from an OCI image index that matches the requested platform. Returns an error if no match is found.

func MediaTypeForKind

func MediaTypeForKind(kind ArtifactKind) string

MediaTypeForKind returns the content media type for an artifact kind.

func NormalizeRegistryHost

func NormalizeRegistryHost(host string) string

NormalizeRegistryHost normalizes common registry hostnames.

func OCIPlatformString added in v0.6.0

func OCIPlatformString(p *ocispec.Platform) string

OCIPlatformString converts an OCI Platform struct to a "os/arch" string.

func ParseCatalogURL added in v0.6.0

func ParseCatalogURL(rawURL string) (registry, repository string)

ParseCatalogURL parses a catalog URL into registry and repository parts, stripping any scheme prefix (oci://, https://, http://).

Examples:

  • "ghcr.io/myorg/scafctl" -> registry: "ghcr.io", repository: "myorg/scafctl"
  • "ghcr.io/myorg" -> registry: "ghcr.io", repository: "myorg"
  • "localhost:5000" -> registry: "localhost:5000", repository: ""

func ParseNameVersion added in v0.6.0

func ParseNameVersion(input string) (string, string)

ParseNameVersion splits "name@version" into (name, version). If no @ is present, returns (input, ""). Handles digest references (e.g., "name@sha256:abc123").

func PlatformToOCI added in v0.6.0

func PlatformToOCI(platform string) (*ocispec.Platform, error)

PlatformToOCI converts a platform string like "linux/amd64" to an OCI Platform struct.

func RegistryUsername added in v0.33.0

func RegistryUsername(ctx context.Context, handler auth.Handler, _ string) (string, error)

RegistryUsername determines the appropriate username for a registry based on the auth handler.

func ResolveCatalogDisplayName added in v0.10.1

func ResolveCatalogDisplayName(ctx context.Context, catalogFlag string) string

ResolveCatalogDisplayName returns a human-readable catalog name for display. When catalogFlag is a name, it is returned as-is. When it is a URL, the registry hostname is extracted. When empty, the default catalog name from config is used. Falls back to "default" if nothing can be resolved.

func ResolveCatalogURL added in v0.6.0

func ResolveCatalogURL(ctx context.Context, catalogFlag string) (string, error)

ResolveCatalogURL resolves a catalog URL from a flag value or config defaults.

Resolution order:

  1. If catalogFlag is a URL (contains "." or ":"), use it directly.
  2. If catalogFlag is a non-empty string, treat it as a catalog name and look it up in config.
  3. If catalogFlag is empty, use the default catalog from config.

Returns the resolved catalog URL string, or an error if no catalog can be resolved.

func SearchPatternFromContext added in v0.10.1

func SearchPatternFromContext(ctx context.Context) string

SearchPatternFromContext returns the search pattern, if any.

func ValidateAlias added in v0.6.0

func ValidateAlias(alias string) error

ValidateAlias checks that an alias tag is valid for use as a non-version tag. It must not be empty, must not be a valid semver version, and must only contain characters valid in OCI tags ([a-zA-Z0-9_.-]).

func WithIncludePreRelease added in v0.9.0

func WithIncludePreRelease(ctx context.Context) context.Context

WithIncludePreRelease returns a context that includes pre-release versions when resolving the latest version from a catalog.

func WithSearchPattern added in v0.10.1

func WithSearchPattern(ctx context.Context, pattern string) context.Context

WithSearchPattern returns a context carrying a substring filter for discovered artifact names (case-insensitive partial match).

Types

type AllowlistCatalog added in v0.17.0

type AllowlistCatalog struct {
	// contains filtered or unexported fields
}

AllowlistCatalog wraps any Catalog and rejects artifact names not in the allowed set. When the allowlist is nil, all artifacts are permitted (transparent pass-through). This enables per-catalog artifact restriction without modifying individual catalog implementations.

func NewAllowlistCatalog added in v0.17.0

func NewAllowlistCatalog(inner Catalog, allowedArtifacts []string) *AllowlistCatalog

NewAllowlistCatalog wraps a catalog with an artifact name allowlist. If allowedArtifacts is nil, all artifacts are permitted (transparent pass-through). If allowedArtifacts is non-nil but empty, all artifacts are denied.

func (*AllowlistCatalog) Delete added in v0.17.0

func (c *AllowlistCatalog) Delete(ctx context.Context, ref Reference) error

Delete delegates to the underlying catalog without restriction. Allowlists govern reads, not mutations.

func (*AllowlistCatalog) Exists added in v0.17.0

func (c *AllowlistCatalog) Exists(ctx context.Context, ref Reference) (bool, error)

Exists checks if an artifact exists, rejecting names not in the allowlist.

func (*AllowlistCatalog) Fetch added in v0.17.0

func (c *AllowlistCatalog) Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

Fetch retrieves an artifact, rejecting names not in the allowlist.

func (*AllowlistCatalog) FetchByPlatform added in v0.17.0

func (c *AllowlistCatalog) FetchByPlatform(ctx context.Context, ref Reference, platform string) ([]byte, ArtifactInfo, error)

FetchByPlatform fetches a platform-specific binary, rejecting names not in the allowlist. If the inner catalog does not implement PlatformAwareCatalog, returns an ArtifactNotFoundError.

func (*AllowlistCatalog) FetchWithBundle added in v0.17.0

func (c *AllowlistCatalog) FetchWithBundle(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)

FetchWithBundle retrieves an artifact and bundle, rejecting names not in the allowlist.

func (*AllowlistCatalog) Inner added in v0.17.0

func (c *AllowlistCatalog) Inner() Catalog

Inner returns the wrapped catalog. Useful for type assertions on the underlying implementation when needed.

func (*AllowlistCatalog) List added in v0.17.0

func (c *AllowlistCatalog) List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

List delegates to the underlying catalog. Listing is not restricted by the allowlist to preserve discovery/enumeration capabilities.

func (*AllowlistCatalog) ListPlatforms added in v0.17.0

func (c *AllowlistCatalog) ListPlatforms(ctx context.Context, ref Reference) ([]string, error)

ListPlatforms returns the platforms available for a multi-platform artifact, rejecting names not in the allowlist. If the inner catalog does not implement PlatformAwareCatalog, returns an ArtifactNotFoundError.

func (*AllowlistCatalog) Name added in v0.17.0

func (c *AllowlistCatalog) Name() string

Name returns the underlying catalog's name.

func (*AllowlistCatalog) Resolve added in v0.17.0

func (c *AllowlistCatalog) Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

Resolve finds the best matching version, rejecting names not in the allowlist.

func (*AllowlistCatalog) Store added in v0.17.0

func (c *AllowlistCatalog) Store(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)

Store delegates to the underlying catalog without restriction. Allowlists govern reads (Resolve/Fetch), not writes.

type AnnotationBuilder

type AnnotationBuilder struct {
	// contains filtered or unexported fields
}

AnnotationBuilder helps construct annotation maps.

func NewAnnotationBuilder

func NewAnnotationBuilder() *AnnotationBuilder

NewAnnotationBuilder creates a new annotation builder.

func (*AnnotationBuilder) Build

func (b *AnnotationBuilder) Build() map[string]string

Build returns the annotation map.

func (*AnnotationBuilder) Set

func (b *AnnotationBuilder) Set(key, value string) *AnnotationBuilder

Set adds an annotation if the value is non-empty.

func (*AnnotationBuilder) SetMap added in v0.15.0

SetMap merges all entries from the given map, skipping empty values. Existing keys are overwritten.

func (*AnnotationBuilder) SetTags

func (b *AnnotationBuilder) SetTags(tags []string) *AnnotationBuilder

SetTags adds tags as a comma-separated annotation.

type ArtifactCacher added in v0.6.0

type ArtifactCacher interface {
	// Get retrieves cached content and bundle data.
	// Returns (nil, nil, false, nil) on cache miss. Returns an error on read failure.
	Get(kind, name, version string) (content, bundleData []byte, ok bool, err error)
	// Put stores artifact content and bundle data in the cache.
	Put(kind, name, version, digest string, content, bundleData []byte) error
}

ArtifactCacher defines the interface for an artifact cache used by SolutionResolver. This interface allows caching downloaded catalog artifacts to reduce repeated fetches.

type ArtifactExistsError

type ArtifactExistsError struct {
	Reference Reference
	Catalog   string
}

ArtifactExistsError provides details about a duplicate artifact.

func (*ArtifactExistsError) Error

func (e *ArtifactExistsError) Error() string

Error implements the error interface.

func (*ArtifactExistsError) Unwrap

func (e *ArtifactExistsError) Unwrap() error

Unwrap returns the base error for errors.Is support.

type ArtifactInfo

type ArtifactInfo struct {
	// Reference is the artifact identifier.
	Reference Reference `json:"reference" yaml:"reference" doc:"Artifact reference"`

	// Tag is the OCI tag label (e.g. "1.0.0", "stable", "latest").
	// For version tags this matches Reference.Version; for aliases it holds
	// the alias string.
	Tag string `json:"tag" yaml:"tag" doc:"OCI tag label"`

	// Digest is the content digest (sha256:...).
	Digest string `json:"digest" yaml:"digest" doc:"Content digest"`

	// ImageRef is the full OCI image reference suitable for cosign verification
	// (e.g., "ghcr.io/org/plugins/providers/name@sha256:abc123...").
	// Populated only by remote catalogs; empty for local catalogs.
	ImageRef string `json:"imageRef,omitempty" yaml:"imageRef,omitempty" doc:"Full OCI image reference for signature verification"`

	// CreatedAt is when the artifact was stored.
	CreatedAt time.Time `json:"createdAt" yaml:"createdAt" doc:"Storage timestamp"`

	// Size is the artifact size in bytes.
	Size int64 `json:"size" yaml:"size" doc:"Size in bytes"`

	// Annotations are OCI annotations from the manifest.
	Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty" doc:"OCI annotations"`

	// Catalog is the name of the catalog this artifact came from.
	Catalog string `json:"catalog" yaml:"catalog" doc:"Source catalog name"`
}

ArtifactInfo contains metadata about a stored artifact.

type ArtifactKind

type ArtifactKind string

ArtifactKind represents the type of artifact stored in the catalog.

const (
	// ArtifactKindSolution represents a solution artifact.
	ArtifactKindSolution ArtifactKind = "solution"

	// ArtifactKindProvider represents a provider artifact (go-plugin binary exposing providers).
	ArtifactKindProvider ArtifactKind = "provider"

	// ArtifactKindAuthHandler represents an auth handler artifact (go-plugin binary exposing auth handlers).
	ArtifactKindAuthHandler ArtifactKind = "auth-handler"
)

func InferKindFromLocalCatalog added in v0.6.0

func InferKindFromLocalCatalog(ctx context.Context, localCatalog *LocalCatalog, name, version string) (ArtifactKind, error)

InferKindFromLocalCatalog searches the local catalog to determine an artifact's kind. It tries each known artifact kind in order and returns the first match.

func InferKindFromRemote added in v0.8.0

func InferKindFromRemote(ctx context.Context, remoteCatalog *RemoteCatalog, name, version string) (ArtifactKind, error)

InferKindFromRemote resolves an artifact's kind by trying each known kind against the remote catalog. For each kind it attempts to resolve the tag; the first successful resolve wins. This avoids fetching the full manifest and works even when the artifact has no scafctl-specific annotations.

If no known kind matches, it returns an error rather than guessing a fallback kind.

func ParseArtifactKind

func ParseArtifactKind(s string) (ArtifactKind, bool)

ParseArtifactKind parses a string into an ArtifactKind. Returns empty string and false if the input is not a valid kind.

func ParseArtifactKindFromPlural

func ParseArtifactKindFromPlural(s string) (ArtifactKind, bool)

ParseArtifactKindFromPlural parses a pluralized path segment into an ArtifactKind. E.g., "solutions" -> ArtifactKindSolution, "providers" -> ArtifactKindProvider

func ValidatePluginKind added in v0.6.0

func ValidatePluginKind(kindStr string) (ArtifactKind, error)

ValidatePluginKind validates that the given kind string is a valid plugin kind (provider or auth-handler). Returns the parsed ArtifactKind or an error.

func (ArtifactKind) IsValid

func (k ArtifactKind) IsValid() bool

IsValid returns true if the artifact kind is valid.

func (ArtifactKind) Plural

func (k ArtifactKind) Plural() string

Plural returns the pluralized form of the artifact kind (for repository paths).

func (ArtifactKind) String

func (k ArtifactKind) String() string

String returns the string representation of the artifact kind.

type ArtifactNotFoundError

type ArtifactNotFoundError struct {
	Reference Reference
	Catalog   string // Optional: which catalog was checked
}

ArtifactNotFoundError provides details about a missing artifact.

func (*ArtifactNotFoundError) Error

func (e *ArtifactNotFoundError) Error() string

Error implements the error interface.

func (*ArtifactNotFoundError) Unwrap

func (e *ArtifactNotFoundError) Unwrap() error

Unwrap returns the base error for errors.Is support.

type Catalog

type Catalog interface {
	// Name returns the catalog identifier (e.g., "local", "company-registry").
	Name() string

	// Store saves an artifact to the catalog.
	// For solutions with bundled files, bundleData contains the tar archive.
	// If bundleData is nil, only the primary content layer is stored.
	// Returns ErrArtifactExists if the version already exists (use force to overwrite).
	Store(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)

	// Fetch retrieves an artifact's primary content from the catalog.
	// Returns ErrArtifactNotFound if the artifact doesn't exist.
	Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

	// FetchWithBundle retrieves an artifact's primary content and bundle layer.
	// The bundle layer contains bundled files as a tar archive.
	// If the artifact has no bundle layer, bundleData is nil.
	// Returns ErrArtifactNotFound if the artifact doesn't exist.
	FetchWithBundle(ctx context.Context, ref Reference) (content, bundleData []byte, info ArtifactInfo, err error)

	// Resolve finds the best matching version for a reference.
	// If no version is specified, returns the highest semver version.
	// Returns ErrArtifactNotFound if no matching artifact exists.
	Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

	// List returns all artifacts matching the criteria.
	// If name is empty, returns all artifacts of the specified kind.
	List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

	// Exists checks if an artifact exists in the catalog.
	Exists(ctx context.Context, ref Reference) (bool, error)

	// Delete removes an artifact from the catalog.
	// Returns ErrArtifactNotFound if the artifact doesn't exist.
	Delete(ctx context.Context, ref Reference) error
}

Catalog defines the interface for artifact storage. Both local and remote catalogs implement this interface.

func NewMockCatalog added in v0.17.0

func NewMockCatalog(name string, opts ...MockOption) Catalog

NewMockCatalog creates a Catalog for testing with the given name and options.

func RemoteCatalogsFromContext added in v0.8.0

func RemoteCatalogsFromContext(ctx context.Context, lgr logr.Logger) []Catalog

RemoteCatalogsFromContext builds Catalog instances for every OCI remote catalog in the application config. These are suitable for passing to WithResolverRemoteCatalogs so the SolutionResolver can auto-pull artifacts that are not in the local catalog.

Catalogs that fail to initialise (e.g. missing credentials) are silently skipped — the resolver will simply not search them.

type ChainCatalog added in v0.5.0

type ChainCatalog struct {
	// contains filtered or unexported fields
}

ChainCatalog tries each catalog in order, returning the first successful result. It implements the Catalog interface for read operations (Fetch, Resolve, List, Exists). Write operations (Store, Delete) are forwarded to the first catalog in the chain.

It also implements PlatformAwareCatalog by delegating to underlying catalogs that support platform-aware operations (e.g., LocalCatalog with OCI image indexes).

func BuildCatalogChain added in v0.5.0

func BuildCatalogChain(cfg *config.Config, authRegistry *auth.Registry, logger logr.Logger, opts ...ChainCatalogOption) (*ChainCatalog, error)

BuildCatalogChain creates a ChainCatalog from the application configuration.

The chain order is deterministic:

  1. Local filesystem catalog (always first)
  2. Embedder/user catalogs from config (in config order, excluding reserved names)
  3. Official catalog (always last, unless disabled via settings.disableOfficialCatalog)

If authRegistry is provided, catalogs with an authProvider field will use the corresponding auth handler for dynamic token injection.

func BuildRemoteCatalogChain added in v0.17.0

func BuildRemoteCatalogChain(cfg *config.Config, authRegistry *auth.Registry, logger logr.Logger) (*ChainCatalog, error)

BuildRemoteCatalogChain creates a ChainCatalog containing only remote catalogs (user/embedder catalogs and the official catalog). The local filesystem catalog is excluded.

This is used by operations that need to check remote registries for newer versions without being shadowed by locally cached artifacts (e.g. plugin update checks).

func NewChainCatalog added in v0.5.0

func NewChainCatalog(logger logr.Logger, catalogs ...Catalog) (*ChainCatalog, error)

NewChainCatalog creates a ChainCatalog that tries catalogs in order. At least one catalog must be provided.

func (*ChainCatalog) Catalogs added in v0.5.0

func (c *ChainCatalog) Catalogs() []Catalog

Catalogs returns the underlying catalogs.

func (*ChainCatalog) Delete added in v0.5.0

func (c *ChainCatalog) Delete(ctx context.Context, ref Reference) error

Delete delegates to the first catalog.

func (*ChainCatalog) Exists added in v0.5.0

func (c *ChainCatalog) Exists(ctx context.Context, ref Reference) (bool, error)

Exists returns true if the artifact exists in any catalog.

func (*ChainCatalog) Fetch added in v0.5.0

func (c *ChainCatalog) Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

Fetch tries each catalog in order, returning the first successful result.

func (*ChainCatalog) FetchByPlatform added in v0.14.0

func (c *ChainCatalog) FetchByPlatform(ctx context.Context, ref Reference, platform string) ([]byte, ArtifactInfo, error)

FetchByPlatform tries each catalog that implements PlatformAwareCatalog in order, returning the first successful result. Catalogs that do not implement PlatformAwareCatalog are skipped. If a catalog explicitly reports the platform is not found (PlatformNotFoundError), the chain stops immediately because the artifact is known to be multi-platform and the platform is genuinely unavailable.

func (*ChainCatalog) FetchWithBundle added in v0.5.0

func (c *ChainCatalog) FetchWithBundle(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)

FetchWithBundle tries each catalog in order.

func (*ChainCatalog) List added in v0.5.0

func (c *ChainCatalog) List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

List returns artifacts from all catalogs (deduplicated by name+version).

func (*ChainCatalog) ListPlatforms added in v0.14.0

func (c *ChainCatalog) ListPlatforms(ctx context.Context, ref Reference) ([]string, error)

ListPlatforms tries each catalog that implements PlatformAwareCatalog in order, returning the first successful result.

func (*ChainCatalog) Name added in v0.5.0

func (c *ChainCatalog) Name() string

Name returns a composite name.

func (*ChainCatalog) Resolve added in v0.5.0

func (c *ChainCatalog) Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

Resolve tries each catalog in order, returning the first successful result.

func (*ChainCatalog) Store added in v0.5.0

func (c *ChainCatalog) Store(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)

Store delegates to the first catalog.

type ChainCatalogOption added in v0.17.0

type ChainCatalogOption func(*ChainCatalogOptions)

func WithAllowedCatalogs added in v0.17.0

func WithAllowedCatalogs(catalogNames []string) ChainCatalogOption

func WithPerCatalogArtifacts added in v0.17.0

func WithPerCatalogArtifacts(perCatalog map[string]PluginPolicy) ChainCatalogOption

WithPerCatalogArtifacts restricts which artifacts each catalog may serve. The map keys are catalog names; values describe the policy for that catalog. Catalogs present with AllowAll=true are unrestricted. Catalogs present with an explicit Plugins list are wrapped with an AllowlistCatalog. Catalogs NOT in the map are wrapped with an empty allowlist (deny all) when restrictions are active.

type ChainCatalogOptions added in v0.17.0

type ChainCatalogOptions struct {
	// contains filtered or unexported fields
}

type CopyOptions

type CopyOptions struct {
	// TargetName overrides the artifact name in the target catalog
	TargetName string

	// Force overwrites existing artifacts
	Force bool

	// OnProgress reports copy progress
	OnProgress func(desc ocispec.Descriptor)
}

CopyOptions configures a copy operation between catalogs.

type CredentialStore

type CredentialStore struct {
	// contains filtered or unexported fields
}

CredentialStore provides OCI registry credentials from docker config. It supports both static credentials and docker credential helpers, with a fallback to scafctl's native credential store.

func NewCredentialStore

func NewCredentialStore(logger logr.Logger) (*CredentialStore, error)

NewCredentialStore creates a credential store from the default docker config. It looks for config in the following order: 1. $DOCKER_CONFIG/config.json 2. ~/.docker/config.json 3. $XDG_RUNTIME_DIR/containers/auth.json (podman rootless) 4. ~/.config/containers/auth.json (podman) 5. /run/containers/$UID/auth.json (podman rootless fallback)

func (*CredentialStore) Credential

func (c *CredentialStore) Credential(ctx context.Context, host string) (auth.Credential, error)

Credential returns the auth credential for a registry host. This implements the auth.CredentialFunc signature for oras-go.

func (*CredentialStore) CredentialFunc

func (c *CredentialStore) CredentialFunc() auth.CredentialFunc

CredentialFunc returns an auth.CredentialFunc for use with oras-go.

func (*CredentialStore) CredentialWithSource added in v0.11.0

func (c *CredentialStore) CredentialWithSource(ctx context.Context, host string) (auth.Credential, string, error)

CredentialWithSource returns the auth credential for a registry host along with a human-readable label describing where the credential came from (e.g. "environment variable", "docker credential helper (desktop)", "native credential store"). Returns empty source for anonymous access.

type DiscoveredArtifact added in v0.10.1

type DiscoveredArtifact struct {
	Kind          ArtifactKind `json:"kind"           yaml:"kind"           doc:"Artifact kind" example:"solution" enum:"solution,provider,auth-handler"`
	Name          string       `json:"name"           yaml:"name"           doc:"Artifact name" example:"starter-kit" maxLength:"255"`
	LatestVersion string       `json:"latestVersion"  yaml:"latestVersion"  doc:"Latest semver version" example:"1.2.0"`

	// Enriched metadata fields, populated from solution YAML when available.
	// Empty for old indexes or non-solution artifacts.
	Description string   `json:"description,omitempty" yaml:"description,omitempty" doc:"Solution description" maxLength:"5000"`
	DisplayName string   `json:"displayName,omitempty" yaml:"displayName,omitempty" doc:"Human-friendly display name" maxLength:"80"`
	Category    string   `json:"category,omitempty"    yaml:"category,omitempty"    doc:"Solution category" example:"deployment" maxLength:"30"`
	Tags        []string `json:"tags,omitempty"        yaml:"tags,omitempty"        doc:"Searchable keywords" maxItems:"100"`

	// Extended metadata for MCP and rich discovery. Populated during index push
	// from the solution YAML's metadata and resolver sections.
	Maintainers []string         `json:"maintainers,omitempty" yaml:"maintainers,omitempty" doc:"Maintainer names" maxItems:"10"`
	Links       []DiscoveredLink `json:"links,omitempty"       yaml:"links,omitempty"       doc:"Related links" maxItems:"10"`
	Providers   []string         `json:"providers,omitempty"   yaml:"providers,omitempty"   doc:"Providers used by the solution" maxItems:"50"`
	Parameters  []string         `json:"parameters,omitempty"  yaml:"parameters,omitempty"  doc:"Parameter resolver names (user inputs)" maxItems:"50"`

	Source      string            `json:"source,omitempty"      yaml:"source,omitempty"      doc:"Source repository URL" maxLength:"500"`
	Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty" doc:"Free-form key-value annotations"`
}

DiscoveredArtifact represents an artifact discovered via registry enumeration. Exported for use by embedders that call ListRepositories directly.

func (DiscoveredArtifact) ToAnnotations added in v0.11.0

func (d DiscoveredArtifact) ToAnnotations() map[string]string

ToAnnotations converts enriched metadata fields into an OCI annotation map. Only non-empty fields are included.

type DiscoveredLink struct {
	Name string `json:"name" yaml:"name" doc:"Link label" example:"Documentation" maxLength:"30"`
	URL  string `json:"url"  yaml:"url"  doc:"Link URL" example:"https://example.com/docs" maxLength:"500"`
}

DiscoveredLink is a named URL reference (documentation, homepage, etc.).

type Index added in v0.10.1

type Index struct {
	// Artifacts is the list of discoverable artifacts.
	Artifacts []DiscoveredArtifact `json:"artifacts"`
}

Index is the JSON payload stored inside the catalog-index artifact. It contains a list of all discoverable artifacts in the catalog.

type IndexDiffChange added in v0.10.1

type IndexDiffChange string

IndexDiffChange represents the type of change for an artifact in the index.

const (
	// IndexDiffAdded indicates a new artifact not present in the current index.
	IndexDiffAdded IndexDiffChange = "added"
	// IndexDiffRemoved indicates an artifact present in the current index but
	// not in the new set.
	IndexDiffRemoved IndexDiffChange = "removed"
	// IndexDiffVersionChanged indicates an artifact whose latest version changed.
	IndexDiffVersionChanged IndexDiffChange = "version-changed"
	// IndexDiffUnchanged indicates an artifact with no changes.
	IndexDiffUnchanged IndexDiffChange = "unchanged"
)

type IndexDiffEntry added in v0.10.1

type IndexDiffEntry struct {
	Change        IndexDiffChange `json:"change"        yaml:"change"        doc:"Type of change" example:"added"`
	Kind          ArtifactKind    `json:"kind"           yaml:"kind"           doc:"Artifact kind" example:"solution"`
	Name          string          `json:"name"           yaml:"name"           doc:"Artifact name" example:"hello-world"`
	LatestVersion string          `json:"latestVersion"  yaml:"latestVersion"  doc:"New latest version" example:"1.2.0"`
	PrevVersion   string          `json:"prevVersion"    yaml:"prevVersion"    doc:"Previous latest version (empty if added)" example:"1.1.0"`
}

IndexDiffEntry represents a single artifact in the index diff.

type IndexDiffSummary added in v0.10.1

type IndexDiffSummary struct {
	Entries []IndexDiffEntry `json:"entries"  yaml:"entries"  doc:"All diff entries"`
	Added   int              `json:"added"    yaml:"added"    doc:"Number of added artifacts"`
	Removed int              `json:"removed"  yaml:"removed"  doc:"Number of removed artifacts"`
	Changed int              `json:"changed"  yaml:"changed"  doc:"Number of version-changed artifacts"`
	Total   int              `json:"total"    yaml:"total"    doc:"Total artifacts in new index"`
}

IndexDiffSummary summarizes the diff result.

func DiffIndex added in v0.10.1

func DiffIndex(current, next []DiscoveredArtifact) IndexDiffSummary

DiffIndex compares a new set of discovered artifacts against the currently published index and returns a summary of changes. Both slices may be nil/empty.

type InvalidReferenceError

type InvalidReferenceError struct {
	Input   string
	Message string
}

InvalidReferenceError provides details about an invalid reference.

func (*InvalidReferenceError) Error

func (e *InvalidReferenceError) Error() string

Error implements the error interface.

func (*InvalidReferenceError) Unwrap

func (e *InvalidReferenceError) Unwrap() error

Unwrap returns the base error for errors.Is support.

type LoadResult

type LoadResult struct {
	// Reference is the artifact that was loaded.
	Reference Reference `json:"reference" yaml:"reference"`
	// Digest is the manifest digest.
	Digest string `json:"digest" yaml:"digest"`
	// Size is the artifact content size in bytes.
	Size int64 `json:"size" yaml:"size"`
	// CreatedAt is when the artifact was originally created.
	CreatedAt time.Time `json:"createdAt" yaml:"createdAt"`
}

LoadResult contains information about the load operation.

type LocalCatalog

type LocalCatalog struct {
	// contains filtered or unexported fields
}

LocalCatalog implements Catalog using a local OCI layout store.

func NewLocalCatalog

func NewLocalCatalog(logger logr.Logger) (*LocalCatalog, error)

NewLocalCatalog creates a catalog at the XDG data path. The path is determined by paths.CatalogDir().

func NewLocalCatalogAt

func NewLocalCatalogAt(path string, logger logr.Logger) (*LocalCatalog, error)

NewLocalCatalogAt creates a catalog at a custom path. Use this for testing or custom installations.

func (*LocalCatalog) Delete

func (c *LocalCatalog) Delete(ctx context.Context, ref Reference) error

Delete removes an artifact from the catalog.

func (*LocalCatalog) Exists

func (c *LocalCatalog) Exists(ctx context.Context, ref Reference) (bool, error)

Exists checks if an artifact exists in the catalog.

func (*LocalCatalog) Fetch

func (c *LocalCatalog) Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

Fetch retrieves an artifact from the catalog.

func (*LocalCatalog) FetchByPlatform added in v0.6.0

func (c *LocalCatalog) FetchByPlatform(ctx context.Context, ref Reference, platform string) ([]byte, ArtifactInfo, error)

FetchByPlatform fetches a plugin binary for the given platform from an artifact that may be stored as either a single-platform manifest or a multi-platform image index. It transparently handles both cases.

func (*LocalCatalog) FetchDedup

func (c *LocalCatalog) FetchDedup(ctx context.Context, ref Reference) (solutionYAML, manifestJSON []byte, layerFetcher func(layer int) ([]byte, error), info ArtifactInfo, err error)

FetchDedup retrieves a deduplicated (v2) bundle by fetching all layers. Returns the solution YAML, bundle manifest JSON, and a layer fetcher that can retrieve individual layers by index.

func (*LocalCatalog) FetchWithBundle

func (c *LocalCatalog) FetchWithBundle(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)

FetchWithBundle retrieves an artifact's primary content and bundle layer. If the artifact has no bundle layer, bundleData is nil.

func (*LocalCatalog) List

func (c *LocalCatalog) List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

List returns all artifacts matching the criteria.

func (*LocalCatalog) ListPlatforms added in v0.6.0

func (c *LocalCatalog) ListPlatforms(ctx context.Context, ref Reference) ([]string, error)

ListPlatforms returns the platforms available for a multi-platform artifact. If the artifact is single-platform, returns nil.

func (*LocalCatalog) Load

func (c *LocalCatalog) Load(ctx context.Context, inputPath string, force bool) (LoadResult, error)

Load imports an artifact from an OCI Image Layout tar archive. Returns ErrArtifactExists if artifact already exists and force is false.

func (*LocalCatalog) Name

func (c *LocalCatalog) Name() string

Name returns "local".

func (*LocalCatalog) Path

func (c *LocalCatalog) Path() string

Path returns the catalog directory path.

func (*LocalCatalog) Prune

func (c *LocalCatalog) Prune(ctx context.Context) (PruneResult, error)

Prune removes orphaned blobs and manifests from the catalog. Orphaned content is any blob or manifest not referenced by a tagged artifact.

func (*LocalCatalog) Resolve

func (c *LocalCatalog) Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

Resolve finds the best matching version for a reference.

func (*LocalCatalog) Save

func (c *LocalCatalog) Save(ctx context.Context, name, version, outputPath string) (SaveResult, error)

Save exports an artifact to an OCI Image Layout tar archive. If version is empty, exports the latest version.

func (*LocalCatalog) Store

func (c *LocalCatalog) Store(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)

Store saves an artifact to the catalog. For solutions with bundled files, bundleData contains the tar archive. If bundleData is nil, only the primary content layer is stored.

func (*LocalCatalog) StoreDedup

func (c *LocalCatalog) StoreDedup(ctx context.Context, ref Reference, solutionYAML, manifestJSON, smallTar []byte, blobLayers [][]byte, annotations map[string]string, force bool) (ArtifactInfo, error)

StoreDedup saves a solution with content-addressable deduplicated layers. manifestJSON is the bundle manifest (v2), smallTar is grouped small files, and blobLayers are individual large file blobs with their media types.

func (*LocalCatalog) StoreMultiPlatform added in v0.6.0

func (c *LocalCatalog) StoreMultiPlatform(ctx context.Context, ref Reference, platformBinaries []PlatformBinary, annotations map[string]string, force bool) (ArtifactInfo, error)

StoreMultiPlatform stores a set of platform-specific plugin binaries as an OCI image index (fat manifest). Each entry in platformBinaries maps a platform string (e.g. "linux/amd64") to the raw binary data. The resulting image index is tagged under the normal kind/name:version scheme.

This replaces any existing single-platform artifact at the same reference.

func (*LocalCatalog) Tag

func (c *LocalCatalog) Tag(ctx context.Context, ref Reference, alias string) (string, error)

Tag creates an alias tag for an existing artifact. The source reference must have a version or digest to resolve. The alias is a freeform string (e.g., "stable", "production"). Returns the previous version string if the alias already existed (empty otherwise).

type MockOption added in v0.17.0

type MockOption func(*mockCatalog)

MockOption configures a mock catalog.

func WithDeleteFunc added in v0.17.0

func WithDeleteFunc(fn func(ctx context.Context, ref Reference) error) MockOption

WithDeleteFunc sets the Delete implementation.

func WithExistsFunc added in v0.17.0

func WithExistsFunc(fn func(ctx context.Context, ref Reference) (bool, error)) MockOption

WithExistsFunc sets the Exists implementation.

func WithFetchBundleFunc added in v0.17.0

func WithFetchBundleFunc(fn func(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)) MockOption

WithFetchBundleFunc sets the FetchWithBundle implementation.

func WithFetchFunc added in v0.17.0

func WithFetchFunc(fn func(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)) MockOption

WithFetchFunc sets the Fetch implementation.

func WithListFunc added in v0.17.0

func WithListFunc(fn func(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)) MockOption

WithListFunc sets the List implementation.

func WithResolveFunc added in v0.17.0

func WithResolveFunc(fn func(ctx context.Context, ref Reference) (ArtifactInfo, error)) MockOption

WithResolveFunc sets the Resolve implementation.

func WithStoreFunc added in v0.17.0

func WithStoreFunc(fn func(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)) MockOption

WithStoreFunc sets the Store implementation.

type NativeCredential added in v0.7.0

type NativeCredential struct {
	Username          string `json:"username"`
	Password          string `json:"password,omitempty"`          //nolint:gosec // stored encrypted via secretsStore when available; JSON field retained for legacy migration
	ContainerAuthFile string `json:"containerAuthFile,omitempty"` // path to the container auth file written on login
	Handler           string `json:"handler,omitempty"`           // auth handler that bridged this credential (e.g. "github")
	Profile           string `json:"profile,omitempty"`           // auth profile used when bridging (e.g. "work")
}

NativeCredential represents a stored registry credential. When a secrets store is available, the Password field is empty in the JSON file and the actual password is retrieved from the encrypted secrets store at runtime. Legacy entries that contain a plaintext password in JSON are still readable.

type NativeCredentialStore added in v0.7.0

type NativeCredentialStore struct {
	// contains filtered or unexported fields
}

NativeCredentialStore manages scafctl-native OCI registry credentials. Credentials are stored at <XDG_CONFIG_HOME>/scafctl/registries.json. When a secrets store is available, passwords are stored encrypted there instead of in the JSON file. Legacy entries with plaintext JSON passwords remain readable.

func NewNativeCredentialStore added in v0.7.0

func NewNativeCredentialStore() *NativeCredentialStore

NewNativeCredentialStore creates a new native credential store without an encrypted secrets backend. Passwords fall back to the plaintext JSON field. Callers that have access to a pre-configured secrets store (e.g. the shared store created in root.go) should use NewNativeCredentialStoreWithSecretsStore so that settings such as RequireSecureKeyring are honoured.

func NewNativeCredentialStoreWithPath added in v0.7.0

func NewNativeCredentialStoreWithPath(path string) *NativeCredentialStore

NewNativeCredentialStoreWithPath creates a native credential store at a custom path. The secrets store is not initialised so passwords are stored in plaintext JSON. This is intended for testing only.

func NewNativeCredentialStoreWithSecretsStore added in v0.7.0

func NewNativeCredentialStoreWithSecretsStore(secretsStore secrets.Store) *NativeCredentialStore

NewNativeCredentialStoreWithSecretsStore creates a new native credential store using the provided, pre-configured secrets store for encrypted password storage. Use this constructor whenever a secrets store is available so that centralised security settings (RequireSecureKeyring, logger) are honoured.

func (*NativeCredentialStore) DeleteAll added in v0.7.0

func (s *NativeCredentialStore) DeleteAll() error

DeleteAll removes all stored credentials.

func (*NativeCredentialStore) DeleteContainerAuth added in v0.7.0

func (s *NativeCredentialStore) DeleteContainerAuth(host string) error

DeleteContainerAuth removes a credential from the container auth file. It uses the path stored in the credential entry so deletion always targets the same file that was written during login. This is a best-effort operation.

func (*NativeCredentialStore) DeleteCredential added in v0.7.0

func (s *NativeCredentialStore) DeleteCredential(host string) error

DeleteCredential removes the credential for the given registry host. The host is normalized so that Docker Hub variants resolve to the canonical key.

func (*NativeCredentialStore) GetCredential added in v0.7.0

func (s *NativeCredentialStore) GetCredential(host string) (*NativeCredential, error)

GetCredential returns the credential for the given registry host. The host is normalized so that Docker Hub variants resolve to a canonical key. When a secrets store is available, the password is retrieved from encrypted storage.

func (*NativeCredentialStore) GetPreviousUsername added in v0.17.0

func (s *NativeCredentialStore) GetPreviousUsername(host string) string

GetPreviousUsername returns the username of the currently stored credential for the given host, or empty string if none exists. Used to detect credential replacement during bridging.

func (*NativeCredentialStore) ListCredentialEntries added in v0.7.0

func (s *NativeCredentialStore) ListCredentialEntries() (map[string]NativeCredential, error)

ListCredentialEntries returns all stored registry hosts and their full credential entries. When a secrets store is available, passwords are retrieved from encrypted storage.

func (*NativeCredentialStore) ListCredentials added in v0.7.0

func (s *NativeCredentialStore) ListCredentials() (map[string]string, error)

ListCredentials returns all stored registry hosts and their usernames.

func (*NativeCredentialStore) Path added in v0.7.0

func (s *NativeCredentialStore) Path() string

Path returns the file path of the credential store.

func (*NativeCredentialStore) SetCredential added in v0.7.0

func (s *NativeCredentialStore) SetCredential(host, username, password, containerAuthFile string) error

SetCredential stores a credential for the given registry host. The host is normalized so that Docker Hub variants are stored under a canonical key. containerAuthFile is the path to the container auth file written during login, or empty if no container auth file was written. When a secrets store is available the password is stored encrypted; only the username and metadata are written to the JSON file.

func (*NativeCredentialStore) SetCredentialWithSource added in v0.17.0

func (s *NativeCredentialStore) SetCredentialWithSource(host, username, password, containerAuthFile, handler, profile string) error

SetCredentialWithSource stores a credential with handler and profile metadata. This records which auth handler and profile bridged the credential, enabling auth status to display the source of registry credentials.

func (*NativeCredentialStore) WriteContainerAuth added in v0.7.0

func (s *NativeCredentialStore) WriteContainerAuth(host, username, password string) (string, error)

WriteContainerAuth writes a credential to the container auth file for Docker/Podman interop. The file is detected in order: 1. $REGISTRY_AUTH_FILE environment variable 2. ~/.config/containers/auth.json 3. ~/.docker/config.json Returns the resolved file path on success so callers can persist it. This is a best-effort operation; callers should treat errors as warnings.

type OCITarReader

type OCITarReader struct {
	// contains filtered or unexported fields
}

OCITarReader reads OCI Image Layout format from a tar archive.

func NewOCITarReader

func NewOCITarReader(r io.Reader) (*OCITarReader, error)

NewOCITarReader reads an OCI tar archive into memory.

func (*OCITarReader) Blobs

func (r *OCITarReader) Blobs() map[string][]byte

Blobs returns all blobs in the archive.

func (*OCITarReader) GetBlob

func (r *OCITarReader) GetBlob(digest string) ([]byte, bool)

GetBlob returns a specific blob by digest.

func (*OCITarReader) HasValidLayout

func (r *OCITarReader) HasValidLayout() bool

HasValidLayout returns true if the archive has a valid oci-layout file.

func (*OCITarReader) Index

func (r *OCITarReader) Index() (*ocispec.Index, error)

Index returns the parsed index.json.

type OCITarWriter

type OCITarWriter struct {
	// contains filtered or unexported fields
}

OCITarWriter writes OCI Image Layout format to a tar archive.

func NewOCITarWriter

func NewOCITarWriter(w io.Writer) *OCITarWriter

NewOCITarWriter creates a new OCI tar writer.

func (*OCITarWriter) Close

func (w *OCITarWriter) Close() error

Close closes the tar writer.

func (*OCITarWriter) WriteBlob

func (w *OCITarWriter) WriteBlob(digestStr string, data []byte) error

WriteBlob writes a blob to the blobs/sha256/ directory.

func (*OCITarWriter) WriteIndex

func (w *OCITarWriter) WriteIndex(index ocispec.Index) error

WriteIndex writes the index.json file.

func (*OCITarWriter) WriteOCILayout

func (w *OCITarWriter) WriteOCILayout() error

WriteOCILayout writes the oci-layout file.

type PlatformAwareCatalog added in v0.6.0

type PlatformAwareCatalog interface {
	Catalog

	// FetchByPlatform fetches a plugin binary for the given platform,
	// transparently handling both single-platform manifests and
	// multi-platform image indexes.
	FetchByPlatform(ctx context.Context, ref Reference, platform string) ([]byte, ArtifactInfo, error)

	// ListPlatforms returns the platforms available for a multi-platform artifact.
	// Returns nil if the artifact is single-platform.
	ListPlatforms(ctx context.Context, ref Reference) ([]string, error)
}

PlatformAwareCatalog extends Catalog with multi-platform image index support. Catalogs that store multi-platform artifacts (e.g. LocalCatalog) implement this interface to allow transparent platform-specific fetching.

type PlatformBinary added in v0.6.0

type PlatformBinary struct {
	// Platform in OCI format, e.g. "linux/amd64".
	Platform string `json:"platform" yaml:"platform" doc:"Target platform in os/arch format"`

	// Data is the raw binary content.
	Data []byte `json:"-" yaml:"-"`
}

PlatformBinary pairs a platform string (e.g. "linux/amd64") with the raw plugin binary for that platform.

func ReadPlatformBinaries added in v0.6.0

func ReadPlatformBinaries(ctx context.Context, platformPaths map[string]string) ([]PlatformBinary, error)

ReadPlatformBinaries validates platform names, resolves file paths to absolute paths, stats each file (rejecting directories), reads binary data, and returns PlatformBinary entries ready for storage.

platformPaths maps platform strings (e.g., "linux/amd64") to file paths. Returns an error if any platform is unsupported, path doesn't exist, path is a directory, or file data is empty or unreadable.

type PlatformNotFoundError added in v0.6.0

type PlatformNotFoundError struct {
	Platform  string   // The requested platform (e.g. "linux/amd64")
	Available []string // Available platforms in the index
}

PlatformNotFoundError provides details about a missing platform in an image index.

func (*PlatformNotFoundError) Error added in v0.6.0

func (e *PlatformNotFoundError) Error() string

Error implements the error interface.

func (*PlatformNotFoundError) Unwrap added in v0.6.0

func (e *PlatformNotFoundError) Unwrap() error

Unwrap returns the base error for errors.Is support.

type PluginFetcher added in v0.5.0

type PluginFetcher struct {
	// contains filtered or unexported fields
}

PluginFetcher fetches plugin binaries from a catalog with platform awareness. It resolves plugin references, selects the appropriate platform variant via the OCI image index (preferred) or AnnotationPlatform annotation (fallback), and returns the raw binary data.

func NewPluginFetcher added in v0.5.0

func NewPluginFetcher(catalog Catalog, logger logr.Logger) *PluginFetcher

NewPluginFetcher creates a PluginFetcher backed by the given catalog.

func (*PluginFetcher) FetchPlugin added in v0.5.0

func (f *PluginFetcher) FetchPlugin(ctx context.Context, name string, kind ArtifactKind, version, platform string) ([]byte, ArtifactInfo, error)

FetchPlugin fetches a plugin binary for the given platform. It uses the following resolution strategy:

  1. If the catalog implements PlatformAwareCatalog, use FetchByPlatform which handles OCI image indexes (fat manifests) transparently.
  2. Otherwise, fall back to listing artifacts and matching the AnnotationPlatform annotation on individual manifests.
  3. If no platform-specific artifact is found, attempt a direct fetch (single-platform fallback).

func (*PluginFetcher) ResolvePlugin added in v0.5.0

func (f *PluginFetcher) ResolvePlugin(ctx context.Context, name string, kind ArtifactKind, versionConstraint string) (ArtifactInfo, error)

ResolvePlugin resolves a plugin by name, kind, and version constraint, returning its artifact info. If versionConstraint is empty, the latest version is returned.

type PluginPolicy added in v0.17.0

type PluginPolicy struct {
	AllowAll bool     // true = wildcard ("catalog/*"), Plugins is ignored
	Plugins  []string // explicit plugin names (only meaningful when AllowAll is false)
}

PluginPolicy describes what is allowed from a single catalog.

type PruneResult

type PruneResult struct {
	// RemovedManifests is the number of orphaned manifests removed
	RemovedManifests int `json:"removedManifests" yaml:"removedManifests"`
	// RemovedBlobs is the number of orphaned blobs removed
	RemovedBlobs int `json:"removedBlobs" yaml:"removedBlobs"`
	// ReclaimedBytes is the total bytes freed
	ReclaimedBytes int64 `json:"reclaimedBytes" yaml:"reclaimedBytes"`
}

PruneResult contains statistics from a prune operation.

type Reference

type Reference struct {
	// Kind is the type of artifact (solution, provider, or auth-handler).
	Kind ArtifactKind `json:"kind" yaml:"kind" doc:"Artifact type"`

	// Name is the artifact identifier (e.g., "my-solution").
	Name string `json:"name" yaml:"name" doc:"Artifact name"`

	// Version is the semantic version (e.g., 1.2.3). Nil means "latest".
	Version *semver.Version `json:"version,omitempty" yaml:"version,omitempty" doc:"Semantic version"`

	// Digest is the content digest for pinning (e.g., "sha256:abc123...").
	// If set, takes precedence over Version for resolution.
	Digest string `json:"digest,omitempty" yaml:"digest,omitempty" doc:"Content digest for pinning"`
}

Reference uniquely identifies an artifact in the catalog.

func ParseReference

func ParseReference(kind ArtifactKind, input string) (Reference, error)

ParseReference parses a reference string into a Reference struct. Supported formats:

  • "name" - artifact name only (version resolved to latest)
  • "name@1.2.3" - artifact with specific version
  • "name@sha256:abc..." - artifact with specific digest

func (Reference) HasDigest

func (r Reference) HasDigest() bool

HasDigest returns true if the reference has a digest specified.

func (Reference) HasVersion

func (r Reference) HasVersion() bool

HasVersion returns true if the reference has a version specified.

func (Reference) String

func (r Reference) String() string

String returns the canonical reference string (e.g., "my-solution@1.2.3").

func (Reference) VersionOrDigest added in v0.8.0

func (r Reference) VersionOrDigest() string

VersionOrDigest returns a display string for the reference identifier. Prefers Version.String() when set, falls back to Digest, then "unknown".

type ReferrerInfo added in v0.8.0

type ReferrerInfo struct {
	// ArtifactType is the media type or artifactType of the referrer.
	ArtifactType string `json:"artifactType" yaml:"artifactType"`

	// Digest is the referrer manifest digest.
	Digest string `json:"digest" yaml:"digest"`

	// Size is the referrer manifest size.
	Size int64 `json:"size" yaml:"size"`

	// Annotations from the referrer manifest.
	Annotations map[string]string `json:"annotations,omitempty" yaml:"annotations,omitempty"`
}

ReferrerInfo describes an artifact attached to a subject via OCI referrers.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry manages multiple catalogs and provides unified access. The local catalog is always first in resolution order.

func NewRegistry

func NewRegistry(logger logr.Logger) (*Registry, error)

NewRegistry creates a registry with the built-in local catalog.

func NewRegistryWithLocal

func NewRegistryWithLocal(local *LocalCatalog, logger logr.Logger) *Registry

NewRegistryWithLocal creates a registry with a custom local catalog. Use for testing with a custom catalog directory.

func (*Registry) AddCatalog

func (r *Registry) AddCatalog(catalog Catalog)

AddCatalog adds a catalog to the registry. Catalogs are searched in the order they are added (after local).

func (*Registry) Catalogs

func (r *Registry) Catalogs() []Catalog

Catalogs returns all registered catalogs.

func (*Registry) Fetch

func (r *Registry) Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

Fetch retrieves an artifact from the first catalog that has it. If cacheRemoteArtifacts is enabled, artifacts fetched from remote catalogs are automatically stored in the local catalog for subsequent offline access.

func (*Registry) FetchWithBundle

func (r *Registry) FetchWithBundle(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)

FetchWithBundle retrieves an artifact with its bundle layer from the first catalog that has it. If cacheRemoteArtifacts is enabled, artifacts fetched from remote catalogs are automatically stored in the local catalog for subsequent offline access.

func (*Registry) List

func (r *Registry) List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

List returns all artifacts matching the criteria from all catalogs.

func (*Registry) Local

func (r *Registry) Local() *LocalCatalog

Local returns the built-in local catalog.

func (*Registry) Resolve

func (r *Registry) Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

Resolve finds an artifact in the first catalog that has it. Searches catalogs in order: local first, then configured catalogs.

func (*Registry) SetCacheRemoteArtifacts added in v0.3.0

func (r *Registry) SetCacheRemoteArtifacts(enabled bool)

SetCacheRemoteArtifacts enables or disables auto-caching of remote catalog fetches into the local catalog. When enabled, artifacts fetched from remote catalogs are automatically stored locally so subsequent fetches are served from the local catalog without network access.

type RegistryUsernameProvider added in v0.7.0

type RegistryUsernameProvider interface {
	RegistryUsername() string
}

RegistryUsernameProvider is an optional interface for auth handlers that declare a custom registry username convention. BridgeAuthToRegistry checks for this interface via type assertion on the default (non-built-in) path.

type RemoteCatalog

type RemoteCatalog struct {
	// contains filtered or unexported fields
}

RemoteCatalog implements Catalog interface for OCI registries.

func BuildRemoteCatalogFromConfig added in v0.11.0

func BuildRemoteCatalogFromConfig(catCfg config.CatalogConfig, credStore *CredentialStore, authRegistry *auth.Registry, logger logr.Logger) (*RemoteCatalog, error)

BuildRemoteCatalogFromConfig creates a RemoteCatalog from a CatalogConfig. Exported for use by packages that need to construct remote catalogs outside the catalog chain (e.g. MCP tools for index-based search).

func NewRemoteCatalog

func NewRemoteCatalog(cfg RemoteCatalogConfig) (*RemoteCatalog, error)

NewRemoteCatalog creates a remote catalog client.

func (*RemoteCatalog) Attach added in v0.8.0

func (c *RemoteCatalog) Attach(ctx context.Context, ref Reference, artifactType string, data []byte, annotations map[string]string) (ocispec.Descriptor, error)

Attach pushes an artifact that references (is attached to) a subject artifact via the OCI referrers mechanism. The subject is identified by ref; the attachment is described by artifactType and its raw bytes.

func (*RemoteCatalog) AuthHandlerUsed added in v0.11.0

func (c *RemoteCatalog) AuthHandlerUsed() string

AuthHandlerUsed returns the name of the auth handler configured for this catalog (e.g. "github", "gcp", "entra"). Returns empty if no handler.

func (*RemoteCatalog) CopyFrom

func (c *RemoteCatalog) CopyFrom(ctx context.Context, source *LocalCatalog, ref Reference, opts CopyOptions) (ArtifactInfo, error)

CopyFrom copies an artifact from a local catalog to this remote catalog.

func (*RemoteCatalog) CopyTo

func (c *RemoteCatalog) CopyTo(ctx context.Context, ref Reference, target *LocalCatalog, opts CopyOptions) (ArtifactInfo, error)

CopyTo copies an artifact from this remote catalog to a local catalog.

func (*RemoteCatalog) CredentialSource added in v0.11.0

func (c *RemoteCatalog) CredentialSource() string

CredentialSource returns a human-readable description of the credential source that was last resolved for registry authentication (e.g. "docker credential helper (desktop)", "github auth handler token", "native credential store"). Empty when no credentials were resolved.

func (*RemoteCatalog) Delete

func (c *RemoteCatalog) Delete(ctx context.Context, ref Reference) error

Delete removes an artifact from the catalog.

The method first attempts a standard OCI delete by digest. If the registry rejects it because the manifest still has tags (e.g., GCP Artifact Registry returns 400 "dangling tag"), it retries by deleting via the tag reference.

func (*RemoteCatalog) Exists

func (c *RemoteCatalog) Exists(ctx context.Context, ref Reference) (bool, error)

Exists checks if an artifact exists in the catalog.

func (*RemoteCatalog) Fetch

func (c *RemoteCatalog) Fetch(ctx context.Context, ref Reference) ([]byte, ArtifactInfo, error)

Fetch retrieves an artifact from the remote catalog.

func (*RemoteCatalog) FetchByPlatform added in v0.14.0

func (c *RemoteCatalog) FetchByPlatform(ctx context.Context, ref Reference, platform string) ([]byte, ArtifactInfo, error)

FetchByPlatform fetches a plugin binary for a specific platform from a remote OCI registry. It transparently handles both single-platform manifests and multi-platform image indexes.

func (*RemoteCatalog) FetchIndex added in v0.10.1

func (c *RemoteCatalog) FetchIndex(ctx context.Context) ([]DiscoveredArtifact, error)

FetchIndex pulls the well-known catalog-index artifact and returns the discovered artifacts. Returns an error if the index does not exist or cannot be parsed.

func (*RemoteCatalog) FetchWithBundle

func (c *RemoteCatalog) FetchWithBundle(ctx context.Context, ref Reference) ([]byte, []byte, ArtifactInfo, error)

FetchWithBundle retrieves an artifact's primary content and bundle layer. If the artifact has no bundle layer, bundleData is nil.

func (*RemoteCatalog) HasStaleCredentials added in v0.11.0

func (c *RemoteCatalog) HasStaleCredentials() bool

HasStaleCredentials returns true if any OCI operation during the lifetime of this catalog client fell back to anonymous access because the stored credentials were rejected by the registry. Callers can use this to show a user-facing hint suggesting re-authentication.

func (*RemoteCatalog) List

func (c *RemoteCatalog) List(ctx context.Context, kind ArtifactKind, name string) ([]ArtifactInfo, error)

List returns all artifacts matching the criteria.

func (*RemoteCatalog) ListPlatforms added in v0.14.0

func (c *RemoteCatalog) ListPlatforms(ctx context.Context, ref Reference) ([]string, error)

ListPlatforms returns the platforms available for a multi-platform artifact. Returns nil if the artifact is single-platform.

func (*RemoteCatalog) ListRepositories added in v0.10.1

func (c *RemoteCatalog) ListRepositories(ctx context.Context) ([]DiscoveredArtifact, error)

ListRepositories enumerates all artifact repositories in this catalog using a registry-specific enumerator. The enumerator is selected automatically based on the auth handler name and registry hostname.

Returns ErrEnumerationNotSupported if the registry cannot be enumerated.

func (*RemoteCatalog) ListTags added in v0.8.0

func (c *RemoteCatalog) ListTags(ctx context.Context, ref Reference) ([]TagInfo, error)

ListTags returns all tags (semver versions and aliases) for an artifact in the remote registry.

func (*RemoteCatalog) Name

func (c *RemoteCatalog) Name() string

Name returns the catalog identifier.

func (*RemoteCatalog) PushIndex added in v0.10.1

func (c *RemoteCatalog) PushIndex(ctx context.Context, artifacts []DiscoveredArtifact) error

PushIndex pushes a catalog index artifact containing the given artifacts to the well-known catalog-index repository. This enables unauthenticated users to discover available packages without needing the registry's enumeration API.

func (*RemoteCatalog) Referrers added in v0.8.0

func (c *RemoteCatalog) Referrers(ctx context.Context, ref Reference, artifactType string) ([]ReferrerInfo, error)

Referrers lists all artifacts that reference the given subject artifact. If artifactType is non-empty, only referrers matching that type are returned.

func (*RemoteCatalog) Registry

func (c *RemoteCatalog) Registry() string

Registry returns the registry address.

func (*RemoteCatalog) Repository

func (c *RemoteCatalog) Repository() string

Repository returns the base repository path.

func (*RemoteCatalog) RepositoryPath added in v0.8.0

func (c *RemoteCatalog) RepositoryPath(ref Reference) string

RepositoryPath returns the full OCI repository path for an artifact reference. This is useful for displaying the resolved path in CLI output.

func (*RemoteCatalog) Resolve

func (c *RemoteCatalog) Resolve(ctx context.Context, ref Reference) (ArtifactInfo, error)

Resolve finds the best matching version for a reference.

func (*RemoteCatalog) ResolveLatestVersions added in v0.10.1

func (c *RemoteCatalog) ResolveLatestVersions(ctx context.Context, artifacts []DiscoveredArtifact)

ResolveLatestVersions enriches a slice of discovered artifacts with their latest semver version by fetching tags from the registry. Uses bounded concurrency to avoid overwhelming the registry. Artifacts whose tags cannot be fetched are left with an empty LatestVersion.

func (*RemoteCatalog) SetClient added in v0.10.1

func (c *RemoteCatalog) SetClient(client *auth.Client)

SetClient overrides the OCI auth client used for registry operations. This is useful for injecting credentials in tooling (e.g. push-index). When the catalog enumerator also supports client replacement, it is updated so that enumeration and repository access use the same credentials.

func (*RemoteCatalog) SetCredentialSourceForTest added in v0.11.0

func (c *RemoteCatalog) SetCredentialSourceForTest(source string)

SetCredentialSourceForTest stores a credential source string. This is intended for use by CLI-layer tests that need to exercise the credential-source display path.

func (*RemoteCatalog) SetStaleForTesting added in v0.11.0

func (c *RemoteCatalog) SetStaleForTesting()

SetStaleForTesting marks the catalog as having stale credentials. This is intended for use by CLI-layer tests that need to exercise the user-facing warning path.

func (*RemoteCatalog) Store

func (c *RemoteCatalog) Store(ctx context.Context, ref Reference, content, bundleData []byte, annotations map[string]string, force bool) (ArtifactInfo, error)

Store saves an artifact to the remote catalog. For solutions with bundled files, bundleData contains the tar archive. If bundleData is nil, only the primary content layer is stored.

func (*RemoteCatalog) Tag

func (c *RemoteCatalog) Tag(ctx context.Context, ref Reference, alias string) (string, error)

Tag creates an alias tag for an existing remote artifact. Returns a non-empty string if the alias already existed pointing to a different digest.

type RemoteCatalogConfig

type RemoteCatalogConfig struct {
	// Name is the catalog identifier (e.g., "company-registry")
	Name string

	// RegistryURL is the registry address (e.g., "ghcr.io", "registry.example.com")
	Registry string

	// Repository is the base repository path (e.g., "myorg/scafctl")
	Repository string

	// CredentialStore provides authentication credentials
	CredentialStore *CredentialStore

	// AuthHandler provides dynamic token injection for this catalog.
	// When set, if the CredentialStore has no credentials for the registry,
	// the handler's token is bridged to OCI registry credentials.
	AuthHandler scafctlauth.Handler

	// AuthScope is the OAuth scope for auth handler token requests.
	AuthScope string

	// Insecure allows HTTP connections (for testing)
	Insecure bool

	// DiscoveryStrategy controls how artifacts are discovered.
	// Empty or "auto" uses API then index fallback (default).
	// "index" skips API and fetches the catalog-index directly.
	// "api" uses API only, no index fallback.
	DiscoveryStrategy config.DiscoveryStrategy

	// Logger for logging operations
	Logger logr.Logger
}

RemoteCatalogConfig holds configuration for creating a remote catalog.

type RemoteReference

type RemoteReference struct {
	// Registry is the registry host (e.g., "ghcr.io", "docker.io")
	Registry string

	// Repository is the repository path (e.g., "myorg/scafctl")
	Repository string

	// Kind is the artifact kind (solution, provider, or auth-handler)
	Kind ArtifactKind

	// Name is the artifact name
	Name string

	// Tag is the version tag or digest
	Tag string
}

RemoteReference represents a parsed remote registry reference.

func ParseRemoteReference

func ParseRemoteReference(input string) (*RemoteReference, error)

ParseRemoteReference parses a full remote reference URL. Supported formats:

  • "ghcr.io/myorg/scafctl/solutions/my-solution@1.0.0"
  • "oci://ghcr.io/myorg/scafctl/solutions/my-solution@1.0.0"
  • "docker.io/myorg/my-solution:1.0.0" (Docker Hub style)

Returns the registry, repository, name, and version/tag.

func (*RemoteReference) String

func (r *RemoteReference) String() string

String returns the full remote reference string.

func (*RemoteReference) ToReference

func (r *RemoteReference) ToReference() (Reference, error)

ToReference converts a RemoteReference to a Reference.

type RemoteSolutionResolver added in v0.8.0

type RemoteSolutionResolver struct {
	// contains filtered or unexported fields
}

RemoteSolutionResolver fetches solutions from remote OCI registries given a full Docker-style reference (e.g., "ghcr.io/myorg/starter-kit@1.0.0"). It implements the get.RemoteResolver interface.

func NewRemoteSolutionResolver added in v0.8.0

func NewRemoteSolutionResolver(cfg RemoteSolutionResolverConfig) *RemoteSolutionResolver

NewRemoteSolutionResolver creates a new RemoteSolutionResolver.

func (*RemoteSolutionResolver) FetchRemoteSolution added in v0.8.0

func (r *RemoteSolutionResolver) FetchRemoteSolution(ctx context.Context, rawRef string) ([]byte, []byte, error)

FetchRemoteSolution fetches a solution from a remote OCI reference. The ref is parsed via ParseRemoteReference. If no kind is specified in the path, the kind defaults to ArtifactKindSolution.

type RemoteSolutionResolverConfig added in v0.8.0

type RemoteSolutionResolverConfig struct {
	// CredentialStore provides authentication credentials for remote registries.
	CredentialStore *CredentialStore

	// AuthHandlerFunc returns an auth handler for a given registry host.
	// When set, the handler is passed to RemoteCatalogConfig.AuthHandler
	// for automatic token bridging. May return nil if no handler is available.
	AuthHandlerFunc func(registry string) scafctlauth.Handler

	// AuthScopeFunc returns an OAuth scope for a given registry host.
	// When set, the scope is passed to RemoteCatalogConfig.AuthScope so
	// providers like GCP/Entra can request appropriately scoped tokens.
	AuthScopeFunc func(registry string) string

	// Insecure allows HTTP connections to registries (for testing).
	Insecure bool

	// ArtifactCache enables caching of fetched remote solutions.
	// When set, fetched artifacts are stored and served from cache within TTL.
	ArtifactCache ArtifactCacher

	// NoCache disables artifact caching even when ArtifactCache is set.
	NoCache bool

	// Logger for logging operations.
	Logger logr.Logger
}

RemoteSolutionResolverConfig holds configuration for the remote solution resolver.

type SaveResult

type SaveResult struct {
	// Reference is the artifact that was saved.
	Reference Reference `json:"reference" yaml:"reference"`
	// OutputPath is the path to the created archive.
	OutputPath string `json:"outputPath" yaml:"outputPath"`
	// Size is the size of the archive in bytes.
	Size int64 `json:"size" yaml:"size"`
	// Digest is the manifest digest.
	Digest string `json:"digest" yaml:"digest"`
}

SaveResult contains information about the save operation.

type SolutionResolver

type SolutionResolver struct {
	// contains filtered or unexported fields
}

SolutionResolver wraps a Catalog to provide solution fetching by name[@version]. It implements the CatalogResolver interface from pkg/solution/get.

func NewSolutionResolver

func NewSolutionResolver(catalog Catalog, logger logr.Logger, opts ...SolutionResolverOption) *SolutionResolver

NewSolutionResolver creates a resolver that fetches solutions from the given catalog. Optional SolutionResolverOption values may be provided to configure artifact caching and cache bypass behavior.

func (*SolutionResolver) FetchSolution

func (r *SolutionResolver) FetchSolution(ctx context.Context, nameWithVersion string) ([]byte, error)

FetchSolution retrieves a solution from the catalog by name[@version]. The input format is "name" or "name@version" (e.g., "my-solution" or "my-solution@1.2.3"). Returns the solution content as bytes.

When an artifact cache is configured and noCache is false, the result is served from cache on a hit (within TTL), otherwise the catalog is fetched and the result is stored for future use.

func (*SolutionResolver) FetchSolutionWithBundle

func (r *SolutionResolver) FetchSolutionWithBundle(ctx context.Context, nameWithVersion string) ([]byte, []byte, error)

FetchSolutionWithBundle retrieves a solution and its bundle from the catalog by name[@version]. The input format is "name" or "name@version" (e.g., "my-solution" or "my-solution@1.2.3"). Returns the solution content bytes, bundle tar bytes (nil if no bundle), and any error.

When an artifact cache is configured and noCache is false, both content and bundle are cached together for TTL-based reuse.

func (*SolutionResolver) LastResolvedCatalog added in v0.10.1

func (r *SolutionResolver) LastResolvedCatalog() string

LastResolvedCatalog returns the name of the catalog that satisfied the most recent FetchSolution or FetchSolutionWithBundle call. Returns "" if no fetch has been performed yet.

type SolutionResolverOption added in v0.6.0

type SolutionResolverOption func(*SolutionResolver)

SolutionResolverOption configures a SolutionResolver.

func WithResolverArtifactCache added in v0.6.0

func WithResolverArtifactCache(c ArtifactCacher) SolutionResolverOption

WithResolverArtifactCache sets the artifact cache for the resolver. When set, fetched artifacts are stored in and served from this cache.

func WithResolverNoCache added in v0.6.0

func WithResolverNoCache(noCache bool) SolutionResolverOption

WithResolverNoCache disables artifact caching for this resolver. When true, the cache is neither read nor written, ensuring fresh catalog fetches.

func WithResolverRemoteCatalogs added in v0.8.0

func WithResolverRemoteCatalogs(remotes []Catalog) SolutionResolverOption

WithResolverRemoteCatalogs sets fallback remote catalogs for the resolver. When the local catalog does not contain the requested artifact, these remotes are tried in order. On a remote hit the artifact is automatically pulled into the local catalog so subsequent runs are instant.

type TagInfo added in v0.8.0

type TagInfo struct {
	Tag      string `json:"tag" yaml:"tag"`
	IsSemver bool   `json:"isSemver" yaml:"isSemver"`
	Version  string `json:"version,omitempty" yaml:"version,omitempty"`
}

TagInfo represents a single tag in a remote OCI repository.

Directories

Path Synopsis
Package search provides catalog search and discovery functionality.
Package search provides catalog search and discovery functionality.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL