跳转到内容
getnextpdf.com

Enterprise 版本

Licensing — 深度参考

本页是 NextPDF Enterprise 授权接口面的深度参考——该模块负责解析其他每一项 Enterprise 能力。它涵盖权益状态模型(EntitlementEvaluatorEntitlementStatusEntitlementResult)、强制能力门控(FeatureGateCapabilityCodeCapabilityRegistry)、双计时器渠道强制机(ChannelEnforcementPolicyOnlineCheckScheduler),以及签名在线客户端(LicenseClient)。普通的权益评估无需任何网络。在线客户端仅为明确的激活、心跳、停用、在线验证与续订探测操作而存在。内部机制细节保留在源代码仓库的内部文档中,不属于本手册的范围。

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

Licensing 是一项基础 Enterprise 接口面。只要 Enterprise 包与 Core 包并列安装,它就始终存在,因为它正是解析其他一切的接口面。NextPDF Pro 没有单独的权益接口面;状态模型、强制机与在线客户端仅随 nextpdf/enterprise 发行。NextPDF Core(Apache-2.0)根本没有权益模型——需要无授权 Apache-2.0 行为的宿主应直接使用 Core 引擎,而非 Enterprise 管线。

所有类型都位于 NextPDF\Enterprise\Licensing 之下。强制机位于 Channel 子命名空间,运行时组合位于 Runtime,签名状态类型位于 Online

符号参数默认行为返回抛出或失败于备注
EntitlementEvaluator::evaluate()?LicenseKey $license, ?DateTimeImmutable $now = null将一份已验证的授权(或 null)映射为完整的权益决策EntitlementResultnull 授权会产出失败即关闭的无授权结果
EntitlementEvaluator::evaluateEnforced()?LicenseKey $license, EnforcementOutcome $outcome将预先计算的强制结果转换为一个权益结果EntitlementResult有效版本来自该结果;每一次强制降级都落到 Core
EntitlementEvaluator::resolveBrandingMode(), ::resolveBrandingStrategy()?LicenseKey $license, ?DateTimeImmutable $now = null为生成输出解析品牌标识决策BrandingMode / BrandingStrategy唯一的品牌标识权威;仅评估渠道会加水印
EntitlementResult只读值对象携带状态、版本、渠道、品牌标识模式、运行时/更新/支持标志,以及一个可选警告辅助方法:isFullyActive()isExpired()isDowngradedToCore()shouldApplyEvaluationBranding()
EntitlementStatus字符串支撑的枚举Active, GracePeriod, DowngradedToCore, EvaluationExpired, NoLicensePerpetualFallback 已保留但被弃用;仅为使 6.0 之前的消费方仍能编译而保留
FeatureGate::hasCapability()CapabilityCode $capability对照强制有效版本与包状态检查该能力bool任何解析器错误都失败即关闭到 core
FeatureGate::requireCapability()CapabilityCode $capability静默通过,或带着结构化的增购上下文抛出voidSpectrumAuthenticationException SPEC-LIC-001(未授权)或 SPEC-LIC-002(包已过期)该上下文携带 required_capabilityrequired_packcurrent_packsupgrade_info_url
FeatureGate::isFeatureEnabled(), ::requireFeature()string $feature旧版功能标识符检查bool / voidSpectrumAuthenticationException SPEC-AUTH-007(require 路径)旧版兼容;优先使用能力检查
FeatureGate 访问器授权自省视情况而定currentEdition(), isLicenseValid(), isInGracePeriod(), getActivePacks(), trialPolicy()
CapabilityCode字符串支撑的枚举规范的能力标识符(core.*pro.*enterprise.*pack.*isPack(), packName(), minimumEdition(), forEdition()
CapabilityRegistryLicenseKey、路由模式分派前的路由-到-能力解析与包查找视情况而定registerDefaultRoutes() 覆盖已记录的各服务路由
LicenseKey只读值对象已解析、已验证的授权声明isExpired()isInGracePeriod()isFullyExpired()hasFeature()hasCapability()activePackNames()maxSlots 仅为合同约定
LicenseValidator::buildVerifiedLicenseKey()array $data从一个已验证的载荷构建一个类型化的 LicenseKeyLicenseKey违反 schema 时抛出 SpectrumAuthenticationException SPEC-AUTH-001支持 schema 2.0、1.0 及旧版载荷
PackDefinition / PackRegistry只读值对象带有独立过期与宽限的附加包状态视情况而定isActive(), isFullyExpired(), activePacks(), activeCapabilities()
TrialPolicybool $enabled, int $maxPagesPerSecond = 2, 水印字段试用吞吐量上限策略fromLicenseKey(), disabled(), isThrottled();品牌标识权威仍归评估器所有
ChannelEnforcementPolicy::enforce()LicenseKey $license, LocalState $state, int $systemNow, bool $revoked = false运行双计时器机,外加吊销与试用重新标注EnforcementOutcome吊销会覆盖每一个计时器与试用状态
OnlineCheckScheduler::evaluate(), ::recordSuccessfulOnlineCheck()授权、本地状态、时间戳双计时器评估;依据已验证的服务器时间续租EnforcementOutcome / LocalStateONLINE_GRACE_SECONDS 在租约之后授予 48 小时的存活宽限
EnforcementDecision / EnforcementReason / EnforcementOutcome枚举 + 只读值对象Active, Grace, DowngradedToCore 以及精确的原因每个原因都确定性地映射到一个决策
Runtime\EntitlementResolver::currentEntitlement()接口:运行时所提供的强制权益EntitlementResult由实现定义EnforcedEntitlementResolver 组合缓存、状态、策略与评估器,失败即关闭到 Core
LicenseStateCache::get(), ::getOrThrow()bool $forceRefresh = false支撑门控的已验证授权缓存?LicenseKeygetOrThrow()SpectrumAuthenticationException SPEC-AUTH-008;接线无效时抛出 LicenseConfigurationExceptioninvalidate(), isCacheValid()
LicenseClient::activate(), ::heartbeat(), ::deactivate(), ::verifyOnline()string $licenseJws, string $deploymentId, ?string $machineFingerprintHash = null, ?string $expectedLicenseId = null, ?string $clientNonce = null一次签名状态交换;响应会对照固定根进行验证,并绑定到所发送的 nonceStatusResponseLicenseClientException(传输、非 200、提供的 nonce 有误);SpectrumAuthenticationException(伪造、篡改、陈旧、nonce 错误、授权错误、类型错误的响应)只会发送加盐的指纹哈希,绝不会发送原始指纹
LicenseClient::checkRenewal()string $licenseId, string $customerId续订可用性探测;404 是正常的“无可用”答复续订数组(availabledownload_urlexpires_atLicenseClientException(不可达、非 200/404,或被策略拒绝的 download_url无签名正文中的 URL 在能逃逸之前会经过 SSRF 筛查
LicenseClient::__construct()参见签名代码块在构造时验证传输、URL scheme 与固定集LicenseConfigurationException(不强制策略的传输、不允许的 scheme、无固定值的纯 HTTP、格式错误的固定集)getPinSet() 为轮换工具暴露活跃的固定集
Online\StatusResponse只读值对象已验证的签名状态revoked / expired / releasedisLive() 为 false;携带 leaseExpiresAt、席位计数与一份权益快照
LicenseChannelMode / OnlineMode / OnlinePolicy枚举 + 只读值对象渠道与在线策略声明signed_source 对比 ioncube;在线模式 required / optional / disabled,带 intervalDaysleaseDays
LicenseEdition字符串支撑的枚举Core, Pro, Enterprisesatisfies() 实现层级体系
public function __construct(private ?EvaluationBrandingConfig $evaluationBrandingConfig = null)
public function evaluate(?LicenseKey $license, ?DateTimeImmutable $now = null): EntitlementResult
public function evaluateEnforced(?LicenseKey $license, EnforcementOutcome $outcome): EntitlementResult
public function resolveBrandingMode(?LicenseKey $license, ?DateTimeImmutable $now = null): BrandingMode
public function resolveBrandingStrategy(?LicenseKey $license, ?DateTimeImmutable $now = null): BrandingStrategy
public function __construct(private readonly LicenseStateCache $licenseCache, private readonly EntitlementResolver $entitlementResolver, private readonly string $upgradeInfoUrl = self::DEFAULT_UPGRADE_URL)
public function isFeatureEnabled(string $feature): bool
public function requireFeature(string $feature): void
public function currentEdition(): ?string
public function isLicenseValid(): bool
public function isInGracePeriod(): bool
public function hasCapability(CapabilityCode $capability): bool
public function requireCapability(CapabilityCode $capability): void
public function getActivePacks(): array
public function trialPolicy(): TrialPolicy
public function __construct(private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, private readonly StatusX5cValidator $statusValidator, private readonly string $baseUrl = self::DEFAULT_BASE_URL, #[SensitiveParameter] private readonly ?string $apiKey = null, array $pinnedPublicKeys = [], array $backupPins = [], private readonly OutboundHttpPolicy $policy = new OutboundHttpPolicy(allowedSchemes: ['https'], connectTimeoutSeconds: 10, readTimeoutSeconds: 30, maxResponseBytes: 10_485_760, followRedirects: false, allowPrivateTargets: false, retryAttempts: 0, retryBaseBackoffMs: 250))
public function activate(string $licenseJws, string $deploymentId, ?string $machineFingerprintHash = null, ?string $expectedLicenseId = null, ?string $clientNonce = null): StatusResponse
public function heartbeat(string $licenseJws, string $deploymentId, ?string $machineFingerprintHash = null, ?string $expectedLicenseId = null, ?string $clientNonce = null): StatusResponse
public function deactivate(string $licenseJws, string $deploymentId, ?string $machineFingerprintHash = null, ?string $expectedLicenseId = null, ?string $clientNonce = null): StatusResponse
public function verifyOnline(string $licenseJws, string $deploymentId, ?string $machineFingerprintHash = null, ?string $expectedLicenseId = null, ?string $clientNonce = null): StatusResponse
public function getPinSet(): ?PinSet
public function checkRenewal(string $licenseId, string $customerId): array

权益评估会产出五种当前状态之一,每种都带有一个固定的运行时后果:

状态运行时有效能力更新 / 支持品牌标识
Active允许所授权的版本 + 活跃各包是(付费);否(评估)无(付费);水印(评估)
GracePeriod允许所授权的版本 + 活跃各包无(付费)
DowngradedToCore允许仅 Core无(付费);水印(试用)
EvaluationExpired受限水印
NoLicense停用(失败即关闭)水印
  • 付费过期绝不会停用已安装的运行时。 runtimeAllowed 对每一种付费状态都保持为 true。超过宽限窗口后,有效版本降级到 Core:Core 能力无限期继续运行,所有 Pro/Enterprise/pack 能力门控关闭,更新与支持被撤销。已弃用的 EntitlementStatus::PerpetualFallback 分支命名了 6.0 之前的过期策略,仅为编译兼容而存在。
  • 并非每一种状态都以 Core 执行告终。 宽限期保留完整的所授权版本,并带有一条注明日期的续订警告。评估过期会限制运行时并保留水印。无授权状态会失败即关闭地停用 Enterprise 路径,并对任何旧版输出可见地加水印,因此它绝不会被误认为付费执行。
  • 固定的解析顺序。 一份已完全过期的授权解析不出任何能力。包能力延后到该包自身的活跃状态与独立过期。版本能力按严格层级 Enterprise ⊇ Pro ⊇ Core 对照强制有效版本解析。原始的 LicenseKey::$edition 绝不会被用于门控咨询。
  • 两个计时器;最严格者胜出。 权益计时器(两条渠道)驱动 Active、Grace 以及超过宽限后的 Core 降级。在线存活计时器仅适用于在线模式为 requiredioncube 渠道:租约加上 48 小时的存活宽限必须保持存活。超过租约后离线时,premium 会暂停在 Core 层级;稍后一次已验证的在线检查会重新确立租约,premium 随之恢复。signed_source 渠道——以及任何非 required 的在线模式——没有存活计时器,保持离线且具备气隙能力。
  • 反回滚是确定性的。 强制会对照一个单调下限进行评估:有效时间取系统时钟与上次已验证时间的最大值,因此时钟回拨不会带来任何额外时间。超过偏移容差与剩余宽限的回滚会强制 Core 降级。
  • 租约仅依据可信时间推进。 一次成功的在线检查会记录已验证的签名服务器时间,而绝不是本地时钟,因此本地时钟向前跳跃无法虚增租约。
  • 品牌标识只有一个权威。 只有评估渠道会触发水印。付费授权在每一种状态下——包括各降级状态——都解析为无品牌标识。
  • 试用是叠加在同一批计时器上的一层策略。 吞吐量被限制(默认每秒 2 页),评估水印生效,且过期严格执行、没有任何宽限。仅所报告的原因会改变;决策与有效版本遵循付费机。
  • 在线契约是端到端签名的。 每个请求都携带一个 CSPRNG 客户端 nonce。每个响应都是媒体类型为 application/nextpdf-status+jwt 的紧凑 EdDSA JWS,经由 x5c 链验证回一个编译内置的固定根,并绑定到所发送的 nonce、已安装的授权 id 与被调用的端点。旧版无签名封套传输已退役;恰好只剩一条信任路径。所配置的 SPKI 固定值作为 X-NextPDF-Pinned-Public-Key 头传输,且一个固定集必须包含一个备用固定值。
  • 没有运行时席位强制执行。 maxSlots 仅为合同约定——普通运行没有任何遥测,也没有强制的回拨上报。
  • 对一份已完全过期的授权进行能力检查,会在任何包或版本逻辑运行之前,短路到“不可用”。
  • 当基础授权处于活跃状态时,一个附加包可能已过期。此时拒绝会读作包已过期(SPEC-LIC-002),而非未授权(SPEC-LIC-001),并列出当前活跃的各包。
  • 一个存在但不可读或不可解析的本地强制状态文件是一个篡改信号。该机会失败即关闭到 Core 降级,并带 EnforcementReason::CorruptState。一个合法缺失的文件不算损坏,会正常继续。
  • 一次低于单调下限、且超过偏移容差(默认 120 秒)与剩余宽限的时钟回拨,会确定性地强制 Core 降级。
  • 一个已失效的存活租约会降级一份原本 Active 的 ioncube 授权;权益过期则无论租约状态如何都会降级。
  • evaluate(null) 是失败即关闭的:运行时被禁止,水印生效,警告会指明定价 URL。需要无授权 Core 行为的宿主必须直接构造 Core 引擎。
  • 一份过期的评估授权没有宽限、也没有回退;运行时受限,水印保持。
  • checkRenewal() 将 HTTP 404 视为正常的“无可用”答复。任何其他非 200 都会引发 LicenseClientException,而一个违反对外策略的 download_url 会在能逃出该方法之前失败即关闭。
  • 一个伪造、篡改、陈旧、nonce 错误或授权错误的状态响应绝不会产出 StatusResponse;验证会抛出 SpectrumAuthenticationException。一个已签名但为 revokedexpiredreleased 的状态会通过验证,并作为降级信号浮现(isLive() 为 false)。
  • 一个为不同交换铸造的状态响应会被拒绝:已验证的 response_type 必须与被调用的端点匹配。
  • FeatureGate 在任何解析器错误上失败即关闭:有效版本降级到 core,且不授予任何 premium 能力。
  • EnforcedEntitlementResolver 是端到端失败即关闭的:其链中的任何异常都会产出无授权(Core)结果与一条 PSR-3 错误日志,绝不会授予 premium。

授权层不附加任何自有的密码学策略。授权与状态签名是由宿主密码学提供方执行的 Ed25519 验证。在一个受 FIPS 约束的构建中,提供方不批准的某个算法会在密码学边界处以一个明确错误失败;不会有任何东西静默降级。这些是关于 NextPDF 代码的能力声明:NextPDF 不是一个经验证的密码学模块,不持有任何认证,也不授予任何认证。

声明标准条款
授权文件与状态响应是紧凑序列化的 JWS,以失败即关闭方式验证。RFC 7515§3.1
授权与状态签名是 Ed25519。RFC 8032§5.1
签名响应中的 x5c 链验证回一个编译内置的固定信任锚。RFC 5280§6.1
一个已配置的 SPKI 固定集必须包含一个用于尚未部署密钥的备用固定值。RFC 7469§4.3
已验证的声明(issaudexpnbfiat)是已注册的声明名。RFC 7519§4.1

所有条款均为转述;不复现任何规范性文本。NextPDF 不作任何认证声明。 支持不等于符合,符合也不等于认证。本模块以能力的形式使其行为与所引条款对齐;对授权或状态封套的接受,由 NextPDF 自身的固定根验证器决定,而非由外部权威决定。

  • 用一个强制策略的传输(规范做法是 SecurityAwareHttpClient)来构造 LicenseClient。原始的 PSR-18 客户端会在构造时被拒绝,从而使授权流量绝不会在缺少反 SSRF、无重定向、字节上限与超时控制的情况下发送。
  • 调用方提供的客户端 nonce 必须解码为至少 16 字节的熵。源代码将此下限依据于 RFC 8555 §6.5;该条款不在所引语料之内,因此此处的下限仅依据产品源代码陈述。
  • 通过 FeatureGate 对功能进行门控;绝不要基于原始的 LicenseKey::$edition 分支。门控只咨询强制有效版本,并失败即关闭到 core
  • 不要在新代码中对已弃用的 EntitlementStatus::PerpetualFallback 进行 switch。改为测试 isDowngradedToCore() 或匹配 DowngradedToCore
  • 将一次能力拒绝视为一份补救载荷。把它的结构化上下文与升级 URL 呈现给调用方,而不要将其坍缩为一个通用错误。
  • 本模块涉及密码学功能;在你自己的评审中应将其视为安全敏感。容量与席位条款由授权协议管辖,而非由运行时强制执行。本参考不构成法律意见;请咨询你自己的合规与法律顾问。

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