Status
Accepted (Revised, 2026-07-25).
Context
A private NuGet feed only limits future downloads; it cannot recall cached packages. Customers run offline, on-prem, in containers, and on Kubernetes, so commercial runtime entitlement needs a trust model independent of package-download rights and tenant Feature entitlement.
Decision
Thirteen decisions define the trust model:
- ES256-signed immutable License Envelope — vendor private key signs; customer Runtime public key verifies; private key only in KMS/HSM or the controlled issuing system.
- The License constrains Customer, Product, Edition, Feature, Version Range, Environment, Deployment Limit, validity, offline lease, grace period, KeyId, and protocol version.
- The request path does not remotely call the License service — online activation issues a short-term lease; Runtime verifies signature + state locally; a background service refreshes safely.
- Fail-closed states — never-activated, forged signature, revoked, or over-privileged fail closed. An already-activated instance may enter limited
Graceon a brief network failure but must alert and expose readiness. - Unified states:
Valid / Grace / Expired / Revoked / Invalid / Unavailable. - License enforcement is not only UI/Host middleware — a commercial module execution entry consumes the License decision via a generated descriptor or the unified Mediator pipeline; a missing Licensing adapter defaults deny.
- DeploymentId is a persistent Site/Deployment identity — not PodId/ContainerId/MAC/CPU — shared across multi-instance so scaling does not re-consume authorization.
- Offline lease — offline deployments use a time-limited signed License file; immediate revocation is impossible, so revocation lists and renewals arrive via a controlled offline process.
- License expiry must not break customer backup/export/migration; write limits and data portability follow the commercial contract.
- Strong Name/obfuscation/integrity/Native AOT only raise attack cost — they are not the trust root.
- Key rotation and protocol portability on expiry — a shared free strategy cannot be an unsigned bypass. Protocol v2 adds
policyId/profile/staticcompositionto the signed payload; protocol v1 canonical signature bytes remain unchanged. A shared DeploymentId is allowed only for the precise combinations activated by ADR 0504. - The base control plane does not depend on customer commercial entitlement — minimum identity sessions, forced password change, password recovery, permission-filtered navigation, and authorized LicenseManagement operations use a compile-time exact message list to bypass the commercial gate. Authentication, authorization, validation, transactions, and audit still execute; registration, invitations, user administration, and business features remain outside the list.
- Base readiness and commercial readiness are separate —
/health/readydecides whether the instance can receive base traffic,/health/licensereports commercial entitlement, and/healthremains the aggregate diagnostic. An unavailable License does not remove the bootstrap control plane from the load balancer; normal business operations still fail closed.
Consequences
The License module is a deep module hiding signature, trusted time, cache, online/offline lease, key rotation, and the state machine behind a stable interface. When a customer fully controls the private host, absolute anti-patch is impossible — truly non-leakable capability stays in a vendor-controlled remote service. PlatformBilling may offer in-customer-system tenant plans but cannot issue or decide the vendor Runtime License.