コンテンツにスキップ
getnextpdf.com

Enterprise エディション

SaaS — 詳細リファレンス

Enterprise SaaS モジュールは、NextPDF ベースのサービスのためのマルチテナントの構成要素を提供します。

  • TenantContext は、認証済みコンテキストからのみ解決される、イミュータブルな ID 値オブジェクトです。
  • ApiKeyGeneratorApiKeyAuthenticator は、プレフィックス付き・チェックサム付き・ハッシュ保存の API キーを発行および検証します。
  • QuotaChecker は、テナントごとのクォータに対してリクエストをゲートします(80% で警告、100% で拒否、使用量が不明な場合はフェイルクローズで拒否)。
  • SidecarJwtMinter は、コンポーネント間呼び出しのための短命な HS256 サービストークンを生成します。
  • UsageMeterStripeMeteringSyncer は、使用量イベントをプルし、決定的なべき等性を用いて課金プロバイダーに同期します。

このケイパビリティは NextPDF Enterprisenextpdf/enterprise)に同梱されており、Enterprise ティアのライセンスエンベロープで有効化されます。その権利がないデプロイでは、ケイパビリティのクラスはロードされません。エディションを比較してライセンスを取得

SaaS サーフェスは Enterprise の基本ケイパビリティであり、機能ごとの独立したフラグはありません。NextPDF Core(Apache-2.0)と NextPDF Pro には、テナンシー、API キー、クォータのモデルがありません。このケイパビリティに下位ティアの同等品はありません。

Terminal window
composer require nextpdf/enterprise:^3

すべてのシンボルは NextPDF\Enterprise\SaaS 配下にあります。

シンボルパラメーターデフォルト挙動戻り値スロー/失敗条件備考
TenantContextstring $tenantId, string $source, array $scopes = ['read']イミュータブルな ID 値オブジェクト値オブジェクトなしソース: jwtmtlsapi_keyhasScope() / hasAnyScope() でスコープを判定
TenantContext::singleTenant()なしreadwriteadmin を持つ固定 default テナントTenantContextなしシングルテナントデプロイ向け
ApiKeyAuthenticator::authenticate()string $rawKey6 ステップの検証後にコンテキスト解決TenantContextApiKeyAuthenticationException(HTTP 401)コンテキストの sourceapi_key。スコープはキーレコードから複製
ApiKeyAuthenticator::requireScope()TenantContext $context, ApiKeyScope $requiredScope明示的なスコープアサーションvoidApiKeyAuthenticationException::insufficientScope()(HTTP 403)スコープ強制は別個の明示的なステップ
ApiKeyGenerator::generateLive() / ::generateTest()なし新規キー: プレフィックス、32 文字の base62 本体(192 ビットエントロピー)、4 文字のチェックサムarray{key, hash, prefix}なしプレフィックス npf_live_ / npf_test_hash はストレージダイジェスト
ApiKeyGenerator::validateChecksum()string $keyプレフィックス、長さ、CRC32 チェックサムの形状チェックboolなしデータストア参照前の入力ミスガード。セキュリティコントロールではない
ApiKeyGenerator::hashKey()(静的)string $key生キーの SHA-256 16 進ダイジェストstringなしキーの唯一の保存表現
ApiKeyGenerator::isLiveKey() / ::isTestKey()string $keyプレフィックス検査boolなし参照なしで環境が判別可能
ApiKeyid、テナント、キーハッシュ、表示プレフィックス、スコープマスク、作成/有効期限/失効の各時刻保存キーレコード。平文は保存されない値オブジェクトなしisActive()isRevoked()isExpired()scopeNames()
ApiKeyScopebacked enum: Read = 1Write = 2Admin = 4ビットマスクスコープモデルenumなしmaskFromNames()fromName()fullAccess()。未知の名前はマスクビルダーで無視
ApiKeyRepositoryInterfaceストレージコントラクト。ハッシュのみ保存実装依存findByHash()findActiveByTenant()store()revoke()
SidecarJwtMinter::__construct()string $secret, issuer, audience, int $ttlSeconds = 300構築時に 16 バイト未満の署名シークレットを拒否インスタンスInvalidArgumentException128 ビットの鍵強度下限。32 バイト以上のランダムバイトを推奨
SidecarJwtMinter::mint()TenantContext $tenantissaudsubscopetenant_idiatexpjti を持つ HS256 JWTstringクレームエンコード失敗時に JsonExceptionデフォルト有効期間 5 分。jti は 16 ランダムバイトの 16 進エンコード
QuotaChecker::check()TenantContext $tenant, TenantQuota $quota現在の使用量を読み取り、80% で警告、100% で拒否、使用量不明時は拒否array{allowed: bool, warning_percentage: float|null}QuotaExceededException, QuotaUnavailableException両しきい値でアラートコールバックを呼び出し
TenantQuotafloat $maxCuPerPeriod、コレクション、ストレージバイト、同時実行ジョブ期間ごとの上限。80% ソフトしきい値定数値オブジェクトなしfromConfig() デフォルト: 10,000 CU、100 コレクション、10 GB、10 ジョブ
QuotaExceededException::toErrorEnvelope()なしSPEC-QUOTA-001 エラーエンベロープarrayHTTP 402、リトライ不可。現在値、上限、リセット時刻を保持
QuotaUnavailableException::toErrorEnvelope()なしSPEC-QUOTA-503 エラーエンベロープarrayHTTP 503、リトライ可能。理由 usage_undeterminable
UsageMeter::pullUsage()array<string, int> $watermarks構成済みの各使用量ソースホストをそのカーソルからポーリングarray{events, instance_id}全ホスト到達不能時に UsageMeterException部分的な障害は許容。到達不能ホストはログ記録してスキップ
UsageMeter::getCurrentUsage()string $tenantId当期のコンピュートユニット使用量float使用量が判定不能な場合に UsageMeterException解析可能なゼロは権威あり。不明な使用量は例外をスロー
StripeMeteringSyncer::sync()array<string, int> $watermarksプル、変換、送信の 1 サイクルarray{watermarks, sent, failed}なし。送信失敗は DLQ コールバックへルーティングプル失敗はカーソルを保持する no-op サイクルを返す
StripeAdapter::sendMeterEvent()MeterEvent $eventべき等性ヘッダー付きでプロバイダーへ POSTvoidStripeSyncExceptionHTTP 429 と 5xx はリトライ可能。その他の 4xx はリトライ不可
StripeAdapter::sendBatch()list<MeterEvent> $events各イベントを送信し、失敗を収集list<StripeSyncException>なし空リストは全イベント成功を意味する
MeterEventメーター名、テナント、値、べき等性キー、タイムスタンプイミュータブルなメーターイベント値オブジェクト値オブジェクトなしtoStripePayload() がプロバイダーペイロードをシリアライズ
final readonly class ApiKeyAuthenticator
{
public function __construct(
private ApiKeyRepositoryInterface $repository,
private ApiKeyGenerator $generator,
private LoggerInterface $logger,
) {}
public function authenticate(string $rawKey): TenantContext {}
public function requireScope(TenantContext $context, ApiKeyScope $requiredScope): void {}
}
final class QuotaChecker
{
public function __construct(
private readonly UsageMeterInterface $usageMeter,
private readonly LoggerInterface $logger,
private readonly Closure $quotaAlertCallback,
) {}
/** @return array{allowed: bool, warning_percentage: float|null} */
public function check(TenantContext $tenant, TenantQuota $quota): array {}
}
interface UsageMeterInterface
{
/** @return array<string, mixed> */
public function pullUsage(array $watermarks): array;
public function getCurrentUsage(string $tenantId): float;
}
final class StripeMeteringSyncer
{
public function __construct(
private readonly UsageMeterInterface $usageMeter,
private readonly StripeAdapterInterface $stripeAdapter,
private readonly LoggerInterface $logger,
private readonly Closure $dlqCallback,
) {}
/** @return array{watermarks: array<string, int>, sent: int, failed: int} */
public function sync(array $watermarks): array {}
}
final readonly class SidecarJwtMinter
{
public function __construct(
private string $secret,
private string $issuer = 'nextpdf-enterprise',
private string $audience = 'nextpdf-spectrum',
private int $ttlSeconds = self::DEFAULT_TTL_SECONDS,
) {}
public function mint(TenantContext $tenant): string {}
}
  • テナント ID。 テナントコンテキストはイミュータブルです(テナント識別子、解決ソース、スコープ)。ID は認証済みコンテキストからのみ解決され(jwtmtlsapi_key)、クライアントが提供したヘッダーやクエリパラメーターからは決して解決されません。シングルテナントデプロイでは、フルスコープを持つ固定の default コンテキストを使用します。
  • 認証順序。 API キー認証は固定された順序で進行します(チェックサム、SHA-256 ハッシュ、リポジトリ参照、失効チェック、有効期限チェック、コンテキスト解決)。未知、失効、有効期限切れのキーは 3 つの別個の結果であり、いずれも HTTP 401。スコープ不足は HTTP 403 です。
  • キーの秘匿。 生キーは保存もログ記録もされず、その SHA-256 ダイジェストのみが保存・参照されます。認証器自体はバイト単位のシークレット比較を行いません。定時間のダイジェスト参照はリポジトリ実装のコントラクトです。
  • クォータしきい値。 80% のソフトリミットではリクエストは続行し、警告パーセンテージが返され、アラートコールバックが発火します。100% のハードリミットでは、リセット時刻(翌月初日の UTC 午前 0 時)を保持する SPEC-QUOTA-001(HTTP 402)とともにリクエストが拒否されます。
  • クォータのフェイルクローズ。 判定不能な使用量は、SPEC-QUOTA-503(HTTP 503、リトライ可能)とともにリクエストを拒否します。不明な使用量がゼロとして扱われることは決してありません。本物の解析可能なゼロ使用量は権威があり、許可されます。
  • アラートの重複排除。 チェッカーはアラートの重複排除を行いません。期間ごとの重複排除はコールバックの責任です。
  • 計測同期。 サイクルはスケジュールされており、リクエストパス上では決して実行されません。ソースごとのウォーターマークから再開し、各カーソルを正常に送信されたイベント ID のうち最も大きいものまで進めます。べき等性キーは決定的であり(テナント、期間、イベント ID)、再送信されたイベントはプロバイダー側の重複排除で統合されます。
  • プル失敗。 失敗したプルは、ウォーターマークを保持する no-op サイクル(sent 0、failed 0)を返します。次のサイクルは、そのウィンドウをスキップするのではなく再試行します。
  • サービストークン。 トークンは共有シークレットを用いた HS256 であり、issaudsubscopetenant_idiatexp、および一意の jti を保持します。デフォルトの有効期間は 5 分です。構築時に 16 バイト未満のシークレットをフェイルクローズで拒否します。
  • 不正な形式のキーはチェックサムに失敗し、データストアへのアクセス前に拒否されます。形式は正しいが未知のキーは、参照後に拒否されます。いずれも無効キーの結果として現れます。
  • 未知、失効、有効期限切れのキーは別個の例外ファクトリを使用します。keyExpired フラグが真になるのは有効期限切れの結果の場合のみです。これらを別個のクライアントレスポンスにマッピングしてください。
  • QuotaChecker::check() は許可時にのみ戻り、返される allowed は常に true です。拒否と利用不可は例外的な結果です。
  • TenantQuota::usagePercentage() は、非正のクォータに対して 0.0 を返します。fromConfig() は、欠落した値をデフォルトで補い、整数の上限を最低 1 にクランプします。
  • ウォーターマークはソースごとです。ウォーターマークが存在しない場合は、そのソースのストリームの先頭(カーソル 0)から開始します。マルチソースのデプロイは独立したウォーターマークを維持します。
  • 変換は、非配列のイベント、操作またはテナントが欠落もしくは空のイベント、非正の値、マッピングされていない操作を、サイクルを失敗させずにスキップします。使用可能な正の整数 ID を欠くイベントは、警告とともに拒否されます。ランダムなフォールバックキーはプロバイダー側の重複排除を無効化し、テナントに二重課金する可能性があるためです。
  • 連続 10 回の送信失敗は、クリティカルログエントリにエスカレーションされます。カウンターは、送信が成功するたびにリセットされます。失敗したすべてのイベントは、それでもデッドレターコールバックに到達します。
  • 使用量ソースホストからの不正な形式の JSON ボディは、サイクル失敗ではなく空のイベントリストを生じます。pullUsage() は、構成済みの全ホストが到達不能な場合にのみ例外をスローします。
  • ダイジェストと MAC のプリミティブは、ホスト PHP の暗号プロバイダーを通じた SHA-256 および HMAC-SHA256 です。FIPS 制約付きのビルドは、承認されていないアルゴリズムに対してダウングレードするのではなくフェイルクローズします。SaaS レイヤーは独自の暗号ポリシーを一切追加しません。
  • キー本体とトークン識別子は、CSPRNG(random_int()random_bytes())から生成されます。
  • CRC32 チェックサムは暗号コントロールではなく、FIPS モードの影響を受けません。

