Skip to content

GP1 — Per-source evidence-pointer contract (#1347) + first-party direct-ingest + signer allowlist #1401

Description

@njhensley

Parent: #1400 · Labels: enhancement, area/recipes (auto, recipes/**), area/ci (auto, .github/**) · Size: M · Depends on: none · Implements: #1347

Goal. Establish the in-repo recipes/evidence/ pointer contract per #1347 as the community/partner entry point, wire first-party UAT to ingest directly (no per-run commit), and stand up the maintained signer allowlist + path-ownership enforcement.

Scope.

  • Create recipes/evidence/ (does not exist at HEAD) and adopt the Make evidence pointers per-source (per contributing party) #1347 Option A per-source, add-only layout recipes/evidence/<slug>/<source>/<bundle-digest>.yaml (see Pointer file topology): one immutable single-attestation V1 pointer per run, where <source> is a stable slug derived from the verified signer OIDC identity. Each file is the existing schema (pkg/evidence/attestation/types.go:188, PointerSchemaVersion = "1.0.0", :34), parseable by LoadAndValidatePointer (pointer.go:37); discovery is by glob <slug>/<source>/*.yaml; nothing is overwritten. (Supersedes the earlier per-signer-file sketch.)
  • Path ownership (anti-squat). Add CODEOWNERS rules (and/or a CI check) per recipes/evidence/<slug>/<source>/ so a party can only write under its own <source>/ — the path is otherwise squattable (Make evidence pointers per-source (per contributing party) #1347 cross-cutting).
  • First-party UAT does NOT commit a pointer per run. That would churn main nightly. The UAT workflow already pushes a signed bundle to OCI (uat-gcp.yaml:291) with its OIDC identity pinned (uat-aws.yaml:258-260); it invokes GP2 ingest directly with the bundle ref instead. (A single low-cadence first-party pointer per launch recipe MAY be committed for discoverability/parity, drift-refreshed à la bom-refresh.yaml — but not per run.)
  • Signer allowlist. An in-tree, PR-reviewed allowlist (recipes/evidence/allowlist.yaml) with a first-party-issuer pin (GitHub-Actions OIDC for NVIDIA/aicr), a community section, and a partner section. Entry semantics so the allowlist is not itself a sybil surface: each entry pins a fully-anchored issuer + identity pair (exact identity, or a tightly-bounded regex with no wildcard org/repo); classes disjoint; no two entries overlap. GP2's verify step and GP4's consensus model read it to classify a signer and decide corroboration weight; an unknown verified signer is admitted as "reported," never corroborating.
  • Verifier/consumer follow-ups (Make evidence pointers per-source (per contributing party) #1347). Generalize the fixed-path Attestations[0] consumers (verify.go:147-148, fetch.go:136-139) to glob-based multi-source discovery, and update the hardcoded emit/publish path (emit.go:166, publish.go:120). Each file stays a single-attestation V1 pointer — no per-file schema change; the delta is discovery + path convention.
  • Community-PR pointer path. A contributor adds their own recipes/evidence/<slug>/<source>/<digest>.yaml (and, for corroboration weight, an allowlist entry) via PR — alongside, never replacing, any other party's; maintainer review is the trust gate; the AICR ingest job (GP2) does the bucket write.

Out of scope. The ingest job (GP2); the bucket/SA (GP3); the generator (GP4). Producing the bundle (existing nightly / ADR-007). The schema-2.0 multi-attestation file (deferred — Option A needs none).

Key files. New recipes/evidence/<slug>/<source>/<digest>.yaml + recipes/evidence/allowlist.yaml + .github/CODEOWNERS entries; pointer schema pkg/evidence/attestation/types.go:188-226; loader pkg/evidence/verifier/pointer.go:37; fixed-path consumers to generalize verifier/verify.go:147-148, verifier/fetch.go:136-139; hardcoded emit/publish path pkg/evidence/attestation/emit.go:166, publish.go:120; first-party identity pin uat-aws.yaml:258-260, UAT OCI push uat-gcp.yaml:291; bot-PR precedent .github/workflows/bom-refresh.yaml; #1347.

Tests. Each committed pointer parses+validates via LoadAndValidatePointer (pointer.go:37) with a non-empty PointerSigner.Identity/Issuer; a two-sources-one-recipe test asserting two files under <slug>/<source-a>/ and <slug>/<source-b>/ both parse and are found by glob discovery (no overwrite); a path-ownership test that a pointer written under another party's <source>/ is rejected by the CODEOWNERS/CI check; an allowlist-schema test (well-formed, classes disjoint, no duplicate identities, no over-broad entry — an identity pattern matching more than its intended single org/workflow is rejected); determinism of any generated pointer YAML (MarshalYAMLDeterministic, pkg/serializer/yaml.go:31).

Acceptance. recipes/evidence/ uses the #1347 per-source add-only layout with CODEOWNERS path-ownership; two distinct sources for one recipe produce two non-colliding immutable pointers discovered by glob; first-party UAT ingests directly without committing a per-run pointer; an in-tree community/partner allowlist with anchored, disjoint, non-overlapping entries exists and is documented; the community-PR path is documented; pointers validate against the existing schema.


Child of #1400 · staged from docs/design/013-interim-evidence-dashboard.md (GP1).

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions