artifact

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jul 11, 2026 License: AGPL-3.0, AGPL-3.0-only Imports: 16 Imported by: 0

Documentation

Index

Constants

View Source
const (
	OutputsManifestPath  = ".stagefreight/outputs.json"
	OutputsSchemaVersion = "2"
)
View Source
const (
	ResultsManifestPath  = paths.Root + "/published.json"
	ResultsSchemaVersion = "2"
)
View Source
const ManagedRoot = paths.Root

ManagedRoot is the single repo-relative directory that holds all StageFreight-managed transferable state — manifests, deps, security, the cas store, binary build output (build.DistDir), etc. It is the Perform→Publish boundary: CI forwards only this prefix between jobs, so any artifact a later phase must open from the filesystem MUST live beneath it. A producer that writes a publish-consumed file outside ManagedRoot reopens the seam where publish receives manifest metadata for files it does not possess (the v0.6.1 "binary archives didn't attach" failure). See the boundary tests.

Variables

View Source
var (
	ErrOutputsManifestNotFound = errors.New("outputs manifest not found")
	ErrOutputsManifestInvalid  = errors.New("outputs manifest invalid")
)
View Source
var (
	ErrResultsManifestNotFound = errors.New("results manifest not found")
	ErrResultsManifestInvalid  = errors.New("results manifest invalid")
)

Functions

func ValidateChecksumsFile added in v0.7.0

func ValidateChecksumsFile(sumsPath string) error

ValidateChecksumsFile confirms every entry in a SHA256SUMS file matches the actual bytes of the named file (resolved relative to the file's directory) — so `stagefreight sign` never signs a checksum manifest that no longer describes its artifacts. This closes the gap that archive-digest validation alone leaves: the SHA256SUMS file itself is the thing being signed, and it must be accurate.

func ValidateRecordedDigests added in v0.7.0

func ValidateRecordedDigests(results *ResultsManifest, distDir string) error

ValidateRecordedDigests recomputes each successfully-built archive's SHA-256 and confirms it matches the results manifest — refusing to attach a signature to an artifact that drifted (or vanished) since the build. Files are located by base name under distDir, so it works whether signing runs on the build host or a separate signer host. This is the "refuse unsigned artifact drift" guard for the additive `stagefreight sign` flow.

func WithinManagedRoot added in v0.7.0

func WithinManagedRoot(repoRoot, p string) bool

WithinManagedRoot reports whether path p (relative to repoRoot, or absolute) resolves to a location at or beneath repoRoot/.stagefreight. It is the predicate behind the Perform→Publish boundary invariant. A "..": escape, or any path that lands outside ManagedRoot, returns false.

func WriteOutputsManifest added in v0.6.0

func WriteOutputsManifest(dir string, manifest OutputsManifest) error

WriteOutputsManifest finalizes (if needed) the manifest and writes it atomically to disk.

func WriteResultsManifest added in v0.6.0

func WriteResultsManifest(dir string, manifest ResultsManifest) error

WriteResultsManifest finalizes (if needed) the manifest and writes it atomically to disk.

Types

type ArchiveDescriptor added in v0.6.0

type ArchiveDescriptor struct {
	Format string `json:"format"`
	Path   string `json:"path"`
}

ArchiveDescriptor describes a packaged archive artifact. Plan-time intent only — content SHA256 is observed at archive time and recorded in the corresponding Outcome.

type ArchiveExecutionView added in v0.6.0

type ArchiveExecutionView struct {
	// Identity (intent)
	ArtifactID   ArtifactID
	ArtifactKind string // always "archive"
	ArtifactName string
	Version      string

	// Descriptor (intent)
	Format string
	Path   string

	// Build observation (outcome)
	BuildStatus OutcomeStatus
	SHA256      string
	Size        int64
	BuildError  string

	// Sibling reference — semantically unordered set of binary ArtifactIDs.
	// Consumers MUST NOT depend on the order of this slice.
	Sources []ArtifactID

	// Provenance
	ExpectedCommit string
}

ArchiveExecutionView is the canonical join for archive artifacts. One view per archive_build outcome. Archive is a sibling of its source binaries (Q3) — Sources references binary ArtifactIDs by string only, never embedding binary fields. Sources is treated as an unordered set by consumers; on-disk serialization sorts it for determinism.

Per the same projection-not-resolver rule as BinaryExecutionView, this view does NOT walk into BinaryExecutionViews for its Sources. Consumers that need binary details must look them up separately.

func BuildArchiveExecutionViews added in v0.6.0

func BuildArchiveExecutionViews(outputs *OutputsManifest, results *ResultsManifest) []ArchiveExecutionView

BuildArchiveExecutionViews produces one view per recorded archive_build outcome. Returns nil for nil inputs. Like BinaryExecutionView, surfaces failures broadly; consumers filter on BuildStatus.

type ArchiveOutcome added in v0.6.0

type ArchiveOutcome struct {
	Status  OutcomeStatus `json:"status"`
	SHA256  string        `json:"sha256,omitempty"`
	Path    string        `json:"path,omitempty"`
	Format  string        `json:"format,omitempty"` // "tar.gz" | "zip" | ...
	Size    int64         `json:"size,omitempty"`
	Sources []ArtifactID  `json:"sources,omitempty"`
	Error   string        `json:"error,omitempty"`
}

ArchiveOutcome is the result of building one archive artifact (a packaging operation over one or more binary artifacts). Sources references source binaries by ArtifactID — sibling relationship, not embedding. Sources is semantically unordered (treat as a set); on-disk serialization sorts it for determinism but consumer logic MUST NOT depend on the order.

Size is parallel to BinaryOutcome.Size — a build-time observation of the final archive file's byte length. It is recorded here so consumers (release_create's BinaryRow construction) don't have to re-stat the file at consumer time, which would leak build-layer I/O into the release layer.

