跳转到内容
getnextpdf.com

Enterprise 版本

Evidence — 深度参考

本页是 NextPDF\Enterprise\Evidence 模块的深度参考。该模块将各项验证发现封存进一个不可变的 EvidencePackage,以确定性 JSON 形式并附带稳定的 SHA-256 摘要将其导出,通过一个可插拔的存储契约进行持久化,并用 ContinuousMonitor 追踪各次运行之间的回归。该模块消费由 Validation 与 Compliance 接口面产出的各项发现;它本身并不执行任何符合性检查。有关工作流程指引,请先阅读 Evidence 能力页

此能力随 NextPDF Enterprisenextpdf/enterprise)发行,并通过一个 Enterprise 层级的授权信封激活。没有该权益的部署不会加载此能力的各个类。比较各版本并获取授权

该接口面受 enterprise.compliance.evidence 能力授权;被拒绝的权益会拒绝该功能。Core 与 Pro 会产出各项发现与报告;而将各项发现封存为一个不可变、确定性、可选带时间戳并具回归追踪的包,则没有 Core 层级或 Pro 层级的对等物。

Terminal window
composer require nextpdf/enterprise:^3
符号参数默认行为返回抛出或失败于说明
EvidencePortal::__constructEvidenceStoreInterface $storeEvidenceExporter $exporter装配 store 与 exporterEvidencePortal未声明两个协作者均可注入
EvidencePortal::generateEvidencestring $documentHashlist<EvidenceRecord> $records?string $tsaTimestamp = null统计通过/失败,用一个全新的 UUID id 与挂钟 generatedAt 封存一个包,并将其持久化EvidencePackage未声明通过 store() 而非 persistImmutable() 进行持久化
EvidencePortal::getEvidencestring $documentHash该哈希对应的最新已存储包?EvidencePackage未声明无存储时为 null
EvidencePortal::getHistorystring $documentHash完整历史,最新在前list<EvidencePackage>未声明排序由 store 提供
EvidencePortal::exportAsJsonEvidencePackage $package委托给 exporternon-empty-stringJsonExceptionEvidenceExporter::toJson 字节相同
EvidencePackage::__construct八个具名参数,见代码块不可变值对象EvidencePackage未声明各计数不会针对 $records 进行校验
EvidencePackage::allPassedfailedCount === 0bool未声明空包时为 true;请以 totalFindings 作为判断闸门
EvidencePackage::passRatepassedCount / totalFindingsfloat未声明totalFindings === 0 时为 0.0
EvidenceRecord::__constructstring $policyNamebool $passedstring $detailsstring $validatorVersionDateTimeImmutable $timestamp不可变的单条策略检查结果EvidenceRecord未声明所有属性均为 public readonly
EvidenceExporter::toJsonEvidencePackage $package固定键顺序的 JSON;不转义斜杠与 Unicodenon-empty-stringJsonException键顺序至关重要
EvidenceExporter::exportHashEvidencePackage $package针对 toJson() 字节计算的 SHA-256non-empty-string(64 位十六进制)JsonException每个包稳定不变
EvidenceStoreInterface::storeEvidencePackage $package追加;允许按文档哈希保留历史void由实现定义要求仅追加语义
EvidenceStoreInterface::persistImmutableEvidencePackage $package在后端支持时进行 WORM 写入void由实现定义非 WORM 后端表现为 store()
EvidenceStoreInterface::findByDocumentHashstring $documentHash该哈希对应的最新包?EvidencePackage由实现定义
EvidenceStoreInterface::findAllByDocumentHashstring $documentHash该哈希对应的所有包,最新在前list<EvidencePackage>由实现定义
EvidenceStoreInterface::count已存储包的总数int<0, max>由实现定义
InMemoryEvidenceStore用于测试与开发的数组存储不适用不适用不具持久性;无 WORM 语义
ContinuousMonitor::__constructEvidenceStoreInterface $store装配 storeContinuousMonitor未声明
ContinuousMonitor::checkEvidencePackage $currentEvidencestring $documentHash将失败的策略名称与已存储的最新包进行差异比对MonitorResult未声明首次检查会将每一项当前失败都视为新增
ContinuousMonitor::isDuestring $documentHashMonitorSchedule $schedule当不存在先前证据、间隔已过、或已存储证据的日期在未来时,即为到期bool未声明对时钟偏移采取故障安全策略
MonitorResult::__construct八个具名参数,见代码块不可变的差异比对结果MonitorResult未声明同时包含两个包与 checkedAt
MonitorSchedule::__constructMonitorFrequency $frequencyint $retentionDays = 90bool $alertOnNewIssues = true配置值对象MonitorSchedule未声明留存与告警由宿主强制执行
MonitorFrequency字符串背衬的枚举各成员 DailyWeeklyMonthly不适用不适用背衬值 dailyweeklymonthly
MonitorFrequency::intervalSeconds各成员的间隔:86400、604800、2592000positive-int未声明Monthly 为固定的 30 天
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
}

EvidencePortal::generateEvidence(string $documentHash, list<EvidenceRecord> $records, ?string $tsaTimestamp = null): EvidencePackage 是封存的入口点。可从外部观察到的规则如下:

  1. 组装。 generateEvidence 会统计通过与失败的记录,并将 totalFindings 设为二者之和。它会分配一个全新的版本 4 UUID packageId,用挂钟为 generatedAt 打上时间戳,通过 EvidenceStoreInterface::store 持久化该包,并返回它。记录列表按给定顺序原样嵌入,不作修改。
  2. 不可变性。 EvidencePackagefinal readonly,构造后绝不会被修改;它适用于 WORM 存储。allPassed()failedCount === 0passRate()passedCount / totalFindings,当 totalFindings === 0 时为 0.0
  3. 确定性导出。 EvidenceExporter::toJson 以一个固定的、手写的键顺序输出信封与每一条记录;记录顺序跟随包。编码是严格的,失败时抛出异常,斜杠与 Unicode 保持不转义(JSON_UNESCAPED_SLASHES)。时间戳以 DateTimeInterface::RFC3339_EXTENDED 序列化,即带小数秒的 RFC 3339 扩展形式。exportHash 返回恰好针对这些字节计算的 64 字符 SHA-256 十六进制摘要。同一个包在任何主机、任何时间始终产出相同的摘要。为同一文档重新生成证据会产生新的 packageIdgeneratedAt,因而产生新的摘要:确定性是针对每个包的,而非针对每个文档的。
  4. 时间戳是关于时间的证据,而非一项判定。 一个包可以携带一个可选的、由调用方提供的 RFC 3161 令牌(base64 编码)。该令牌将包的数据绑定到一个时间值。该模块将其作为一个不透明字符串嵌入;它不会获取、解析或验证令牌,也不会为 TSA 担保。令牌验证属于 Signature 与 Security 模块。
  5. 回归追踪。 ContinuousMonitor::check 会加载该文档哈希已存储的最新包,并对唯一的失败策略名称进行差异比对。问题被归类为 newIssues(当前失败、此前未失败)、resolvedIssues(此前失败、当前未失败)与 unchangedIssues(两次均失败)。仅当存在新增或已解决的问题时 hasChanges 才为 true;仅有未变更的失败会报告为 false。在首次检查时,每一项当前失败都是新增的。
  6. 调度。 当该哈希不存在证据、自已存储的 generatedAt 起经过的时间达到调度频率间隔、或已存储证据相对于轮询主机的日期在未来时,ContinuousMonitor::isDue 返回 true。未来日期的情形是故障安全的:最坏只是多做一次重新检查,绝不会漏掉一次。
  7. 存储契约。 EvidenceStoreInterface 的实现必须支持仅追加语义;每个文档哈希对应的多个包构成历史,最新在前。persistImmutable 面向具备 WORM 能力的后端;非 WORM 实现必须表现得与 store 完全一致。
  • 空包会报告 allPassed()truepassRate()0.0。在将一个包视为通过之前,请以 totalFindings > 0 作为判断闸门。
  • 直接构造 EvidencePackage 不会针对 $records 校验各计数。请使用门户,或自行保持各计数一致。
  • generateEvidence 会在返回前进行持久化。请在持久化新包之前,用它运行 ContinuousMonitor::check;在持久化之后再检查会将该包与其自身比对,从而报告无变更。
  • exportHash 覆盖的是确切的 toJson 字节。由任何其他序列化器、键顺序或转义策略重新计算的摘要都不会匹配。
  • MonitorFrequency::Monthly 是一个固定的 30 天窗口,而非一个日历月。
  • MonitorSchedule::$retentionDays$alertOnNewIssues 是为宿主调度器携带的配置。该模块从不删除证据,也从不发送告警。
  • InMemoryEvidenceStore 供测试与开发使用。各个包会在进程退出时丢失,且其 persistImmutable 不具 WORM 语义。
  • 记录的 details 字符串会被原样导出;导出器不作脱敏。请勿将机密与受监管的个人数据放入 details。驻留地、留存与访问控制取决于运营方的存储实现。
  • tsaTimestamp 参数被作为一个不透明字符串接受。格式错误的令牌会原样嵌入,仅在下游验证时才会显现。

本模块计算 SHA-256 摘要并嵌入一个由调用方提供的 RFC 3161 令牌。它不执行任何签名,也不进行任何密钥保管。FIPS 模式行为由 SecuritySignature 模块管辖。

声明标准条款
时间戳令牌表明某个数据在某个特定时间点已存在。IETF RFC 3161§2
导出的时间戳使用 ISO 8601 的互联网日期/时间剖面,并带小数秒。IETF RFC 3339§5.6
嵌入 PDF 内部的验证材料属于 Document Security Store;那一接口面属于 Signature 模块,而非本模块。ISO 32000-2:2020§12.8.4

所有条款均为释义;NextPDF 不复现规范性文本。NextPDF 不作任何认证声明。 证据采集可支撑审计工作流程;它不是一份法律证明,也不是一项审计认证。时间戳令牌仅是关于时间的证据,且本模块不断言任何内容是符合规范的。有效性与符合性仍是最终文件外加一个校验器的属性。本参考不构成法律意见;请咨询你自己的合规与法律顾问。

  • 模块源码标注 @since 2.2.0;本参考记录的是 nextpdf/enterprise 3.1.0 中发行的接口面。
  • 一切都在你主机上的进程内运行。该模块不执行任何网络 I/O,也从不自行联系 TSA。
  • 导出器的数组字面量键顺序在设计上就是关键所在。重新排序会改变 exportHash 并使先前存储的摘要失效;源码禁止这样做。
  • packageId 是由 \random_bytes(16) 输出组装而成的版本 4 UUID;标识符唯一但不可复现。
  • 持久化存储由宿主提供。WORM 强制与访问控制是运营方的责任;内存存储是唯一随附的实现。
  • MonitorResult 是一个 final readonly 值对象;它的八个属性均为 public,包括 checkedAt,即检查的挂钟时间。

本页仅记录可从外部观察到的行为与受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、runbook 文件名与工单前缀均不在范围内。