Skip to content

About

Claude Code plug-in that converts GitHub issues into working code with cryptographically signed provenance bundles

Resources

Stars

2 stars

Watchers

0 watching

Forks

Repository files navigation

ForgeProof

CI GitHub Action: forgeproof-verify

Turn GitHub issues into working code with cryptographically signed provenance bundles.

When you invoke ForgeProof, Claude reads a GitHub issue, extracts requirements, plans an implementation, writes code and tests, then packages everything into a tamper-evident .rpack bundle. The bundle proves what was done, why, and that nothing was altered after signing. From v1.3.0 every bundle also carries a standards-conformant in-toto/SLSA attestation (a DSSE-signed Sigstore bundle, including the human plan approval and the AI builder identity) that verifies with plain cosign — no ForgeProof code required.

Install

In Claude Code:

/plugin marketplace add anthropics/claude-plugins-community
/plugin install forgeproof@claude-community
/reload-plugins

Or browse interactively: run /plugin, go to the Discover tab, search for forgeproof, press Enter, and choose your install scope.

Prefer the CLI? Same commands without the leading slash:

claude plugin marketplace add anthropics/claude-plugins-community
claude plugin install forgeproof@claude-community

Requirements

  • Python 3.11+ (stdlib only — no pip dependencies). Either python3 or python on PATH works; the plugin detects which one you have.
  • OpenSSH 8.1+ (provides ssh-keygen for Ed25519 signing). Included on macOS and Linux. On Windows it ships as the "OpenSSH Client" optional feature (present by default on Windows 10+, occasionally disabled — enable it under Settings → System → Optional features, or install Git for Windows, which bundles it).
  • GitHub CLI (gh) authenticated to your account — install

ForgeProof checks all of this for you at the start of every /forgeproof:run (the preflight step) and tells you exactly what is missing.

Upgrading from 1.0.x

v1.1.0 renames the command surface so slash commands read naturally. Old names are removed, not aliased:

v1.0.x v1.1.0
/forgeproof <issue> /forgeproof:run <issue>
/forgeproof-push /forgeproof:push
/forgeproof-verify <path> /forgeproof:verify <path>
/forgeproof-reset <issue|--all> /forgeproof:reset <issue|--all>

No state migration is needed: the .forgeproof/ directory layout is unchanged, and every bundle ever signed by any v1.0.x, v1.1.x, v1.2.x, or v1.3.x release still verifies — that is a permanent compatibility promise, enforced in CI by four frozen fixture bundles generated by the unmodified v1.0.1, v1.1.0, v1.2.2, and v1.3.0 engines.

Supported Languages

ForgeProof auto-detects your project's language and toolchain:

Language Config file Test runner Linter
Python pyproject.toml, setup.cfg, setup.py, requirements.txt pytest ruff, flake8
TypeScript/JavaScript package.json jest, vitest, mocha eslint
Go go.mod go test golangci-lint

Usage

Generate code from an issue

/forgeproof:run 42

Runs the full pipeline: fetch issue → extract requirements → plan → generate code → run tests → sign .rpack bundle. You'll be asked to approve the plan before code generation begins.

Browse your assigned issues instead:

/forgeproof:run

Push to a PR

/forgeproof:push

Creates a git branch and opens a pull request with the provenance summary embedded in the PR description.

Verify a bundle

/forgeproof:verify .forgeproof/issue-42.rpack

Checks the Ed25519 signature, hash chain integrity, artifact hashes, and (for v1.3.0+ bundles) the embedded attestation — its DSSE signature under the bundle's own key and its signed subjects against the bundle's artifacts. Reports whether the bundle has been tampered with. The JSON output separates two verdicts: verified (integrity — nothing that could be checked was altered) and complete (completeness — the chain and every recorded artifact were actually found). Add --strict to turn missing evidence into failures (recommended in the origin repo or CI), and --format markdown for a full human-readable audit report.

Requires Python 3.11+ and ssh-keygen (for the SSHSIG check) — nothing else.

