Zero Trust Supply Chain · SBOM · Sigstore

forgeseal

Supply chain security toolkit: CycloneDX SBOMs from 16 lockfile formats, keyless Sigstore signing, SLSA provenance, and OpenVEX, built for EU CRA compliance.

Released

View on GitHub ↗

In one line. forgeseal generates a tamper-evident record of every dependency, who built the artifact, and how, so a consumer can verify a release was built from the source it claims, before trusting it.

Most software you run was assembled from hundreds of pieces you never inspected. When one of those pieces is swapped for a malicious version, the way it happened in the SolarWinds and xz-utils incidents, there is usually no way for the person installing the software to notice. forgeseal closes that gap: it attaches a signed, verifiable "bill of materials and origin" to a build, so tampering becomes detectable instead of silent.

Before vs. after a signed build recordBEFOREA releaseyou downloadYou install it,no way to checktrust blindlyvs.AFTERSame release +signed recordYou verify itbefore trustingcheck the proof Figure 1. Left: a release you have to trust blindly. Right: the same release with a signed record you can independently verify.


Terms in 30 seconds

Domain engineers: skip ahead, this is orientation for everyone else.

  • SBOM (Software Bill of Materials): a machine-readable list of every component and dependency in a piece of software. (CISA)
  • CycloneDX: a widely-used SBOM format, now the Ecma ECMA-424 standard, maintained by OWASP and Ecma TC54. (cyclonedx.org)
  • SLSA v1.2 (Supply-chain Levels for Software Artifacts): an OpenSSF framework of levels for build and source integrity. (slsa.dev)
  • Sigstore: keyless signing: you sign artifacts with a short-lived identity instead of a long-lived private key. (sigstore.dev)
  • VEX / OpenVEX (Vulnerability Exploitability eXchange): a record of which known CVEs are not actually exploitable in your product, to cut scanner noise.
  • EU CRA (Cyber Resilience Act): EU regulation requiring machine-readable SBOMs for products with digital elements; reporting obligations apply from 11 Sep 2026, full applicability 11 Dec 2027 (status as of June 2026). (European Commission)

Why this matters

  • For anyone shipping software: a supply-chain compromise can ship malware to every one of your users at once; a verifiable build record makes that class of attack detectable.
  • For teams selling into the EU: the Cyber Resilience Act makes machine-readable SBOMs mandatory (reporting from 11 Sep 2026, full applicability 11 Dec 2027), with penalties up to €15M or 2.5% of worldwide turnover; forgeseal produces a conformant SBOM as a build step.
  • For open-source maintainers: proving provenance manually means stitching together four different tools; forgeseal makes it one command in CI.

🧭 If you only read this far: forgeseal turns "trust me, this build is clean" into "here is the cryptographic proof, check it yourself."


The problem, for engineers who don't live in this space

Imagine buying a sealed bottle of medicine. The tamper-evident seal does not stop someone from tampering, it makes tampering visible so you do not swallow the pill if the seal is broken. Software has almost no equivalent. You npm install or pip install and run code assembled from a tree of dependencies, any of which could have been altered between the author's machine and yours.

Here is the concrete version. Say you publish a Python package. Your CI builds it, pushes it to PyPI, and users install it. Nothing in that chain produces a signed statement of which exact dependency versions went in, who (which CI identity) built it, or how. If an attacker compromises one transitive dependency or your publish step, the install looks identical to a clean one.

Today's publish flow produces no proofSourcerepoCIbuildRegistry(PyPI / npm)Userinstallno signed record of contents or origin is ever produced Figure 2. The current publish flow. There is no point at which a verifiable record of contents and origin is produced (red gap).

Why this is genuinely hard. The four artifacts you would need, a dependency inventory, a signature, a provenance statement, and a vulnerability-exception record, come from four different ecosystems with four different formats, and keeping them consistent across language stacks is where most efforts stall.


What exists today (and the gap)

ToolSBOM (CycloneDX)Keyless signingSLSA provenanceVEXUnified across 16 lockfiles
Syft✅❌❌❌⚠️
cosign❌✅⚠️❌✅
slsa-github-generator❌⚠️✅❌⚠️
forgeseal✅✅✅✅✅

No existing tool produces all four supply-chain artifacts, inventory, signature, provenance, and vulnerability-exception record, from a single command with consistent output across the major language ecosystems. That unified surface, not any one artifact, is the contribution.


How it works

forgeseal wraps four established standards behind one CLI, driven by forgeseal pipeline. It has four parts:

  • Inventory: emits a CycloneDX SBOM that enumerates every resolved dependency, parsed natively from 16 lockfile formats (npm, Yarn classic and Berry, pnpm, Bun, pip, Poetry, PDM, uv, Go modules, Cargo, Gradle).
  • Signer: produces a Sigstore keyless signature tied to a workload identity, written as a .sigstore.json bundle.
  • Provenance: emits a SLSA v1 in-toto attestation (.intoto.jsonl) describing the build.
  • Exceptions: triages CVEs against OSV.dev and emits an OpenVEX document recording known-but-not-applicable findings.

