devproof

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Sep 14, 2026 License: Apache-2.0 Imports: 31 Imported by: 0

Documentation

Overview

Package devproof is a Go-embeddable, extensible, multi-source artifact bundler with canonical identity across platforms, provenance evidence, policy-based verification, and safe deterministic expansion.

It resolves content from one or more sources, composes it into a canonical filesystem tree, packages that tree as an OCI artifact, records provenance as independently verifiable evidence, evaluates the result against policy, and safely expands the artifact back to a filesystem.

Identity

A bundle's OCI subject digest is a function of the canonical payload and the bundle format version, and of nothing else. Source URLs, timestamps, builder identity, registry location, tags, signatures, and attestations do not affect it. Two equal canonical trees therefore have equal subject digests even when assembled from different sources by different builders, and evidence can be added, renewed, or copied without changing what it describes.

Three independent answers

Verification reports integrity, trust, and semantics separately. Integrity asks whether the descriptors, config inventory, layer, and tree agree. Trust asks whether the supplied evidence satisfies the caller's policy. Semantics asks whether a caller-supplied validator accepts the payload. Passing integrity never implies trust, and the absence of a policy reports "not-evaluated", never "pass".

DevProof proves artifact identity, integrity, provenance, and policy compliance under a supplied trust policy. It does not claim that payload content is correct, safe, vulnerability-free, or semantically valid.

Layout

This package is the facade. The operation contracts live alongside it:

Canonical byte production is deliberately internal. It is reached through stable high-level operations rather than low-level knobs, because every one of those bytes is a compatibility surface.

Index

Examples

Constants

View Source
const (
	CodeInvalidInput       = fault.CodeInvalidInput
	CodeUnsupportedVersion = fault.CodeUnsupportedVersion
	CodeUnsupportedSource  = fault.CodeUnsupportedSource
	CodeSourceResolution   = fault.CodeSourceResolution
	CodeStaleLock          = fault.CodeStaleLock
	CodeUnsafePath         = fault.CodeUnsafePath
	CodeUnsupportedFile    = fault.CodeUnsupportedFile
	CodePathCollision      = fault.CodePathCollision
	CodeLimitExceeded      = fault.CodeLimitExceeded
	CodeDigestMismatch     = fault.CodeDigestMismatch
	CodeInvalidArtifact    = fault.CodeInvalidArtifact
	CodeAuthentication     = fault.CodeAuthentication
	CodeAuthorization      = fault.CodeAuthorization
	CodeTransport          = fault.CodeTransport
	CodeEvidenceInvalid    = fault.CodeEvidenceInvalid
	CodePolicyFailed       = fault.CodePolicyFailed
	CodeDestinationExists  = fault.CodeDestinationExists
	CodeTimeout            = fault.CodeTimeout
	CodeCanceled           = fault.CodeCanceled
	CodeInternal           = fault.CodeInternal
)

Stable classification codes. These appear in JSON results and map onto the documented CLI exit codes.

View Source
const DefaultLockName = "devproof.lock.json"

DefaultLockName is the lock file written beside a manifest.

Variables

This section is empty.

Functions

func IsTransient

func IsTransient(err error) bool

IsTransient reports whether err may succeed on a later attempt.

It is advisory for callers deciding whether to retry. DevProof's own retries are governed by its bounded retry policy, not by this function, and a caller that retries a non-transient failure will simply fail identically: a digest mismatch, a policy failure, and an authentication denial are all permanent for a given input.

Types

type BuildRequest

type BuildRequest struct {
	// SpecPath is a manifest file to build from.
	SpecPath string
	// Spec is a manifest supplied in memory. SpecDir gives the directory
	// relative source paths resolve against.
	Spec    *bundle.Spec
	SpecDir string

	// SourcePath builds a single local directory directly, without a
	// manifest. Mutually exclusive with SpecPath and Spec.
	SourcePath string
	// MountPath places a direct source below a prefix.
	MountPath string
	// Include and Exclude filter a direct source.
	Include []string
	Exclude []string

	// LockPath is the lock to enforce. When a manifest build finds a lock
	// beside the manifest, it is enforced by default (DP-004).
	LockPath string
	// Lock is a lock supplied in memory.
	Lock *bundle.Lock
	// UpdateLock resolves afresh and writes a new lock rather than enforcing
	// the existing one. Never implied: a build does not silently relock.
	UpdateLock bool
	// SkipLock builds without a lock at all.
	SkipLock bool

	// Destination is where to publish: "oci-layout://./artifact" for a local
	// layout, or "oci://registry.example.com/team/config" for a registry.
	// A bare reference is treated as a registry reference.
	Destination string
	// Tag optionally names the subject at the destination. It is assigned
	// last, after the published manifest has been read back and compared.
	Tag string

	// Attest signs provenance and attaches it to the published subject.
	// Requires an attester; see WithSigstore or WithAttester.
	Attest bool

	// Limits tightens the client's bounds for this operation.
	Limits Limits
}

