PHPackages                             ahg/inference-receipts - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. ahg/inference-receipts

ActiveLibrary[Security](/categories/security)

ahg/inference-receipts
======================

Tamper-evident receipts for AI inference calls: SHA-256 hash chain, RFC 8785 JCS canonicalization, Ed25519 signatures. Pure PHP, zero framework dependencies. EU AI Act Article 12 / NIST AI RMF aligned.

v0.1.1(2mo ago)01Apache-2.0PHPPHP ^8.2CI passing

Since May 25Pushed 2mo agoCompare

[ Source](https://github.com/ArchiveHeritageGroup/inference-receipts)[ Packagist](https://packagist.org/packages/ahg/inference-receipts)[ RSS](/packages/ahg-inference-receipts/feed)WikiDiscussions main Synced 2w ago

READMEChangelogDependencies (1)Versions (3)Used By (0)

ahg/inference-receipts
======================

[](#ahginference-receipts)

Tamper-evident receipt chains for AI inference calls. Pure PHP, zero framework dependencies.

Implements the three primitives that EU AI Act Article 12 ("logs that cannot be modified") needs in practice:

1. **SHA-256 hash chain** - each receipt embeds the hash of its predecessor, so modifying or deleting an entry breaks the chain at a detectable point.
2. **RFC 8785 JSON Canonicalization Scheme (JCS)** - deterministic byte serialization of the receipt payload, so two implementations always agree on what to hash and sign.
3. **Ed25519 detached signatures** - each entry is signed under a publisher key; verifiers can confirm authenticity offline given the public key.

Built for EU AI Act / NIST AI RMF / ISO 42001 record-keeping requirements, but the primitives are generic. Anywhere you need an append-only signed audit trail for AI agent actions, this fits.

Why
---

[](#why)

The AI Act enforces from 2 August 2026. Article 12 requires high-risk AI systems to keep automatic event logs across their lifecycle, in a form that ensures traceability and cannot be silently modified. Plain database rows do not meet that bar; tamper-evident logs do.

This library is byte-compatible with the [nobulex](https://github.com/arian-gogani/nobulex) TypeScript / Python reference where the on-wire format overlaps, so receipts can be cross-verified by any implementation that consumes the same vectors.

Installation
------------

[](#installation)

```
composer require ahg/inference-receipts
```

Requirements: PHP 8.2+, `ext-sodium`, `ext-json`, `ext-mbstring`.

Quick start
-----------

[](#quick-start)

```
use AhgInferenceReceipts\KeyPair;
use AhgInferenceReceipts\Signer;
use AhgInferenceReceipts\ReceiptChain;
use AhgInferenceReceipts\Storage\ArrayChainStore;

$keyPair = KeyPair::generate();
$keyPair->saveTo('/etc/ai-keys/signing.sk', '/etc/ai-keys/signing.pk');

$store = new ArrayChainStore();
$chain = ReceiptChain::withSingleKey($store, new Signer($keyPair));

$receipt = $chain->append([
    'service'            => 'llm',
    'model_id'           => 'mistral:7b-instruct-v0.2',
    'model_version'      => 'mistral-7b@2024-01',
    'input_fingerprint'  => hash('sha256', $userPrompt),
    'output_fingerprint' => hash('sha256', $modelResponse),
    'request_id'         => $requestId,
    'user_id'            => $userId,
    'tenant_id'          => $tenantId,
]);

// later, audit time
$result = $chain->verify();
echo $result;  // "PASS (1 receipts verified)" or "FAIL at seq N: "
```

On-wire format
--------------

[](#on-wire-format)

```
{
  "v": 1,
  "seq": 0,
  "ts": "2026-05-25T12:00:00.000Z",
  "prev_hash": "0000000000000000000000000000000000000000000000000000000000000000",
  "payload": { ... arbitrary caller-supplied JSON-able data ... },
  "kid": "a1b2c3d4e5f60718",
  "alg": "ed25519",
  "entry_hash": "",
  "signature":  ""
}
```

- `v`: format version (currently `1`)
- `seq`: 0-based monotonic counter within the chain
- `ts`: RFC 3339 with millisecond precision in UTC
- `prev_hash`: `entry_hash` of the previous receipt, or 64 zero bytes for genesis
- `payload`: caller payload (input/output fingerprints, model id, etc)
- `kid`: stable 16-hex-char id derived from `SHA-256(publicKey)`
- `alg`: signature algorithm name (`ed25519`)
- `entry_hash`: SHA-256 of `JcsEncoder::encode(toSigningView())` where the signing view is `{v, seq, ts, prev_hash, payload, kid, alg}`
- `signature`: base64-encoded Ed25519 signature over the raw 32-byte `entry_hash` digest

Storage adapters
----------------

[](#storage-adapters)

The library ships `ArrayChainStore` for tests and ephemeral use. Production callers implement `ChainStore`:

```
interface ChainStore {
    public function append(Receipt $receipt): void;
    public function head(): ?Receipt;
    public function count(): int;
    public function range(int $fromSeq = 0, ?int $toSeq = null): iterable;
}
```

Most production deployments back this with a relational table that has a `UNIQUE` constraint on `entry_hash` (so concurrent writers cannot both win the same `seq`). See the [`ahg/ai-compliance`](https://github.com/ArchiveHeritageGroup/heratio) Laravel package for an Eloquent-backed reference implementation.

Key rotation
------------

[](#key-rotation)

`ReceiptChain` accepts a `publicKeyResolver` callable so verifiers can support multiple historical keys:

```
$resolver = function (string $kid): ?string {
    return match ($kid) {
        '2025-key-id' => file_get_contents('/etc/ai-keys/2025-signing.pk'),
        '2026-key-id' => file_get_contents('/etc/ai-keys/2026-signing.pk'),
        default       => null,
    };
};

$chain = new ReceiptChain($store, new Signer($currentKeyPair), $resolver);
```

A receipt signed under a rotated-out key still verifies as long as its `kid` resolves to the public-key bytes that were valid at signing time.

Verification
------------

[](#verification)

```
$result = $chain->verify();
if (!$result->ok) {
    fprintf(STDERR, "Chain broken at seq %d: %s\n", $result->brokenAtSeq, $result->reason);
    exit(1);
}
echo "Verified {$result->checkedCount} receipts\n";
```

`verify()` walks the chain in order and confirms, for each entry:

1. `seq` increments monotonically from `fromSeq`
2. `prev_hash` matches the previous receipt's `entry_hash` (or the genesis hash for `seq == 0`)
3. `entry_hash` equals `SHA-256(JcsEncoder::encode(receipt.toSigningView()))`
4. `signature` validates under the public key resolved from `kid`

If any check fails, verification stops and returns the first failure point.

Conformance
-----------

[](#conformance)

The conformance suite under `tests/Conformance/` consumes the [nobulex](https://github.com/arian-gogani/nobulex) test vectors as fixtures, so this implementation stays byte-compatible with the broader ecosystem.

```
./tools/fetch-nobulex-vectors.sh
vendor/bin/phpunit --testsuite conformance
```

If the fixtures are absent the relevant tests are skipped, so the default suite stays green for downstream consumers who do not pull external vectors.

Threat model
------------

[](#threat-model)

Detected:

- Modification of any field of any stored receipt (payload, ts, seq, kid, alg, prev\_hash).
- Re-signing a tampered receipt under a different key (if the verifier's `publicKeyResolver` rejects unknown kids).
- Re-ordering, splicing, or deleting receipts (the chain's `prev_hash` linkage breaks).

Not detected (out of scope for this library):

- Operator with both write access to the chain AND control of the signing key can append fraudulent entries that verify cleanly. Mitigation: anchor chain head to an off-host append-only log on a different trust boundary; consider RFC 3161 trusted timestamping for high-value chains.
- Replay of a stale receipt to a different chain or tenant. Mitigation: include `tenant_id` and `request_id` in the payload and check them at verification time.

License
-------

[](#license)

Apache-2.0. See `LICENSE` and `NOTICE`.

Provenance
----------

[](#provenance)

Built for [Heratio](https://github.com/ArchiveHeritageGroup/heratio) (a Laravel 12 GLAM platform by The Archive and Heritage Group) to satisfy EU AI Act Article 12 record-keeping. Released standalone because the PHP ecosystem needs a peer to the TypeScript / Python `nobulex` implementation; receipts produced here are byte-compatible with the nobulex format where the shapes overlap.

Tracking the IETF draft `draft-gogani-nobulex-proof-of-behavior` for on-wire format alignment as the spec progresses.

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance88

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

60d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/40993577?v=4)[Johan Pieterse](/maintainers/johanpiet2)[@johanpiet2](https://github.com/johanpiet2)

---

Tags

Ed25519hash-chaincomplianceaudit-logprovenanceai-actarticle-12tamper-evidentjcsrfc8785

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ahg-inference-receipts/health.svg)

```
[![Health](https://phpackages.com/badges/ahg-inference-receipts/health.svg)](https://phpackages.com/packages/ahg-inference-receipts)
```

###  Alternatives

[paragonie/certainty

Up-to-date, verifiable repository for Certificate Authorities

2662.6M22](/packages/paragonie-certainty)[laravel-chronicle/core

Tamper-evident audit ledger for Laravel applications.

1213.7k7](/packages/laravel-chronicle-core)[simplito/elliptic-php

Fast elliptic curve cryptography

2302.4M275](/packages/simplito-elliptic-php)[devrabiul/laravel-cookie-consent

A GDPR-compliant cookie consent solution for Laravel applications with fully customizable cookie banners, granular consent control, and enterprise-grade compliance features.

18355.4k1](/packages/devrabiul-laravel-cookie-consent)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