以下の記述は、引用された条項に対するケイパビリティを説明するものです。これらは認証の主張ではありません。NextPDF はこのモジュールについていかなる認証も保持していません。

挙動参照
サービストークン exp の not-after セマンティクスRFC 7519 §4.1.4
サービストークンの JWS コンパクトシリアライゼーションRFC 7515 §3.1
16 バイトの HS256 シークレット下限。人間が記憶可能なパスワードを MAC キーに使用しないRFC 8725 §3.5(脅威: §2.2)
リポジトリのダイジェスト参照における定時間コントラクトOWASP ASVS 5.0 §11.2.4
API キーストレージダイジェスト SHA-256FIPS 180-4(コード宣言)

RFC 8725 および OWASP ASVS 5.0 の引用は RAG 検証済みであり、完全な参照識別子はこのページのフロントマターに記録されています。FIPS 180-4、FIPS 198-1、BSI TR-02102-1 の参照は、製品ソース内でコード宣言されており(hash('sha256', …) およびミンターの文書化された鍵下限)、このページのために RAG コーパスから取得したものではありません。ASVS §11.2.4 の定時間要件は、認証器クラス自体ではなく、オペレーターが提供するリポジトリ実装を拘束します。

  • ApiKeyRepositoryInterfaceStripeAdapterInterface の永続的な実装を提供してください。パッケージはコントラクトと PSR-18 プロバイダークライアントを同梱しており、永続化層は含みません。
  • 依存関係は PSR 抽象化のみです(PSR-3 ロガー、PSR-18 HTTP クライアント、PSR-17 リクエストおよびストリームファクトリ)。プロバイダー SDK は不要です。
  • 計測同期はスケジュールされたジョブとして実行してください。各サイクル後に、返されたウォーターマークを永続的に保存してください。
  • クォータの警告パーセンテージを、たとえば警告ヘッダーとしてクライアントに表示し、クォータアラートをコールバック内で期間ごとに重複排除してください。
  • トークンミンターのシークレットは、高エントロピーのランダム値として構成から供給してください。32 バイト以上のランダムバイトを推奨します。パスワードから導出しては決してなりません。
  • キープレフィックスにより、参照なしで環境が判別可能です。プレフィックスは保存されるダイジェストに含まれるため、sandbox キーと production キーが衝突することは決してありません。
  • 内部メカニズムの詳細は、ソースリポジトリの内部ドキュメントに留まり、本マニュアルの対象外です。

本ページは、外部から観測可能な挙動とサポート対象のパブリック API サーフェスのみを記載します。内部名前空間パス、ヘルパークラス、メカニズムテーブル、ランブックのファイル名、チケットプレフィックスは対象外です。