BuildRequest describes a bundle to build.

A build is driven by either a manifest or a single direct source, never both. Direct mode synthesizes a one-source manifest and runs the identical pipeline, so there is no second implementation whose behavior could drift.

type BuildResult

type BuildResult struct {
	// SubjectDigest is the OCI manifest digest: the bundle's identity.
	SubjectDigest string
	TreeDigest    string
	ConfigDigest  string
	LayerDigest   string
	// SpecDigest is the digest of the bundle manifest that was built from,
	// computed over its normalized typed model.
	//
	// Named for the spec rather than the manifest because this result also
	// carries an OCI manifest digest, four lines above, and in an OCI tool
	// "manifest digest" means that one. A caller anchoring policy or
	// provenance to the wrong object would get no type error. BuildRequest
	// has said SpecPath and Spec all along.
	SpecDigest string
	LockDigest string
	Format     string
	FileCount  int64
	TotalBytes int64
	LayerBytes int64
	// Reference is the canonical digest reference of what was published.
	// A result always names its subject by digest, never by the tag it may
	// also carry (DP-007).
	Reference string
	Tag       string

	// Lock is the resolution this build used, whether loaded or generated.
	Lock *bundle.Lock
	// LockBytes is its canonical encoding, for a caller that wants to
	// persist it.
	LockBytes []byte
	// Evidence describes the provenance attached, when signing was asked
	// for. Attaching it never changed the subject digest above (DP-003).
	Evidence *EvidenceResult
}

BuildResult describes what was built.

Every identifier is a digest. A tag is reported separately and never stands in for one (DP-007).

type Change added in v0.2.0

type Change string

Change classifies one difference between two inventories.

const (
	// ChangeAdded means the path exists only on the right side.
	ChangeAdded Change = "added"
	// ChangeRemoved means the path exists only on the left side.
	ChangeRemoved Change = "removed"
	// ChangeModified means the content digest differs. It takes precedence
	// over a mode change on the same path: if the bytes are not the same
	// bytes, that is the fact worth leading with.
	ChangeModified Change = "modified"
	// ChangeModeChanged means the content is identical and only the
	// executable bit differs.
	ChangeModeChanged Change = "mode-changed"
)

type Client

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

Client is the SDK facade. Every operation the CLI performs is a method here; the CLI parses input, calls one of these, and renders the result (DP-001).

A Client is safe for concurrent use after construction. Its methods never call os.Exit, print, prompt, or open a browser — those are a command-line program's business, and a library that does them cannot be embedded.

func New

func New(opts ...Option) (*Client, error)

New constructs a Client.

Defaults are safe and deterministic: unset limits become the documented defaults rather than "unlimited", and no logger means no output rather than output to stderr.

func (*Client) Build

func (c *Client) Build(ctx context.Context, req BuildRequest) (_ *BuildResult, retErr error)

Build packages one or more sources into an OCI image layout.

Sources are resolved into private snapshots before anything is encoded, so the artifact describes one frozen moment rather than directories that may be changing underneath it. Blobs are written before the manifest, and the tag is assigned last, so a name never points at content that is not completely present.

Example

Building a directory into a local OCI layout.

The subject digest is a function of content alone, so the same tree always produces the same digest — on any machine, in any directory, at any time.

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thingzio/devproof/pkg/devproof"
)

// sampleTree writes a small source directory and returns its path.
//
// Examples build from a temporary directory so that they run anywhere and
// leave nothing behind.
func sampleTree() string {
	dir, err := os.MkdirTemp("", "devproof-example")
	if err != nil {
		panic(err)
	}
	if err := os.MkdirAll(filepath.Join(dir, "config"), 0o755); err != nil {
		panic(err)
	}
	files := map[string]string{
		"README.md":           "example\n",
		"config/service.yaml": "replicas: 3\n",
	}
	for name, content := range files {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
			panic(err)
		}
	}
	return dir
}

func main() {
	source := sampleTree()
	defer func() { _ = os.RemoveAll(source) }()

	layout, err := os.MkdirTemp("", "devproof-layout")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(layout) }()

	client, err := devproof.New()
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()

	result, err := client.Build(context.Background(), devproof.BuildRequest{
		SourcePath:  source,
		Destination: "oci-layout://" + filepath.Join(layout, "artifact"),
		Tag:         "v1",
	})
	if err != nil {
		panic(err)
	}

	fmt.Println("subject:", result.SubjectDigest)
	fmt.Println("files:", result.FileCount)
}
Output:
subject: sha256:93847c4cd259d1ae9af9d61b3b84aaf82949007335883723f5770ac9cf90f437
files: 2
Example (Reproducible)

Identity is a function of content, so building the same tree twice produces the same subject — which is what makes a bundle comparable across machines and rebuilds.

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thingzio/devproof/pkg/devproof"
)

// sampleTree writes a small source directory and returns its path.
//
// Examples build from a temporary directory so that they run anywhere and
// leave nothing behind.
func sampleTree() string {
	dir, err := os.MkdirTemp("", "devproof-example")
	if err != nil {
		panic(err)
	}
	if err := os.MkdirAll(filepath.Join(dir, "config"), 0o755); err != nil {
		panic(err)
	}
	files := map[string]string{
		"README.md":           "example\n",
		"config/service.yaml": "replicas: 3\n",
	}
	for name, content := range files {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
			panic(err)
		}
	}
	return dir
}

func main() {
	source := sampleTree()
	defer func() { _ = os.RemoveAll(source) }()

	work, err := os.MkdirTemp("", "devproof-work")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(work) }()

	client, err := devproof.New()
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()

	ctx := context.Background()
	digests := make([]string, 0, 2)
	for i, name := range []string{"first", "second"} {
		built, err := client.Build(ctx, devproof.BuildRequest{
			SourcePath:  source,
			Destination: "oci-layout://" + filepath.Join(work, name),
		})
		if err != nil {
			panic(fmt.Sprintf("build %d: %v", i, err))
		}
		digests = append(digests, built.SubjectDigest)
	}

	fmt.Println("identical:", digests[0] == digests[1])
}
Output:
identical: true

func (*Client) Close

func (c *Client) Close() error

Close releases client-owned resources. It is idempotent.

It does not remove artifacts, layouts, or output directories: those belong to the caller, and a library that deleted them on shutdown would be impossible to reason about.

Closing while an operation is in flight is allowed and is not a data race, but it does not wait: the operation either completes or fails, and which one is a matter of timing. A caller that needs a definite answer finishes its operations first.

func (*Client) Copy

func (c *Client) Copy(ctx context.Context, req CopyRequest) (_ *CopyResult, retErr error)

Copy moves a subject between registries, layouts, or repositories.

The subject's integrity is verified at the source and the copy is published under the same rules as a build, so a copy cannot launder a broken artifact into a destination that looks authoritative.

The subject digest is unchanged by definition: identity is a function of content and format version, and a repository name is neither (DP-002). Evidence is not copied here; that arrives with the referrer work in phase 4, and until then a copy carries the payload only.

func (*Client) Diff added in v0.2.0

func (c *Client) Diff(ctx context.Context, req DiffRequest) (_ *DiffResult, retErr error)

Diff compares two canonical trees and reports what changed.

It answers the question a configuration pipeline actually has — what is different between what I published and what I have now — using only facts both sides already carry. A bundle's inventory comes from its config blob, which was verified against the layer on the way in; a directory is canonicalized through the same path a build would use, so "no differences" means a build of that directory would produce the subject it was compared against.

Nothing is fetched beyond the manifest, config, and layer needed to verify integrity, and nothing is written.

func (*Client) Expand

func (c *Client) Expand(ctx context.Context, req ExpandRequest) (_ *ExpandResult, retErr error)

Expand verifies a subject and materializes its payload.

Integrity verification is mandatory and has no flag that disables it. When a policy is supplied it is evaluated before anything is written, and an unsatisfied policy writes nothing at all: the whole point of gating an expansion is that untrusted content never reaches the filesystem, and content that is deleted after being written has already been available to anything watching the directory.

The result is returned only after the destination has been published, so a non-nil result means the files are there, complete, and verified.

Example

Expanding materializes a verified payload. The destination must not exist: v1 has no overwrite or merge option, so a failed expansion leaves nothing behind.

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thingzio/devproof/pkg/devproof"
)

// sampleTree writes a small source directory and returns its path.
//
// Examples build from a temporary directory so that they run anywhere and
// leave nothing behind.
func sampleTree() string {
	dir, err := os.MkdirTemp("", "devproof-example")
	if err != nil {
		panic(err)
	}
	if err := os.MkdirAll(filepath.Join(dir, "config"), 0o755); err != nil {
		panic(err)
	}
	files := map[string]string{
		"README.md":           "example\n",
		"config/service.yaml": "replicas: 3\n",
	}
	for name, content := range files {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
			panic(err)
		}
	}
	return dir
}

func main() {
	source := sampleTree()
	defer func() { _ = os.RemoveAll(source) }()

	work, err := os.MkdirTemp("", "devproof-work")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(work) }()

	client, err := devproof.New()
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()

	ctx := context.Background()

	built, err := client.Build(ctx, devproof.BuildRequest{
		SourcePath:  source,
		Destination: "oci-layout://" + filepath.Join(work, "artifact"),
	})
	if err != nil {
		panic(err)
	}

	expanded, err := client.Expand(ctx, devproof.ExpandRequest{
		Reference:   built.Reference,
		Destination: filepath.Join(work, "out"),
	})
	if err != nil {
		panic(err)
	}

	fmt.Println("files:", expanded.FileCount)
	fmt.Println("same subject:", expanded.SubjectDigest == built.SubjectDigest)
}
Output:
files: 2
same subject: true

func (*Client) Inspect

func (c *Client) Inspect(ctx context.Context, req InspectRequest) (*InspectResult, error)

Inspect reports metadata without expanding payload content.

It performs no remote mutation and writes nothing. Every fact it reports carries how it was established, so a reader can tell a claim from a proof.

func (*Client) Lock

func (c *Client) Lock(ctx context.Context, req LockRequest) (_ *LockResult, retErr error)

Lock resolves a manifest and records the resolution.

Writing is atomic and all-or-nothing: a manifest whose sources partly fail produces no lock at all, because a lock describing some of a manifest is worse than none (DP-011).

func (*Client) Verify

func (c *Client) Verify(ctx context.Context, req VerifyRequest) (*policy.Report, error)

Verify checks a subject's integrity and, when a policy is supplied, its trust.

Integrity is always evaluated and has no flag that disables it. Trust is evaluated only when a policy is given, and its absence reports not-evaluated rather than pass — a consumer whose trust configuration never took effect has no other way to notice (DP-010).

Example

Verifying a subject without a policy reports trust as not-evaluated, which is deliberately not the same as passing: a consumer whose trust configuration never took effect has no other way to notice.

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thingzio/devproof/pkg/devproof"
)

// sampleTree writes a small source directory and returns its path.
//
// Examples build from a temporary directory so that they run anywhere and
// leave nothing behind.
func sampleTree() string {
	dir, err := os.MkdirTemp("", "devproof-example")
	if err != nil {
		panic(err)
	}
	if err := os.MkdirAll(filepath.Join(dir, "config"), 0o755); err != nil {
		panic(err)
	}
	files := map[string]string{
		"README.md":           "example\n",
		"config/service.yaml": "replicas: 3\n",
	}
	for name, content := range files {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
			panic(err)
		}
	}
	return dir
}

func main() {
	source := sampleTree()
	defer func() { _ = os.RemoveAll(source) }()

	layout, err := os.MkdirTemp("", "devproof-layout")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(layout) }()

	client, err := devproof.New()
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()

	ctx := context.Background()
	destination := "oci-layout://" + filepath.Join(layout, "artifact")

	built, err := client.Build(ctx, devproof.BuildRequest{
		SourcePath:  source,
		Destination: destination,
	})
	if err != nil {
		panic(err)
	}

	report, err := client.Verify(ctx, devproof.VerifyRequest{Reference: built.Reference})
	if err != nil {
		panic(err)
	}

	fmt.Println("integrity:", report.Integrity)
	fmt.Println("trust:", report.Trust)
}
Output:
integrity: pass
trust: not-evaluated
Example (Policy)

A policy makes verification say something. Without one, trust is not-evaluated; with one, an unsatisfied rule is a failure.

package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"github.com/thingzio/devproof/pkg/devproof"
	"github.com/thingzio/devproof/pkg/policy"
)

// sampleTree writes a small source directory and returns its path.
//
// Examples build from a temporary directory so that they run anywhere and
// leave nothing behind.
func sampleTree() string {
	dir, err := os.MkdirTemp("", "devproof-example")
	if err != nil {
		panic(err)
	}
	if err := os.MkdirAll(filepath.Join(dir, "config"), 0o755); err != nil {
		panic(err)
	}
	files := map[string]string{
		"README.md":           "example\n",
		"config/service.yaml": "replicas: 3\n",
	}
	for name, content := range files {
		if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
			panic(err)
		}
	}
	return dir
}

func main() {
	source := sampleTree()
	defer func() { _ = os.RemoveAll(source) }()

	layout, err := os.MkdirTemp("", "devproof-layout")
	if err != nil {
		panic(err)
	}
	defer func() { _ = os.RemoveAll(layout) }()

	client, err := devproof.New()
	if err != nil {
		panic(err)
	}
	defer func() { _ = client.Close() }()

	ctx := context.Background()

	built, err := client.Build(ctx, devproof.BuildRequest{
		SourcePath:  source,
		Destination: "oci-layout://" + filepath.Join(layout, "artifact"),
	})
	if err != nil {
		panic(err)
	}

	// This bundle was built without signing, so a policy demanding
	// provenance cannot be satisfied.
	report, err := client.Verify(ctx, devproof.VerifyRequest{
		Reference: built.Reference,
		Policy: &policy.Document{
			APIVersion: "devproof.thingz.io/v1alpha1",
			Kind:       "VerificationPolicy",
			Metadata:   policy.Metadata{Name: "requires-provenance"},
			Spec: policy.Spec{
				Provenance: policy.ProvenanceRules{Required: true},
			},
		},
	})
	if err != nil {
		panic(err)
	}

	fmt.Println("integrity:", report.Integrity)
	fmt.Println("trust:", report.Trust)
	fmt.Println("satisfied:", report.OK())
}
Output:
integrity: pass
trust: fail
satisfied: false

type Code

type Code = fault.Code

Code classifies a failure. It is also a valid errors.Is target.

type Confidence

type Confidence string

Confidence records how a reported fact was established.

Marking every fact is what stops an inspect report reading as a verification. "The manifest says this URL" and "a signature proves this URL" look identical in a flat listing, and only one of them is evidence.

const (
	// ConfidenceAuthored means a human wrote it in a manifest. It is intent,
	// not fact.
	ConfidenceAuthored Confidence = "authored"
	// ConfidenceResolved means a resolver produced it, recorded in a lock.
	ConfidenceResolved Confidence = "resolved"
	// ConfidenceDigestVerified means content was checked against a digest.
	ConfidenceDigestVerified Confidence = "digest-verified"
	// ConfidenceSignatureVerified means a signature over it verified.
	ConfidenceSignatureVerified Confidence = "signature-verified"
	// ConfidencePolicyAccepted means a policy rule accepted it.
	ConfidencePolicyAccepted Confidence = "policy-accepted"
)

type CopyRequest

type CopyRequest struct {
	// Source is the subject to copy, by tag or by digest.
	Source string
	// Destination is where to write it.
	Destination string
	// Tag optionally names the copy at the destination. It is assigned last.
	Tag string
	// RequireDigest rejects a source tag before anything is fetched.
	RequireDigest bool
	// Limits tightens the client's bounds for this operation.
	Limits Limits
}

CopyRequest describes a subject to copy between locations.

type CopyResult

type CopyResult struct {
	SubjectDigest string
	Source        string
	Destination   string
	Tag           string
}

CopyResult describes a completed copy.

type DiffEntry added in v0.2.0

type DiffEntry struct {
	Path   string `json:"path"`
	Change Change `json:"change"`

	// The zero value on either side means the path is absent there.
	OldMode   uint32 `json:"oldMode,omitempty"`
	NewMode   uint32 `json:"newMode,omitempty"`
	OldSize   int64  `json:"oldSize"`
	NewSize   int64  `json:"newSize"`
	OldDigest string `json:"oldDigest,omitempty"`
	NewDigest string `json:"newDigest,omitempty"`
}

DiffEntry is one changed path.

type DiffRequest added in v0.2.0

type DiffRequest struct {
	// From is the left side: the baseline.
	From string
	// To is the right side: what it is compared against.
	To string

	// Limits tightens the client's bounds for this operation.
	Limits Limits
}

DiffRequest asks what changed between two canonical trees.

Each operand is either an OCI reference — anything carrying a "://" scheme — or a path to a local directory.

type DiffResult added in v0.2.0

type DiffResult struct {
	From DiffSide `json:"from"`
	To   DiffSide `json:"to"`

	// Identical reports equal tree digests. When it is true, Changes is
	// empty: the tree digest is a function of exactly the paths, modes,
	// sizes, and content digests this comparison walks.
	Identical bool `json:"identical"`

	// Changes is sorted by canonical path.
	Changes []DiffEntry `json:"changes,omitempty"`

	Added       int `json:"added"`
	Removed     int `json:"removed"`
	Modified    int `json:"modified"`
	ModeChanged int `json:"modeChanged"`
}

DiffResult is the complete comparison.

type DiffSide added in v0.2.0

type DiffSide struct {
	// Reference is the operand exactly as supplied.
	Reference string `json:"reference"`
	// Kind is what it resolved to.
	Kind OperandKind `json:"kind"`
	// TreeDigest is the canonical payload identity.
	TreeDigest string `json:"treeDigest"`
	// Subject is the OCI subject digest, present only for a bundle.
	Subject string `json:"subject,omitempty"`
	// FileCount is how many files the side holds.
	FileCount int64 `json:"fileCount"`
}

