From 27b485b5b27c4c41d3a56a9cc52e75737c9a65c4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 00:04:53 +0000 Subject: [PATCH 1/3] Integrate agent-sign into publish action for Sigstore attestation Bake always-further/agent-sign into the publish composite action so marketplace files are automatically signed with Sigstore keyless attestation before PR creation. The .bundle sidecar files are picked up by create-pull-request and included in the generated PR. - Add sign step to publish/action.yml (on by default, SHA-pinned) - Add sign-files input + id-token permission to reusable workflow - Add id-token: write to update-marketplace template - Add test-sign job to test.yml https://claude.ai/code/session_01VeXZGRwCdMMNLA1H4JAW3w --- .github/workflows/agentic-marketplace.yml | 6 ++++ .github/workflows/test.yml | 31 +++++++++++++++++++ agentic-marketplace/publish/action.yml | 15 +++++++++ .../workflows/update-marketplace.yml.template | 1 + 4 files changed, 53 insertions(+) diff --git a/.github/workflows/agentic-marketplace.yml b/.github/workflows/agentic-marketplace.yml index c270a2d..8f4209c 100644 --- a/.github/workflows/agentic-marketplace.yml +++ b/.github/workflows/agentic-marketplace.yml @@ -22,6 +22,10 @@ on: description: 'Version for releases (default: YYYY.MM.DD)' default: '' type: string + sign-files: + description: 'Sign generated marketplace files with Sigstore attestation' + default: true + type: boolean secrets: token: description: 'GitHub token for read-only operations' @@ -83,6 +87,7 @@ jobs: permissions: contents: write pull-requests: write + id-token: write steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -99,6 +104,7 @@ jobs: uses: bitcomplete/bc-github-actions/agentic-marketplace/publish@v1 with: github-token: ${{ secrets.pat }} + sign-files: ${{ inputs.sign-files }} auto-merge: ${{ inputs.auto-merge }} create-opencode-release: ${{ inputs.create-opencode-release }} release-version: ${{ inputs.release-version }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c272e3a..410e567 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,3 +101,34 @@ jobs: echo "ERROR: marketplace.json not generated" exit 1 fi + + test-sign: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Generate marketplace files + working-directory: test-fixtures/valid + run: ../../scripts/dist/discover-components.cjs generate + + - name: Sign marketplace files + uses: always-further/agent-sign@8d70fb7bdcdfb921a1613d0701ca44c7d6c34e6f # v0.0.8 + with: + files: 'test-fixtures/valid/.claude-plugin/marketplace.json' + per-file: 'true' + commit: 'false' + verify: 'false' + + - name: Verify bundle exists + run: | + if [ -f "test-fixtures/valid/.claude-plugin/marketplace.json.bundle" ]; then + echo "✓ Sigstore attestation bundle created" + else + echo "ERROR: No bundle file found" + find test-fixtures/valid -name "*.bundle" 2>/dev/null + exit 1 + fi diff --git a/agentic-marketplace/publish/action.yml b/agentic-marketplace/publish/action.yml index de645fe..5467c26 100644 --- a/agentic-marketplace/publish/action.yml +++ b/agentic-marketplace/publish/action.yml @@ -6,6 +6,10 @@ inputs: github-token: description: 'GitHub token for creating PRs' required: true + sign-files: + description: 'Sign generated marketplace files with Sigstore attestation' + required: false + default: 'true' auto-merge: description: 'Enable auto-merge for generated PR' required: false @@ -33,6 +37,16 @@ outputs: runs: using: 'composite' steps: + - name: Sign marketplace files + if: ${{ inputs.sign-files == 'true' }} + uses: always-further/agent-sign@8d70fb7bdcdfb921a1613d0701ca44c7d6c34e6f # v0.0.8 + with: + files: '.claude-plugin/marketplace.json' + per-file: 'true' + commit: 'false' + upload-artifacts: 'false' + verify: 'false' + - name: Create Pull Request id: create-pr uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0 @@ -56,6 +70,7 @@ runs: **Generated files:** - `.claude-plugin/marketplace.json` - marketplace manifest with unique source paths per plugin - `category/plugin-name/.claude-plugin/plugin.json` - individual plugin metadata + - `.claude-plugin/marketplace.json.bundle` - Sigstore attestation bundle (when signing is enabled) **This PR will auto-merge after CI checks pass.** branch: auto-update-marketplace diff --git a/templates/.github/workflows/update-marketplace.yml.template b/templates/.github/workflows/update-marketplace.yml.template index da27000..12b328d 100644 --- a/templates/.github/workflows/update-marketplace.yml.template +++ b/templates/.github/workflows/update-marketplace.yml.template @@ -10,6 +10,7 @@ on: permissions: contents: write pull-requests: write + id-token: write jobs: update: From 14c9bb8ebdedab81b395c00aeeeecae92b8bbe20 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 00:11:52 +0000 Subject: [PATCH 2/3] Document Sigstore attestation signing and verification Explain what the signatures are, how they work, how users verify them with nono trust verify, what a trust-policy.json looks like, what attacks this prevents, and how to use nono run for runtime enforcement. https://claude.ai/code/session_01VeXZGRwCdMMNLA1H4JAW3w --- README.md | 19 +++++-- agentic-marketplace/README.md | 99 ++++++++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 654bbbb..37baad4 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Automates Claude Code plugin marketplace management through auto-discovery, vali - Auto-discovery of plugins and components - Structure and naming validation - Automatic marketplace.json generation +- Sigstore attestation signing for supply chain security - PR-based workflow with optional auto-merge [View agentic-marketplace action documentation →](agentic-marketplace/README.md) @@ -25,8 +26,9 @@ graph LR A[Push to main] --> B[Discover] B --> C[Validate] C --> D[Generate] - D --> E[Create PR] - E --> F[Auto-merge] + D --> E[Sign] + E --> F[Create PR] + F --> G[Auto-merge] B -->|Finds| B1[Plugins] B -->|Finds| B2[Commands] @@ -38,6 +40,8 @@ graph LR D -->|Updates| D1[marketplace.json] D -->|Updates| D2[Component files] + + E -->|Creates| E1[.bundle attestation] ``` **How it works:** @@ -45,8 +49,9 @@ graph LR 1. **Discover** - Scans your repository for plugins, commands, skills, and other components based on your configuration 2. **Validate** - Checks that all components follow naming conventions, have required metadata, and match your validation rules 3. **Generate** - Creates or updates marketplace.json with all discovered components -4. **Create PR** - Opens a pull request with the changes for review -5. **Auto-merge** - Optionally merges the PR automatically if validation passes +4. **Sign** - Creates a Sigstore attestation bundle for marketplace.json, proving it was built in CI from this repository +5. **Create PR** - Opens a pull request with the changes for review +6. **Auto-merge** - Optionally merges the PR automatically if validation passes This happens automatically on every push to your main branch. No manual JSON editing required. @@ -65,6 +70,11 @@ on: pull_request: branches: [main] +permissions: + contents: write + pull-requests: write + id-token: write # Required for Sigstore attestation signing + jobs: update: uses: bitcomplete/bc-github-actions/.github/workflows/agentic-marketplace.yml@v1 @@ -72,6 +82,7 @@ jobs: config-path: .claude-plugin/generator.config.toml secrets: token: ${{ secrets.GITHUB_TOKEN }} + pat: ${{ secrets.PAT }} ``` Or use individual actions in your own workflow: diff --git a/agentic-marketplace/README.md b/agentic-marketplace/README.md index 2429837..e182253 100644 --- a/agentic-marketplace/README.md +++ b/agentic-marketplace/README.md @@ -4,11 +4,12 @@ Automates Claude Code plugin marketplace management through auto-discovery, vali ## Overview -The agentic-marketplace action provides three composable actions that work together to manage your Claude Code plugin marketplace: +The agentic-marketplace action provides composable actions that work together to manage your Claude Code plugin marketplace: 1. **discover** - Finds plugins, commands, agents, skills, hooks, and MCP servers 2. **validate** - Validates component structure, naming, and metadata -3. **generate** - Creates marketplace.json and plugin.json files, opens PR with auto-merge +3. **generate** - Creates marketplace.json and plugin.json files +4. **publish** - Signs generated files with Sigstore attestation and opens a PR with auto-merge ## Quick Start @@ -25,6 +26,11 @@ on: pull_request: branches: [main] +permissions: + contents: write + pull-requests: write + id-token: write # Required for Sigstore attestation signing + jobs: update: uses: bitcomplete/bc-github-actions/.github/workflows/agentic-marketplace.yml@v1 @@ -32,6 +38,7 @@ jobs: config-path: .claude-plugin/generator.config.toml secrets: token: ${{ secrets.GITHUB_TOKEN }} + pat: ${{ secrets.PAT }} ``` ### Using Individual Actions @@ -144,6 +151,7 @@ Generates marketplace.json and plugin.json files, then creates a pull request wi **Generated files:** - `.claude-plugin/marketplace.json` - Marketplace manifest with all plugins and components +- `.claude-plugin/marketplace.json.bundle` - Sigstore attestation bundle (when signing is enabled) - `category/plugin-name/.claude-plugin/plugin.json` - Individual plugin metadata **Example:** @@ -201,6 +209,7 @@ The marketplace action expects this structure: your-marketplace/ ├── .claude-plugin/ │ ├── marketplace.json # Generated automatically +│ ├── marketplace.json.bundle # Sigstore attestation (generated) │ └── generator.config.toml # Your configuration ├── .github/ │ └── workflows/ @@ -258,6 +267,92 @@ The generate action creates or updates marketplace files: 4. If `auto-merge: true`, enables auto-merge on the PR 5. PR auto-merges when CI checks pass +### Signing and Attestation + +The publish action automatically signs generated marketplace files using [Sigstore](https://sigstore.dev) keyless attestation via the [agent-sign](https://github.com/always-further/agent-sign) action. This creates a cryptographic proof that the file was built in your CI pipeline from your repository — not modified after the fact. + +**What gets signed:** + +- `.claude-plugin/marketplace.json` is signed, producing a `.claude-plugin/marketplace.json.bundle` sidecar file +- The `.bundle` file is included in the auto-generated PR alongside the marketplace manifest + +**How signing works:** + +1. GitHub Actions mints a short-lived OIDC token identifying the workflow, repository, and branch +2. Sigstore's Fulcio CA issues an ephemeral certificate binding that identity to a signing key +3. The marketplace file is signed with that key, producing a [DSSE envelope](https://github.com/secure-systems-lab/dsse) with an [in-toto](https://in-toto.io/) statement +4. The signature is logged in Sigstore's [Rekor](https://docs.sigstore.dev/logging/overview/) transparency log +5. The resulting `.bundle` file contains the signature, certificate, and log inclusion proof — everything needed for offline verification + +No private keys to manage. Identity comes from the CI environment itself. + +**How users verify signatures:** + +Install the [nono CLI](https://github.com/always-further/nono), then verify against a trust policy: + +```bash +nono trust verify --policy trust-policy.json --all +``` + +A trust policy defines who is allowed to sign which files. Example `trust-policy.json`: + +```json +{ + "version": 1, + "includes": [".claude-plugin/marketplace.json"], + "publishers": [ + { + "name": "marketplace CI", + "issuer": "https://token.actions.githubusercontent.com", + "repository": "your-org/your-marketplace", + "workflow": ".github/workflows/agentic-marketplace.yml", + "ref_pattern": "refs/heads/main" + } + ], + "enforcement": "deny" +} +``` + +Verification checks four things: + +1. **Certificate chain** — the signing certificate was issued by Sigstore's CA +2. **Transparency log** — the signature was recorded in Rekor within the certificate's validity window +3. **Signature validity** — the ECDSA signature over the file content is authentic +4. **Publisher identity** — the OIDC claims in the certificate (repository, workflow, branch) match the trust policy + +If any check fails, verification fails. With `"enforcement": "deny"`, files matching the `includes` patterns are rejected unless they have a valid signature from a trusted publisher. + +**What this protects against:** + +- **Tampered marketplace files** — if someone modifies marketplace.json after it was generated in CI, the signature won't match +- **Unauthorized publishing** — only the configured workflow in the configured repository can produce valid signatures +- **Replay attacks** — signatures are timestamped via the transparency log; stale signatures can be rejected +- **Key compromise** — there are no long-lived keys to steal; signing keys are ephemeral and exist only during the CI run + +**Runtime enforcement with nono:** + +For stronger guarantees, run agents through `nono run` which verifies signatures at the kernel level before the agent can read instruction files: + +```bash +nono run --profile claude-code -- claude +``` + +This prevents time-of-check/time-of-use (TOCTOU) attacks where files are swapped between verification and read. + +**Disabling signing:** + +Signing is on by default. To disable it: + +```yaml +jobs: + update: + uses: bitcomplete/bc-github-actions/.github/workflows/agentic-marketplace.yml@v1 + with: + sign-files: false +``` + +When disabled, no `.bundle` files are created and the `id-token: write` permission is unused. + ## Troubleshooting ### No components discovered From 681e7d4c6c315481217f59c0be43ecc3f474f3a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 21 Mar 2026 00:17:32 +0000 Subject: [PATCH 3/3] Add signing FAQ to agentic-marketplace docs Answer common questions: whether nono is required, whether .bundle files break anything, gitignore guidance, id-token permission safety, retroactive verification, and when to disable signing. https://claude.ai/code/session_01VeXZGRwCdMMNLA1H4JAW3w --- agentic-marketplace/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/agentic-marketplace/README.md b/agentic-marketplace/README.md index e182253..0d1f5d9 100644 --- a/agentic-marketplace/README.md +++ b/agentic-marketplace/README.md @@ -432,3 +432,33 @@ Use validation output to implement custom logic: ## Examples See the [main README](../README.md) for complete workflow examples and diagrams. + +## FAQ + +### Do I need nono to use this workflow? + +No. Signing is included by default, but the `.bundle` sidecar files are inert — they sit alongside your marketplace.json and are ignored by everything except nono verification tooling. Claude Code, plugin consumers, and your existing tooling won't read or care about `.bundle` files. You get supply chain security for free if you ever decide to verify later, and zero impact if you don't. + +### Will the `.bundle` files break anything? + +No. A `.bundle` file is a standalone JSON file containing the Sigstore attestation. It doesn't modify marketplace.json or any other file. Tools that don't know about it will ignore it. It's no different from having a `.gitignore` or `LICENSE` file in the directory — present but harmless. + +### Should I add `.bundle` files to `.gitignore`? + +No. The `.bundle` files need to be committed alongside the files they attest to. Verification works by comparing the `.bundle` against the file it signs, so both must be present in the repository. If you `.gitignore` them, you lose the ability to verify later. + +### Do I need the `id-token: write` permission if I'm not using nono? + +The workflow requests this permission to obtain the OIDC token used for keyless signing. It's harmless — the token is scoped to Sigstore's Fulcio CA and can only be used to request a signing certificate. If you disable signing with `sign-files: false`, the permission is unused but still safe to include. + +### Can I start verifying signatures later? + +Yes. Every `.bundle` file committed to your repository is independently verifiable at any point in the future. The Rekor transparency log entry is permanent. You can adopt nono verification whenever you're ready — the signatures will already be there. + +### When should I disable signing? + +Most users should leave signing enabled. Reasons to disable: + +- Your CI environment doesn't support OIDC tokens (self-hosted runners without OIDC configured) +- You have strict policies against external network calls during CI (Sigstore requires reaching Fulcio and Rekor) +- You're running in an air-gapped environment