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
| Property | Value |
|---|---|
| Package | @nx/licensing |
| Code | SVC-00140-LICENSING |
| Type | Microservice |
| Runtime | Bun |
| Base Class | VerifierApplication |
| Location | packages/licensing |
| Base Path | /v1/api/licensing |
| Dev Port | 31120 |
| Container Port | 3000 (external 31120) |
| Snowflake ID | 11 |
| DB Schema | licensing (5 tables) |
| Binding Namespace | @nx/licensing |
| Run Modes | startup, migrate (RUN_MODE env) |
SVC-00140-LICENSINGis registered in core'sServiceCodesenum. It was reassigned fromSVC-00110(which@nx/outreachowns) to resolve the collision;SVC-00120went to helpdesk, so licensing tookSVC-00140. See ADR-0002.
2. Purpose & Scope
| Included | Excluded |
|---|---|
| Policy templates (TRIAL / SUBSCRIPTION / PERPETUAL) | Billing, invoicing, dunning |
Typed feature flags (PolicyFeature, polymorphic value) | Subscription / payment integration |
| License issuance, renew, suspend, reinstate, revoke | Usage 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 distribution | Certificate verification (done by @nx/core middleware) |
| Self-service free-trial issuance | |
LicenseEvent lifecycle audit log |
3. Tech Stack
External:
| Library | Purpose |
|---|---|
@venizia/ignis | IoC container, DI, VerifierApplication, ControllerFactory |
@venizia/ignis-helpers | Logger, DefaultRedisHelper, applicationEnvironment, AES helper |
@hono/zod-openapi | OpenAPI generation from Zod schemas |
@scalar/hono-api-reference | OpenAPI explorer at /doc |
hono | HTTP server (via IGNIS) |
drizzle-orm | DB access via PostgresCoreDataSource |
pg | PostgreSQL driver |
node:crypto | Ed25519 sign/verify, AES-256-GCM (in @nx/core cert helpers) |
bullmqand@platformatic/kafkaare listed inpackage.jsonbut not wired — the application registers no Kafka, Queue, WebSocket or Mail component (see §8).
Internal:
| Package | Purpose |
|---|---|
@nx/core | Policy/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.json5. 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):
| Controller | Base path | Notes |
|---|---|---|
PolicyController | /policies | CRUD (status soft-delete, no hard delete) |
PolicyFeatureController | /policy-features | CRUD |
LicenseController | /licenses | CRUD + issue / suspend / reinstate / renew / revoke / free-trial. Read routes temporarily skip authz ⚠️ (ADR-0003) |
ActivationController | /activations | CRUD |
ValidationController | /validation | stateless validate (returns cert) |
Async surface — see API Events:
| Direction | Channel | Count |
|---|---|---|
| Inbound | Kafka | 0 (no consumer) |
| Outbound | Kafka | 0 (no producer) |
| Inbound / Outbound | BullMQ | 0 (no queue) |
| Outbound | WebSocket | 0 (no WS component) |
The only cross-service async channel is Redis: licensing
SETs a signed cert atlic:certs:<entityType>:<entityId>; consumersGETit viaLicenseMiddleware.
8. Components
| Component | Registration | Purpose |
|---|---|---|
| Redis cache | useCacheRedis({ bindingKey: BindingKeys.APPLICATION_REDIS_CACHE }) | Certificate distribution + authorization permission cache |
No Kafka, Queue (BullMQ), WebSocket, or Mail component is registered.
configureComponents()calls onlysuper.configureComponents()+useCacheRedis(...).
9. Services
| Service | File | One-liner |
|---|---|---|
LicenseManagementService | services/licensing/license-management.service.ts | Issue / renew / suspend / reinstate / revoke; self-service free trial. All mutations transactional + row-locked; re-publishes certificate post-commit |
ActivationService | services/licensing/activation.service.ts | activate() (idempotent fast-path → tx + lock → limit check) and deactivate() |
ValidationService | services/licensing/validation.service.ts | validate() pipeline: status → expiry/grace → features → activation → result code |
LicensingBaseService | services/licensing/licensing-base.service.ts | Shared 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.
| Repository | Table | Source |
|---|---|---|
PolicyRepository | licensing.Policy | @nx/core |
PolicyFeatureRepository | licensing.PolicyFeature | @nx/core |
LicenseRepository | licensing.License | @nx/core |
ActivationRepository | licensing.Activation | @nx/core |
LicenseEventRepository | licensing.LicenseEvent | @nx/core |
Also bound for authorization seeds:
MigrationRepository,PermissionRepository,PolicyDefinitionRepository,RoleRepository(all@nx/core).
11. Entry Points
| File | Purpose |
|---|---|
src/index.ts | Service entry → bootstrapApplication() |
src/migrate.ts | Migration entry → bootstrapMigration() |
src/application.ts | Application extends VerifierApplication |
12. Configuration
Env vars + feature flags + seeded data: see Configuration.
13. Operations
Deployment + observability + security + runbook + operation walkthrough: see Operations.
14. Related Pages
Concepts — why/how:
- Architecture
- Domain Model
- Integration — trust seam with
@nx/coreLicenseMiddleware
Reference — lookup:
- API Events
- Configuration
- Operations
- REST endpoints — live OpenAPI at
/v1/api/licensing/doc/openapi.json
Features — deep dives:
Decisions: