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:
- github.com/thingzio/devproof/pkg/bundle: format constants, manifest, lock, inventory, and resource limits.
- github.com/thingzio/devproof/pkg/source: the source resolver contract and the built-in local-path and HTTPS Git resolvers.
- github.com/thingzio/devproof/pkg/artifact: OCI descriptors, references, and the transport contract.
- github.com/thingzio/devproof/pkg/evidence: provenance statements and the attester contract.
- github.com/thingzio/devproof/pkg/policy: verification policy documents and result types.
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 ¶
- Constants
- func IsTransient(err error) bool
- type BuildRequest
- type BuildResult
- type Change
- type Client
- func (c *Client) Build(ctx context.Context, req BuildRequest) (_ *BuildResult, retErr error)
- func (c *Client) Close() error
- func (c *Client) Copy(ctx context.Context, req CopyRequest) (_ *CopyResult, retErr error)
- func (c *Client) Diff(ctx context.Context, req DiffRequest) (_ *DiffResult, retErr error)
- func (c *Client) Expand(ctx context.Context, req ExpandRequest) (_ *ExpandResult, retErr error)
- func (c *Client) Inspect(ctx context.Context, req InspectRequest) (*InspectResult, error)
- func (c *Client) Lock(ctx context.Context, req LockRequest) (_ *LockResult, retErr error)
- func (c *Client) Verify(ctx context.Context, req VerifyRequest) (*policy.Report, error)
- type Code
- type Confidence
- type CopyRequest
- type CopyResult
- type DiffEntry
- type DiffRequest
- type DiffResult
- type DiffSide
- type Error
- type EvidenceInfo
- type EvidenceResult
- type ExpandRequest
- type ExpandResult
- type FileInfo
- type InspectKind
- type InspectRequest
- type InspectResult
- type Limits
- type LockInfo
- type LockRequest
- type LockResult
- type LockedSourceInfo
- type ManifestInfo
- type OperandKind
- type Option
- func WithAbsolutePathSources() Option
- func WithAttester(attester evidence.Attester) Option
- func WithClock(clock func() time.Time) Option
- func WithInsecureRegistry(provider artifact.CredentialProvider) Option
- func WithLimits(limits Limits) Option
- func WithLogger(logger *slog.Logger) Option
- func WithOffline() Option
- func WithRegistryCredentials(provider artifact.CredentialProvider) Option
- func WithResolver(resolver source.Resolver) Option
- func WithSigstore(opts evidence.SigstoreOptions) Option
- func WithTempRoot(dir string) Option
- func WithTransport(transport artifact.Transport) Option
- func WithTrustRoots(roots ...[]byte) Option
- func WithVerifier(verifier evidence.Verifier) Option
- type SourceErrors
- type SourceInfo
- type SubjectInfo
- type VerifyRequest
Examples ¶
Constants ¶
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.
const DefaultLockName = "devproof.lock.json"
DefaultLockName is the lock file written beside a manifest.
Variables ¶
This section is empty.
Functions ¶
func IsTransient ¶
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 ¶
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 ¶
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 ¶
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 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithAttester registers the implementation that signs evidence.
func WithClock ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithTransport registers an artifact transport, replacing any transport already registered for the same scheme.
func WithTrustRoots ¶
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 ¶
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.