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 4
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 4, 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"`
// RequireAttestation is a schema v4 addition (Plan-8 Task 6, C6-01b/
// C6-02): unlike the four fields above, it is NOT a relaxation of the
// strict contract -- it TIGHTENS it, requiring every executed record to
// carry a valid, trusted-key-signed attestation sidecar (see pkg/attest,
// pkg/gotest.AttestationValid) or be dropped/demoted. It therefore does
// not appear in Overrides() or Degraded(): those two report ways a run
// accepted LESS proof than strict, not ways it demanded MORE. It is
// still part of Policy (and thus Hash()) so a receipt's policy identity
// reflects whether attestation was required, the same as every other
// policy dimension.
RequireAttestation bool `json:"require_attestation"`
}
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
// ToolchainPath, ToolchainDigest, and SourceDigest are schema v4
// additions (C6-01a/C6-06). ToolchainPath/ToolchainDigest record the
// absolute path and SHA-256 of the Go toolchain executable that produced
// the record -- resolved PATH-independently (config pin, else
// GOROOT/bin/go, else exec.LookPath("go") as a last, untrusted resort) --
// so the evidence itself names which "go" ran, not just that "a go" ran.
// SourceDigest (C6-06) is the content digest of every token-bearing file
// under the scan root (index.ComputeScanDigest), computed by `evidence
// run-go-test` immediately before AND after the child process runs --
// the two must be equal, or the run refuses outright (a concurrent edit,
// even one restored afterward, is caught) -- and gated again by `canary
// verify` against its own freshly computed digest before an executed
// record with one may count as evidence (pkg/cmds/verify). All three are
// optional: an imported record (from-go-test, hand-authored, or a
// run-go-test whose toolchain resolution fell back to an untrusted PATH
// lookup) may lack any of them, and a record without a SourceDigest is
// simply unaffected by verify's gate (legacy/imported evidence
// predating this field).
ToolchainPath string `json:"toolchain_path,omitempty"`
ToolchainDigest string `json:"toolchain_digest,omitempty"` // "sha256:" + 64 lowercase hex
SourceDigest string `json:"source_digest,omitempty"` // "sha256:" + 64 lowercase hex
// ProducerIdentity is a schema v4 addition (Plan-8 Task 6, C6-01b/C6-02):
// pkg/attest.Fingerprint of the ed25519 public key that signed this
// record's attestation sidecar, stamped only by `evidence run-go-test
// --sign-key`. Its presence is informational -- a label the producer
// claims -- and proves nothing on its own; only a caller that loads the
// sidecar (pkg/gotest.AttestationValid) and verifies its signature
// against an explicitly trusted key learns anything trustworthy. A
// record without this field is simply unsigned, the same default
// filesystem-trust posture every executed record has always had.
ProducerIdentity string `json:"producer_identity,omitempty"`
}
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.