Add commit-signature plumbing and sigstore bundle storage - #6084
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #6084 +/- ##
==========================================
+ Coverage 72.30% 72.41% +0.10%
==========================================
Files 728 731 +3
Lines 75496 75753 +257
==========================================
+ Hits 54590 54854 +264
+ Misses 16987 16983 -4
+ Partials 3919 3916 -3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
JAORMX
left a comment
There was a problem hiding this comment.
Panel review of #6084 — three orthogonal axes (Spec vs RFC THV-0080/#5899, Standards vs repo rules, Domain panel: security / architecture / duplication / devex / library-reuse). Verified locally: branch builds, go test ./pkg/git/... ./pkg/skills/... ./pkg/storage/... green, no new dependencies.
Spec axis (RFC THV-0080 + tracking issue #5899, PR10 scope)
Scope is exactly PR10 ("migration for sigstore bundle blob + git commit-signature plumbing") — no creep. Two borderline notes, no blockers:
- Nothing consumes the new fields yet.
ResolveResult.CommitSignatureand the store'sSigstoreBundleround-trip have zero producers/consumers on this branch (same for the pre-existingInstallOptions.SigstoreBundle, which nothing populates). Consistent with the stack plan, but worth calling out in the PR description that these are PR11 wiring points. - Shape verified against the RFC: HEAD is the pinned commit at read time (
Clonechecks out the pinned commit beforeResolvecallsHeadCommitSignature), so the signature/hash pair is read from the same ref. BLOB column is one of the two options the RFC's data-model section explicitly offers ("adjacent blob or dedicated column/table").opts.SigstoreBundledeferral to PR11 is legitimate.
Standards axis (CLAUDE.md + .claude/rules/)
- Hard violation: commit subject is 58 chars —
Add commit-signature plumbing and sigstore bundle storageexceeds the 50-char limit (CLAUDE.md § Commit Guidelines). Otherwise compliant (imperative, no conventional prefix, explains what/why; the Co-Authored-By trailer matches recent merged precedent). - Clean otherwise: migration 004 mirrors 003's structure exactly; the hand-written
blockingCloneClientstub predates this PR (#4051) and extending it is forced by the interface change; PR size is within limits (10 files, well under 400 code lines); tests follow testing.md (t.Cleanup in parallel tests, table-driven where applicable); doc comments are accurate and synchronized.
Domain axis
Cross-confirmed (architecture + standards + duplication, 3 axes independently): the HeadCommitHash/HeadCommitSignature split. See inline comment — the one finding worth acting on before PR11 hardens the contract.
Security (secure-code-reviewer): no actionable findings. Signature is transported as an opaque string — never parsed, logged, or formatted into errors; all SQL parameterized; NULL→nil scan semantics tested; json:"-" verified as the sole and effective control against API leakage (pkg/api/v1/skills_types.go embeds InstalledSkill directly); migration is transactional, metadata-only, and old binaries tolerate the extra column. One residual risk lives in the follow-up verifier PR, not here: the armored text is the only place the gitsign identity lives, so PR11/12 must verify the signature over the commit object and not trust self-described payload contents.
Architecture (software-architect): layering is right — PGPSignature is commit metadata honestly reported by git.Client, verification logic correctly absent from this layer; the column choice is sound (1:1, never independently queried). Watch-items for PR11: (1) CommitSignature (armored gitsign string, git path) and SigstoreBundle (bytes, OCI path) are different artifacts from different provenance flows — don't pour one into the other; (2) buildInstalledSkill has no seam for the bundle yet — thread it via InstallOptions so git and OCI share one construction point.
Duplication / library-reuse / devex: nothing actionable. All duplication candidates are at the two-site threshold (Rule of Three not met) or essential; the code uses go-git's API properly rather than hand-parsing; empty-string-for-unsigned is conventional; interface addition is a theoretical break but git.Client has only in-tree implementations. Polish notes as inline comments.
Verdict: approve — the commit-subject length is the only hard item; the interface-collapse suggestion is optional but cheapest now.
JAORMX
left a comment
There was a problem hiding this comment.
Inline findings from the panel review (summary in the review above). Only the HeadCommitHash/HeadCommitSignature collapse suggestion rises above polish — everything else is optional.
49bf5c8 to
c1a3b49
Compare
Install-time signature verification (RFC THV-0080) needs two pieces of groundwork: git installs must surface the commit's gitsign signature so it can be verified, and OCI installs must persist the Sigstore bundle captured at install time so sync can re-verify offline without contacting the registry. Collapses git.Client's HeadCommitHash into a new HeadCommit method returning hash and (unverified) signature from a single lookup, so both always describe the same commit; the resolver threads them into ResolveResult. Adds migration 004 with an installed_skills .sigstore_bundle BLOB column (NULL for unsigned installs, so rows created by earlier schemas read back as unsigned) and wires it through the SQLite skill store and InstalledSkill. Part of #5899; consumed by the install-verification and sync re-verification PRs in this stack. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c1a3b49 to
6e8e888
Compare
Note
Stack 2 of RFC THV-0080 (tracking issue #5899) — PR 10 of the Sigstore signing/verification series. Follows #6015 (schema) and #6023 (signer), both merged. This is the core-independent half of the "git + storage" step; the gitsign verification logic lands with the verifier work once stacklok/toolhive-core#192 is released, since it consumes core's exported trusted material.
Summary
Install-time signature verification needs two pieces of groundwork that have no dependency on the toolhive-core release, so they land now to keep the stack moving:
git.Client'sHeadCommitHashis collapsed into a newHeadCommitmethod returning hash and signature from a single lookup (per review — both always describe the same commit), with the signature empty for unsigned commits and explicitly documented as UNVERIFIED until the install-time verifier checks it.gitresolver.ResolveResultgainsCommitSignaturealongsideCommitHash. Unlike the POC (Add project-level skills lock file (POC) #5715), the lookup error is propagated, not silently swallowed.004_add_skill_sigstore_bundle.sqladdsinstalled_skills.sigstore_bundle BLOB DEFAULT NULL;InstalledSkill.SigstoreBundle []byte(never serialized to API responses,json:"-") is wired through the SQLite store's Create/Update/scan paths. NULL means unsigned, so rows created by earlier schema versions read back as unsigned rather than acquiring phantom provenance. This is what letssyncre-verify signatures offline later in the stack, without re-contacting the registry.Type of change
Test plan
task test)task lint-fix, 0 issues)Does this introduce a user-facing change?
No — nothing consumes the new fields yet. Verification behavior arrives with the install-verification PR later in the stack.
Special notes for reviewers
ResolveResult.CommitSignatureandInstalledSkill.SigstoreBundleare wiring points for the install-verification PR (PR11 in the stack), which threads them throughInstallOptionsso git and OCI installs share one construction point.ALTER TABLE ... ADD COLUMNwith a NULL default; Down drops the column).Generated with Claude Code