type Artifact added in v0.6.0

type Artifact struct {
	ID      ArtifactID `json:"id"`
	Kind    string     `json:"kind"`
	Name    string     `json:"name"`
	Version string     `json:"version,omitempty"`

	// Digest is the content-addressable identity of the produced artifact,
	// materialized at build completion (NOT at publish). For docker artifacts
	// this is the OCI image index digest as reported by buildx
	// `containerimage.digest` — the identity a registry serves for the tag and
	// the thing publish projects, stable across the multi-platform case. For
	// binary/archive artifacts it is the sha256 of the produced bytes.
	//
	// Digest is the sole identity primitive: it is derived from bytes, never
	// from daemon state (NOT docker inspect {{.Id}}, which is a per-platform
	// config ID that diverges from the index digest on multi-platform builds).
	//
	// Phase 1 status: Digest is authoritative identity but NOT yet load-bearing
	// for any approval decision — review still resolves its scan target from
	// publication outcomes until a persistence layer (CAS) can retrieve and
	// re-hash these bytes. A digest without retrievable bytes is a claim, not a
	// verified identity; do not gate approval on it until persistence exists.
	Digest Digest `json:"digest,omitempty"`

	// Persistence is how StageFreight retrieves the artifact's exact bytes in a
	// later phase (review, publish) without re-deriving them. It is a retrieval
	// handle, NOT a distribution location: by construction there is no variant
	// that names a user-facing registry, so "already published" is unexpressible
	// here. The bytes are reachable only through StageFreight-controlled
	// resolution (the cas store), never directly by an external system.
	//
	// Phase 2 status: this handle is written by perform once the build's bytes
	// are retained in the content store, but it is consumed by NOTHING yet — no
	// review or publish decision reads it. It becomes load-bearing only when the
	// review inversion (a later phase) resolves and re-hashes through it. A
	// present handle must never be read as implicit trust.
	Persistence PersistenceHandle `json:"persistence,omitempty"`

	Docker  *DockerDescriptor  `json:"docker,omitempty"`
	Binary  *BinaryDescriptor  `json:"binary,omitempty"`
	Archive *ArchiveDescriptor `json:"archive,omitempty"`

	Targets []Target `json:"targets"`
}

Artifact is a single output produced by perform. ID is the stable identity used to correlate intent ↔ result; Name is human-friendly. Exactly one of the kind-specific descriptor pointers is set, matching Kind.

ID is typed (ArtifactID, not bare string) so identity values cannot be assembled inline from fields elsewhere in the codebase. The only approved constructor is NewArtifactID.

type ArtifactID added in v0.6.0

type ArtifactID string