Verify with cosign (optional — interop only)

Bundles from v1.3.0 export their attestation as .forgeproof/issue-<N>.sigstore.json with the key as .forgeproof/issue-<N>.pub.pem, and anyone can verify it with plain cosign — no ForgeProof code present:

cosign verify-blob-attestation --key .forgeproof/issue-42.pub.pem \
  --bundle .forgeproof/issue-42.sigstore.json \
  --type slsaprovenance1 --insecure-ignore-tlog <artifact-path>

This is an additional view for ecosystems that already speak in-toto/SLSA; baseline verification never requires cosign and it is deliberately not part of the requirements or preflight. Details, pinned tested versions (v2.6.5 and v3.1.3), and pitfalls: docs/cosign-interop.md. What the attestation does and does not claim: docs/compliance-mapping.md.

Verify on every PR

The companion forgeproof-verify GitHub Action verifies the bundle in a checked-out pull request: strict verification, an audit report in the job summary and as a PR comment, and a red check on tamper or missing evidence. This repo dogfoods it.

Excerpt from this repo's own workflow — full file: verify-provenance.yml, setup recipe: docs/branch-protection.md:

jobs:
  verify:
    name: forgeproof-verify   # the name to require in branch protection
    runs-on: ubuntu-latest
    if: startsWith(github.head_ref, 'forgeproof/')
    steps:
      - uses: actions/checkout@v4
      - uses: ryanjmichie-git/forgeproof-verify@6101dd74e10d94fb28b1e25f7435eda070b052ca # v1.1.0

With the head-branch filter, human PRs skip the check (skipped counts as satisfied) while forgeproof/* branches must verify. Full setup — rulesets, classic branch protection, fork-PR behavior, strict-vs-lenient guidance — in docs/branch-protection.md.

Clean up state

/forgeproof:reset 42

Removes provenance chains, bundles, ephemeral keys, and branches for a specific issue. Use --all to clean everything.

Re-running on the same issue

ForgeProof handles re-runs gracefully. Running /forgeproof:run 42 again will:

  • Clean up the previous chain and bundle (via --force)
  • Delete and recreate the local branch
  • Push with --force-with-lease if the remote branch exists
  • Update the existing PR instead of creating a duplicate

How It Works

ForgeProof operates in four phases:

  1. Parse & Plan — Fetches the GitHub issue, extracts structured requirements (REQ-1, REQ-2, ...), scans the repo, and proposes a plan. Waits for your approval.
  2. Generate — Writes implementation and tests. Every file edit and decision is logged to a SHA-256 hash chain with Ed25519 signatures.
  3. Evaluate — Runs your project's test suite and linter. Maps results back to requirements. Attempts one auto-fix if something fails.
  4. Package — Builds the .rpack provenance bundle: manifest, artifact hashes, requirement coverage, decision log, and a root Ed25519 signature. The ephemeral private key is deleted after signing.

The .rpack Bundle

The .rpack file is a JSON document containing:

  • Issue metadata — number, title, URL
  • Requirements — extracted from the issue, with coverage status
  • Artifacts — every file created or modified, with SHA-256 hashes
  • Decisions — why Claude chose each approach
  • Evaluation — test results, lint results, coverage percentage
  • Attestation (v1.3.0+) — an in-toto Statement v1 / SLSA Provenance v1 predicate in a DSSE-signed Sigstore bundle: the artifacts as signed subjects, human approval events (agent-recorded), and the builder identity (model id self-reported, Claude Code version measured, plugin version). Also exported beside the bundle as issue-<N>.sigstore.json + issue-<N>.pub.pem for cosign
  • Signature — Ed25519 signature over a root digest of all the above (the embedded attestation included)

The evaluation status is one of:

  • pass — all requirements covered, all tests pass
  • partial — some requirements uncovered or tests failing (details included)
  • fail — critical failures

Bundles are always produced regardless of status. The status tells reviewers whether to trust the bundle at a glance.

Format identity

The format has a normative specification, skills/run/references/rpack-format.md, and a media type name: application/vnd.forgeproof.rpack+json (vendor tree; status: docs/media-type.md). A name is not a trust signal, and it is never written into a bundle — format and version remain the in-band identity.

A JSON Schema (2020-12) for format 1.x ships at schemas/forgeproof-rpack-1.schema.json. Its immutable, tag-pinned $id is https://raw.githubusercontent.com/ryanjmichie-git/forgeproof-plugin/v1.4.0/schemas/forgeproof-rpack-1.schema.json. It describes the specification's member table and allows unknown members at every level. It is for editors and tooling: verify never reads it.

GitHub renders a .rpack as JSON, in the file view and in diffs, if your repository's .gitattributes carries this line:

*.rpack linguist-language=JSON

Optional and cosmetic: it changes how GitHub highlights the file; verification is unaffected either way, and no -text is needed for bundles.

Detection files for the desktop MIME database and for file(1) ship under share/. ForgeProof never installs them, and never writes .gitattributes or editor settings into your repository. The install recipes, the editor association snippet, and the known limits of detection are in docs/media-type.md.

Security Model

  • Ephemeral keys — a new Ed25519 keypair is generated per bundle. The private key is deleted after signing. The public key is embedded in the .rpack for self-contained verification.
  • Tamper evidence — modifying any field in the bundle, any block in the chain, or any artifact file causes verification to fail.
  • No external data transmission — all data stays local. ForgeProof only calls gh CLI (which uses your existing GitHub auth) and ssh-keygen.

Privacy

ForgeProof stores provenance data locally in the .forgeproof/ directory at your project root. No data is sent to external servers beyond what gh CLI sends to GitHub (issue reads, PR creation). No telemetry, no analytics, no third-party services.

Troubleshooting

"No chain found for issue N" — Run /forgeproof:run N first to initialize the chain.

"No ephemeral key found" — The key is session-scoped. If you initialized the chain in a previous session, you'll need to re-run /forgeproof:run N to generate a new key.

"ssh-keygen failed" — Ensure OpenSSH 8.1+ is installed. On macOS, the built-in ssh-keygen works. On Linux, install openssh-client. On Windows, enable the "OpenSSH Client" optional feature or install Git for Windows.

"artifact recheck failed" during finalize — A recorded file changed on disk after it was recorded. This is finalize refusing to sign a bundle that doesn't match reality. Record the current state of each named file (record --action file-edit) and finalize again.

"gh issue list failed" — Run gh auth status to check authentication. Run gh auth login if needed.

Verification fails with "Root digest mismatch" — The bundle contents were modified after signing. This is the tamper detection working as intended.

Verification fails with "[strict] Chain file not found" or "[strict] Artifact not found" — Evidence is missing and you asked for --strict, which makes absence a failure instead of a warning. This is expected when verifying a lone bundle cross-repo (the receipt traveled without its source tree) — drop --strict there. It is not tamper: tamper errors say "tampered" or "mismatch".

Verification fails with "Artifact tampered" or a hash mismatch — A recorded file or the chain was modified after signing. Unlike missing evidence, this is always red, in both strict and lenient modes.

Verification warns "Artifact not found" — Normal when verifying a bundle from a different checkout or branch. ForgeProof deliberately treats a missing artifact or chain file as a warning (it cannot be checked here), while a modified one is a hard error (tamper). A .rpack is a portable receipt, so absence means "not present in this checkout," not "altered." When verifying in the origin repo where the files should exist, a missing-artifact warning means your working tree is incomplete.

Known Limitations

  • Recording completeness is prompt-enforced — finalize re-hashes every recorded file and refuses to sign if any no longer matches disk, so a signed bundle is guaranteed to match reality for the files it records, at signing time. That Claude recorded every edit it made is enforced by the skill instructions, not by cryptography. Provenance makes work attributable and tamper-evident; it does not make unrecorded work impossible.
  • Post-rebase commit SHA mismatch — If you rebase a forgeproof branch after finalization, the commit_sha in the bundle no longer matches the branch HEAD. Verification still passes (it checks artifacts and chain integrity, not git commits). Workaround: re-run /forgeproof:run after rebasing.
  • Ephemeral keys are session-scoped — The Ed25519 private key exists only in the system temp directory for the current session. If the session ends before finalization, re-run /forgeproof:run to generate a new key.
  • No .gitignore enforcement — ForgeProof warns if no .gitignore exists but does not create one. Ensure your project has one to avoid committing __pycache__/ and other generated files.

Hooks (Automatic Behavior)

ForgeProof registers two hooks. Honest accounting of what they cost and when they act:

  • PreToolUse (PR gate) — Spawns on every Bash or PowerShell tool call while the plugin is enabled (a fast, read-only Python process that exits immediately for anything that isn't gh pr create). When the command is gh pr create and no signed .rpack exists in .forgeproof/, it blocks the call and tells Claude to run /forgeproof:run first. Both shell tools are gated — covering Bash alone would let PRs bypass provenance from Windows PowerShell sessions. It blocks through two independent protocols (a structured permission denial on stdout and exit code 2), so the gate fails closed regardless of which one your Claude Code version honors.

  • PostToolUse (lint feedback) — Spawns after each Edit/Write and exits silently unless an active ForgeProof chain exists (.forgeproof/chain-*.json). During an active run it lints only the edited file (never the whole project) and surfaces up to 20 lines of findings to Claude as context. It never blocks an edit.

Both hooks are registered twice, once for python3 and once for python, so whichever interpreter your system has delivers the verdict — a missing interpreter produces harmless spawn noise, never a silently disabled gate. On systems with both interpreters the hooks run twice; both are read-only and idempotent, so the duplicate is cosmetic.

Testing & Validation

ForgeProof includes 311 automated tests covering:

  • Utility functions (SHA-256, canonical JSON determinism)
  • Cryptographic primitives — the pure-stdlib RFC 8032 Ed25519 implementation is locked to the RFC's own test vectors and rejects malleable or non-canonical hostile encodings; DSSE/Sigstore encoding has golden-string tests
  • Chain operations (hash linkage, block structure, save/load)
  • All subcommands (init, record, finalize, verify, detect, summary, reset, lint-hook, gate-pr)
  • Attestation emission and verification — byte-exact sidecars, a per-invariant tamper matrix, and hostile-input shapes that must fail cleanly
  • Backward compatibility — four frozen fixture bundles, generated by the unmodified v1.0.1, v1.1.0, v1.2.2, and v1.3.0 engines, must verify forever — with zero warnings, in strict mode included
  • Hook configuration — the exact configured hook commands are spawned against block/allow scenarios, so a never-fires misconfiguration fails CI loudly
  • Skill contract — every engine invocation documented in the skills is parsed against the real CLI, so a stale example fails CI instead of breaking a run
  • End-to-end integration (full pipeline with real Ed25519 signing and tamper detection)

CI runs the suite on Ubuntu, macOS, and Windows (both Git Bash and cmd.exe), plus a python3-only Debian container that has no python command at all. A separate cosign-interop job builds a real bundle with the actual engine and verifies its attestation with two exactly-pinned cosign binaries (v2.6.5 and v3.1.3) — cosign hashing the artifact files itself — and proves the tampered-artifact and tampered-envelope cases fail.

Run the test suite:

python -m pytest skills/run/scripts/test_forgeproof.py -v

Plugin validation (validate the plugin manifest explicitly — pointing validate at the repo root triggers marketplace validation instead, because .claude-plugin/marketplace.json exists):

claude plugin validate .claude-plugin/plugin.json

The plugin was validated end-to-end across 4 GitHub issues on a real Python project, covering bug fixes, feature additions, search functionality, and JSON serialization. All provenance bundles were verified with /forgeproof:verify.

Changelog

See CHANGELOG.md for version history.

About

Claude Code plug-in that converts GitHub issues into working code with cryptographically signed provenance bundles

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages