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
Plugin install-time verification and offline re-verification in sync --check need groundwork that has to land before the verifier does: OCI installs must be able to persist the Sigstore bundle captured at install time so sync can re-check it without contacting the registry, and git installs must surface the resolved commit's gitsign signature and the payload it covers so the verifier has something to check. Adds migration 006 with an installed_plugins.sigstore_bundle BLOB column (NULL for unsigned installs, so rows created by earlier schemas read back as unsigned) and wires it through the SQLite plugin store and InstalledPlugin. cloneAndCollectPlugin now returns the whole git.HeadCommit instead of just the hash, so the signature and payload travel with the commit they describe rather than being looked up again. Nothing verifies yet — the values are carried, not consumed. Mirrors what #6084 did for skills. Part of #6300. Signed-off-by: Samuele Verzi <samu@stacklok.com>
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