Packages · supply-chain

k2gl/rekor-client

A PSR-18 client for the Rekor transparency log (v2 / rekor-tiles).

Submit and retrieve transparency-log entries with the HTTP client you already use.

k2gl/rekor-client version on Packagist k2gl/rekor-client total downloads k2gl/rekor-client license

Install

composer require k2gl/rekor-client
PHP
>=8.1
k2gl dependencies
k2gl/sigstore-bundle

Reach for it when

  • You need to submit to or query a Rekor v2 transparency log directly.
  • You want to bring your own PSR-18 HTTP client.

Look elsewhere when

  • You want the full signing flow — sigstore-sign submits to Rekor for you.
  • You need the legacy Rekor v1 REST API (this targets v2).

Rekor v2 client for PHP

Submit entries to a Rekor v2 (rekor-tiles) transparency log from PHP 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\RekorClient;
use K2gl\RekorClient\Verifier;
use K2gl\RekorClient\KeyDetails;

$rekor = new RekorClient(
    httpClient:     $psr18Client,
    requestFactory: $psr17Factory,
    streamFactory:  $psr17Factory,
    baseUrl:        'https://rekor.sigstore.dev', // the v2 log URL from your signing config
);

// 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();

DSSE attestations

Rekor v2 has no 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 release covers submission (the write path a signer needs) against Rekor v2. 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.

Pull requests are always welcome

Collaborate with pull requests

API

Public classes and methods, generated from the source.

K2gl\RekorClient\Exception\InvalidArgumentException class

K2gl\RekorClient\Exception\RekorClientException interface

K2gl\RekorClient\Exception\RekorRequestException class

K2gl\RekorClient\Exception\RekorResponseException class

  • __construct(string $message, public readonly ?int $statusCode = null)

K2gl\RekorClient\KeyDetails enum

K2gl\RekorClient\RekorClient class

  • __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, string $baseUrl, )
  • submitHashedRekord(string $digest, string $signature, Verifier $verifier): TransparencyLogEntry

K2gl\RekorClient\Verifier class

  • publicKey(string $der, KeyDetails $keyDetails): self
  • certificate(string $der, KeyDetails $keyDetails): self
  • toArray(): array