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
- func Digest(content []byte) string
- func IsIndex(mediaType string) bool
- func ManifestDigestFromLayout(dir string) (string, error)
- func VerifyDigest(content []byte, want string) error
- type Client
- func (c *Client) GetBlob(ctx context.Context, registry, repo, digest string) ([]byte, error)
- func (c *Client) GetManifest(ctx context.Context, ref Reference) (*RawManifest, error)
- func (c *Client) ListTags(ctx context.Context, registry, repo string) ([]string, error)
- func (c *Client) Ping(ctx context.Context, registry string) error
- func (c *Client) PutBlob(ctx context.Context, registry, repo string, data []byte) (Descriptor, error)
- func (c *Client) PutManifest(ctx context.Context, registry, repo, reference, mediaType string, data []byte) (Descriptor, error)
- func (c *Client) PutReferrer(ctx context.Context, registry, repo, subjectDigest, artifactType string, ...) (Descriptor, error)
- func (c *Client) Referrers(ctx context.Context, registry, repo, subjectDigest, artifactType string) (*Index, error)
- func (c *Client) ResolveDigest(ctx context.Context, ref Reference) (string, error)
- type Descriptor
- type Index
- type Manifest
- type MemoryRegistry
- type Option
- type RawManifest
- type Reference
Constants ¶
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.
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.
const DefaultTag = "latest"
DefaultTag is the tag assumed when a reference names neither tag nor digest.
Variables ¶
This section is empty.
Functions ¶
func Digest ¶
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 ManifestDigestFromLayout ¶
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 ¶
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 (*Client) GetManifest ¶
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) Ping ¶
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.
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 ¶
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 ¶
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 ¶
WithBasicAuth sets credentials used to obtain bearer tokens (and as a direct fallback). Credentials are never logged.
func WithHTTPClient ¶
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 ¶
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 ¶
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 ¶
RefForPull returns the manifest reference to fetch: the digest if pinned (immutable, preferred), otherwise the tag.