Skip to content

Licensing Service

@nx/licensing issues software licenses from reusable policies, binds them to devices via activations, validates them at runtime, and signs Ed25519 certificates that @nx/core's LicenseMiddleware verifies offline. It owns no schema (all tables live in @nx/core) and depends only on @nx/core. Billing, subscriptions, invoicing, usage-metering and dunning are explicitly out of scope.

1. Quick Reference

PropertyValue
Package@nx/licensing
CodeSVC-00140-LICENSING
TypeMicroservice
RuntimeBun
Base ClassVerifierApplication
Locationpackages/licensing
Base Path/v1/api/licensing
Dev Port31120
Container Port3000 (external 31120)
Snowflake ID11
DB Schemalicensing (5 tables)
Binding Namespace@nx/licensing
Run Modesstartup, migrate (RUN_MODE env)

SVC-00140-LICENSING is registered in core's ServiceCodes enum. It was reassigned from SVC-00110 (which @nx/outreach owns) to resolve the collision; SVC-00120 went to helpdesk, so licensing took SVC-00140. See ADR-0002.

2. Purpose & Scope

IncludedExcluded
Policy templates (TRIAL / SUBSCRIPTION / PERPETUAL)Billing, invoicing, dunning
Typed feature flags (PolicyFeature, polymorphic value)Subscription / payment integration
License issuance, renew, suspend, reinstate, revokeUsage metering / heartbeat reaper
Device activation (fingerprint binding, seat limit)Per-feature runtime enforcement (consumers do that)
Runtime validation pipeline (POST /validation/validate)License UI (frontend concern)
Ed25519 certificate signing + Redis distributionCertificate verification (done by @nx/core middleware)
Self-service free-trial issuance
LicenseEvent lifecycle audit log

3. Tech Stack

External:

LibraryPurpose
@venizia/ignisIoC container, DI, VerifierApplication, ControllerFactory
@venizia/ignis-helpersLogger, DefaultRedisHelper, applicationEnvironment, AES helper
@hono/zod-openapiOpenAPI generation from Zod schemas
@scalar/hono-api-referenceOpenAPI explorer at /doc
honoHTTP server (via IGNIS)
drizzle-ormDB access via PostgresCoreDataSource
pgPostgreSQL driver
node:cryptoEd25519 sign/verify, AES-256-GCM (in @nx/core cert helpers)

bullmq and @platformatic/kafka are listed in package.json but not wired — the application registers no Kafka, Queue, WebSocket or Mail component (see §8).

Internal:

PackagePurpose
@nx/corePolicy/License/Activation/PolicyFeature/LicenseEvent schemas + repositories (re-exported), VerifierApplication, LicenseCertSignerHelper, LicenseMiddleware, LicenseStatuses/PolicyTypes/DurationUnits, Redis helpers. This package owns no schema definitions.

4. Project Structure

packages/licensing/
├── src/
│   ├── application.ts            # VerifierApplication subclass
│   ├── index.ts                  # bootstrapApplication()
│   ├── migrate.ts                # bootstrapMigration()
│   ├── common/                   # constants, keys, rest-paths, types
│   ├── controllers/              # 5 controllers (policy, policy-feature, license, activation, validation)
│   ├── datasources/              # PostgresCoreDataSource binding
│   ├── errors/                   # license / activation / policy / duration errors
│   ├── migrations/processes/     # 4 seeds (permissions, free-trial, role-perms, guest-perms)
│   ├── models/requests/          # zod request schemas
│   ├── repositories/             # re-exports from @nx/core
│   └── services/licensing/       # 3 services + shared base
├── package.json
└── tsconfig.json

5. Architecture

Trust seam: licensing signs the Ed25519 certificate and writes it to Redis; every other service verifies it offline against the public key shipped in @nx/core. No service calls licensing for runtime checks. Detail: see Architecture.

6. Domain Snapshot

Full ERD + per-entity tables: see Domain Model.

7. Surface Summary

REST controllers — full reference rendered live from /v1/api/licensing/doc/openapi.json (Scalar viewer at /doc, gateway portal):

ControllerBase pathNotes
PolicyController/policiesCRUD (status soft-delete, no hard delete)
PolicyFeatureController/policy-featuresCRUD
LicenseController/licensesCRUD + issue / suspend / reinstate / renew / revoke / free-trial. Read routes temporarily skip authz ⚠️ (ADR-0003)
ActivationController/activationsCRUD
ValidationController/validationstateless validate (returns cert)

Async surface — see API Events:

DirectionChannelCount
InboundKafka0 (no consumer)
OutboundKafka0 (no producer)
Inbound / OutboundBullMQ0 (no queue)
OutboundWebSocket0 (no WS component)

The only cross-service async channel is Redis: licensing SETs a signed cert at lic:certs:<entityType>:<entityId>; consumers GET it via LicenseMiddleware.

8. Components

ComponentRegistrationPurpose
Redis cacheuseCacheRedis({ bindingKey: BindingKeys.APPLICATION_REDIS_CACHE })Certificate distribution + authorization permission cache

No Kafka, Queue (BullMQ), WebSocket, or Mail component is registered. configureComponents() calls only super.configureComponents() + useCacheRedis(...).

9. Services

ServiceFileOne-liner
LicenseManagementServiceservices/licensing/license-management.service.tsIssue / renew / suspend / reinstate / revoke; self-service free trial. All mutations transactional + row-locked; re-publishes certificate post-commit
ActivationServiceservices/licensing/activation.service.tsactivate() (idempotent fast-path → tx + lock → limit check) and deactivate()
ValidationServiceservices/licensing/validation.service.tsvalidate() pipeline: status → expiry/grace → features → activation → result code
LicensingBaseServiceservices/licensing/licensing-base.service.tsShared base (not bound as a service): key generation, feature resolution, publishCertificate(), logEvent()

10. Repositories

All re-exported from @nx/core — this package defines no repository classes and adds no custom methods.

RepositoryTableSource
PolicyRepositorylicensing.Policy@nx/core
PolicyFeatureRepositorylicensing.PolicyFeature@nx/core
LicenseRepositorylicensing.License@nx/core
ActivationRepositorylicensing.Activation@nx/core
LicenseEventRepositorylicensing.LicenseEvent@nx/core

Also bound for authorization seeds: MigrationRepository, PermissionRepository, PolicyDefinitionRepository, RoleRepository (all @nx/core).

11. Entry Points

FilePurpose
src/index.tsService entry → bootstrapApplication()
src/migrate.tsMigration entry → bootstrapMigration()
src/application.tsApplication extends VerifierApplication

12. Configuration

Env vars + feature flags + seeded data: see Configuration.

13. Operations

Deployment + observability + security + runbook + operation walkthrough: see Operations.

Concepts — why/how:

Reference — lookup:

Features — deep dives:

Decisions:

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.