k2gl / rekor-client
A PSR-18 client for the Rekor transparency log (v1 and v2) — submit hashedrekord entries from PHP
Requires
- php: >=8.1
- ext-json: *
- k2gl/sigstore-bundle: ^1.0
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1|^2.0
Requires (Dev)
- k2gl/composer-attest: ^1.1
- k2gl/composer-license-gate: ^1.0
- k2gl/phpunit-fluent-assertions: ^12
- laravel/pint: ~1.20.0
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10|^11|^12
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-04 04:08:07 UTC
README
Submit entries to a Rekor transparency log from PHP — both the original
v1 REST log and
v2 (rekor-tiles) — and get back the
transparency-log entry Rekor integrated, the same value
k2gl/sigstore-bundle takes, so a signer goes
submit → add to bundle with no glue in between.
Transport is any PSR-18 HTTP client you supply (Guzzle, Symfony HttpClient, …). This package speaks the Rekor API; it owns no socket.
Requirements
- PHP 8.1+
- A PSR-18 HTTP client and a PSR-17 factory (e.g.
nyholm/psr7+symfony/http-client) k2gl/sigstore-bundle
Installation
composer require k2gl/rekor-client
Usage
use K2gl\RekorClient\RekorApiVersion; use K2gl\RekorClient\RekorClient; use K2gl\RekorClient\Verifier; use K2gl\RekorClient\KeyDetails; $rekor = new RekorClient( httpClient: $psr18Client, requestFactory: $psr17Factory, streamFactory: $psr17Factory, baseUrl: 'https://rekor.sigstore.dev', apiVersion: RekorApiVersion::V1, ); // A hashedrekord entry: the artifact digest, the signature, and the key or // certificate that signed it. $entry = $rekor->submitHashedRekord( digest: $artifactSha256, // raw 32-byte digest signature: $rawSignature, verifier: Verifier::certificate($fulcioLeafDer, KeyDetails::PKIX_ECDSA_P256_SHA_256), ); // $entry is a K2gl\SigstoreBundle\TransparencyLogEntry — drop it straight in: $json = BundleBuilder::forMessageSignature($messageSignature) ->withCertificate($fulcioLeafDer) ->addTransparencyLogEntry($entry) ->toJson();
Which log version
Take both the URL and the version from the log entry in Sigstore's signing config
rather than assuming — that is what majorApiVersion there is for, and
RekorApiVersion::from() accepts it directly. It matters: Sigstore's default signing
config still lists only rekor.sigstore.dev at major version 1, and the v2 logs live
in a separate, opt-in config.
The two differ in more than the path. v1 takes the verifier as PEM and the digest as hex, answers with a map keyed by entry UUID, hex-encodes proof hashes, and stamps every entry with an integrated time and a signed entry timestamp. v2 takes raw DER and base64, answers with the entry itself, and has no per-entry time — a v2 bundle needs an RFC 3161 timestamp to be verifiable. All of that is handled here; the only choice you make is the version.
A v1 submission takes a SHA-256, SHA-384 or SHA-512 digest (hashedrekord 0.0.1 names the
algorithm, and it is read from the digest length).
DSSE attestations
Neither version has a DSSE entry type. Submit the DSSE PAE digest and the envelope signature as a hashedrekord — the entry Rekor returns is the one a DSSE bundle carries.
Signing identity
Verifier::publicKey($der, $keyDetails)— a bare public key.Verifier::certificate($der, $keyDetails)— a Fulcio (keyless) certificate.
KeyDetails names the algorithm (PKIX_ECDSA_P256_SHA_256, PKIX_ED25519, …).
Errors
Everything thrown implements K2gl\RekorClient\Exception\RekorClientException:
RekorRequestException (transport failed / request could not be built),
RekorResponseException (Rekor answered with an error status or an unparseable body,
with the HTTP statusCode), and InvalidArgumentException (bad input).
Scope
This package covers submission (the write path a signer needs) against both log
versions. Reading back entries and tiles (the C2SP tlog-tiles read API) is not implemented yet;
verifying an entry already in a bundle is what
k2gl/sigstore-verify does.