DiffSide describes one operand as it was resolved.

type Error

type Error = fault.Error

Error is the error type every DevProof operation returns. Inspect it with errors.As, and classify with errors.Is against a Code:

if errors.Is(err, devproof.CodeStaleLock) {
    // refresh the lock
}

var dperr *devproof.Error
if errors.As(err, &dperr) {
    log.Printf("source %q failed at %q", dperr.Source, dperr.Path)
}

See fault.Error for the fields. Branch on Code, never on Msg: Code is the stable contract, and Msg is written for the human deciding what to do next and will be reworded.

type EvidenceInfo

type EvidenceInfo struct {
	Digest        string     `json:"digest"`
	PredicateType string     `json:"predicateType"`
	Identities    []string   `json:"identities"`
	Confidence    Confidence `json:"confidence"`
	// TransparencyLog reports whether a log inclusion proof verified.
	TransparencyLog bool `json:"transparencyLog"`
	// Statement is the decoded statement, when it was asked for. Present
	// only for evidence whose signatures verified.
	Statement *evidence.Statement `json:"statement,omitempty"`
}

EvidenceInfo summarizes one verified evidence object.

type EvidenceResult

type EvidenceResult struct {
	// Digest identifies the referrer manifest.
	Digest string
	// BlobDigest identifies the evidence blob itself.
	BlobDigest string
	// PredicateType is what the statement asserts.
	PredicateType string
	// Attester names the implementation that signed.
	Attester string
	// Storage is "referrers" or "tag-fallback". The fallback replaces rather
	// than accumulates, so a caller needs to know which applied (DP-028).
	Storage string
	// SubjectDigest is what the evidence is bound to.
	SubjectDigest string
}

EvidenceResult describes one attached evidence object.

type ExpandRequest

type ExpandRequest struct {
	Reference string
	// Destination must not exist. v1 has no overwrite or merge option.
	Destination   string
	RequireDigest bool

	// Policy is the verification policy to apply before anything is written.
	// Without one, trust reports not-evaluated rather than pass, exactly as
	// it does for [Client.Verify].
	Policy *policy.Document
	// PolicyPath loads a policy from a file. Mutually exclusive with Policy.
	PolicyPath string

	Limits Limits
}

ExpandRequest describes an expansion.

type ExpandResult

type ExpandResult struct {
	Destination   string
	SubjectDigest string
	TreeDigest    string
	FileCount     int64
	TotalBytes    int64
	Verification  *policy.Report
}

ExpandResult describes a published expansion.

type FileInfo

type FileInfo struct {
	Path   string `json:"path"`
	Mode   uint32 `json:"mode"`
	Size   int64  `json:"size"`
	Digest string `json:"digest"`
}

FileInfo is one inventory entry.

type InspectKind

type InspectKind string

InspectKind is what was inspected.

const (
	// InspectKindBundle is a published subject, read from a registry or a
	// local layout.
	InspectKindBundle InspectKind = "bundle"
	// InspectKindManifest is a bundle manifest on disk: authored intent.
	InspectKindManifest InspectKind = "manifest"
	// InspectKindLock is a lock file on disk: resolved material.
	InspectKindLock InspectKind = "lock"
)

type InspectRequest

type InspectRequest struct {
	// Reference is an OCI subject to inspect.
	Reference string
	// Path is a local manifest or lock file to inspect. Mutually exclusive
	// with Reference.
	Path string

	// Files includes the file inventory, which can be large.
	Files bool
	// Evidence includes referrer summaries.
	Evidence bool
	// EvidenceContent includes the decoded statements. Only statements whose
	// signatures verified are included; a statement that did not verify is
	// reported as rejected instead, never shown as though it were a fact.
	EvidenceContent bool

	Limits Limits
}

InspectRequest asks for metadata without expanding anything.

type InspectResult

type InspectResult struct {
	Kind InspectKind `json:"kind"`

	// Bundle facts, when a subject was inspected.
	Subject *SubjectInfo `json:"subject,omitempty"`
	// Manifest facts, when a manifest file was inspected.
	Manifest *ManifestInfo `json:"manifest,omitempty"`
	// Lock facts, when a lock file was inspected.
	Lock *LockInfo `json:"lock,omitempty"`
}

InspectResult describes what was found.

type Limits

type Limits = bundle.Limits

Limits bounds the resources one operation may consume. A zero field inherits the documented default; zero never means unlimited.

