Documentation
¶
Overview ¶
Package evidence holds strict parsing of CANARY evidence records and the single completion function ("has every claimed requirement been proven at the current commit") that downstream verification consumes. It is self-contained: stdlib only, no imports from the rest of this repo, so any package may depend on it without creating a cycle.
Index ¶
Constants ¶
const MaxEvidenceFileBytes = 64 << 20
MaxEvidenceFileBytes bounds a loadable evidence store.
Variables ¶
This section is empty.
Functions ¶
func CompleteReq ¶
func CompleteReq(reqID string, keys []FeatureKey, recs []Record, projectID, commit string) bool
CompleteReq is the single-requirement form of Complete, and the shared entry point behind the two surfaces that answer "is this local dependency satisfied?" -- `canary next`'s dependency gate and `canary deps check`. reqID is complete iff every key has a PASS record for projectID at commit. An empty keys slice yields false (EMPTY_CLAIMS): a dependency with nothing to prove is not proven. Routing both callers through here keeps ONE completion definition -- a declared STATUS=TESTED is a claim on both surfaces, never proof.
Types ¶
type FeatureKey ¶
type FeatureKey struct {
Feature string `json:"feature"`
Aspect string `json:"aspect"`
Tests []string `json:"tests,omitempty"` // declared TEST= names, optionally "pkg/path:Name"
Benches []string `json:"benches,omitempty"` // declared BENCH= names, optionally qualified
}
FeatureKey identifies one claimed feature/aspect within a requirement, and optionally the exact declared TEST=/BENCH= names that must each have their own passing evidence record. An entry may be package-qualified as "pkg/path:Name"; a bare name matches any package.
type File ¶
type File struct {
SchemaVersion int `json:"schema_version"` // must be 3
Records []Record `json:"records"`
}
File is the top-level shape of an evidence document.
func Load ¶
Load reads and strictly parses the evidence file at path. A missing file returns an error wrapping fs.ErrNotExist so callers can distinguish it (via errors.Is) from a malformed one.
func Parse ¶
Parse strictly decodes an evidence file from r. It rejects, with an error naming the offending record index and field: unknown fields anywhere, duplicate fields anywhere (encoding/json silently keeps the last value on a duplicate key, so this is caught with an explicit token walk before the struct decode), a schema_version other than 3, any empty required field, a result other than exactly "PASS", a malformed commit_sha, artifact_digest, or observed_at, an origin other than "executed" or "imported", an empty or control-character-carrying argv element, and a negative run_exit_status.
type Missing ¶
type Missing struct {
RequirementID string `json:"requirement_id"`
Key FeatureKey `json:"key"`
Reason string `json:"reason"` // "no_evidence" | "wrong_commit" | "scope_mismatch" | "test_unproven" | "bench_unproven" | "test_ambiguous" | "bench_ambiguous"
}
Missing describes one required feature/aspect that Complete could not find satisfying evidence for.
type Policy ¶ added in v0.3.6
type Policy struct {
AllowImported bool `json:"allow_imported"`
AllowDirty bool `json:"allow_dirty"`
AllowEmpty bool `json:"allow_empty"`
AllowUnknownExternal bool `json:"allow_unknown_external"`
}
Policy records which relaxations of the strict verification contract were in effect for a run. Every field is an opt-out that widens what verify accepts as proof -- imported (not canary-executed) evidence, a dirty working tree, an empty claims file, or an unresolvable external dependency. The zero value is StrictPolicy: nothing relaxed.
A bare "VERIFIED" cannot, on its own, tell a downstream consumer whether the run was strict or degraded by one of these overrides (C5-10). Policy plus the receipt fields it feeds on evidence.Verdict close that gap: the verdict now carries the policy's hash, its named overrides, and whether it was degraded at all.
func StrictPolicy ¶ added in v0.3.6
func StrictPolicy() Policy
StrictPolicy is the zero-value Policy -- no overrides, the default verification contract. Named for readability at call sites.
func (Policy) Degraded ¶ added in v0.3.6
Degraded reports whether any override is active -- the run accepted something the strict contract would have refused.
func (Policy) Hash ¶ added in v0.3.6
Hash returns a stable content identity for p: "sha256:" followed by the hex-encoded sha256 of p's canonical JSON encoding. Policy's field order is fixed in source, so json.Marshal of the struct (never a map) is deterministic across runs -- identical policies always hash identically, and any differing field changes the hash.
type Record ¶
type Record struct {
ProjectID string `json:"project_id"`
RequirementID string `json:"requirement_id"`
Feature string `json:"feature"`
Aspect string `json:"aspect"`
TestID string `json:"test_id"`
Package string `json:"package,omitempty"` // import path the test ran in; "" in legacy records
Kind string `json:"kind,omitempty"` // "" or "test" means test; "bench" means benchmark
Command string `json:"command"`
Result string `json:"result"` // must be exactly "PASS"
CommitSHA string `json:"commit_sha"` // exactly 40 lowercase hex
ObservedAt string `json:"observed_at"` // RFC3339, must be UTC (Z or +00:00)
Runner string `json:"runner"`
ArtifactDigest string `json:"artifact_digest"` // "sha256:" + 64 lowercase hex
Origin string `json:"origin"` // "executed" or "imported"
Dirty bool `json:"dirty,omitempty"` // true when the working tree was not HEAD when this was recorded
// Argv and RunExitStatus are schema v3 additions: they record the exact
// argv vector a fixed-runner producer (e.g. `canary evidence
// run-go-test`) executed and the process's exit status. Both are
// optional -- an imported record (from-go-test, hand-authored) never
// ran a process itself and so has neither.
Argv []string `json:"argv,omitempty"` // executed: the exact argv vector run
RunExitStatus *int `json:"run_exit_status,omitempty"` // executed: the process's exit code
}
Record is one evidence claim: a single passing test run, tying a requirement's feature/aspect to a commit via a reproducible command and an observed result.
type Verdict ¶
type Verdict struct {
OK bool `json:"ok"`
State string `json:"state"` // "VERIFIED" | "UNVERIFIED" | "UNKNOWN"
Code string `json:"code"` // "OK" | "EVIDENCE_MISSING" | "EMPTY_CLAIMS" | "SCAN_INCOMPLETE" | "EXTERNAL_UNKNOWN"
Message string `json:"message"`
Missing []Missing `json:"-"`
// ProjectID and CommitSHA identify what was verified. CommitSHA may be
// empty when the verdict was reached before HEAD could be determined
// (e.g. SCAN_INCOMPLETE from an unreadable config).
ProjectID string `json:"project_id,omitempty"`
CommitSHA string `json:"commit_sha,omitempty"`
// SourceDigest is the content digest of the scanned token-bearing tree
// (index.ComputeScanDigest), when it could be computed.
SourceDigest string `json:"source_digest,omitempty"`
// PolicyHash, Overrides and Degraded describe the verification policy in
// effect for this run -- see Policy.
PolicyHash string `json:"policy_hash,omitempty"`
Overrides []string `json:"overrides,omitempty"`
Degraded bool `json:"degraded,omitempty"`
// VerifierVersion is the canary binary version that produced this
// verdict, and GeneratedAt is when it did so (RFC3339, UTC).
VerifierVersion string `json:"verifier_version,omitempty"`
GeneratedAt string `json:"generated_at,omitempty"`
}
Verdict is the result of a completion check.
The trailing fields form the verification receipt (C5-10): identity and policy context stamped onto every verdict a verifier emits, so a downstream consumer can distinguish a strict pass from one reached only because some part of the contract was relaxed (--allow-dirty, --allow-imported, --allow-empty, --allow-unknown-external). They are all `omitempty` so the pre-C5-10 JSON shape stays a strict subset of the current one.
func Complete ¶
func Complete(required map[string][]FeatureKey, recs []Record, projectID, commit string, allowEmpty bool) Verdict
Complete is THE completion function: every required feature/aspect of every required requirement must have at least one record with Result=PASS (guaranteed by Parse), ProjectID==projectID, and CommitSHA==commit. An empty required map yields EMPTY_CLAIMS unless allowEmpty is set, in which case it is treated as trivially satisfied.
The Missing list, when non-empty, is sorted deterministically by (RequirementID, Feature, Aspect) so repeated runs over the same inputs produce byte-identical output.