ArtifactID is the system-wide identity primitive. The typed string alias turns identity construction into a compile-visible gesture rather than a runtime string assembly — any code that needs an ArtifactID must obtain it from a manifest or a view, never assemble one from fields inline. This is the structural lock that prevents the friendly-name shortcut pattern (e.g. `binaryName + "-" + os + "-" + arch`) from reintroducing alternate identity systems.

Format: "<kind>:<name>". The constructor NewArtifactID is the only approved way to mint a new ID inside the package; external callers receive IDs from view/manifest read operations.

func NewArtifactID added in v0.6.0

func NewArtifactID(kind, name string) ArtifactID

NewArtifactID mints the stable identity of an artifact across reruns. Derived purely from (kind, name). Mutable inputs (commit SHA, pipeline ID, time) MUST NOT be embedded — that would defeat intent↔result correlation when publish runs in a separate job from perform. Uniqueness across the artifacts slice within a manifest is enforced at Write time.

type ArtifactTransport added in v0.7.0

type ArtifactTransport struct {
	Path   string // archive file, under ManagedRoot
	Format string // "tar.gz" | "zip"
}

ArtifactTransport is a successful build's publish-transport representation inside ManagedRoot — today always an archive (tar.gz/zip). It is the ONLY thing the publish phase consumes for a build: publish never reaches back to an original build output directory (which lives outside ManagedRoot and does not cross the perform→publish job boundary). Extract materializes it into a publish workspace.

Returning a value with Extract (rather than a bare path) keeps resolution and materialization as separate responsibilities, leaving room for other transports (zip/OCI/CAS/remote) without changing callers.

func ResolveSuccessfulBuildOutput added in v0.7.0

func ResolveSuccessfulBuildOutput(rootDir, buildID string) (ArtifactTransport, error)

ResolveSuccessfulBuildOutput returns the transport archive for a successful build, sourced SOLELY from the manifests — never globbing the filesystem (the archive resolution invariant, see distribution.go). A build's binary/tree output is wrapped by an archive whose Sources reference the build's artifact; that archive is the transport. Callers (e.g. the pages publish runner) depend only on this seam, not on how a build's output is currently modeled (today a Kind:"binary" artifact whose Path is a directory) — so a future first-class tree artifact is an internal change here.

func (ArtifactTransport) Extract added in v0.7.0

func (t ArtifactTransport) Extract(destDir string) error

Extract materializes the transport archive into destDir (created if needed).

type AttestationOutcome added in v0.6.0

type AttestationOutcome struct {
	Status         OutcomeStatus `json:"status"`
	Kind           string        `json:"kind,omitempty"` // "cosign" | "in_toto" | "slsa"
	SignatureRef   string        `json:"signature_ref,omitempty"`
	AttestationRef string        `json:"attestation_ref,omitempty"`
	VerifiedDigest string        `json:"verified_digest,omitempty"`
	TrustEvidence
	Error string `json:"error,omitempty"`
}

AttestationOutcome is the result of attempting to sign or attest an already-published reference. Linked to the corresponding PushOutcome via shared ArtifactID + Target — never embedded inside it.

type BinaryDescriptor added in v0.6.0

type BinaryDescriptor struct {
	OS        string `json:"os"`
	Arch      string `json:"arch"`
	Path      string `json:"path"`
	Toolchain string `json:"toolchain,omitempty"`
	BuildID   string `json:"build_id,omitempty"` // the build that produced it; lets a docker build stage from it
}

BinaryDescriptor describes a compiled binary artifact. Plan-time intent only — SHA256 and final digest are observed at build time and recorded in the corresponding Outcome, not here.

type BinaryExecutionView added in v0.6.0

type BinaryExecutionView struct {
	// Identity (intent)
	ArtifactID   ArtifactID
	ArtifactKind string // always "binary"
	ArtifactName string
	Version      string

	// Descriptor (intent)
	OS        string
	Arch      string
	Path      string
	Toolchain string

	// Build observation (outcome)
	BuildStatus OutcomeStatus
	SHA256      string
	Size        int64
	BuildID     string
	BuildError  string

	// Provenance
	ExpectedCommit string
}

BinaryExecutionView is the canonical consumer-side join over an OutputsManifest (intent) and a ResultsManifest (execution facts) for binary artifacts. Each successful binary build produces one view.

Binary artifacts are un-targeted by design (Q2): there are no Target coordinates, no registry, no remote ref. The build artifact IS the truth. Distribution destinations are decided at a later layer.

Per the architectural rule: "Views are projections, not resolvers." BinaryExecutionView does NOT include archive information, does NOT resolve dependencies, does NOT walk into other views. Consumers that need cross-domain context (e.g., archives that wrap this binary) must query the relevant view builder separately and join at the consumer level. Sibling artifact relationships are referenced by ArtifactID only.

func BuildBinaryExecutionViews added in v0.6.0

func BuildBinaryExecutionViews(outputs *OutputsManifest, results *ResultsManifest) []BinaryExecutionView

BuildBinaryExecutionViews produces one view per recorded binary_build outcome. Returns nil for nil inputs. Surfaces failed builds too — the view layer is broad; consumers narrow by filtering on BuildStatus.

type BinaryOutcome added in v0.6.0

type BinaryOutcome struct {
	Status  OutcomeStatus `json:"status"`
	SHA256  string        `json:"sha256,omitempty"`
	Path    string        `json:"path,omitempty"`
	Size    int64         `json:"size,omitempty"`
	BuildID string        `json:"build_id,omitempty"`
	Error   string        `json:"error,omitempty"`
}

BinaryOutcome is the result of building one binary artifact. The authoritative identity event for binaries — there is no separate "push to registry" step. SHA256 is the binary's content hash, computed at build time over the final on-disk artifact bytes.

Per the unified determinism rule, SHA256 reflects whatever the build produced; the recording layer does not verify or normalize.

type BlobSignatureOutcome added in v0.7.0

type BlobSignatureOutcome struct {
	Status        OutcomeStatus `json:"status"`
	Kind          string        `json:"kind,omitempty"` // signer mechanism, e.g. "cosign"
	BlobPath      string        `json:"blob_path,omitempty"`
	SignaturePath string        `json:"signature_path,omitempty"`
	TrustEvidence
	Error string `json:"error,omitempty"`
}

BlobSignatureOutcome is the result of signing a detached blob (e.g. SHA256SUMS) with cosign sign-blob. Un-targeted by design — a blob signature is the truth about the file's bytes, not about any registry endpoint, so it carries the blob + detached-signature paths and the trust class that signed it, never an OutcomeTarget. Class is the resolved trust class (key | oidc | kms | hardware), so a consumer can tell *how* the bytes were vouched for.

type Digest added in v0.6.0

type Digest string

Digest is a content-addressable artifact identity ("sha256:..."). Named primitive (not bare string) so "this is computed identity" is legible at every call site and a registry tag cannot be passed where a digest belongs.

type DockerDescriptor added in v0.6.0

type DockerDescriptor struct {
	Dockerfile      string   `json:"dockerfile"`
	Context         string   `json:"context"`
	Platforms       []string `json:"platforms"`
	BuildArgsDigest string   `json:"build_args_digest,omitempty"`
}

DockerDescriptor describes a docker image to be built.

type ForgeReleaseAssetTarget added in v0.6.0

type ForgeReleaseAssetTarget struct {
	AssetName string `json:"asset_name"`
}

ForgeReleaseAssetTarget describes a forge release asset destination (GitLab/GitHub/Gitea release with attached file).

type OCILayoutRef added in v0.6.0

type OCILayoutRef struct {
	Path string `json:"path"`
}

OCILayoutRef points at an OCI layout retained in the content store. Path is store-relative; it is resolved only by the cas store, never consumed as a remotely-fetchable address.

type Outcome added in v0.6.0

type Outcome struct {
	Type   OutcomeType    `json:"type"`
	Target *OutcomeTarget `json:"target,omitempty"`

	Push                  *PushOutcome                  `json:"push,omitempty"`
	Attestation           *AttestationOutcome           `json:"attestation,omitempty"`
	Binary                *BinaryOutcome                `json:"binary,omitempty"`
	Archive               *ArchiveOutcome               `json:"archive,omitempty"`
	BlobSignature         *BlobSignatureOutcome         `json:"blob_signature,omitempty"`
	ProvenanceAttestation *ProvenanceAttestationOutcome `json:"provenance_attestation,omitempty"`
}

Outcome is one observed fact about one execution event. Outcomes are atomic and independent: push success and signing success are separate Outcomes with separate Type values. They are NOT combined fields of a single record. That separation is load-bearing — collapsing them would let a missing attestation be silently encoded as a successful push outcome.

Discriminated union: Type selects which of the kind-specific sub-pointers is populated. Target is nil-able because not every outcome type has a target (binary build, archive build) — see Type-vs-Target rule in validateOutcome.

type OutcomeStatus added in v0.6.0

type OutcomeStatus string

OutcomeStatus is a closed enum applied per-sub-outcome. Push and attestation each carry their own Status — never shared.

const (
	OutcomeSuccess OutcomeStatus = "success"
	OutcomeFailed  OutcomeStatus = "failed"
	OutcomeSkipped OutcomeStatus = "skipped"
)

func (OutcomeStatus) Valid added in v0.6.0

func (s OutcomeStatus) Valid() bool

Valid reports whether s is one of the defined OutcomeStatus values. Empty is invalid: every recorded sub-outcome must declare a status.

type OutcomeTarget added in v0.6.0

type OutcomeTarget struct {
	Kind string `json:"kind"`
	Host string `json:"host,omitempty"`
	Path string `json:"path,omitempty"`
	Tag  string `json:"tag,omitempty"`
}

OutcomeTarget identifies the specific target an outcome refers to. Mirrors (a slice of) the intent Target — kind + the identifying coordinates.

type OutcomeType added in v0.6.0

type OutcomeType string

OutcomeType discriminates the Outcome sub-kind. Closed enum.

const (
	OutcomeTypePush                  OutcomeType = "push"
	OutcomeTypeAttestation           OutcomeType = "attestation"
	OutcomeTypeBinaryBuild           OutcomeType = "binary_build"
	OutcomeTypeArchive               OutcomeType = "archive_build"
	OutcomeTypeBlobSignature         OutcomeType = "blob_signature"
	OutcomeTypeProvenanceAttestation OutcomeType = "provenance_attestation"
)

func (OutcomeType) Valid added in v0.6.0

func (t OutcomeType) Valid() bool

Valid reports whether t is one of the defined OutcomeType values.

type OutputsManifest added in v0.6.0

type OutputsManifest struct {
	SchemaVersion string     `json:"schema_version"`
	GeneratedAt   string     `json:"generated_at"`
	Commit        string     `json:"commit,omitempty"`
	Pipeline      *Pipeline  `json:"pipeline,omitempty"`
	Artifacts     []Artifact `json:"artifacts"`
	Checksum      string     `json:"checksum,omitempty"`
}

OutputsManifest is the immutable description of the outputs produced by perform and the publication requirements approved by review. It is written by the perform phase, read by review and publish, and never modified after perform completes. Publish records observed outcomes in a separate ResultsManifest tied to this file via intent_checksum.

Determinism is a property of the type system here, not the encoder: all fields are strictly typed, struct field order is fixed, and no maps appear in the schema. encoding/json's standard MarshalIndent is sufficient to produce byte-deterministic output.

func ReadOutputsManifest added in v0.6.0

func ReadOutputsManifest(dir string) (*OutputsManifest, error)

ReadOutputsManifest reads and verifies the outputs manifest. Verification fails on schema mismatch, malformed JSON, RFC3339 violation, enum violation, or checksum mismatch.

func (*OutputsManifest) Finalize added in v0.6.0

func (m *OutputsManifest) Finalize() error

Finalize populates derived fields (schema_version, generated_at, artifact ids), validates and normalizes structure, and computes the embedded SHA-256 checksum. After a successful Finalize, the manifest is byte-deterministic and ready to be either written or used as the intent snapshot for a ResultsBuilder.

Idempotent on already-finalized manifests when content has not changed. Mutates the receiver.

func (*OutputsManifest) LocalFilesystemPaths added in v0.7.0

func (m *OutputsManifest) LocalFilesystemPaths() map[ArtifactID]string

LocalFilesystemPaths returns the on-disk paths of every artifact whose bytes a later phase retrieves directly from the filesystem (binary, archive) or the content store (oci_layout persistence). These are exactly the paths the Perform→Publish boundary governs — docker image identity travels by Digest, not a filesystem path, so it is intentionally excluded. The result is keyed by ArtifactID so a boundary violation names the offending artifact.

type PersistenceHandle added in v0.6.0

type PersistenceHandle struct {
	Kind      PersistenceKind `json:"kind,omitempty"`
	OCILayout *OCILayoutRef   `json:"oci_layout,omitempty"`
}

PersistenceHandle is an inert, serializable retrieval handle: data that tells StageFreight where to get an artifact's exact bytes, resolved only through the content store. It is a closed union (exactly one variant pointer set, matching Kind); it carries no behavior and no external-fetch capability.

type PersistenceKind added in v0.6.0

type PersistenceKind string

PersistenceKind enumerates how an artifact's bytes are retained for later phases. Closed set by design: a registry/distribution variant is deliberately absent so the persistence handle can never express "lives in a user-facing registry" — persistence is not distribution.

const (
	// PersistenceNone means no bytes are retained (e.g. plan-time, or an
	// artifact whose persistence has not been wired). The zero value.
	PersistenceNone PersistenceKind = ""
	// PersistenceOCILayout means the bytes are an OCI image layout retained in
	// the StageFreight content store, addressed by Digest.
	PersistenceOCILayout PersistenceKind = "oci_layout"
)

type Pipeline added in v0.6.0

type Pipeline struct {
	ID       string `json:"id,omitempty"`
	Provider string `json:"provider,omitempty"`
}

Pipeline captures the CI context that produced the manifest.

type ProvenanceAttestationOutcome added in v0.7.0

type ProvenanceAttestationOutcome struct {
	Status         OutcomeStatus `json:"status"`
	Kind           string        `json:"kind,omitempty"`              // attestor mechanism, e.g. "cosign"
	PredicateType  string        `json:"predicate_type,omitempty"`    // in-toto predicateType, e.g. "slsaprovenance"
	ProvenancePath string        `json:"provenance_path,omitempty"`   // the predicate file attested
	ProvenanceSHA  string        `json:"provenance_sha256,omitempty"` // sha256 of the predicate bytes (deterministic identity)
	AttestationRef string        `json:"attestation_ref,omitempty"`   // ref of the attached attestation (the subject digest ref)
	VerifiedDigest string        `json:"verified_digest,omitempty"`   // the image manifest digest attested
	TrustEvidence
	Error string `json:"error,omitempty"`
}

ProvenanceAttestationOutcome is the result of cryptographically attaching a build-provenance predicate to an already-published image digest (cosign attest). A FIRST-CLASS evidence object, deliberately NOT an AttestationOutcome: a provenance attestation is a *statement over a predicate about a subject*, authorized by a trust tier — categorically distinct from "this digest was signed." It records the predicate's deterministic identity (path + sha256) so later adjudication never has to rediscover which provenance belongs to which subject heuristically, the attested subject digest, and the same TrustEvidence the artifact signature carried (the provenance is vouched for by the same tier).

type PublicationView added in v0.6.0

type PublicationView struct {
	// ── Identity (intent) ──────────────────────────────────────────────
	ArtifactID   ArtifactID // "<kind>:<name>" — the cross-domain join key
	ArtifactKind string
	ArtifactName string
	Version      string // from Artifact.Version; may be empty

	// ── Target coordinates (observation) ───────────────────────────────
	Host string
	Path string
	Tag  string

	// ── Push observation (results.outcomes[type=push]) ─────────────────
	PushStatus     OutcomeStatus
	Digest         string // immutable digest if known; empty if registry did not return one
	ObservedDigest string
	ObservedBy     string
	PushError      string // empty on success

	// ── Attestation observation (results.outcomes[type=attestation]) ──
	// SigningAttempted is derived: true iff any attestation outcome
	// exists for (ArtifactID, Host, Path, Tag), regardless of status.
	// Absence of an attestation outcome means signing was NOT attempted
	// — per the architectural invariant established in Phase 3.
	SigningAttempted bool
	Attestation      *AttestationOutcome

	// ── Joined intent metadata ────────────────────────────────────────
	ExpectedTags   []string     // all tags configured for this (host, path) registry target
	ExpectedCommit string       // OutputsManifest.Commit
	Requirements   Requirements // intent-side requirements for this target (sign, attest, sbom)
}

PublicationView is the canonical consumer-side join over an OutputsManifest (intent) and a ResultsManifest (execution facts). One PublicationView per (artifact, target, tag) tuple where execution recorded a push outcome — successful or otherwise.

The view is the first read-model primitive in the v2 truth pipeline: instead of reading the dual-purpose v1 PublishManifest, consumers (security_scan, release_create, verify, etc.) consume []PublicationView produced by BuildPublicationViews. All join logic lives here, not in each consumer.

