image

package
v0.1.18 Latest Latest
Warning

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

Go to latest
Published: Aug 26, 2026 License: Apache-2.0, MIT Imports: 20 Imported by: 0

Documentation

Overview

Package image stages Linux binaries into signed APK repositories and a locked OCI layout, and verifies that layout against the release contract.

Build validates staged binary facts, copies them into a scratch workspace, verifies each one as a static ELF, and then drives APKBuilder and Composer. VerifyLayout reads the on-disk layout byte for byte and checks the index, manifests, configs, and layer binary against ExpectedImage. VerifySBOMs checks the architecture SPDX documents. CanonicalDigests hashes work/sources/<arch>/<binary-name>. The package does not import the staging projection; callers convert that wire type into BuildInput.

Index

Constants

View Source
const (
	// PlatformAMD64 is linux/amd64.
	PlatformAMD64 Platform = "linux/amd64"
	// PlatformARM64 is linux/arm64.
	PlatformARM64 Platform = "linux/arm64"
	// ArchX8664 is the APK architecture for [PlatformAMD64].
	ArchX8664 APKArch = "x86_64"
	// ArchAArch64 is the APK architecture for [PlatformARM64].
	ArchAArch64 APKArch = "aarch64"
)
View Source
const (
	// BuildSchema is the versioned image-build result identifier.
	BuildSchema = "release.dev/image-build/v2"
)
View Source
const (
	// VerifySchema is the versioned image-verify result identifier.
	VerifySchema = "release.dev/image-verify/v2"
)

Variables

This section is empty.

Functions

func CanonicalDigests

func CanonicalDigests(work fs.FS, arches []APKArch, names []string) (map[APKArch]map[string]rel.Digest, error)

CanonicalDigests streams sources/<arch>/<name> through SHA-256.

work is a fs.FS rooted at the scratch workspace. Each architecture and name is hashed independently and never buffered. A missing path or a non-regular entry is an error that names the file. The returned map is keyed by architecture, then binary name.

func VerifySBOMs

func VerifySBOMs(fsys fs.FS, version rel.Version, arches []APKArch) error

VerifySBOMs requires one architecture SPDX document per arch.

fsys is a fs.FS rooted at the sboms directory. For each architecture, sbom-<arch>.spdx.json must be a regular JSON document of at most [jsonLimitBytes] that contains at least one packages[] entry with primaryPackagePurpose APPLICATION and versionInfo "<version>-r0". sbom-index.spdx.json is ignored. Decode uses a local struct; there is no SPDX dependency. A missing file or a document that fails the APPLICATION package check is a verification failure that names the file.

Types

type APKArch

type APKArch string

APKArch is a Melange and apko architecture name.

Known values are produced by Platform.APKArch. The zero value is invalid.

func (APKArch) String

func (a APKArch) String() string

String returns the APK architecture name.

type APKBuildRequest

type APKBuildRequest struct {
	// Config is the absolute Melange configuration path.
	Config string
	// VarsFile is the absolute Melange vars file path.
	VarsFile string
	// KeyPath is the absolute ephemeral signing key path the builder generates.
	KeyPath string
	// OutDir is the absolute APK repository output directory.
	OutDir string
	// Sources is the build order; Sources[0].Arch is also the compile-check architecture.
	Sources []APKBuildSource
	// Runner is the container runner, "docker".
	Runner string
	// Namespace is the APK namespace.
	Namespace string
	// BuildDate is the RFC 3339 reproducible build timestamp.
	BuildDate string
	// GitRepoURL is the provenance repository URL.
	GitRepoURL string
	// GitCommit is the provenance commit SHA.
	GitCommit string
}

APKBuildRequest is the input to APKBuilder.Build.

type APKBuildSource

type APKBuildSource struct {
	// Arch is the APK architecture built from this source tree.
	Arch APKArch
	// Dir is the absolute directory containing the staged binary files.
	Dir string
}

APKBuildSource is one architecture's staged binary tree.

type APKBuilder

type APKBuilder interface {
	// Build compiles, signs, and writes APK repositories for request.
	//
	// request.Sources are built in order. The returned [APKRepositories.Dir]
	// must equal request.OutDir and [APKRepositories.PublicKey] must equal
	// request.KeyPath with a ".pub" suffix.
	Build(ctx context.Context, request APKBuildRequest) (APKRepositories, error)
}

APKBuilder compiles Melange packages and builds signed APK repositories.

APKBuilder.Build first compiles the configuration for Sources[0].Arch, generates the ephemeral signing key at KeyPath, then builds one repository per source in order. Implementations invoke `melange` and must not require the caller to buffer a binary.

