Guides

From in-toto statement to signed SLSA provenance

Provenance is a stack of small formats: a SLSA predicate says how the artifact was built, an in-toto statement binds that claim to the artifact’s digest, and a DSSE envelope (or a full Sigstore bundle) makes it tamper-evident. One package per layer; this walks the whole chain.

Describe the build

use K2gl\InToto\ResourceDescriptor;
use K2gl\Slsa\BuildDefinition;
use K2gl\Slsa\Builder;
use K2gl\Slsa\Provenance;
use K2gl\Slsa\RunDetails;

$provenance = new Provenance(
    buildDefinition: new BuildDefinition(
        buildType: 'https://example.com/pipelines/build/v1',
        externalParameters: ['ref' => 'refs/tags/1.0.0'],
    ),
    runDetails: new RunDetails(builder: new Builder(id: 'https://ci.example.com')),
);

$statement = $provenance->toStatement([
    new ResourceDescriptor(
        name: 'app.phar',
        digest: ['sha256' => hash_file('sha256', 'dist/app.phar')],
    ),
]);

Sign it

The bare-envelope route — your key, no network:

use K2gl\Dsse\EcdsaP256Signer;

$envelope = $statement->sign(EcdsaP256Signer::fromPem($privatePem, keyId: 'release-key'));
file_put_contents('provenance.dsse.json', $envelope->toJson());

Or the Sigstore route — certificate plus transparency log, one call:

use K2gl\SigstoreSign\SigstoreSigner;

$bundle = new SigstoreSigner($rekor)->signAttestation(
    $statement->toJson(),
    'application/vnd.in-toto+json',
    $key,   // e.g. keyless — see the Actions guide
);

Look at what you made

Paste the envelope into the provenance viewer — subjects, builder and parameters render decoded; a bundle goes to the bundle inspector. Packages: in-toto-attestation · slsa-provenance · dsse · sigstore-sign. For the keyless $key, see keyless signing in GitHub Actions.