# Deep Integration Module registry, dependency injection, feature gates, quotas, and advanced policy enforcement patterns. Canonical HTML URL: https://corelicense.net/docs/nodejs-sdk/deep-integration Plain-text URL: https://corelicense.net/docs/nodejs-sdk/deep-integration.txt ---# CoreLicense Node.js SDK — Design & Deep Integration Documentation > Goal: build a license/policy SDK for Node.js that ensures a developer's product only runs with a valid license, can revoke access when usage policies are violated, but **does not send business data / end-user data back to the developer's server**. --- ## 1. Design Philosophy CoreLicense should not be just middleware. If it only sits at the middleware layer, users with source code on their server can remove the middleware and continue running the app. Therefore CoreLicense must be designed as a **Runtime Layer** embedded deep within multiple critical points of the system: - Bootstrap app - Runtime config - Module registry - Service container - Business services - Worker / Queue / Cron - CLI command - Quota / Limit - Local audit - Policy token verification Principle: ```txt Don't guard the door. Build the whole house around CoreLicense. ``` CoreLicense must make removing the license a matter of restructuring the architecture, not simply deleting one line of middleware. --- ## 2. SDK v1 Scope SDK v1 prioritizes the Node.js ecosystem: - Express.js - Fastify - NestJS - Node.js service - PM2 process - Docker deployment - Worker / Queue / Cron - CLI tools - TypeScript project - JavaScript project Official package: ```txt @corelicense/node ``` --- ## 3. Overview Model ```txt ┌────────────────────────────────────────────┐ │ Node.js App │ │ Routes / Controllers / Views / API │ └────────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Core Runtime Layer │ │ Bootstrap / Config / Registry / Container │ └────────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ CoreLicense SDK │ │ Verify Token / Feature / Limit / Policy │ └────────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Business Modules │ │ Worker / Automation / Export / API / Jobs │ └────────────────────┬───────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ Database / Queue / Storage │ └────────────────────────────────────────────┘ ``` Standard placement of CoreLicense: ```txt App bootstrap ↓ CoreLicense boot() ↓ Runtime config ↓ Module registry ↓ Service container ↓ Business module ↓ Worker / Queue / Cron / CLI ``` --- ## 4. What CoreLicense Is Allowed to Do CoreLicense is allowed to: - Check whether the license is valid. - Verify signed policy tokens. - Check product ID. - Check instance ID. - Check license status: active, suspended, revoked, expired. - Check feature flags. - Check quota / limits. - Cache tokens locally. - Allow a grace period when the license server is temporarily unreachable. - Write local audit logs about license status. - Return runtime config to the app. - Disable sensitive modules when policy is violated. CoreLicense should not: - Send the customer's database to the developer's server. - Send business logs / private data to the developer's server. - Delete customer files on its own. - Destroy services/servers on its own. - Hide its process. - Self-recover like malware. - Install secret services. - Bypass system administration privileges. --- ## 5. License Status Standard statuses: ```txt active - Normal usage allowed limited - Usage allowed but with feature restrictions suspended - Temporarily suspended due to suspicion/violation/unverified revoked - Permanently or severely revoked expired - License expired offline - Cannot reach license server, using cache grace_expired - Grace period has ended invalid - Token/license incorrect or tampered ``` Behavior mapping: | Status | Web/API | Worker | CLI | Dashboard | Notes | |---|---|---|---|---|---| | active | Allow | Allow | Allow | Allow access | Normal | | limited | Partial allow | Limited | Limited | Allow access | Use feature flags | | suspended | Block sensitive modules | Stop sensitive workers | Block sensitive commands | Allow access to view status | Should not delete data | | revoked | Block main functionality | Stop | Stop | License/status view only | Revoked | | expired | Block or limited | Stop | Stop | Allow renewal | Depends on policy | | offline | Run using cache | Run if within grace | Run if within grace | Show warning | Cannot reach server | | grace_expired | Limited or block | Stop | Stop | Allow status access | Reconnection required | --- ## 6. Signed Policy Token CoreLicense should not trust raw JSON responses from the server. The license server must sign policy tokens with a private key. The SDK only holds the public key for verification. Recommended algorithms: - Ed25519 - Or RSA-PSS - Or ES256 - JWT/JWS may be used if a common standard is preferred Example payload: ```json { "licenseId": "lic_01HXABC", "productId": "mediahub", "customerId": "cus_01HXDEF", "instanceId": "ins_9c28a1", "status": "active", "features": { "dashboard": true, "api": true, "worker": true, "automation": false, "bulkTask": false, "export": true, "webhook": true }, "limits": { "maxUsers": 10, "maxWorkers": 2, "maxJobsPerDay": 5000, "maxConcurrentJobs": 3, "maxDomains": 5, "maxProjects": 10 }, "policyVersion": "2026.06.01", "issuedAt": 1780000000, "expiresAt": 1780086400, "graceUntil": 1780260000 } ``` The token must include: - `licenseId` - `productId` - `instanceId` - `status` - `features` - `limits` - `issuedAt` - `expiresAt` - `graceUntil` - `policyVersion` It should not contain: - End-user data - Database contents - Business logs - Customer secrets - Customer system access tokens --- ## 7. Instance ID The SDK needs to identify an instance to bind the license to a specific installation environment. Instance ID should be a hash; do not send raw sensitive data. Possible sources: - Product ID - License key - Primary domain - Hashed machine ID - Random install ID stored locally - Project ID / App ID Example: ```txt instanceSeed = productId + licenseKey + installId + normalizedDomain instanceId = sha256(instanceSeed) ``` Notes: - Do not send the real hostname unless necessary. - Do not send internal IP addresses. - Do not send server paths. - Do not send system usernames. - Allow instance rebind via the admin panel. --- ## 8. SDK Configuration Example: ```ts import { CoreLicense } from "@corelicense/node"; export const license = new CoreLicense({ productId: "mediahub", licenseKey: process.env.CORELICENSE_KEY, apiUrl: "https://license.example.com", publicKey: process.env.CORELICENSE_PUBLIC_KEY, cachePath: "./storage/corelicense", checkIntervalMs: 6 * 60 * 60 * 1000, requestTimeoutMs: 5000, offlineGrace: true, defaultBlockMode: "limited", instance: { strategy: "install-id-domain-hash", domain: process.env.APP_DOMAIN }, audit: { enabled: true, path: "./storage/corelicense/audit.log" // default: {cachePath}/audit.log } }); ``` --- ## 9. Proposed SDK API ### 9.1. Initialization ```ts const license = new CoreLicense(options); ``` ### 9.2. Bootstrap ```ts await license.boot(); ``` Responsibilities: - Load local cache. - Verify existing token if present. - If the token is expired or near expiry, call the server to check. - Verify signed token. - Build runtime config. - Start background checker if enabled. ### 9.3. Require active ```ts await license.requireActive(); ``` If the license is not permitted to run, throw `LicenseError`. ### 9.4. Express Middleware ```ts app.use(license.express()); ``` Whitelist configuration is supported: ```ts app.use(license.express({ allowPaths: [ "/health", "/license/status", "/license/reactivate", "/login" ], mode: "block" })); ``` ### 9.5. Fastify Middleware ```ts await app.register(license.fastifyPlugin()); ``` ### 9.6. NestJS Guard ```ts @Module({ imports: [ CoreLicenseModule.forRoot({ productId: "mediahub" }) ] }) export class AppModule {} ``` ```ts @UseGuards(CoreLicenseGuard) @Controller("automation") export class AutomationController {} ``` ### 9.7. Feature gate ```ts await license.requireFeature("automation"); await license.requireFeature("export.users"); await license.requireFeature("worker.video"); ``` ### 9.8. Limit checker ```ts await license.checkLimit("maxConcurrentJobs", currentRunningJobs); await license.checkLimit("maxUsers", currentUserCount); ``` ### 9.9. Worker guard ```ts await license.guardWorker("main-worker"); queue.process(async (job) => { await license.requireFeature(`job.${job.name}`); await license.consumeQuota(`job.${job.name}`); return handleJob(job); }); ``` ### 9.10. CLI guard ```ts await license.guardCommand("export"); await runExport(); ``` ### 9.11. Runtime config ```ts const runtime = await license.getRuntimeConfig(); ``` Return: ```ts { status: "active", modules: { dashboard: true, worker: true, automation: false }, limits: { maxWorkers: 2, maxJobsPerDay: 5000 }, policyVersion: "2026.06.01" } ``` ### 9.12. Status ```ts const status = await license.getStatus(); ``` ### 9.13. Manual refresh ```ts await license.refresh(); ``` --- ## 10. Deep Bootstrap Integration Do not: ```ts startServer(); ``` Instead: ```ts async function main() { await license.boot(); const runtime = await license.getRuntimeConfig(); const app = await createApp({ runtime, license }); await app.listen(process.env.PORT || 3000); } main().catch((err) => { console.error(err); process.exit(1); }); ``` Bootstrap must handle: - License missing - Token invalid - License expired - License revoked - Offline but cache valid - Offline and grace expired Example policy: ```txt active -> start normally limited -> start restricted mode suspended -> start dashboard-only mode revoked -> start license-status-only mode or exit expired -> start renewal mode offline -> start if grace remains grace_expired -> restricted mode or exit ``` --- ## 11. Runtime Config CoreLicense must produce runtime config for the app. Example: ```ts const runtimeConfig = await license.buildRuntimeConfig(); ``` Contents: ```json { "mode": "active", "modules": { "dashboard": true, "api": true, "worker": true, "automation": false, "export": true }, "limits": { "maxUsers": 10, "maxWorkers": 2 }, "routes": { "automation": false, "export": true }, "workers": { "video": true, "bulk": false } } ``` The app must boot based on this config. Do not hardcode: ```ts enableAutomation(); enableExport(); ``` Instead: ```ts if (runtime.modules.automation) { moduleRegistry.register("automation", new AutomationModule()); } ``` --- ## 12. Module Registry CoreLicense should integrate with a module registry. ```ts class ModuleRegistry { private modules = new Map(); register(name: string, module: unknown) { this.modules.set(name, module); } get(name: string): T { if (!this.modules.has(name)) { throw new Error(`Module ${name} is not available`); } return this.modules.get(name) as T; } } ``` Boot: ```ts const runtime = await license.getRuntimeConfig(); if (runtime.modules.dashboard) { registry.register("dashboard", new DashboardModule()); } if (runtime.modules.automation) { registry.register("automation", new AutomationModule(license)); } if (runtime.modules.export) { registry.register("export", new ExportModule(license)); } ``` Controller: ```ts app.post("/automation/run", async (req, res) => { const automation = registry.get("automation"); const result = await automation.run(req.body); res.json(result); }); ``` If the license does not include automation, the module does not exist. --- ## 13. Service Container If the app has a service container, the license must sit in the resolver. Example: ```ts container.bind("AutomationService", async () => { await license.requireFeature("automation"); return new AutomationService(license); }); ``` Controller: ```ts const service = await container.resolve("AutomationService"); await service.run(payload); ``` Even without middleware, the service will not resolve when the feature is unavailable. --- ## 14. Business Service Guard Every important service must check features on its own. Example: ```ts class ExportService { constructor(private license: CoreLicense) {} async exportUsers(params: ExportParams) { await this.license.requireFeature("export.users"); await this.license.consumeQuota("export.users", 1); return this.doExportUsers(params); } } ``` ```ts class AutomationService { constructor(private license: CoreLicense) {} async runTask(input: RunTaskInput) { await this.license.requireFeature("automation.run"); await this.license.checkLimit("maxConcurrentJobs", await this.countRunningJobs()); return this.execute(input); } } ``` Do not let the controller handle the real business logic. --- ## 15. Worker / Queue / Cron Workers must go through CoreLicense. Do not: ```ts queue.process("send", sendHandler); ``` Instead: ```ts queue.process("*", async (job) => { await license.guardWorker("main-worker"); await license.requireFeature(`job.${job.name}`); return jobRegistry.run(job.name, job); }); ``` Cron: ```ts cron.schedule("*/5 * * * *", async () => { await license.guardCommand("cron.sync"); await syncService.run(); }); ``` CLI: ```ts async function main() { await license.boot(); await license.guardCommand(process.argv[2]); await commandRegistry.run(process.argv[2]); } ``` --- ## 16. Quota / Usage The CoreLicense SDK can enforce quota locally. Example: ```ts await license.consumeQuota("jobs.daily", 1); ``` Local usage must be persisted: ```txt .storage/corelicense/usage.json ``` Example: ```json { "date": "2026-06-07", "jobs.daily": 120, "export.users": 3 } ``` The server may return limits; the SDK enforces them locally. For absolute accuracy, the server must record usage centrally, but that increases data sent back. With a privacy-first goal, v1 should enforce locally or only send minimal aggregates if the customer agrees. --- ## 17. Local Cache Cache path: ```txt .storage/corelicense/ ├── policy.token ├── instance.json ├── runtime.json ├── usage.json └── audit.log ``` Cache requirements: - Restricted permissions. - No sensitive data. - Signed token. - Runtime config can be rebuilt from the token. - Usage can be reset by day/month. --- ## 18. Offline Grace Period Flow: ```txt App calls license server ↓ Success -> receive new token ↓ Server error/network loss -> verify cached token ↓ Token still valid -> run normally ↓ Token expired but within graceUntil -> run in offline mode ↓ Past graceUntil -> limited/block ``` Recommended policy: ```txt Token TTL: 6-24 hours Grace period: 24-72 hours Background check: every 6 hours ``` --- ## 19. Background Checker The SDK can run a background checker: ```ts license.startBackgroundCheck(); ``` Responsibilities: - Refresh token periodically. - Update runtime config. - If status changes, emit an event. Events: ```ts license.on("statusChanged", (status) => {}); license.on("policyUpdated", (policy) => {}); license.on("licenseRevoked", () => {}); license.on("licenseSuspended", () => {}); license.on("offlineMode", () => {}); ``` Example: ```ts license.on("licenseSuspended", async () => { await workerManager.stopSensitiveWorkers(); }); ``` --- ## 20. Local Audit CoreLicense should write local audit logs. Example log: ```json { "time": "2026-06-07T14:20:00Z", "event": "license_check_success", "status": "active", "policyVersion": "2026.06.01" } ``` ```json { "time": "2026-06-07T18:20:00Z", "event": "feature_blocked", "feature": "automation.run", "reason": "feature_disabled" } ``` Do not log: - Customer payloads - End-user tokens - Database rows - Export contents - Private information --- ## 21. Error Classes The SDK should provide clear errors: ```ts LicenseError LicenseRevokedError LicenseSuspendedError LicenseExpiredError LicenseInvalidError FeatureDisabledError QuotaExceededError OfflineGraceExpiredError PolicySignatureError ``` Example: ```ts try { await license.requireFeature("automation"); } catch (err) { if (err instanceof FeatureDisabledError) { return res.status(403).json({ code: "FEATURE_DISABLED", message: "This feature is not available for your license." }); } } ``` --- ## 22. Express Integration ```ts import express from "express"; import { CoreLicense } from "@corelicense/node"; const app = express(); const license = new CoreLicense({ productId: "mediahub", licenseKey: process.env.CORELICENSE_KEY!, apiUrl: process.env.CORELICENSE_API_URL!, publicKey: process.env.CORELICENSE_PUBLIC_KEY! }); await license.boot(); app.use(license.express({ allowPaths: ["/health", "/license/status"] })); app.get("/license/status", async (req, res) => { res.json(await license.getStatus()); }); app.post("/export/users", async (req, res) => { await license.requireFeature("export.users"); res.json({ ok: true }); }); app.listen(3000); ``` --- ## 23. NestJS Integration Core module: ```ts @Module({}) export class CoreLicenseModule { static forRoot(options: CoreLicenseOptions): DynamicModule { const provider = { provide: CoreLicense, useFactory: async () => { const license = new CoreLicense(options); await license.boot(); return license; } }; return { module: CoreLicenseModule, providers: [provider, CoreLicenseGuard], exports: [CoreLicense, CoreLicenseGuard] }; } } ``` Guard: ```ts @Injectable() export class CoreLicenseGuard implements CanActivate { constructor(private license: CoreLicense) {} async canActivate(context: ExecutionContext): Promise { await this.license.requireActive(); return true; } } ``` Feature decorator: ```ts @RequireFeature("automation.run") @Post("/run") run() {} ``` --- ## 24. Fastify Integration ```ts await fastify.register(coreLicenseFastifyPlugin, { license }); ``` Hook: ```ts fastify.addHook("preHandler", async (request, reply) => { await license.requireActive(); }); ``` --- ## 25. PM2 Integration PM2 is not a security boundary. Do not rely on PM2 alone. However, the SDK can support: ```bash CORELICENSE_KEY=xxx pm2 start ecosystem.config.js ``` Health check: ```ts app.get("/health/license", async (req, res) => { res.json(await license.getStatus()); }); ``` PM2 ecosystem: ```js module.exports = { apps: [ { name: "mediahub", script: "dist/server.js", instances: 4, exec_mode: "cluster", env: { CORELICENSE_KEY: "xxx", CORELICENSE_API_URL: "https://license.example.com" } } ] }; ``` --- ## 26. Docker Integration Environment: ```yaml services: app: image: mediahub:latest environment: CORELICENSE_KEY: "${CORELICENSE_KEY}" CORELICENSE_API_URL: "https://license.example.com" CORELICENSE_PUBLIC_KEY: "${CORELICENSE_PUBLIC_KEY}" volumes: - ./storage/corelicense:/app/storage/corelicense ``` Do not bake the license key into a public image. --- ## 27. Security Model CoreLicense cannot provide 100% protection if someone has full source code and permission to modify the server. Practical goals: ```txt - Prevent simple bypass. - Increase the cost of modification/removal. - Bind the license to app architecture. - Provide a clear legal/policy basis. - Do not turn the SDK into malware. ``` Protection layers: 1. Signed policy token. 2. Runtime config depends on the token. 3. Module registry depends on runtime config. 4. Business services self-check features. 5. Worker/CLI/Cron guard. 6. Separate core package. 7. Part of verification may be moved into a Go/Rust binary if needed. 8. Moderate code obfuscation when distributing source/bundle. --- ## 28. Architectural Bypass Prevention Not sufficient: ```txt app.use(license.middleware()) ``` More sufficient: ```txt license.boot() runtimeConfig = license.getRuntimeConfig() moduleRegistry.boot(runtimeConfig) serviceContainer.bindWithLicense() businessService.requireFeature() worker.guardWorker() cli.guardCommand() quota.consume() ``` Bypass at this point requires modifying: - Bootstrap - Runtime config - Module registry - Service container - Business services - Worker - CLI - Cron - Quota - Error handling --- ## 29. SDK Files Proposed structure: ```txt corelicense-node/ package.json tsconfig.json README.md src/ index.ts core-license.ts types/ options.ts policy.ts status.ts runtime.ts limits.ts crypto/ verify-policy-token.ts key-loader.ts client/ license-api-client.ts cache/ cache-store.ts file-cache-store.ts memory-cache-store.ts instance/ instance-id.ts install-id.ts guards/ express-middleware.ts fastify-plugin.ts nest-guard.ts feature-guard.ts quota-guard.ts worker-guard.ts cli-guard.ts runtime/ runtime-config.ts module-registry.ts service-container.ts audit/ local-audit.ts errors/ license-error.ts feature-error.ts quota-error.ts policy-error.ts events/ event-bus.ts ``` --- ## 30. Public API v1 ```ts class CoreLicense { constructor(options: CoreLicenseOptions) boot(): Promise refresh(): Promise requireActive(): Promise requireFeature(feature: string): Promise checkLimit(limitName: string, currentValue: number): Promise consumeQuota(quotaName: string, amount?: number): Promise getStatus(): Promise getPolicy(): Promise getRuntimeConfig(): Promise express(options?: ExpressGuardOptions): RequestHandler fastifyPlugin(options?: FastifyGuardOptions): FastifyPluginCallback guardWorker(name: string): Promise guardCommand(name: string): Promise startBackgroundCheck(): void stopBackgroundCheck(): void on(event: CoreLicenseEvent, handler: Function): void } ``` --- ## 31. License Server API Called by the SDK The SDK needs at minimum: ```txt POST /v1/client/activate POST /v1/client/check POST /v1/client/refresh ``` Check request: ```json { "productId": "mediahub", "licenseKey": "lic_key_xxx", "instanceId": "ins_hash", "sdk": { "name": "@corelicense/node", "version": "1.0.0", "runtime": "node", "nodeVersion": "22.0.0" } } ``` Response: ```json { "ok": true, "status": "active", "policyToken": "eyJhbGciOiJFZERTQSJ9...", "serverTime": "2026-06-07T12:00:00Z" } ``` --- ## 32. Minimal Data Policy The SDK only sends: - productId - licenseKey - instanceId - sdk version - app version if available - hash of previous policy token if needed - timestamp It does not send: - customer database - user table - request body - uploaded files - internal logs - secrets - server env - IP list - business data --- ## 33. Deep Integration Checklist for a Node.js App Required: - [ ] Call `license.boot()` before starting the server. - [ ] App boots from `runtimeConfig`. - [ ] Routes/API have middleware. - [ ] Sensitive modules register via Module Registry. - [ ] Important services call `requireFeature()`. - [ ] Workers call `guardWorker()`. - [ ] Queue jobs call `requireFeature(jobName)`. - [ ] Cron calls `guardCommand()`. - [ ] CLI calls `guardCommand()`. - [ ] Quota uses `checkLimit()` or `consumeQuota()`. - [ ] Local cache is configured. - [ ] Offline grace is enabled. - [ ] `/license/status` route exists. - [ ] Local audit is enabled. - [ ] Terms of Use documentation exists. - [ ] End-user data is not sent to the license server. --- ## 34. SDK Roadmap ### v0.1 - TypeScript core - Signed token verification - File cache - Express middleware - `boot()` - `requireActive()` - `requireFeature()` - `checkLimit()` - Local audit ### v0.2 - Worker guard - CLI guard - Background refresh - Offline grace - Runtime config ### v0.3 - Fastify plugin - NestJS module/guard - Module registry helper - Quota local usage ### v0.5 - PM2/Docker examples - Better error mapping - Policy versioning - Event system ### v1.0 - Stable API - Full docs - Production-ready - Security review - Example apps --- ## 35. Legal & Transparency Principles Products integrating CoreLicense must clearly disclose: ```txt The software uses license verification and policy status checks. The system only sends minimal information to verify the license, including product ID, license key, hashed instance ID, and SDK version. The system does not send end-user data, databases, file contents, business logs, or private information to the license server. ``` Terms of use should state: ```txt The developer reserves the right to suspend or revoke a license if there is evidence or reasonable grounds that the software is used for fraud, spam, phishing, malware, system attacks, data theft, illegal activity, or infringement of third-party rights. ``` --- ## 36. Conclusion The CoreLicense SDK for Node.js should be a runtime layer, not a simple middleware. Correct design: ```txt Bootstrap + Runtime Config + Module Registry + Service Guard + Worker Guard + CLI Guard + Signed Token ``` When implemented this way, someone with source code can still modify it, but they cannot remove the license by deleting one line of middleware. They must change the entire app architecture, rewrite runtime config, module registry, service guards, worker guards, and the quota layer. Practical goals: ```txt Hard to bypass simply. Transparent. Does not collect private data. Has lawful revocation rights. Easy to integrate for Node.js products. Can be extended to Laravel, Python, Go later. ```