type APKRepositories

type APKRepositories struct {
	// Dir is the repository root the build wrote.
	Dir string
	// PublicKey is the generated signing public key path.
	PublicKey string
}

APKRepositories is the output of APKBuilder.Build.

type Annotation

type Annotation struct {
	// Key is the annotation name.
	Key string
	// Value is the annotation value.
	Value string
}

Annotation is one ordered OCI image annotation.

type BinaryDigest added in v0.1.18

type BinaryDigest struct {
	// Name is the binary filename.
	Name string `json:"name"`
	// Digest is the verified canonical digest of the staged file.
	Digest string `json:"digest"`
}

BinaryDigest is one staged binary's verified digest.

type BuildBinary

type BuildBinary struct {
	// Platform is the Linux OCI platform this binary was built for.
	Platform Platform
	// Name is the binary filename for this platform.
	Name string
	// Path is the Source-relative confined path of the staged file.
	Path string
	// Digest is the expected canonical digest of the file at Path.
	Digest rel.Digest
}

BuildBinary is one canonical Linux binary fact supplied to Build.

type BuildInput

type BuildInput struct {
	// Binaries are the canonical Linux facts. [Build] requires a nonempty
	// identical name set on [PlatformAMD64] and [PlatformARM64].
	Binaries []BuildBinary
	// Source is the extracted oci-input artifact root.
	Source fs.FS
	// Work is the scratch workspace root.
	Work *os.Root
	// Output is the authoritative artifact output root.
	Output *os.Root
	// Version is the candidate MAJOR.MINOR.PATCH release.
	Version rel.Version
	// BuildDate is the RFC 3339 reproducible build timestamp.
	BuildDate string
	// Namespace is the APK namespace, typically the GitHub owner.
	Namespace string
	// SourceURL is the provenance repository URL.
	SourceURL string
	// Commit is the provenance commit SHA.
	Commit string
	// Reference is the local image reference, e.g. "local/release:1.2.3".
	Reference string
	// MelangeConfig is the Melange configuration document to copy.
	MelangeConfig io.Reader
	// ApkoConfig is the apko configuration document to copy.
	ApkoConfig io.Reader
}

BuildInput is the staged binaries, configs, and roots Build consumes.

type BuildResult

type BuildResult struct {
	// Schema identifies the image-build result version and is always [BuildSchema].
	Schema string `json:"schema"`
	// Version is the candidate MAJOR.MINOR.PATCH version.
	Version string `json:"version"`
	// Binaries are the staged binary filenames, sorted name-ascending.
	Binaries []string `json:"binaries"`
	// Work is the scratch workspace path, [os.Root.Name] of the work root.
	Work string `json:"work"`
	// Output is the authoritative output path, [os.Root.Name] of the output root.
	Output string `json:"output"`
	// BuildDate is the RFC 3339 reproducible build timestamp.
	BuildDate string `json:"build_date"`
	// Packages are the per-architecture APK facts in canonical platform order.
	Packages []PackageResult `json:"packages"`
}

BuildResult is the versioned document produced by Build.

func Build

func Build(ctx context.Context, input BuildInput, apk APKBuilder, composer Composer) (BuildResult, error)

Build stages binaries, builds signed APK repositories, and composes an OCI layout.

It fails closed before any write when the input is incomplete or malformed: a nil context, root, reader, or port; a nonempty binary list whose name set is not identical across PlatformAMD64 and PlatformARM64; a duplicate (platform, name) pair; a zero version or digest; a BuildDate that is not RFC 3339; or an empty Namespace, SourceURL, Commit, or Reference. Both the work and output roots must contain no entries; a pre-existing entry is refused and names the populated root and one offending entry. Workspace and output directories are then created with os.Root.Mkdir and must not already exist; only the intermediate work/sources element uses MkdirAll.

Each binary is streamed once through SHA-256 into work/sources/<apkarch>/<binary-name> at mode 0755. The computed digest must match the expected digest. The written file is then parsed as ELF and must be a static 64-bit little-endian ET_EXEC for that architecture.

After staging, Build writes work/vars.json, copies the Melange and apko configs to output/configuration, and writes output/canonical-binaries.sha256 in GNU coreutils form with x86_64 first and names ascending within each architecture. It then calls APKBuilder.Build and requires the returned repository root and public key to match the request, plus exactly one nonempty APK and a nonempty APKINDEX.tar.gz per architecture. The public key is copied to the output root. Composer.Build then locks and writes the layout. Build requires apko.lock.json, layout/index.json, layout/oci-layout, and both architecture SBOMs to be nonempty regular files. It does not parse those files and does not write image-digest.txt.

Platforms are processed in canonical order: linux/amd64, then linux/arm64.

type ComposeRequest

type ComposeRequest struct {
	// Dir is the absolute working directory both apko invocations run in.
	Dir string
	// Config is the Dir-relative apko configuration path.
	Config string
	// Repository is the Dir-relative APK repository path.
	Repository string
	// Keyring is the Dir-relative APK signing public key path.
	Keyring string
	// Lockfile is the Dir-relative lock output path.
	Lockfile string
	// SBOMPath is the Dir-relative SBOM output directory.
	SBOMPath string
	// Layout is the Dir-relative OCI layout output directory.
	Layout string
	// Reference is the local image reference, e.g. "local/release:1.2.3".
	Reference string
	// BuildDate is the RFC 3339 reproducible build timestamp.
	BuildDate string
	// Arches is the architecture list, one --arch flag per entry.
	Arches []APKArch
	// Annotations is the ordered annotation list applied to the image.
	Annotations []Annotation
}

ComposeRequest is the input to Composer.Build.

type Composer

type Composer interface {
	// Build locks request.Config and writes the OCI layout under request.Layout.
	//
	// Both invocations run with working directory request.Dir. Architecture
	// flags follow request.Arches in order.
	Build(ctx context.Context, request ComposeRequest) error
}

Composer locks an apko configuration and writes a multi-architecture OCI layout.

Composer.Build first writes the lockfile, then builds the layout. Implementations invoke `apko` with cmd.Dir set to request.Dir.

type ExpectedImage

type ExpectedImage struct {
	// Version is the candidate MAJOR.MINOR.PATCH release.
	Version rel.Version
	// Binaries are the filenames that must appear at /usr/bin/<name> in each layer.
	Binaries []string
	// Revision is the expected org.opencontainers.image.revision, typically GITHUB_SHA.
	Revision string
	// Source is the expected org.opencontainers.image.source URL.
	Source string
	// Canonical maps each APK architecture onto per-name digests of
	// sources/<arch>/<name>.
	Canonical map[APKArch]map[string]rel.Digest
}

ExpectedImage is the provenance and staged-binary facts VerifyLayout checks.

type Layout

type Layout struct {
	// IndexBytes is the exact index.json contents, retained verbatim.
	IndexBytes []byte
	// IndexDigest is SHA-256 over IndexBytes, never over re-marshaled JSON.
	IndexDigest rel.Digest
	// Annotations are the index annotations as they appear on disk.
	Annotations map[string]string
	// Platforms are the index manifests in file order.
	Platforms []LayoutPlatform
}

Layout is a validated on-disk OCI layout.

func ReadLayout

func ReadLayout(fsys fs.FS) (Layout, error)

ReadLayout loads a two-platform OCI layout from fsys.

fsys is a fs.FS rooted at the layout directory. A regular oci-layout file must exist. index.json is read verbatim; Layout.IndexDigest is SHA-256 over those exact bytes and is never computed from re-marshaled JSON. The index must use schemaVersion 2 and media type ocispec.MediaTypeImageIndex and must list exactly two manifests whose platforms are linux/amd64 and linux/arm64 in any order. Each manifest descriptor is validated and its blob must exist as a regular file of the declared size. The manifest must use schemaVersion 2 and media type ocispec.MediaTypeImageManifest and must name exactly one layer. The config and layer blobs must exist as regular files of the declared size and are not buffered. index.json and manifests are buffered up to [jsonLimitBytes].

ReadLayout does not compare annotations, labels, or layer bytes to an ExpectedImage. Call VerifyLayout for those checks.

type LayoutPlatform

type LayoutPlatform struct {
	// Platform is the Linux OCI platform recorded on the index descriptor.
	Platform Platform
	// Manifest is the digest of the platform manifest blob.
	Manifest rel.Digest
	// Config is the digest of the image config blob.
	Config rel.Digest
	// Layer is the digest of the single layer blob.
	Layer rel.Digest
	// LayerMedia is the layer descriptor media type.
	LayerMedia string
	// Annotations are the manifest annotations as they appear on disk.
	Annotations map[string]string
}

LayoutPlatform is one platform manifest plus the blobs it names.

type PackageResult

type PackageResult struct {
	// Platform is the Linux OCI platform, such as linux/amd64.
	Platform string `json:"platform"`
	// Arch is the APK architecture, such as x86_64.
	Arch string `json:"arch"`
	// Package is the output-relative APK path, packages/<arch>/<file>.apk.
	Package string `json:"package"`
	// BinaryDigests are the verified canonical digests, sorted by name.
	BinaryDigests []BinaryDigest `json:"binary_digests"`
}

