A reusable, profile-extensible SLSA provenance builder foundation.
- Purpose and goals
- How it works
- Why should I choose slsa-builder?
- Features
- Security and trust model
- Project status and scope
- Spread the word
- Specifications and ADRs
- Development setup
- Contributors
- License
slsa-builder is a clean, modern, profile-extensible foundation for trusted SLSA provenance builders with a small, auditable trusted computing base (TCB). As a spiritual successor to slsa-github-generator, it carries on the original intent and philosophy without inheriting the legacy surface. Its goal is to freshly implement and support SLSA Build L3+ release workflows for diverse language and registry ecosystems, against the latest SLSA v1.2 specification.
slsa-builder started as a project to meet Windlass's supply-chain security policies, standards, and goals, and it can be broadly useful beyond Windlass to all of the target users described below.
SLSA (Supply-chain Levels for Software Artifacts, "salsa") is a security framework: a checklist of standards and controls to prevent tampering, improve integrity, and protect packages and infrastructure. It is a means of securing the highest feasible resilience at every step of the supply chain, going beyond merely being "safe enough."
Supply-chain Levels for Software Artifacts, or SLSA (“salsa”), is a set of incrementally adoptable guidelines for supply chain security, established by industry consensus. The specification set by SLSA is useful for both software producers and consumers: producers can follow SLSA’s guidelines to make their software supply chain more secure, and consumers can use SLSA to make decisions about whether to trust a software package.
SLSA is described in terms of tracks and levels. Each SLSA track focuses on a particular aspect of the supply chain; as of v1.2 there is a Build track and a Source track.
Within each track, a higher level means a stronger security posture. Higher levels guarantee stronger defense against supply-chain threats, at a higher implementation cost. Lower SLSA levels are designed for easy adoption but provide limited guarantees. "SLSA 0" is sometimes used to describe software that does not yet meet any SLSA level. The SLSA build track currently spans Build L1 through L3, and the official SLSA site states that higher levels are planned for future revisions.
Combining tracks and levels makes it easy to state whether software meets a specific set of requirements. Saying that an artifact meets SLSA Build L3 means that the software artifact was built in accordance with a set of security practices that industry experts recognize as effective at preventing specific supply-chain compromises.
Provenance is metadata about how a software artifact was produced. It can include information about the source code used, the build system, and the build steps — and even who initiated the build and why. Provenance can be used to judge the authenticity and trustworthiness of the software artifacts you use.
SLSA defines a provenance format for recording this metadata.
slsa-builder is a tooling foundation for building and distributing software artifacts in a way that
meets SLSA Build L3, and that includes automatically issuing and distributing appropriate provenance
under the in-toto attestation framework and the
SLSA build provenance v1 format("predicateType": "https://slsa.dev/provenance/v1").
Caution
As the Mini Shai-Hulud supply-chain attack of May 11, 12026 shows, provenance and signatures are necessary conditions for safety, not sufficient ones. Provenance is only one component of the SLSA framework. If the build platform that builds a software artifact and issues its provenance is itself compromised by an attacker, compromised packages can end up being distributed with cryptographically "validly signed" provenance. The packages compromised in the Mini Shai-Hulud incident were using npm's OIDC Trusted Publishing and built-in provenance, which correspond to SLSA Build L2, but the build-environment isolation corresponding to SLSA Build L3 was not in place.
SLSA Build L2 is by no means meaningless — it is far safer than L0 or L1 — but there is a meaningful gap between Build L2 and L3, and it is worth understanding exactly what that gap is and the limits it implies. The following articles may help:
As the official SLSA overview states, SLSA targets software producers, consumers, and infrastructure providers, and it broadly helps anyone who produces, supplies, or distributes packages. For producers, it offers protection against supply-chain tampering, reduced insider risk, and assurance that software reaches consumers as intended — along with a common vocabulary for communication, an actionable checklist, a measure of SSDF alignment, and clearer shared expectations between suppliers and consumers.
slsa-builder can help this same broad SLSA audience, and it is especially useful if one or more of the following applies to you:
- Repository maintainers, release workflow authors, and package publishers.
- Advanced users who compose workflows from producer and publisher primitives.
- Downstream verifiers who use the release manifest, verification policy, and fixtures.
A thin trusted core works together with profile-owned
reusable GitHub Actions workflows.
Producer and publisher profiles — and, within a profile, each task (build, sign, publish, and so on)
— run isolated with distinct permissions. Producers build artifacts and SLSA provenance, then hand
them off to a publisher, which verifies and distributes them. The handoff includes digest and
provenance verification, and the procedure aborts immediately if a problem is detected. A signed
release manifest maps release versions to workflow SHAs, builder.id values, and buildType URIs.
The exact, observable behavior of each contract outlined in this section is defined in the following technical specifications:
- Core profile contract: the boundary between the thin trusted core and profile-owned reusable workflows.
- Identity and build types:
builder.id,buildTypeURIs, and release-metadata linkage. - SLSA provenance v1: the common in-toto Statement and SLSA v1 predicate contract.
- Composed workflow internal handoff: producer-to-publisher handoff within a single run.
- GitHub Release asset publisher: the publisher contract that distributes only verified bytes.
- Release manifest: the signed release manifest and its three-job signing boundary.
slsa-builder aims to meaningfully lower the barrier to adopting the SLSA security framework for members of diverse language and package-registry ecosystems, and to contribute to ecosystem supply-chain security by more broadly disseminating and encouraging SLSA Build L3+ package-distribution practices (see ADR 0001 and ADR 0002). Other existing tools remain useful within their intended scopes, but slsa-builder's strength is providing one integrated, profile-owned trust contract — spanning build, provenance, publishing, distribution, and verification — with a low adoption barrier.
The SLSA get-started guide recommends starting at the highest feasible level from the outset to avoid unnecessary rework. On the GitHub Actions platform, SLSA Build L3 is achievable with a properly designed and configured trust boundary, but existing alternatives have the following limitations.
slsa-github-generatorandslsa-verifier(maintenance discontinued):- Starting with the
first public release in June 12022,
the SLSA framework team developed and maintained
slsa-github-generatorandslsa-verifierfor roughly three years, and for a long time they contributed greatly to lowering the barrier to SLSA Build L3 adoption. - However, due to a
design limitation of
slsa-verifier, the builders provided byslsa-github-generatorcannot be referenced by digest; only tag-version references in the@vX.Y.Zform are possible. This contradicts commonly accepted security best practices and Windlass's own security guidance.- See: ADR 0028
- Crucially, these projects were effectively unmaintained from around July 12025 — after
slsa-github-generatorv2.1.0 andslsa-verifierv2.7.1 — and maintenance was officially discontinued on August 7, 12026. - The latest SLSA specification version is v1.2, but the provenance format supported by slsa-github-generator has not been updated since v0.2. Using these tools is therefore no longer recommended.
- slsa-github-generator does not support
workflow_dispatch-driven releases: a dispatch run cannot carry the caller-selected release target tag into provenance, so that information is not recorded, and artifacts built by dispatch therefore failslsa-verifier --source-tagverification. The verifier documented--source-tagsupport as limited to tag and release triggers. The tracking issue, slsa-github-generator#1947, remained open and unresolved through the end of maintenance. This left no supported fixed-pipeline retry path: a release that failed because of a pipeline defect could not be re-run from a fixed caller workflow without moving or recreating the tag.- See: ADR 0079, ADR 0080, osv-scanner#632
- Starting with the
first public release in June 12022,
the SLSA framework team developed and maintained
- GitHub
actions/attest:- GitHub Artifact Attestations — the
attestaction — make it possible to build and distribute packages on the GitHub Actions platform while meeting SLSA Build L3 requirements. - GitHub Artifact Attestations automatically handle work such as provenance issuance and signing backed by a Sigstore instance.
- However, using GitHub Artifact Attestations on GitHub-hosted runners alone achieves only up to
SLSA Build L2. Meeting Build L3's
provenance-unforgeable
and isolated requirements additionally
requires setting up a dedicated
reusable GitHub Actions workflow
for building and signing, so that the build and distribution process runs in an isolated
environment in a repository different from the caller workflow. This is not solved by the
attestaction alone, and it is a factor that comparatively raises the barrier to adoption. - In addition, the
attestaction supports only SHA-256 digest output in Provenance mode. This is fine in many cases, but for package managers and registries like npm that accept only SHA-512 input, using--provenance-fileor similar options may not be possible.
- GitHub Artifact Attestations — the
- Low-barrier SLSA adoption meeting Build L3 requirements: As covered above, meeting Build L3's
provenance-forgery-resistance and build-isolation requirements calls for an isolated reusable
workflow in a repository separate from the target package, and designing and maintaining that
trust boundary yourself can feel like a burden for each repository maintainer. slsa-builder
designs this boundary for you as profile-owned reusable workflows, offered as a paved road to SLSA
Build L3. From a caller workflow, a
uses:reference and a handful of inputs such aspackage-directorybring build, provenance issuance, signing, publishing, and verification in as a single contract. Workflow references can be pinned to commit SHAs instead of tags — avoiding the tag-based-reference limitation noted earlier — and provenance follows the latest SLSA v1.2 specification. The goal is to carry forward, on top of the current specification, the low-barrier model that slsa-github-generator demonstrated on GitHub (ADR 0002, ADR 0003, ADR 0023, ADR 0028; spec: JS/TS npm package profile). - Minimized trust surface: Rather than inheriting slsa-github-generator's broad legacy and BYOB framework surface, slsa-builder chose a completely fresh start and a smaller, deliberately selected trust surface (ADR 0001, ADR 0002, ADR 0003; spec: Core profile contract).
- Canonical provenance semantics: slsa-builder records the profile's
builder.id,buildType,externalParameters, subject, digest, publish, and verification semantics, and assembles and signs exact Statement bytes with a Go-native Sigstore DSSE signer. A single subject can also carry both SHA-256 and SHA-512 digests over the same tarball bytes, which provides better compatibility than tools that support only a single digest output per subject (ADR 0029, ADR 0042, ADR 0064, ADR 0077; spec: SLSA provenance v1, Identity and build types). - End-to-end release trust: Strict signed-JSON handling, immutable builder and source binding, Rekor-backed offline verification with a governed trust root, a signed release manifest, provenance-gated release-asset publishing, and controlled release mutation — all connected end-to-end from source build to release distribution (ADR 0031, ADR 0049, ADR 0050, ADR 0051, ADR 0053, ADR 0061, ADR 0066, ADR 0067, ADR 0068, ADR 0069, ADR 0072, ADR 0073, ADR 0074, ADR 0075, ADR 0076; spec: Release manifest, GitHub Release asset publisher, Verification policy and fixtures).
- Improved flexibility in release-target tag selection and retry: A release that fails because
of a pipeline defect can be retried by dispatching from the ref that carries the fixed pipeline
(for example
main), while the built and attested content remains the signed release tag. The optional, tags-onlysource-refinput keeps provenance anchored to the tag's commit, and the dispatch ref is recorded separately as auditable invocation context — closing a gap the predecessor ecosystem never shipped (ADR 0079, ADR 0080; spec: JS/TS npm package profile).
slsa-builder currently provides issuance, distribution, and verification of SLSA provenance for JS/TS packages, GitHub Releases, and npm. Choose the profile that matches your ecosystem and distribution target, and reference it from a caller workflow. Support for additional ecosystems and distribution targets will continue to be added over time.
| Ecosystem | Profile | Description | Status |
|---|---|---|---|
| JS/TS npm | JS/TS npm package profile | npm package build, SLSA v1 provenance issuance and signing, npm publish | Pre-release |
- Exactly one package per run: Select the target package with the required
package-directoryinput. The public contract consists of this one required input and eight optional inputs. - Fixed-pipeline release retry: The optional, tags-only
source-refinput lets a caller retry a failed release by dispatching from a ref that carries the fixed pipeline (for examplemain) while the built and attested content remains the signed release tag — no retagging, no weakened provenance claim. Provenance records the built tag identity, and the dispatch ref is recorded separately as invocation context (see ADR 0079 and ADR 0080). - Manifest-first package manager selection: Supports npm, pnpm, and Yarn Berry v4+ through Corepack, and runs build scripts only when declared (see JS/TS npm build and pack).
- Secretless trusted publishing: Authenticates with npm OIDC trusted publishing, so no long-lived publish secrets are needed. The SLSA v1 provenance slsa-builder generates carries both SHA-512 and SHA-256 digests of the same tarball bytes in a single npm Package URL subject, is signed with the Go-native Sigstore DSSE signer, and is published through a three-job publish graph (see JS/TS npm provenance and publish).
Uploads the built tarball and its provenance sidecar (tarball.intoto.jsonl) to an existing GitHub
Release after digest verification (see
npm-to-release-asset composition).
For downstream verifiers, slsa-builder provides a verification policy schema, a fixture taxonomy, and reference commands. The initial profiles do not include a standalone verifier CLI (see Verification policy and fixtures).
slsa-builder's trust model starts from the premise that "signed provenance is a necessary condition for safety, not a sufficient one". This section summarizes which threats slsa-builder defends against, which external parties it must trust, and where the limits of its defense lie.
- No long-lived secrets: npm publish authentication uses OIDC trusted publishing, so no publish tokens need to be stored in the repository (ADR 0024).
- Keyless signing: Signing uses Sigstore (Fulcio short-lived certificates and OIDC workload identity), so the operational burden of issuing, storing, and rotating private keys disappears entirely. Assembling and signing the exact Statement bytes is handled by the Go-native Sigstore DSSE signer (ADR 0077).
- Strict signed bytes: Duplicate members in signed JSON are rejected, preventing parser differentials from splitting the signed byte payload (ADR 0061).
- Immutable identity binding: Verification binds to the
builder.idof a commit-SHA-pinned workflow and to an immutable source identity, not to movable tags (ADR 0068). Source expectations bind to the signed provenance fields, while the signing certificate's platform-fixed source claims authenticate the invocation context — the two stay cryptographically bound even when a dispatch retry builds a tag from a different invocation ref (ADR 0080). - Transparency log and governed trust root: Every signature must be recorded in the Rekor transparency log, and the Sigstore trust root uses a pinned copy governed by the project, enabling offline verification (ADR 0069).
- Dual digests: A single subject carries both SHA-512 and SHA-256 over the same tarball bytes, supporting verification paths that require different digest algorithms (ADR 0064).
- The verification policy schema, fixture taxonomy, and reference commands are defined in Verification policy and fixtures.
- Signed release manifest: A manifest mapping release versions to workflow SHAs,
builder.id, andbuildTypeis signed across a three-job signing boundary (ADR 0031, ADR 0053). - Provenance-gated publishing: The publisher distributes only after verifying the producer's provenance and digests; unverified bytes are never distributed (ADR 0049, ADR 0050, ADR 0051).
- Controlled release mutation: Release mutations are serialized with job-class concurrency, and repeated runs within the same run converge to the same result (ADR 0066, ADR 0067).
slsa-builder places trust in GitHub Actions (hosted runners and the OIDC provider), Sigstore (Fulcio, Rekor), and the npm registry. It satisfies SLSA Build L3's isolation requirement by separating build and signing into reusable workflows in a repository different from the caller workflow (ADR 0028). The project is SLSA Build L3-oriented.
Please report security vulnerabilities through the channel described in the Windlass security policy, not via public issues.
Pre-release. The repository contains a real Go implementation, but the project does not yet claim a stable release; workflow interfaces may change before the first stable version.
Initial non-goals:
- Generic files or containers.
- A standalone verifier CLI.
- Custom-token or PAT release mutation.
- Raw file uploads without producer provenance.
- First publication of a brand-new npm package identity.
- Private dependency credentials as publish credentials.
Whether it is an introduction, an endorsement, or constructive criticism pointing out problems and areas for improvement, the more content — articles, videos, and the like — about slsa-builder and the SLSA framework, the more we can raise awareness of the framework and these tools across the ecosystem and drive adoption. Contributions and feedback are always welcome (contributing guidelines, issue tracker).
There is also a much simpler way to help slsa-builder. If your project uses or supports slsa-builder, spread the word by linking to the project from your README or other project pages. We provide several kinds of badges for inclusion in a README or similar project documentation.
The badges are static SVGs that include the project logo, stored in
assets/badges/. Because this repository serves them directly instead of going
through an external badge service such as shields.io, the full-color logo is preserved and the badge
assets are versioned together with the project. Copy the snippets below as-is, or copy the SVG files
and host them yourself. Replace main in the snippet URLs with a release tag or commit SHA to pin
an immutable reference.
For projects that build and distribute artifacts with slsa-builder.
Markdown:
[](https://github.com/windlasstech/slsa-builder)HTML:
<a href="https://github.com/windlasstech/slsa-builder"
><img
src="https://raw.githubusercontent.com/windlasstech/slsa-builder/main/assets/badges/built-with-slsa-builder.svg"
alt="built with slsa-builder"
/></a>For downstream verifiers that verify releases with slsa-builder's verification policy and fixtures.
Markdown:
[](https://github.com/windlasstech/slsa-builder)HTML:
<a href="https://github.com/windlasstech/slsa-builder"
><img
src="https://raw.githubusercontent.com/windlasstech/slsa-builder/main/assets/badges/verified-with-slsa-builder.svg"
alt="verified with slsa-builder"
/></a>Single-background logo badges for simply pointing to the project, provided in two colors: gray (default) and green.
Markdown:
[](https://github.com/windlasstech/slsa-builder)
[](https://github.com/windlasstech/slsa-builder)HTML:
<a href="https://github.com/windlasstech/slsa-builder"
><img
src="https://raw.githubusercontent.com/windlasstech/slsa-builder/main/assets/badges/slsa-builder.svg"
alt="slsa-builder"
/></a>
<a href="https://github.com/windlasstech/slsa-builder"
><img
src="https://raw.githubusercontent.com/windlasstech/slsa-builder/main/assets/badges/slsa-builder-green.svg"
alt="slsa-builder"
/></a>Each badge is provided in four styles, similar to shields.io. The default flat is provided without
a suffix; the other styles are distinguished by a filename suffix.
The same suffix rule applies to the verified-with-slsa-builder and slsa-builder badges. For the
green variant of the logo badge, the color suffix comes before the style suffix (e.g.,
slsa-builder-green-for-the-badge.svg). For example, a for-the-badge style Markdown snippet looks
like this:
[](https://github.com/windlasstech/slsa-builder)See the architecture index and the ADR index. ADRs record the rationale in MADR format, while architecture specifications define exactly observable behavior.
This repository uses mise to install and pin the development-tool runtime versions. Go is the primary implementation language; Node.js and pnpm are used only for development tooling such as Prettier and Lefthook.
- mise installed
- Git with a configured user name and email
mise install
pnpm installThis installs the pinned versions of Go, Node.js, pnpm, and the CLI tools defined in mise.toml.
Lefthook hooks are installed automatically as a postinstall step when mise installs Lefthook. The
pnpm install step then installs the project-local development dependencies declared in
package.json.
In CI, run mise with locked mode to avoid API calls to registries:
MISE_LOCKED=1 mise install
pnpm installAfter bootstrap, the following commands are available through mise:
go version
node --version
pnpm --version
golangci-lint --version
shellcheck --version
shfmt --version
lefthook --version
actionlint --versionmise installs language runtimes and standalone CLI binaries:
- Go, Node.js, and pnpm
golangci-lint,shellcheck,shfmt,lefthook,actionlint
Go source formatting and import normalization is handled by golangci-lint formatters (gofmt,
goimports), configured in .golangci.yml, rather than by standalone formatter binaries.
pnpm installs Node.js-based development dependencies that are coupled to repository configuration files:
prettier(configured by.prettierrc)markdownlint-cli2(configured by.markdownlint-cli2.jsonc)
Keeping Prettier and markdownlint-cli2 as project-local pnpm dependencies preserves their full
dependency graph in pnpm-lock.yaml and keeps them aligned with editor integrations and the
organization's dependency-review workflow.
Tool versions are declared in mise.toml. A mise.lock file is committed to ensure reproducible
installs across platforms. If you change a tool version in mise.toml, regenerate the lockfile
with:
mise lockThis project requires a Signed-off-by: trailer on every commit (DCO). Lefthook is configured to
enforce this locally, while CI and branch protection perform the authoritative verification.
Thanks to everyone who has contributed to this project. You can find the list of contributors on the GitHub contributors graph.
Distributed under the Apache License 2.0.