k2gl / slsa-provenance
SLSA Provenance predicates (v1 and v0.2) for PHP
Requires
- php: >=8.1
- k2gl/in-toto-attestation: ^1.2
Requires (Dev)
- k2gl/composer-attest: ^1.1
- k2gl/composer-license-gate: ^1.0
- k2gl/phpunit-fluent-assertions: ^12
- laravel/pint: ~1.20.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10|^11|^12
README
Typed SLSA Provenance predicates for PHP, both the current v1 and the legacy v0.2 that most real-world bundles still carry. Built on in-toto attestations.
SLSA Provenance describes how an artifact was built — the build definition (inputs)
and the run details (who built it, when, and what came out). This package models that
predicate as typed value objects and plugs it straight into an in-toto Statement, ready
to sign with k2gl/dsse.
Install
composer require k2gl/slsa-provenance
Requires PHP 8.1+. Pulls in k2gl/in-toto-attestation and k2gl/dsse.
Usage
use K2gl\Slsa\Provenance; use K2gl\Slsa\BuildDefinition; use K2gl\Slsa\RunDetails; use K2gl\Slsa\Builder; use K2gl\Slsa\BuildMetadata; use K2gl\InToto\ResourceDescriptor; $provenance = new Provenance( new BuildDefinition( buildType: 'https://example.com/buildtypes/v1', externalParameters: ['repository' => 'https://github.com/k2gl/dsse', 'ref' => 'refs/tags/1.0.0'], resolvedDependencies: [ new ResourceDescriptor(uri: 'git+https://github.com/k2gl/dsse', digest: ['gitCommit' => '…']), ], ), new RunDetails( builder: new Builder(id: 'https://github.com/actions/runner', version: ['runner' => '2.x']), metadata: new BuildMetadata(invocationId: 'run-42', startedOn: '2026-05-30T00:00:00Z'), ), ); // Wrap as an in-toto Statement over the built artifacts, then sign with k2gl/dsse: $statement = $provenance->toStatement([ new ResourceDescriptor(name: 'app.phar', digest: ['sha256' => '…']), ]); $envelope = $statement->sign($signer); // K2gl\Dsse\Envelope
Parsing back (after verifying the envelope's signatures):
use K2gl\InToto\Statement; use K2gl\Slsa\Provenance; $statement = Statement::fromEnvelope($envelope); $provenance = Provenance::fromStatement($statement); // checks predicateType $provenance->buildDefinition->buildType; // 'https://example.com/buildtypes/v1' $provenance->runDetails->builder->id; // 'https://github.com/actions/runner' $provenance->runDetails->metadata?->invocationId; // 'run-42'
SLSA Provenance v0.2
Most provenance found in real Sigstore bundles is the older v0.2 predicate
(https://slsa.dev/provenance/v0.2), which has a different shape from v1. It lives
under K2gl\Slsa\V02 with its own typed value objects:
use K2gl\InToto\Statement; use K2gl\Slsa\V02\Provenance; $statement = Statement::fromEnvelope($envelope); // verify signatures first $provenance = Provenance::fromStatement($statement); $provenance->builder->id; // 'https://github.com/…' $provenance->buildType; // 'https://…/generic@v1' $provenance->invocation?->configSource?->uri; // 'git+https://github.com/…' $provenance->metadata?->completeness?->parameters; // true $provenance->materials[0]->digest; // ['sha1' => '…']
Building one wraps it in an in-toto Statement v0.1 by default — the version real-world v0.2 provenance is paired with. The two versions are orthogonal, so the Statement version can be overridden:
use K2gl\InToto\StatementVersion; use K2gl\Slsa\V02\Builder; use K2gl\Slsa\V02\Provenance; $provenance = new Provenance( builder: new Builder(id: 'https://github.com/actions/runner'), buildType: 'https://github.com/slsa-framework/slsa-github-generator/generic@v1', ); $statement = $provenance->toStatement([$subject]); // in-toto Statement v0.1 $statement = $provenance->toStatement([$subject], StatementVersion::V1); // …or v1
Verification Summary (VSA)
Where provenance says how an artifact was built, a Verification Summary Attestation
(https://slsa.dev/verification_summary/v1) records that a verifier checked it against a
policy — which SLSA levels it reached and whether it passed. This is the attestation a
verifier emits, carried by an in-toto Statement v1:
use K2gl\InToto\ResourceDescriptor; use K2gl\Slsa\VerificationResult; use K2gl\Slsa\VerificationSummary; use K2gl\Slsa\Verifier; $vsa = new VerificationSummary( verifier: new Verifier(id: 'https://github.com/slsa-framework/slsa-verifier', version: ['slsa-verifier' => 'v2.4.1']), timeVerified: '2026-07-11T12:00:00Z', resourceUri: 'pkg:composer/k2gl/dsse@1.3.0', policy: new ResourceDescriptor(uri: 'https://example.com/policy.yaml', digest: ['sha256' => '…']), verificationResult: VerificationResult::Passed, verifiedLevels: ['SLSA_BUILD_LEVEL_3'], slsaVersion: '1.0', dependencyLevels: ['SLSA_BUILD_LEVEL_3' => 5], // optional ); $statement = $vsa->toStatement([ new ResourceDescriptor(name: 'dsse.zip', digest: ['sha256' => '…']), ]); $envelope = $statement->sign($signer);
Parsing back (after verifying the envelope's signatures):
use K2gl\InToto\Statement; use K2gl\Slsa\VerificationResult; use K2gl\Slsa\VerificationSummary; $statement = Statement::fromEnvelope($envelope); $vsa = VerificationSummary::fromStatement($statement); // checks predicateType $vsa->verificationResult === VerificationResult::Passed; // true $vsa->verifiedLevels; // ['SLSA_BUILD_LEVEL_3'] $vsa->policy->digestFor('sha256'); // '…'
Predicate registry
Register the SLSA predicate types so in-toto's Statement::predicate() returns a typed
object instead of a raw array:
use K2gl\InToto\Statement; use K2gl\Slsa\Predicates; use K2gl\Slsa\Provenance; Predicates::register(); // registers v1, v0.2 and VSA in the shared registry $statement = Statement::fromEnvelope($envelope); $predicate = $statement->predicate(); // a K2gl\Slsa\Provenance (or V02\Provenance), else the raw array if ($predicate instanceof Provenance) { echo $predicate->buildDefinition->buildType; }
License
MIT — see LICENSE. Independent, clean-room implementation of the SLSA Provenance specification.