PackageResult is one architecture's signed APK recorded by Build.

type Platform

type Platform string

Platform is a canonical Linux OCI platform string.

The only constructor is ParsePlatform. The zero value is invalid.

func ParsePlatform

func ParsePlatform(raw string) (Platform, error)

ParsePlatform constructs a Platform from a canonical Linux platform string.

Only PlatformAMD64 and PlatformARM64 are accepted. Any other value, including the empty string, is rejected. Error text names the problem and echoes the input.

func (Platform) APKArch

func (p Platform) APKArch() APKArch

APKArch returns the APK architecture that p maps onto.

PlatformAMD64 maps to ArchX8664. PlatformARM64 maps to ArchAArch64. An unrecognized platform returns the zero value.

func (Platform) String

func (p Platform) String() string

String returns the canonical linux/<arch> platform.

type VerifiedImage

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

VerifiedImage is a layout that satisfied VerifyLayout.

func VerifyLayout

func VerifyLayout(fsys fs.FS, expected ExpectedImage) (VerifiedImage, error)

VerifyLayout checks a two-platform OCI layout against expected.

fsys is a fs.FS rooted at the layout directory. Cheap structural checks run first through ReadLayout: oci-layout exists, index.json parses with schemaVersion 2 and media type ocispec.MediaTypeImageIndex, the sorted platform architecture set is amd64 and arm64, every platform OS is linux, and each manifest names exactly one layer whose blobs exist at the declared size. Annotation checks then require the six org.opencontainers.image.{description,licenses,revision,source,title,version} keys on the index; description, licenses, and title must be nonempty; revision, source, and version must equal expected. Each platform's manifest annotations and config labels must equal those six index values. Each config must have architecture equal to the descriptor architecture, os linux, Entrypoint ["/usr/bin/<name>"] for some expected name, and User "65532". The single layer is then streamed once, never buffered: gzip when the media type ends in +gzip, otherwise plain tar. Every expected name must appear exactly once as usr/bin/<name> (a leading "./" is accepted), a regular file whose Mode low twelve bits are exactly 0755, with uid/gid 0/0, and a declared tar Size of at most [maxBinaryBytes]; each payload is hashed through SHA-256 with io.CopyN bounded by that Size and must equal expected.Canonical for that platform's APK architecture and name. A missing, duplicate, leftover, non-regular, oversized, or mismatched entry is a verification failure that names the platform. Layer media types other than the OCI tar and tar+gzip types are rejected.

VerifyLayout does not inspect SBOMs and does not write image-digest.txt. Call VerifySBOMs and the command layer for those.

func (VerifiedImage) IndexDigest

func (image VerifiedImage) IndexDigest() rel.Digest

IndexDigest returns SHA-256 over the exact index.json bytes.

func (VerifiedImage) Result

func (image VerifiedImage) Result(expected ExpectedImage) VerifyResult

Result returns the versioned VerifyResult document for expected.

Platforms are listed in canonical order: linux/amd64, then linux/arm64.

type VerifiedPlatform

type VerifiedPlatform struct {
	// Platform is the Linux OCI platform, such as linux/amd64.
	Platform string `json:"platform"`
	// Arch is the APK architecture, such as x86_64.
	Arch string `json:"arch"`
	// Manifest is the digest of the platform manifest blob.
	Manifest string `json:"manifest"`
	// Config is the digest of the image config blob.
	Config string `json:"config"`
	// Layer is the digest of the single layer blob.
	Layer string `json:"layer"`
	// BinaryDigests are the streamed SHA-256 of each usr/bin/<name> in that layer.
	BinaryDigests []BinaryDigest `json:"binary_digests"`
}

VerifiedPlatform is one platform recorded by VerifyResult.

type VerifyResult

type VerifyResult struct {
	// Schema identifies the image-verify result version and is always [VerifySchema].
	Schema string `json:"schema"`
	// Version is the candidate MAJOR.MINOR.PATCH version.
	Version string `json:"version"`
	// Binaries are the staged binary filenames, sorted name-ascending.
	Binaries []string `json:"binaries"`
	// IndexDigest is SHA-256 over the exact index.json bytes.
	IndexDigest string `json:"index_digest"`
	// Platforms are the verified platforms in canonical order: linux/amd64, linux/arm64.
	Platforms []VerifiedPlatform `json:"platforms"`
}

VerifyResult is the versioned document produced by VerifiedImage.Result.

Jump to

Keyboard shortcuts

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