Add skills verifier wrapping toolhive-core Sigstore exports - #6091
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #6091 +/- ##
==========================================
- Coverage 72.43% 72.41% -0.03%
==========================================
Files 733 737 +4
Lines 75856 75960 +104
==========================================
+ Hits 54945 55005 +60
- Misses 17016 17055 +39
- Partials 3895 3900 +5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
56af24b to
f41b86e
Compare
Install-time and sync-time signature verification (RFC THV-0080) need a skills-domain verification API. This adds pkg/skills/verifier as a thin wrapper over toolhive-core's container/verifier exports: all cryptography, including binding the expected identity into the Sigstore verification policy, happens in core. The wrapper adds lock file provenance conversion, the unsigned/invalid/mismatch error taxonomy (classified with typed sentinels via errors.Is — a signer mismatch is told apart from a broken signature by re-verifying without the identity constraint), and the trust-on-first-use flow where a nil expected identity verifies the chain of trust only. Verification uses core's embedded trusted root — hermetic, no TUF fetch — so unit tests and sync re-verification run without network. The round-trip test drives the real chain: the signer package signs an artifact into an in-process registry, core retrieves the key-signed bundle, and the wrapper verifies it against the key. Part of #5899. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
f41b86e to
dafa3d2
Compare
JAORMX
left a comment
There was a problem hiding this comment.
Panel-review comment on the verifier wrapper (reviewed together with #6121 and #6129 against RFC THV-0080). One finding, inline: VerifyBundleOffline/ResultFromBundle have no callers until PR12 — keep with a TODO pointer if PR12 is next, drop if it slips.
The rest of the package is in good shape: binding the expected identity into the Sigstore policy via WithCertificateIdentity (rather than comparing post-hoc) fixes the POC's biggest gap; the failure-path-only re-verification used to distinguish ErrSignerMismatch from ErrSignatureInvalid is safe (no success path, no oracle — both outcomes are 403 to callers); ToLockProvenance returning nil for key-signed results is documented in three places and tested; and the dependency direction (verifier → lockfile → core) is acyclic and correct.
Note
Stack 2 of RFC THV-0080 (tracking issue #5899) — PR 8 of the Sigstore series. Follows #6015 (schema) and #6023 (signer), both merged; parallel with #6084 (git plumbing + storage).
No go.mod change — main already pins toolhive-core v0.0.35, the release carrying stacklok/toolhive-core#192 and stacklok/toolhive-core#197.
Summary
Install-time verification (next PR in the stack) and sync-time offline re-verification need a skills-domain verification API. This adds
pkg/skills/verifieras a thin wrapper over toolhive-core'scontainer/verifierexports — all cryptography happens in core; the wrapper adds the skills vocabulary:VerifyOCI(ctx, ref, digest, expected)— keyless (Fulcio) verification. A non-nil expected identity from the lock file is bound into the Sigstore verification policy (core'sWithCertificateIdentity), not compared post-hoc — fixing the POC's (Add project-level skills lock file (POC) #5715) biggest gap. Nil expected is the trust-on-first-use case: chain-of-trust verification only, with the caller recording the observed identity.VerifyOCIWithKey(ctx, ref, digest, pubKeyPEM)— the cosign key-pair flow, verifying whatthv skill push --key(Add Sigstore signer package for skill OCI artifacts #6023) produces.VerifyBundleOffline(bundle, digest, expected)/ResultFromBundle— re-verify stored bundles against core's embedded trusted root: hermetic, zero network, used by sync and skill adoption later in the stack.ErrUnsigned/ErrSignatureInvalid/ErrSignerMismatchas typed sentinels (classification viaerrors.Is, never string matching). Since the identity is enforced in-policy, a mismatch surfaces as a verification failure; the wrapper tells mismatch apart from a broken signature by re-verifying without the identity constraint — only on the failure path.Result.ToLockProvenance()— maps a verified identity to the lock file'sprovenance:block; key-signed results (no certificate identity) deliberately yield nil rather than fabricating provenance.Type of change
Test plan
task test)task lint-fix, 0 issues)ErrUnsignedon both flows; key-signed artifact rejected by the keyless path; offline verification matrix (empty/malformed/certificate-less bundles, and the expected-identity-against-unverifiable-bundle case that must NOT be misclassified as mismatch);ToLockProvenanceand identity-conversion tables. Zero network — everything runs againsthttptest+ the embedded trust root.Does this introduce a user-facing change?
No — nothing consumes the verifier yet. Verification behavior arrives with the install-verification PR.
Special notes for reviewers
ErrSignerMismatchpositive path (a bundle that verifies under TOFU but not under the pinned identity) requires a Fulcio-issued certificate and can't be produced hermetically in unit tests; it's covered by the classification logic tests here and will get E2E coverage via the install-verification PR. The inverse case (unverifiable bundle + expected identity staysErrSignatureInvalid) is tested.Generated with Claude Code