registry

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package registry is a from-scratch OCI distribution (registry v2) client plus a small in-memory registry for offline tests and demos. It resolves image references, pulls and inspects manifests (Docker Schema 2 and OCI), and reads/writes OCI 1.1 referrers — the plumbing the supply-chain phase needs to attach and fetch signatures and attestations. It re-implements only the HTTP surface it uses (net/http, encoding/json); it depends on no container tooling.

Network is strictly opt-in: nothing here dials out until a caller invokes a method that must, and every such method degrades gracefully (a clear error, no panic) when offline. All tests run against the in-memory MemoryRegistry, so the suite is fully hermetic.

Index

Constants

View Source
const (
	MediaTypeDockerManifest     = "application/vnd.docker.distribution.manifest.v2+json"
	MediaTypeDockerManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
	MediaTypeDockerConfig       = "application/vnd.docker.container.image.v1+json"
	MediaTypeOCIManifest        = "application/vnd.oci.image.manifest.v1+json"
	MediaTypeOCIIndex           = "application/vnd.oci.image.index.v1+json"
	MediaTypeOCIConfig          = "application/vnd.oci.image.config.v1+json"
	// MediaTypeEmptyJSON is the OCI "empty" config blob ("{}"), used as the
	// config of a referrer artifact that carries its data elsewhere.
	MediaTypeEmptyJSON = "application/vnd.oci.empty.v1+json"
)

Media types for the manifest shapes we read and write. We support both the Docker Schema 2 lineage (still the default from many registries) and the OCI image spec, because "verify any image" means not caring which a registry used.

View Source
const DefaultRegistry = "registry-1.docker.io"

DefaultRegistry is the host assumed when a reference names none. It matches the Docker convention of resolving bare names against Docker Hub.

View Source
const DefaultTag = "latest"

DefaultTag is the tag assumed when a reference names neither tag nor digest.

Variables

This section is empty.

Functions

func Digest

func Digest(content []byte) string

Digest computes the OCI content digest of bytes: "sha256:<hex>". A manifest's digest is the digest of its exact bytes, which is why callers must digest the bytes they received, never a re-serialization (re-marshaling can change bytes).

func IsIndex

func IsIndex(mediaType string) bool

IsIndex reports whether a media type denotes a multi-manifest index/list.

func ManifestDigestFromLayout

func ManifestDigestFromLayout(dir string) (string, error)

ManifestDigestFromLayout returns the primary image manifest digest recorded in an OCI image layout directory's index.json. If the top-level entry is itself an image index, it follows one level to the first image manifest. It does not verify layer content — only the digest identity the layout advertises.

func VerifyDigest

func VerifyDigest(content []byte, want string) error

VerifyDigest checks that content hashes to want. A registry (or a man-in-the-middle) that serves content not matching the digest you asked for is either buggy or hostile; either way, fail.

Types

type Client

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

Client talks to an OCI distribution (registry v2) endpoint. The zero value is not usable; construct with New. It is safe for concurrent use once built.

func New

func New(opts ...Option) *Client

New builds a Client with sensible defaults.

func (*Client) GetBlob

func (c *Client) GetBlob(ctx context.Context, registry, repo, digest string) ([]byte, error)

GetBlob fetches a blob by digest and verifies its content.

func (*Client) GetManifest

func (c *Client) GetManifest(ctx context.Context, ref Reference) (*RawManifest, error)

GetManifest fetches the manifest for a reference (by digest if pinned, else by tag), verifies the bytes against the digest when the reference pinned one, and returns the raw bytes + media type + digest. Verifying the digest here is the linchpin of the whole trust chain: everything downstream signs or reasons over this digest, so a registry that lies about content is caught immediately.

func (*Client) ListTags

func (c *Client) ListTags(ctx context.Context, registry, repo string) ([]string, error)

ListTags returns the tags in a repository.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context, registry string) error

Ping checks the registry supports the v2 API (GET /v2/ → 200 or 401). A 401 still means "v2 registry, but auth required", which is useful posture signal.

func (*Client) PutBlob

func (c *Client) PutBlob(ctx context.Context, registry, repo string, data []byte) (Descriptor, error)

PutBlob uploads a blob via the monolithic (POST-then-PUT) flow and returns its descriptor.

func (*Client) PutManifest

func (c *Client) PutManifest(ctx context.Context, registry, repo, reference, mediaType string, data []byte) (Descriptor, error)

PutManifest uploads a manifest under a reference (tag or digest) and returns its descriptor. Used to push signatures/attestations as referrer manifests.

func (*Client) PutReferrer

func (c *Client) PutReferrer(ctx context.Context, registry, repo, subjectDigest, artifactType string, artifact []byte, annotations map[string]string) (Descriptor, error)

PutReferrer attaches an artifact (e.g. a signature/attestation bundle) to a subject digest. It uploads the artifact blob and an OCI manifest whose `subject` is the target image, then also writes the fallback referrers tag so registries without native support still resolve it. Returns the referrer manifest's descriptor.