Field provenance is explicit in the comments below: each field is sourced from intent OR observation. Derived fields are reconstructions over outcome existence, not stored state.

func BuildPublicationViews added in v0.6.0

func BuildPublicationViews(outputs *OutputsManifest, results *ResultsManifest) []PublicationView

BuildPublicationViews produces the canonical join over outputs (intent) and results (observations). Returns one view per push outcome.

Returns nil if either manifest is nil. If outputs contains no matching intent for a recorded artifact_id, that artifact's outcomes are still surfaced — the cross-domain validation already happened at ResultsBuilder.Build time; here we only do enrichment.

Per-target attestation outcomes are joined to push outcomes by exact (artifact_id, host, path, tag) tuple. If multiple attestation outcomes exist for the same tuple (e.g. retry behavior), the last one in append order wins — that represents the final attempted state.

func (PublicationView) DigestRef added in v0.6.0

func (v PublicationView) DigestRef() string

DigestRef returns the immutable reference (host/path@digest) when a digest is known, or empty string otherwise. Consumers that require an immutable target (e.g. security scanning) should check for empty before using.

func (PublicationView) Ref added in v0.6.0

func (v PublicationView) Ref() string

Ref returns the canonical mutable image reference: host/path:tag.

type PushOutcome added in v0.6.0

type PushOutcome struct {
	Status         OutcomeStatus `json:"status"`
	Digest         string        `json:"digest,omitempty"`
	ObservedDigest string        `json:"observed_digest,omitempty"`
	ObservedBy     string        `json:"observed_by,omitempty"`
	Error          string        `json:"error,omitempty"`
}

PushOutcome is the result of pushing one artifact reference to one registry target. Independent of any later signing/attestation activity.

type RegistryTarget added in v0.6.0

type RegistryTarget struct {
	Host       string   `json:"host"`
	Path       string   `json:"path"`
	Tags       []string `json:"tags"`
	NativeScan bool     `json:"nativeScan,omitempty"` // trigger the registry's built-in scan after a successful push (Publish-owned)
}

RegistryTarget describes a container registry destination.

type Requirements added in v0.6.0

type Requirements struct {
	Sign   bool `json:"sign,omitempty"`
	Attest bool `json:"attest,omitempty"`
	SBOM   bool `json:"sbom,omitempty"`
}

Requirements expresses publication requirements approved by review. Adding a new requirement is a typed field addition (backward compatible: zero value = not required).

type ResolvedArchiveAsset added in v0.7.0

type ResolvedArchiveAsset struct {
	ArtifactID ArtifactID
	Name       string
	Path       string
	SHA256     string
	Size       int64
	Sources    []ArtifactID
}

ResolvedArchiveAsset is a successfully-built archive ready for distribution. Every field is sourced from the v2 manifests (outputs + results): ArtifactID is the identity, Path/SHA256/Size come from the build result, Sources lists the binary ArtifactIDs the archive wraps. Name is display-only.

func ResolveSuccessfulArchiveAssets added in v0.7.0

func ResolveSuccessfulArchiveAssets(rootDir string) ([]ResolvedArchiveAsset, error)

ResolveSuccessfulArchiveAssets reads the v2 manifests and returns the archives that built successfully. This is the shared archive-resolution entry point for distribution backends that don't already hold the views.

NON-NEGOTIABLE INVARIANT: assets are derived SOLELY from the manifests (results → archive execution view → resolved assets). This function never globs the filesystem, never parses an artifact name to infer type/platform/identity, and never reconstructs an ArtifactID. ArtifactID is the only identity. Any future change that reaches outside the manifests to find or name an asset is a regression — see TestArchiveResolutionIsManifestSourced.

func SuccessfulArchiveAssets added in v0.7.0

func SuccessfulArchiveAssets(views []ArchiveExecutionView) []ResolvedArchiveAsset

SuccessfulArchiveAssets filters built archive views to those that built successfully, preserving input order. Pure — operates on already-built views, so a caller that already holds the views (e.g. the release path, which also needs binary/publication views from the same manifests) reuses them without a re-read.

type ResolvedSignatureAsset added in v0.7.0

type ResolvedSignatureAsset struct {
	ArtifactID ArtifactID
	Path       string // the detached signature (.sig) path
	BlobPath   string // the signed blob it covers (e.g. SHA256SUMS)
	TrustClass string // resolved trust class that signed it (display/provenance)
}

ResolvedSignatureAsset is a successfully-produced detached signature (e.g. SHA256SUMS.sig) ready for distribution. Manifest-sourced: ArtifactID + Path come from the results manifest's blob_signature outcomes — never a filesystem glob.

func SuccessfulBlobSignatureAssets added in v0.7.0

func SuccessfulBlobSignatureAssets(results *ResultsManifest) []ResolvedSignatureAsset

SuccessfulBlobSignatureAssets extracts the successful detached blob signatures from a results manifest, preserving order. Pure + manifest-sourced — same non-globbing invariant as SuccessfulArchiveAssets; the .sig path is recorded by the signer, never reconstructed from a name.

type Result added in v0.6.0

type Result struct {
	ArtifactID   ArtifactID `json:"artifact_id"`
	ArtifactName string     `json:"artifact_name"`
	Kind         string     `json:"kind"`
	Outcomes     []Outcome  `json:"outcomes"`
}

Result groups all outcomes for a single intent artifact. ArtifactID is typed (the ArtifactID alias, not bare string) so the cross-domain join key cannot be silently confused with a free-form string elsewhere in the codebase.

type ResultsManifest added in v0.6.0

type ResultsManifest struct {
	SchemaVersion  string   `json:"schema_version"`
	CompletedAt    string   `json:"completed_at"`
	IntentChecksum string   `json:"intent_checksum"`
	Results        []Result `json:"results"`
	Checksum       string   `json:"checksum,omitempty"`
}

ResultsManifest records what was actually externalized by publish. It is cryptographically tied to the OutputsManifest it fulfills via IntentChecksum. Written by publish, read by narrate and external auditors.

func ReadResultsManifest added in v0.6.0

func ReadResultsManifest(dir string) (*ResultsManifest, error)

ReadResultsManifest reads and verifies the results manifest. Verification fails on schema mismatch, malformed JSON, RFC3339 violation, enum violation, or checksum mismatch.

func (*ResultsManifest) Finalize added in v0.6.0

func (m *ResultsManifest) Finalize() error

Finalize populates derived fields, validates structure, and computes the embedded SHA-256 checksum. Mirrors OutputsManifest.Finalize.

type Target added in v0.6.0

type Target struct {
	Kind string `json:"kind"`

	Registry          *RegistryTarget          `json:"registry,omitempty"`
	ForgeReleaseAsset *ForgeReleaseAssetTarget `json:"forge_release_asset,omitempty"`

	Requirements Requirements `json:"requirements,omitempty"`
}

Target is one destination an artifact must be distributed to. Exactly one of the kind-specific target pointers is set, matching Kind. Credentials are not part of this contract — they are deployment configuration.

type TrustEvidence added in v0.7.0

type TrustEvidence struct {
	TrustClass       string `json:"trust_class,omitempty"`       // key | oidc | kms | hardware
	Tier             string `json:"tier,omitempty"`              // assurance tier, e.g. "tier0-software" (auto-provisioned); empty = operator-supplied
	PhysicalPresence bool   `json:"physical_presence,omitempty"` // signer demonstrated physical presence
	NonExportable    bool   `json:"non_exportable,omitempty"`    // signing key was hardware-bound / non-exportable
	Transparency     bool   `json:"transparency,omitempty"`      // recorded in a transparency log
	SignerRef        string `json:"signer_ref,omitempty"`        // signer identity material (key/kms ref, oidc identity)
	SignedAt         string `json:"signed_at,omitempty"`         // RFC3339 timestamp the signature was produced
	// TrustDomain names the signing-authority ecosystem for an oidc/keyless signature
	// (the Sigstore deployment — "internal", "public-sigstore", a Fulcio host). Durable
	// so later adjudication can reason over WHICH ecosystem vouched for the evidence
	// (public vs internal, trust-root migration, multi-domain policy). Empty for
	// key/kms/hardware, which have no Sigstore trust domain.
	TrustDomain string `json:"trust_domain,omitempty"`
}

TrustEvidence is the resolved assurance a signature actually carried, recorded at sign time so a consumer can answer "what did this signature attest?" from the manifest alone — never reducing a signing event to signed=true. These are facts resolved from the SignPlan, not a re-adjudication (that is the verification phase's job); recording them now is pure serialization-of-facts.

Jump to

Keyboard shortcuts

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