Limits supplied by the client, by a request, and by a verification policy are intersected, so any of them may tighten a bound and none may relax one.

func DefaultLimits

func DefaultLimits() Limits

DefaultLimits returns the documented default resource bounds.

type LockInfo

type LockInfo struct {
	Path           string             `json:"path"`
	Digest         string             `json:"digest"`
	ManifestDigest string             `json:"manifestDigest"`
	TreeDigest     string             `json:"treeDigest"`
	Format         string             `json:"format"`
	Confidence     Confidence         `json:"confidence"`
	FileCount      int                `json:"fileCount"`
	Sources        []LockedSourceInfo `json:"sources"`
	Files          []FileInfo         `json:"files,omitempty"`
}

LockInfo describes a lock file.

type LockRequest

type LockRequest struct {
	SpecPath string
	Spec     *bundle.Spec
	SpecDir  string

	// OutputPath is where to write the lock. Empty defaults to
	// devproof.lock.json beside the manifest.
	OutputPath string
	// Check verifies an existing lock without writing anything.
	Check bool
	// ExistingLock is compared against when Check is set. Empty loads from
	// OutputPath.
	ExistingLock *bundle.Lock

	Limits Limits
}

LockRequest describes a lock to produce.

It has no destination and no signing mode: locking resolves sources and records what they resolved to, and mixing publication into that would make "what does this manifest mean" a question you cannot answer without a registry.

type LockResult

type LockResult struct {
	Lock       *bundle.Lock
	LockBytes  []byte
	LockDigest string
	// SpecDigest is the digest of the manifest that was resolved. Named as in
	// BuildResult, for the same reason.
	SpecDigest  string
	TreeDigest  string
	SourceCount int
	FileCount   int
	OutputPath  string
	// Matched reports whether an existing lock already described this
	// resolution. Under Check, a false value is a failure.
	Matched bool
}

LockResult describes a lock.

type LockedSourceInfo

type LockedSourceInfo struct {
	Name       string         `json:"name"`
	Type       string         `json:"type"`
	Resolver   string         `json:"resolver"`
	TreeDigest string         `json:"treeDigest"`
	MountPath  string         `json:"mountPath,omitempty"`
	Requested  map[string]any `json:"requested,omitempty"`
	Resolved   map[string]any `json:"resolved,omitempty"`
}

LockedSourceInfo is one resolved source.

type ManifestInfo

type ManifestInfo struct {
	Path       string       `json:"path"`
	APIVersion string       `json:"apiVersion"`
	Name       string       `json:"name"`
	Digest     string       `json:"digest"`
	Confidence Confidence   `json:"confidence"`
	Sources    []SourceInfo `json:"sources"`
}

ManifestInfo describes a manifest file.

Everything here is authored: it is what someone asked for, and none of it has been resolved or checked against anything.

type OperandKind added in v0.2.0

type OperandKind string

OperandKind is what a diff operand turned out to be.

const (
	// OperandBundle is an OCI subject, read from a registry or a layout.
	OperandBundle OperandKind = "bundle"
	// OperandDirectory is a local directory, canonicalized the same way a
	// build would canonicalize it.
	OperandDirectory OperandKind = "directory"
)

type Option

type Option func(*Client) error

Option configures a Client.

func WithAbsolutePathSources

func WithAbsolutePathSources() Option

WithAbsolutePathSources allows path sources to name absolute paths.

Off by default: a manifest that reaches outside its own directory is not portable, and a manifest that does so by accident should say so loudly.

func WithAttester

func WithAttester(attester evidence.Attester) Option

WithAttester registers the implementation that signs evidence.

func WithClock

func WithClock(clock func() time.Time) Option

WithClock supplies the time a verification evaluates against.

The clock is used for evidence and diagnostics only. Canonical artifact encoding has no clock dependency at all, which is why a bundle built at two different moments has one identity (DP-012).

func WithInsecureRegistry

func WithInsecureRegistry(provider artifact.CredentialProvider) Option

WithInsecureRegistry disables TLS for registry transport.

It exists for a local test registry. Using it against anything else sends credentials and content in the clear, which is why it is a named option rather than a field on a config struct somebody might set by accident.

func WithLimits

func WithLimits(limits Limits) Option

WithLimits sets the client's resource bounds.

These are intersected with any per-request and policy limits, so this sets a ceiling the rest of the system can tighten but never relax (DP-021).

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger sets a structured logger for diagnostics.

Events name the operation and the logical source. They never carry a credential-bearing URL or a secret header, and nothing logged is ever an input to artifact identity.

func WithOffline added in v0.2.0

func WithOffline() Option

WithOffline refuses every operation that would use the network.