func (*Client) Referrers

func (c *Client) Referrers(ctx context.Context, registry, repo, subjectDigest, artifactType string) (*Index, error)

Referrers returns the manifests that refer to subjectDigest, optionally filtered to a single artifactType. It tries the referrers API and falls back to the tag scheme. A subject with no referrers yields an empty index, not an error — "nothing is signed" is a valid, and security-relevant, answer.

func (*Client) ResolveDigest

func (c *Client) ResolveDigest(ctx context.Context, ref Reference) (string, error)

ResolveDigest returns the manifest digest for a reference. If the reference is already digest-pinned it is returned as-is; otherwise a HEAD resolves the tag.

type Descriptor

type Descriptor struct {
	MediaType    string            `json:"mediaType"`
	Digest       string            `json:"digest"`
	Size         int64             `json:"size"`
	ArtifactType string            `json:"artifactType,omitempty"`
	Annotations  map[string]string `json:"annotations,omitempty"`
}

Descriptor points at content in a registry by digest, size, and media type. It is the OCI spec's universal "here is a blob/manifest" reference.

type Index

type Index struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType,omitempty"`
	Manifests     []Descriptor      `json:"manifests"`
	Annotations   map[string]string `json:"annotations,omitempty"`
}

Index is an image index / manifest list: a set of manifests, one per platform (or, for a referrers response, one per referring artifact).

func ParseIndex

func ParseIndex(data []byte) (*Index, error)

ParseIndex decodes index bytes into an Index.

type Manifest

type Manifest struct {
	SchemaVersion int               `json:"schemaVersion"`
	MediaType     string            `json:"mediaType,omitempty"`
	ArtifactType  string            `json:"artifactType,omitempty"`
	Config        Descriptor        `json:"config"`
	Layers        []Descriptor      `json:"layers"`
	Subject       *Descriptor       `json:"subject,omitempty"`
	Annotations   map[string]string `json:"annotations,omitempty"`
}

Manifest is an image manifest (Docker Schema 2 or OCI image manifest). The Subject field (OCI 1.1) turns a manifest into a referrer: it declares that this manifest is *about* another manifest (the subject), which is how signatures and attestations attach to an image.

func ParseManifest

func ParseManifest(data []byte) (*Manifest, error)

ParseManifest decodes manifest bytes into a Manifest.

type MemoryRegistry

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

MemoryRegistry is an http.Handler implementing the registry v2 API in memory. The zero value is not ready; use NewMemoryRegistry. Safe for concurrent use.

func NewMemoryRegistry

func NewMemoryRegistry() *MemoryRegistry

NewMemoryRegistry returns an empty in-memory registry.

func (*MemoryRegistry) ServeHTTP

func (m *MemoryRegistry) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP routes distribution API requests.

func (*MemoryRegistry) SetRequireAuth

func (m *MemoryRegistry) SetRequireAuth(v bool)

SetRequireAuth toggles whether the registry demands authentication. With it on, unauthenticated data requests get 401 (no token endpoint is provided, so this models a registry that forbids anonymous access).

type Option

type Option func(*Client)

Option configures a Client.

func WithBasicAuth

func WithBasicAuth(user, pass string) Option

WithBasicAuth sets credentials used to obtain bearer tokens (and as a direct fallback). Credentials are never logged.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient injects the underlying HTTP client (inject an httptest client in tests, or one with custom TLS/timeouts in production).

func WithPlainHTTP

func WithPlainHTTP() Option

WithPlainHTTP makes the client speak http:// instead of https://. Use only for local/test registries; production registries must be TLS.

type RawManifest

type RawManifest struct {
	Bytes     []byte
	MediaType string
	Digest    string
}

RawManifest is a fetched manifest with the exact bytes, its digest, and its media type. Keeping the raw bytes matters: the digest and any signature are over these bytes, so re-serializing would break verification.

type Reference

type Reference struct {
	// Registry is the host[:port] of the registry.
	Registry string
	// Repository is the full repository path (e.g. "library/alpine").
	Repository string
	// Tag is the human tag, if the reference had one ("" if digest-only).
	Tag string
	// Digest is the "sha256:<hex>" digest, if the reference pinned one.
	Digest string
}

Reference is a parsed image reference: registry host, repository path, and either a tag or a digest (or both, with digest taking precedence for pulls).

func ParseReference

func ParseReference(ref string) (Reference, error)

ParseReference parses a string like "registry:5000/team/app:1.2@sha256:...". It applies Docker's defaulting rules: a first path component that looks like a hostname (contains '.', ':', or is "localhost") is the registry; otherwise the reference targets the default registry and a bare name is prefixed with "library/". A reference with neither tag nor digest defaults to :latest.

func (Reference) RefForPull

func (r Reference) RefForPull() string

RefForPull returns the manifest reference to fetch: the digest if pinned (immutable, preferred), otherwise the tag.

func (Reference) String

func (r Reference) String() string

String renders the reference back to canonical form.

Jump to

Keyboard shortcuts

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