One CLI, four standard artifacts, one signed bundleLockfiles16 formatsforgesealdetect + parseCycloneDX SBOMSigstore signatureSLSA v1.2 provenanceOpenVEX exceptionssigned bundlesealed Figure 3. One CLI, four standard artifacts, bound to a single signed bundle.

Inventory

Each lockfile format implements a small parser interface (Parse() plus type metadata); detection follows a priority hierarchy, with Bun's binary lockfile taking precedence. Package URLs follow the spec, npm scoped packages strip the @ prefix, PyPI names get PEP 503 normalization, so the CycloneDX output is consistent regardless of which stack produced it. Doing this natively (rather than shelling out to an external scanner) is what keeps output uniform across ecosystems.

Signer

Uses Sigstore's keyless flow, no long-lived private key to leak; the signature is bound to a short-lived Fulcio identity proven via OIDC and recorded in the Rekor transparency log. This is what makes the record trustworthy without key-management overhead.

Provenance & Exceptions

SLSA v1 records how the artifact was built; the VEX triage batches package URLs in groups of 1,000 against OSV.dev's batch API and classifies findings by CVSS severity, so downstream scanners do not drown maintainers in vulnerabilities that are present but not exploitable.


The decisions that actually mattered

Native lockfile parsing vs. shelling out to existing scanners

The fork. Reuse Syft/Trivy as subprocesses, or parse each ecosystem's lockfile directly. Options. External scanner vs. native parser interface. Chosen. Native parsing. Trade-off accepted. More code to maintain per ecosystem, in exchange for consistent CycloneDX output and no external binary dependencies, which matters because some lockfiles (e.g. pdm.lock, uv.lock) were unsupported or inconsistent across Syft, Trivy, and cdxgen at the time.

One bundle vs. four loose files

The fork. Emit four artifacts separately, or bind them into one verifiable bundle. Chosen. A single signed bundle. Trade-off accepted. A custom bundle layout to document, in exchange for atomic verification, you cannot end up with a signature that matches a different SBOM than the one you are inspecting.


Why this is new, and why it's significant

What's novel. forgeseal is, to my knowledge, the first single-command tool to unify CycloneDX SBOM, Sigstore keyless signing, SLSA v1 provenance, and OpenVEX with consistent output across 16 lockfile formats spanning JS/TS, Python, Go, Rust, and Java, including formats (uv, PDM, Bun) that existing scanners did not reliably support.

Why it's significant to the field. Software supply-chain integrity is an actively-standardized open problem: CycloneDX (now Ecma ECMA-424, via TC54), SLSA, and Sigstore all exist precisely because no unified, verifiable build record was standard practice. That significance is now regulatory, not just cultural, the EU CRA makes machine-readable SBOMs mandatory from 2026–2027, so a tool that makes producing all four artifacts a single CI step lowers the activation cost of a control the ecosystem, and now the law, is pushing toward default.

Standards & ecosystem alignment. This work implements:

  • CycloneDX (ECMA-424): SBOM output conforms to the CycloneDX schema.
  • Sigstore (OpenSSF Graduated): keyless signing via Fulcio short-lived certs plus the Rekor transparency log.
  • SLSA v1.2: provenance statements follow the v1 in-toto predicate.
  • OpenVEX: exploitability statements in the OpenVEX (JSON-LD) format.

📌 Honest scope. forgeseal is validated across its supported ecosystems in CI; it has not yet been independently adopted at scale, and the bundle layout has not been proposed to a standards body. The novelty claim is about unification and coverage, not about inventing any underlying primitive.


See it run

go install github.com/sns45/forgeseal/cmd/forgeseal@latest
forgeseal pipeline --provenance --vex
✓ sbom        cyclonedx   sbom.cdx.json                    (142 components)
✓ signature   sigstore    sbom.cdx.json.sigstore.json      # ← keyless, no stored key
✓ provenance  slsa-v1     sbom.cdx.json.intoto.jsonl       builder=github-actions
✓ vex         openvex     vex.json                         (triaged against OSV.dev)

seal → verify, end to endCI buildforgesealSigstoreConsumer1. artifact + lockfiles2. request cert3. short-lived id4. signed bundle5. verify in log Figure 5. end-to-end seal-then-verify sequence, actor by actor.

🔬 Go deeper. The four subcommands (sbom, sign, attest, vex) compose under pipeline; forgeseal verify validates a bundle independently. (Pin to a specific commit SHA when you link the source.)


What's next

Near-term: extend native lockfile coverage to the remaining JVM build tools. forgeseal is the first piece of a three-part arc, it proves what code is running; svidmint proves who is running it, and assayward decides whether to let it run. Together they aim to make verifiable, identity-bound deployment a default rather than an expert exercise.


Appendix / references