Skip to content

feat(cli): add evidence digest subcommand for recipe canonical hash#1055

Merged
mchmarny merged 10 commits into
NVIDIA:mainfrom
njhensley:feat/cli-evidence-digest
May 27, 2026
Merged

feat(cli): add evidence digest subcommand for recipe canonical hash#1055
mchmarny merged 10 commits into
NVIDIA:mainfrom
njhensley:feat/cli-evidence-digest

Conversation

@njhensley

Copy link
Copy Markdown
Member

Summary

Adds aicr evidence digest -r <recipe-or-overlay> — prints the canonical sha256 of a resolved recipe, byte-for-byte identical to predicate.recipe.digest emitted by aicr validate --emit-attestation.

Motivation / Context

CI evidence gates need a cheap way to detect drift between a signed evidence pointer (checked into the repo or sitting in an OCI artifact) and the current recipe on the PR branch. Today the only path is aicr evidence verify against the signed bundle, which requires pulling the OCI artifact and a registry round-trip. A pure-local digest of the resolved recipe lets a workflow short-circuit before any network I/O.

Fixes: N/A
Related: docs/design/007-recipe-evidence.md

Type of Change

  • New feature (non-breaking change that adds functionality)

Component(s) Affected

  • CLI (cmd/aicr, pkg/cli)
  • Docs/examples (docs/, examples/)

Implementation Notes

  • Goes through recipe.LoadFromFile so overlays/mixins are hydrated on the same path as aicr validate -r — the digest is computed against the fully resolved RecipeResult, not the raw overlay bytes.
  • Uses serializer.MarshalYAMLDeterministic + attestation.SubjectDigest (the existing canonicalization helpers) so output is bit-identical to the attestation predicate; no parallel canonicalizer.
  • Output: lowercase hex sha256 to cmd.Root().Writer (testable per pkg/cli convention). Errors return structured pkg/errors codes.
  • New --kubeconfig flag is accepted only because recipe.LoadFromFile requires it for cm:// URIs; no cluster traffic on file/HTTP paths.

Testing

```bash
golangci-lint run -c .golangci.yaml ./pkg/cli/... # 0 issues
go test -race -count=1 ./pkg/cli/... # PASS
```

  • Unit tests cover: subcommand registration, required-flag validation, hex format, run-to-run determinism, content-change sensitivity.
  • pkg/cli coverage: 66.7% → 66.7% (no change; new code covered by new tests).

Risk Assessment

  • Low — Additive CLI subcommand, no existing code paths modified beyond adding one line to evidenceCmd().Commands.

Rollout notes: N/A — pure addition.

Checklist

  • Tests pass locally (go test -race ./pkg/cli/...)
  • Linter passes (golangci-lint on ./pkg/cli/...)
  • I did not skip/disable tests to make CI green
  • I added/updated tests for new functionality
  • I updated docs (docs/user/cli-reference.md — new ### aicr evidence digest section)
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)

njhensley added 2 commits May 26, 2026 18:32
Adds `aicr evidence digest -r <recipe-or-overlay>` which prints the
lowercase hex sha256 of the canonical YAML — byte-for-byte the same
value recorded in predicate.recipe.digest by
`aicr validate --emit-attestation`.

Enables CI gates to detect drift between a signed evidence pointer and
the current recipe on the PR branch without pulling the OCI artifact:

  signed=$(aicr evidence verify recipes/evidence/<slug>.yaml --format json \
           | jq -r .predicate.recipe.digest)
  current=$(aicr evidence digest -r recipes/overlays/<file>.yaml)
  [[ "$signed" == "$current" ]] || echo "evidence is stale"
Adds a CLI reference section for `aicr evidence digest`, including the
drift-gate snippet that pairs it with `aicr evidence verify`.
@github-actions

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 488b33c1-90b4-4054-90c6-449ac72fa765

📥 Commits

Reviewing files that changed from the base of the PR and between 0db428f and 0f0cf68.

📒 Files selected for processing (1)
  • pkg/evidence/attestation/recipe_digest.go

📝 Walkthrough

Walkthrough

This PR adds a new aicr evidence digest CLI subcommand that computes the canonical SHA-256 digest of a resolved recipe or overlay input. The command is registered under the existing evidence command, loads the recipe via the standard hydration path (matching aicr validate -r behavior), deterministically marshals it to YAML, and outputs the lowercase hex digest. The implementation includes comprehensive unit tests covering registration, flag validation, error handling, digest determinism, and content sensitivity, along with user documentation and drift-gate workflow examples.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

size/M

Suggested reviewers

  • mchmarny
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a new CLI evidence digest subcommand for computing recipe canonical hash.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, covering motivation, implementation details, testing, and risk assessment.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/user/cli-reference.md`:
- Around line 2075-2094: Add blank lines around the fenced code blocks and the
Flags table to satisfy markdownlint MD031/MD058: ensure there's a blank line
before and after the initial ```shell block under "Synopsis" and similarly a
blank line before the "Flags:" table and one blank line after that table (before
"Exit codes"), and add a blank line before the final ```shell block under
"Examples"; update the fenced blocks to start with "```shell" and have an empty
line immediately after the opening fence and an empty line immediately before
the closing fence to keep spacing consistent.

In `@pkg/cli/evidence_digest.go`:
- Around line 71-100: runEvidenceDigestCmd mixes CLI orchestration with business
logic (hydrating recipe, canonicalizing YAML, computing digest); extract that
logic into a new function (e.g., ComputeRecipeDigest(ctx, path, kubeconfig,
version) placed in a functional package such as attestation or pkg/evidence)
that performs recipe.LoadFromFile, serializer.MarshalYAMLDeterministic and
attestation.SubjectDigest and returns the digest string or an error; then
simplify runEvidenceDigestCmd to only validate flags, call the new
ComputeRecipeDigest with cmd.String("kubeconfig") and path, and print the
returned digest, preserving existing error wrapping behavior.
- Line 82: The recipe loader currently calls serializer.FromFileWithKubeconfig
using context.Background(), which ignores the caller ctx passed into
recipe.LoadFromFile; change recipe.LoadFromFile (in pkg/recipe/loader.go) to
accept and forward the caller ctx into serializer functions, and inside the
serializer call sites replace context.Background() usages with derived contexts
created from the caller ctx using context.WithTimeout(ctx,
defaults.FileReadTimeout) (and defaults.ConfigMapWriteTimeout where applicable)
so caller cancellation/timeouts are honored; update the
serializer.FromFileWithKubeconfig (and related serializer methods) signatures to
accept a context parameter and propagate it through file/ConfigMap I/O calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 49782566-c718-41f2-92fd-cb00d0cf71e2

📥 Commits

Reviewing files that changed from the base of the PR and between cce25f0 and 9c00fab.

📒 Files selected for processing (4)
  • docs/user/cli-reference.md
  • pkg/cli/evidence.go
  • pkg/cli/evidence_digest.go
  • pkg/cli/evidence_digest_test.go

Comment thread docs/user/cli-reference.md
Comment thread pkg/cli/evidence_digest.go
Comment thread pkg/cli/evidence_digest.go Outdated
Moves the load → canonicalize → digest pipeline out of `pkg/cli` and
into `pkg/evidence/attestation.ComputeRecipeDigest`. `pkg/cli` is
supposed to be user-interaction-only — keeping the recipe-builder call
inline meant business logic was leaking through the CLI boundary, and
any other caller (API server, future workflow tooling) that wanted the
same digest would have had to copy the canonicalization sequence
verbatim.

The helper preserves the existing input contract (`cm://` URIs honor
`kubeconfig`; everything else ignores it) so the CLI subcommand is now
a thin flag-parsing shim.

Also tightens docs/user/cli-reference.md to add a blank line after each
`**Bold:**` paragraph label in the new section, matching the spacing
the rest of the file already uses.
@njhensley njhensley self-assigned this May 27, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/evidence/attestation/recipe_digest.go`:
- Around line 42-45: The marshal error is being unconditionally wrapped with
errors.ErrCodeInternal, which discards any existing structured code; replace the
current wrap in the block that calls serializer.MarshalYAMLDeterministic(rec) so
that you use errors.PropagateOrWrap(err, errors.ErrCodeInternal, "failed to
marshal recipe for digest") instead of errors.Wrap, preserving coded errors from
serializer.MarshalYAMLDeterministic while still providing the contextual message
(update the error return in the function containing recipeYAML and the
serializer call).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 6cdf2ac9-64ab-457d-8a63-14b9f5e183b6

📥 Commits

Reviewing files that changed from the base of the PR and between 9c00fab and 0db428f.

📒 Files selected for processing (3)
  • docs/user/cli-reference.md
  • pkg/cli/evidence_digest.go
  • pkg/evidence/attestation/recipe_digest.go

Comment thread pkg/evidence/attestation/recipe_digest.go
@mchmarny mchmarny enabled auto-merge (squash) May 27, 2026 11:10
@mchmarny mchmarny assigned mchmarny and unassigned njhensley May 27, 2026
@mchmarny mchmarny merged commit 9e1841f into NVIDIA:main May 27, 2026
32 checks passed
@njhensley njhensley deleted the feat/cli-evidence-digest branch June 23, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants