Enterprise edition
Evidence — Deep Reference
At a glance
Section titled “At a glance”This page is the deep reference for the NextPDF\Enterprise\Evidence module. The module seals validation findings into an immutable EvidencePackage, exports it as deterministic JSON with a stable SHA-256 digest, persists it through a pluggable store contract, and tracks regressions between runs with ContinuousMonitor. The module consumes findings produced by the Validation and Compliance surfaces; it performs no conformance checks itself. For workflow guidance, read the Evidence capability page first.
Availability & licensing
Section titled “Availability & licensing”This capability ships in NextPDF Enterprise (nextpdf/enterprise) and activates with an Enterprise-tier license envelope. A deployment without that entitlement does not load the capability’s classes. Compare editions and get a license.
The surface is licensed by the enterprise.compliance.evidence capability; a denied entitlement denies the feature. Core and Pro produce findings and reports; sealing findings into an immutable, deterministic, optionally timestamped package with regression tracking has no Core-tier or Pro-tier equivalent.
Public API surface
Section titled “Public API surface”composer require nextpdf/enterprise:^3| Symbol | Parameters | Default behavior | Returns | Throws or fails with | Notes |
|---|---|---|---|---|---|
EvidencePortal::__construct | EvidenceStoreInterface $store, EvidenceExporter $exporter | Wires the store and exporter | EvidencePortal | Nothing declared | Both collaborators are injectable |
EvidencePortal::generateEvidence | string $documentHash, list<EvidenceRecord> $records, ?string $tsaTimestamp = null | Counts pass/fail, seals a package with a fresh UUID id and wall-clock generatedAt, persists it | EvidencePackage | Nothing declared | Persists through store(), not persistImmutable() |
EvidencePortal::getEvidence | string $documentHash | Latest stored package for the hash | ?EvidencePackage | Nothing declared | null when none stored |
EvidencePortal::getHistory | string $documentHash | Full history, newest first | list<EvidencePackage> | Nothing declared | Ordering is supplied by the store |
EvidencePortal::exportAsJson | EvidencePackage $package | Delegates to the exporter | non-empty-string | JsonException | Same bytes as EvidenceExporter::toJson |
EvidencePackage::__construct | eight named parameters, see fence | Immutable value object | EvidencePackage | Nothing declared | Counts are not validated against $records |
EvidencePackage::allPassed | none | failedCount === 0 | bool | Nothing declared | true for an empty package; gate on totalFindings |
EvidencePackage::passRate | none | passedCount / totalFindings | float | Nothing declared | 0.0 when totalFindings === 0 |
EvidenceRecord::__construct | string $policyName, bool $passed, string $details, string $validatorVersion, DateTimeImmutable $timestamp | Immutable single policy-check result | EvidenceRecord | Nothing declared | All properties are public readonly |
EvidenceExporter::toJson | EvidencePackage $package | Fixed-key-order JSON; unescaped slashes and Unicode | non-empty-string | JsonException | Key order is load-bearing |
EvidenceExporter::exportHash | EvidencePackage $package | SHA-256 over the toJson() bytes | non-empty-string (64 hex) | JsonException | Stable per package |
EvidenceStoreInterface::store | EvidencePackage $package | Appends; history per document hash is allowed | void | Implementation-defined | Append-only semantics required |
EvidenceStoreInterface::persistImmutable | EvidencePackage $package | WORM write where the backend supports it | void | Implementation-defined | Non-WORM backends behave as store() |
EvidenceStoreInterface::findByDocumentHash | string $documentHash | Most recent package for the hash | ?EvidencePackage | Implementation-defined | |
EvidenceStoreInterface::findAllByDocumentHash | string $documentHash | All packages for the hash, newest first | list<EvidencePackage> | Implementation-defined | |
EvidenceStoreInterface::count | none | Total number of stored packages | int<0, max> | Implementation-defined | |
InMemoryEvidenceStore | class | Array-backed store for tests and development | n/a | n/a | Not durable; no WORM semantics |
ContinuousMonitor::__construct | EvidenceStoreInterface $store | Wires the store | ContinuousMonitor | Nothing declared | |
ContinuousMonitor::check | EvidencePackage $currentEvidence, string $documentHash | Diffs failed policy names against the stored latest package | MonitorResult | Nothing declared | First check treats every current failure as new |
ContinuousMonitor::isDue | string $documentHash, MonitorSchedule $schedule | Due when no prior evidence, the interval elapsed, or stored evidence is future-dated | bool | Nothing declared | Fail-safe on clock skew |
MonitorResult::__construct | eight named parameters, see fence | Immutable diff result | MonitorResult | Nothing declared | Includes both packages and checkedAt |
MonitorSchedule::__construct | MonitorFrequency $frequency, int $retentionDays = 90, bool $alertOnNewIssues = true | Configuration value object | MonitorSchedule | Nothing declared | Retention and alerting are host-enforced |
MonitorFrequency | string-backed enum | Cases Daily, Weekly, Monthly | n/a | n/a | Backing values daily, weekly, monthly |
MonitorFrequency::intervalSeconds | none | Interval per case: 86400, 604800, 2592000 | positive-int | Nothing declared | Monthly is a fixed 30 days |
Entry-point signatures
Section titled “Entry-point signatures”final class EvidencePortal{ public function __construct( private readonly EvidenceStoreInterface $store, private readonly EvidenceExporter $exporter, )
public function generateEvidence(string $documentHash, array $records, ?string $tsaTimestamp = null): EvidencePackage
public function getEvidence(string $documentHash): ?EvidencePackage
public function getHistory(string $documentHash): array
public function exportAsJson(EvidencePackage $package): string}final readonly class EvidencePackage{ public function __construct( public string $packageId, public string $documentHash, public array $records, public int $totalFindings, public int $passedCount, public int $failedCount, public DateTimeImmutable $generatedAt, public ?string $tsaTimestamp = null, )
public function allPassed(): bool
public function passRate(): float}final readonly class EvidenceRecord{ public function __construct( public string $policyName, public bool $passed, public string $details, public string $validatorVersion, public DateTimeImmutable $timestamp, )}final readonly class EvidenceExporter{ public function toJson(EvidencePackage $package): string
public function exportHash(EvidencePackage $package): string}interface EvidenceStoreInterface{ public function store(EvidencePackage $package): void;
public function persistImmutable(EvidencePackage $package): void;
public function findByDocumentHash(string $documentHash): ?EvidencePackage;
public function findAllByDocumentHash(string $documentHash): array;
public function count(): int;}final class ContinuousMonitor{ public function __construct( private readonly EvidenceStoreInterface $store, )
public function check(EvidencePackage $currentEvidence, string $documentHash): MonitorResult
public function isDue(string $documentHash, MonitorSchedule $schedule): bool}final readonly class MonitorSchedule{ public function __construct( public MonitorFrequency $frequency, public int $retentionDays = 90, public bool $alertOnNewIssues = true, )}
enum MonitorFrequency: string{ case Daily = 'daily'; case Weekly = 'weekly'; case Monthly = 'monthly';
public function intervalSeconds(): int}Behavior contract
Section titled “Behavior contract”EvidencePortal::generateEvidence(string $documentHash, list<EvidenceRecord> $records, ?string $tsaTimestamp = null): EvidencePackage is the sealing entry point. Externally observable rules:
- Assembly.
generateEvidencecounts passed and failed records and setstotalFindingsto their sum. It assigns a fresh version-4 UUIDpackageId, stampsgeneratedAtwith the wall clock, persists the package throughEvidenceStoreInterface::store, and returns it. The record list is embedded in the given order, unmodified. - Immutability.
EvidencePackageisfinal readonlyand never mutated after construction; it is suitable for WORM storage.allPassed()isfailedCount === 0.passRate()ispassedCount / totalFindings, and0.0whentotalFindings === 0. - Deterministic export.
EvidenceExporter::toJsonemits the envelope and each record with a fixed, hand-written key order; the record sequence follows the package. Encoding is strict and throws on failure, with slashes and Unicode left unescaped (JSON_UNESCAPED_SLASHES). Timestamps serialize withDateTimeInterface::RFC3339_EXTENDED, the RFC 3339 extended form with fractional seconds.exportHashreturns the 64-character SHA-256 hex digest over exactly those bytes. The same package always yields the same digest, on any host, at any time. Re-generating evidence for the same document yields a newpackageIdandgeneratedAt, so a new digest: determinism is per package, not per document. - Timestamp is evidence of time, not a verdict. A package may carry an optional caller-supplied RFC 3161 token (base64-encoded). The token binds the package datum to a time value. The module embeds it as an opaque string; it does not fetch, parse, or verify tokens, and it does not vouch for the TSA. Token verification belongs to the Signature and Security modules.
- Regression tracking.
ContinuousMonitor::checkloads the stored latest package for the document hash and diffs unique failed policy names. Issues are categorized asnewIssues(failed now, not before),resolvedIssues(failed before, not now), andunchangedIssues(failed in both).hasChangesistrueonly when new or resolved issues exist; unchanged failures alone reportfalse. On a first check, every current failure is new. - Scheduling.
ContinuousMonitor::isDuereturnstruewhen no evidence exists for the hash, when the elapsed time since the storedgeneratedAtreaches the schedule frequency interval, or when the stored evidence is future-dated relative to the polling host. The future-dated case is fail-safe: at worst an extra re-check, never a missed one. - Store contract.
EvidenceStoreInterfaceimplementations must support append-only semantics; multiple packages per document hash form the history, newest first.persistImmutabletargets WORM-capable backends; non-WORM implementations must behave exactly likestore.
Edge cases & failure modes
Section titled “Edge cases & failure modes”- An empty package reports
allPassed()trueandpassRate()0.0. Gate ontotalFindings > 0before treating a package as a pass. - Direct
EvidencePackageconstruction does not validate the counts against$records. Use the portal, or keep the counts consistent yourself. generateEvidencepersists before returning. RunContinuousMonitor::checkwith the new package before persisting it; a check after persistence diffs the package against itself and reports no changes.exportHashcovers the exacttoJsonbytes. A digest recomputed by any other serializer, key order, or escaping policy will not match.MonitorFrequency::Monthlyis a fixed 30-day window, not a calendar month.MonitorSchedule::$retentionDaysand$alertOnNewIssuesare configuration carried for host schedulers. The module never deletes evidence and never sends alerts.InMemoryEvidenceStoreis for tests and development. Packages are lost at process exit, and itspersistImmutablehas no WORM semantics.- Record
detailsstrings are exported verbatim; the exporter does not redact. Keep secrets and regulated personal data out ofdetails. Residency, retention, and access control follow the operator’s store implementation. - The
tsaTimestampargument is accepted as an opaque string. A malformed token is embedded unchanged and surfaces only at downstream verification.
FIPS-mode behavior
Section titled “FIPS-mode behavior”This module computes SHA-256 digests and embeds a caller-supplied RFC 3161 token. It performs no signing and no key custody. FIPS-mode behavior is governed by the Security and Signature modules.
Conformance
Section titled “Conformance”| Claim | Standard | Clause |
|---|---|---|
| A time-stamp token indicates that a datum existed at a particular point in time. | IETF RFC 3161 | §2 |
| Exported timestamps use the Internet date/time profile of ISO 8601, with fractional seconds. | IETF RFC 3339 | §5.6 |
| Validation material embedded inside a PDF belongs to the Document Security Store; that surface is the Signature module’s, not this one. | ISO 32000-2:2020 | §12.8.4 |
All clauses are paraphrased; NextPDF does not reproduce normative text. NextPDF makes no certification claim. Evidence capture supports audit workflows; it is not a legal attestation and not an audit certification. A timestamp token is evidence of time only, and this module does not assert that any content is compliant. Validity and conformance remain properties of the final file plus a validator. This reference is not a legal opinion; consult your own compliance and legal advisers.
Development notes
Section titled “Development notes”- The module source carries
@since 2.2.0; this reference documents the surface as shipped innextpdf/enterprise3.1.0. - Everything runs in process on your host. The module performs no network I/O and never contacts a TSA itself.
- The exporter’s array-literal key order is load-bearing by design. Reordering it would change
exportHashand invalidate previously stored digests; the source forbids it. packageIdis a version-4 UUID assembled from\random_bytes(16)output; identifiers are unique but not reproducible.- Durable persistence is supplied by the host. WORM enforcement and access control are the operator’s responsibility; the in-memory store is the only bundled implementation.
MonitorResultis afinal readonlyvalue object; its eight properties arepublic, includingcheckedAt, the wall-clock time of the check.
Publication boundary
Section titled “Publication boundary”This page documents externally observable behavior and the supported public API surface only. Internal namespace paths, helper classes, mechanism tables, runbook filenames, and ticket prefixes are out of scope.
See also
Section titled “See also”- Evidence — the capability page with workflow guidance.
- Validation — Deep Reference
- Compliance — Deep Reference
- AST audit trail — Deep Reference
- Specifications: PAdES