It is a capability boundary rather than a preference: a transport that has not promised to stay local is rejected when it is selected, before a reference is resolved or a byte is fetched. A flag that merely expressed an intention would be worse than none, because the situations where offline matters -- an air gap, an incident, a machine that must not phone home -- are exactly the ones where nobody is watching for an unexpected connection.

Evidence verification must also be offline: supply a trusted root with WithSigstore, or a key with WithVerifier. Without one, verification would fetch the public Sigstore root over TUF, and this option cannot reach inside an attester or verifier an application supplied.

func WithRegistryCredentials

func WithRegistryCredentials(provider artifact.CredentialProvider) Option

WithRegistryCredentials supplies per-host registry credentials.

Lookup is by host, so a credential issued for one registry is never offered to another (DP-013).

func WithResolver

func WithResolver(resolver source.Resolver) Option

WithResolver registers a source resolver, replacing any resolver already registered for the same type.

Registration is explicit and per-client. There is no global registry, no plugin loading, and nothing discovered from a bundle, so the set of things that can fetch material during a build is exactly what the embedding application chose (DP-009).

func WithSigstore

func WithSigstore(opts evidence.SigstoreOptions) Option

WithSigstore enables keyless signing and verification.

This is the default posture for a build that asks to sign: an ephemeral key certified by Fulcio against an OIDC identity, recorded in a transparency log, with nothing durable to protect or rotate. On a CI runner that already has an OIDC token it needs no configuration at all.

func WithTempRoot

func WithTempRoot(dir string) Option

WithTempRoot sets the parent directory for snapshot and layout scratch space.

It deliberately does not affect expansion staging, which must be a sibling of the destination for publication to be an atomic same-filesystem rename (DP-022).

func WithTransport

func WithTransport(transport artifact.Transport) Option

WithTransport registers an artifact transport, replacing any transport already registered for the same scheme.

func WithTrustRoots

func WithTrustRoots(roots ...[]byte) Option

WithTrustRoots supplies trust material for evidence verification.

Supplying roots is what makes offline verification possible: nothing is fetched, and the material came from somewhere the caller chose rather than from the network at verification time.

func WithVerifier

func WithVerifier(verifier evidence.Verifier) Option

WithVerifier registers the implementation that checks evidence signatures.

Verification and signing are configured separately on purpose: a consumer verifies without ever signing, and making one imply the other would mean every verifier carried a signing path it never uses.

type SourceErrors

type SourceErrors = fault.SourceErrors

SourceErrors reports a multi-source failure: the cause that stopped the operation, plus the other sources that also failed, in deterministic order. errors.Is and errors.As traverse all of them.

type SourceInfo

type SourceInfo struct {
	Name      string   `json:"name"`
	Type      string   `json:"type"`
	MountPath string   `json:"mountPath,omitempty"`
	Include   []string `json:"include,omitempty"`
	Exclude   []string `json:"exclude,omitempty"`
}

SourceInfo is one declared source.

type SubjectInfo

type SubjectInfo struct {
	Reference  string     `json:"reference"`
	Digest     string     `json:"digest"`
	TreeDigest string     `json:"treeDigest"`
	Format     string     `json:"format"`
	Confidence Confidence `json:"confidence"`

	FileCount  int64 `json:"fileCount"`
	TotalBytes int64 `json:"totalBytes"`
	LayerBytes int64 `json:"layerBytes"`

	ConfigDigest string `json:"configDigest"`
	LayerDigest  string `json:"layerDigest"`

	Files    []FileInfo     `json:"files,omitempty"`
	Evidence []EvidenceInfo `json:"evidence,omitempty"`
	// EvidenceStorage reports how evidence was found. The tag fallback
	// cannot express a set, so a caller needs to know which mode answered.
	EvidenceStorage string `json:"evidenceStorage,omitempty"`
	// RejectedEvidence lists candidates that did not verify.
	RejectedEvidence []string `json:"rejectedEvidence,omitempty"`
}

SubjectInfo describes a bundle.

type VerifyRequest

type VerifyRequest struct {
	// Reference is the subject to verify: a registry or layout reference,
	// by tag or by digest. A tag is resolved once and the resolved digest is
	// what every later step uses.
	Reference string
	// RequireDigest rejects a tag before any content is fetched.
	RequireDigest bool

	// Policy is the verification policy to apply. Without one, trust reports
	// not-evaluated rather than pass.
	Policy *policy.Document
	// PolicyPath loads a policy from a file. Mutually exclusive with Policy.
	PolicyPath string

	// Limits tightens the client's bounds for this operation.
	Limits Limits
}

VerifyRequest describes a subject to verify.

Jump to

Keyboard shortcuts

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