npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ikl-sdk

v0.0.5

Published

IKLicensing SDK client with online/offline validation, signed licenses, device binding, software matching, and watchdog support.

Downloads

729

Readme

IKLicensing Client

Cliente Node.js para activar, almacenar y validar licencias de software. La libreria soporta activacion online, validacion online/offline, solicitudes offline, importacion de licencias offline, verificacion de firma digital, validacion por dispositivo, validacion por nombre/version de software y watchdog manual de integridad.

Requisitos

  • Node.js con soporte para ES Modules.
  • Backend de licencias compatible con los endpoints /activate y /validate.
  • Llave publica RSA configurada en src/crypto/PublicKey.js.
  • Dependencias instaladas con npm install.

Ejemplos

La carpeta examples/ contiene scripts ejecutables para los flujos principales:

| Archivo | Caso de uso | | --- | --- | | examples/activate-online.js | Activacion online con verificacion antes de guardar. | | examples/validate-startup.js | Validacion recomendada al iniciar la aplicacion. | | examples/validate-offline.js | Validacion local sin backend. | | examples/offline-generate-request.js | Generacion de solicitud offline. | | examples/offline-import-license.js | Importacion y validacion de licencia offline. | | examples/watchdog-manual.js | Inicio manual del watchdog. | | examples/request-error-handling.js | Manejo de errores tipados. | | examples/check-fingerprint.js | Consulta del fingerprint actual. | | examples/clear-license.js | Eliminacion de licencia local. |

Consulta examples/README.md para comandos y variables de entorno.

Conceptos

La licencia guardada tiene esta forma general:

{
  "payload": {
    "licenseId": 5,
    "licenseKey": "VIDEO-TEST-0002-EEEE",
    "type": "PRO",
    "fingerprint": "5e5c97012c5ef30ad082a07be45a43a5ea1722861f819fecf6405f2f75bf5abb",
    "expiresAt": "2027-12-31",
    "softwareName": "VideoConverter",
    "softwareVersion": "1.0.*"
  },
  "signature": "..."
}

El cliente valida:

  • Que exista una licencia.
  • Que la firma digital sea valida.
  • Que no este expirada.
  • Que pertenezca al software que se esta ejecutando.
  • Que pertenezca a la version del software o a un patron compatible.
  • Que pertenezca al dispositivo actual mediante fingerprint.

Versiones con comodines

softwareName se compara de forma exacta. softwareVersion acepta coincidencia exacta o comodines * dentro de la licencia:

| Version en licencia | Version actual | Resultado | | --- | --- | --- | | 1.0.0 | 1.0.0 | Valida | | 1.0.* | 1.0.7 | Valida | | 1.* | 1.2.3 | Valida | | 2.* | 1.9.0 | Invalida |

Configuracion basica

Ejemplo de integracion usando los modulos de la libreria:

import {
  LicenseClient,
  FileStorage,
  HardwareProvider,
  ApiClient,
  ActivationService,
  ValidationService,
  OfflineRequestService,
  OfflineLicenseImporter,
  PUBLIC_KEY
} from "ikl-sdk";


const SOFTWARE_NAME = "PhotoEditor";
const SOFTWARE_VERSION = "2.5.1";

const apiClient = new ApiClient({
  baseURL: "https://tu-api.com/api",
  timeout: 10000
});

const storage = new FileStorage({
  softwareName: SOFTWARE_NAME
});
const hardwareProvider = new HardwareProvider();

const activationService = new ActivationService({
  apiClient,
  publicKey: PUBLIC_KEY,
  hardwareProvider,
  storage,
  softwareName: SOFTWARE_NAME,
  softwareVersion: SOFTWARE_VERSION
});

const validationService = new ValidationService({
  apiClient,
  hardwareProvider,
  storage,
  softwareName: SOFTWARE_NAME,
  softwareVersion: SOFTWARE_VERSION
});

const offlineRequestService = new OfflineRequestService({
  hardwareProvider,
  softwareName: SOFTWARE_NAME,
  softwareVersion: SOFTWARE_VERSION
});

const offlineLicenseImporter = new OfflineLicenseImporter({
  storage
});

const client = new LicenseClient({
  publicKey: PUBLIC_KEY,
  storage,
  hardwareProvider,
  activationService,
  validationService,
  offlineRequestService,
  offlineLicenseImporter,
  softwareName: SOFTWARE_NAME,
  softwareVersion: SOFTWARE_VERSION,
  watchdogIntervalMs: 5 * 60 * 1000
});

Activacion online

activateOnline(licenseKey) llama al backend y recibe una licencia generada online. Antes de guardar la licencia, el cliente ejecuta todas las verificaciones locales contra la licencia recibida:

  • Firma digital.
  • Expiracion.
  • Software y version.
  • Fingerprint del dispositivo.

Solo si la licencia recibida es valida se guarda en disco.

try {
  const activation = await client.activateOnline("PHOTO-TEST-0001-AAAA");

  if (!activation.success) {
    console.error("No se pudo activar", activation.reason || activation.error);
    process.exit(1);
  }

  console.log("Licencia activada");
} catch (error) {
  console.error(error.code, error.message);
}

Validacion

Validacion automatica online/offline

validate() intenta validar online. Si el backend confirma que la licencia sigue activa, igualmente ejecuta la validacion local completa contra license.dat. Si no puede validar online por un error de red no definitivo, cae a validacion offline.

const result = await client.validate();

if (!result.valid) {
  console.error("Licencia invalida:", result.reason);
  process.exit(1);
}

console.log("Licencia valida", result.license.payload);

Validacion online

const result = await client.validateOnline();

El endpoint /validate recibe:

{
  "licenseId": 5,
  "fingerprint": "...",
  "softwareName": "PhotoEditor",
  "softwareVersion": "2.5.1"
}

Si el backend responde valid: true, el cliente valida localmente la licencia guardada antes de devolver exito.

Validacion offline

const result = await client.validateOffline();

Esta validacion no llama al backend. Usa la licencia almacenada localmente y verifica firma, expiracion, software, version y fingerprint.

Flujo offline

Cuando una maquina no puede activar online, genera una solicitud offline:

const { path, request } = await client.generateOfflineRequest("PHOTO-TEST-0001-AAAA");

console.log("Solicitud generada en:", path);
console.log(request.toJSON());

La solicitud incluye:

  • licenseKey
  • fingerprint
  • timestamp
  • deviceName
  • softwareName
  • softwareVersion

Luego se importa una licencia emitida por el backend:

const license = await client.importOfflineLicense("/ruta/a/license.dat");
console.log("Licencia importada", license);

Despues de importar, se recomienda ejecutar:

const validation = await client.validateOffline();

Watchdog manual

El watchdog no se inicia automaticamente. Debe activarse manualmente cuando la aplicacion ya decidio que corresponde monitorear la integridad de la licencia.

client.startWatchdog({
  intervalMs: 60 * 1000,
  runImmediately: true,
  onValidation: (result) => {
    console.log("Watchdog:", result.valid, result.reason);
  },
  onInvalid: (result) => {
    console.error("Licencia invalida durante ejecucion:", result.reason);
    process.exit(1);
  },
  onError: (error) => {
    console.error("Error de watchdog:", error.code, error.message);
  }
});

Para detenerlo:

client.stopWatchdog();

El watchdog usa ConnectivityService para detectar internet con:

https://www.google.com/generate_204

Si hay conexion, valida online y luego localmente. Si no hay conexion, valida offline. El watchdog evita validaciones solapadas si una ejecucion tarda mas que el intervalo.

Almacenamiento local

FileStorage guarda los archivos en una carpeta dependiente del sistema operativo:

| Sistema | Ruta base | | --- | --- | | Windows | %APPDATA%/MyLicenseApp | | macOS | ~/Library/Application Support/MyLicenseApp | | Linux | ~/.mylicenseapp |

Archivos usados:

| Archivo | Uso | | --- | --- | | license/license.dat | Licencia activa | | license/request.dat | Solicitud offline | | license/validation.json | Cache de validacion, disponible en storage |

API principal

| Metodo | Descripcion | | --- | --- | | activateOnline(licenseKey) | Activa online y guarda la licencia solo si pasa todas las verificaciones locales. | | validate() | Intenta validar online y cae a offline cuando corresponde. | | validateOnline() | Valida contra el backend y luego valida localmente la licencia guardada. | | validateOffline() | Valida localmente sin llamar al backend. | | generateOfflineRequest(licenseKey) | Genera request.dat para activacion offline. | | importOfflineLicense(filePath) | Importa una licencia desde archivo. | | getFingerprint() | Devuelve el fingerprint del dispositivo. | | getLicense() | Devuelve la licencia guardada o null. | | isActivated() | Indica si existe una licencia local. | | clearLicense() | Elimina la licencia local. | | startWatchdog(options) | Inicia manualmente el watchdog. | | stopWatchdog() | Detiene el watchdog. | | runWatchdogValidation() | Ejecuta una validacion de watchdog una sola vez. |

Resultado de validacion

Las validaciones devuelven un objeto compatible con:

{
  valid: true,
  reason: null,
  status: "VALID",
  license: {
    payload: {},
    signature: "..."
  }
}

Cuando falla:

{
  valid: false,
  reason: "INVALID_SOFTWARE",
  status: "INVALID",
  license: null
}

Errores y codigos

Errores de licencia

Estos codigos aparecen normalmente como ValidationResult.reason. Tambien pueden preservarse como error.code cuando el backend los devuelve en el body.

| Codigo | Significado | Causa comun | Accion sugerida | | --- | --- | --- | --- | | VALID | Licencia valida. | Todas las verificaciones pasaron. | Permitir ejecucion. | | INVALID | Licencia invalida sin razon especifica. | Falla generica o respuesta incompleta. | Reintentar validacion o pedir reactivacion. | | NO_LICENSE | No existe licencia local. | license.dat no fue creado o fue eliminado. | Activar online, generar request offline o importar licencia. | | INVALID_SIGNATURE | La firma digital no corresponde al payload. | Licencia alterada, corrupta o firmada con otra llave privada. | Rechazar ejecucion y solicitar una licencia nueva. | | EXPIRED | La licencia expiro. | payload.expiresAt es anterior a la fecha actual. | Renovar licencia. | | INVALID_DEVICE | La licencia pertenece a otro dispositivo. | El fingerprint actual no coincide con payload.fingerprint. | Reemitir licencia para este equipo. | | INVALID_SOFTWARE | La licencia pertenece a otro software o version incompatible. | softwareName no coincide o softwareVersion no cumple el patron. | Usar una licencia emitida para este producto/version. |

Errores de request

ApiClient normaliza errores HTTP, red y timeout. Todos extienden RequestError y exponen name, code, status, message, details y toJSON().

| Clase | Codigo por defecto | HTTP | Significado | Accion sugerida | | --- | --- | --- | --- | --- | | BadRequestError | BAD_REQUEST | 400 | Request mal formado. | Revisar payload enviado al backend. | | UnauthorizedError | UNAUTHORIZED | 401 | No autorizado. | Revisar credenciales, tokens o configuracion del backend. | | ForbiddenError | FORBIDDEN | 403 | El backend rechazo la operacion. | Revisar details; puede contener un codigo de negocio como INVALID_SOFTWARE. | | NotFoundError | NOT_FOUND | 404 | Endpoint o recurso no encontrado. | Revisar baseURL y rutas del backend. | | ConflictError | CONFLICT | 409 | Conflicto de estado. | Revisar si la licencia ya esta activada o asignada. | | UnprocessableEntityError | UNPROCESSABLE_ENTITY | 422 | El backend entiende el request, pero no lo puede procesar. | Mostrar el mensaje del backend y corregir datos. | | ServerError | SERVER_ERROR | 5xx | Error del servidor. | Reintentar luego o revisar logs del backend. | | RequestError | REQUEST_ERROR | Otros | Error HTTP no clasificado. | Revisar status y details. | | NetworkError | NETWORK_ERROR | N/A | No hubo respuesta HTTP. | Verificar conexion, DNS, firewall o backend apagado. | | TimeoutError | REQUEST_TIMEOUT | N/A | El request excedio el timeout. | Reintentar, aumentar timeout o revisar latencia. |

Codigos preservados desde el backend

Si el backend responde con esta forma:

{
  "success": false,
  "error": {
    "code": "INVALID_SOFTWARE",
    "message": "Invalid software"
  }
}

El cliente preserva el codigo de negocio como error.code, incluso si el HTTP status es, por ejemplo, 403.

try {
  await client.activateOnline("PHOTO-TEST-0001-AAAA");
} catch (error) {
  console.error(error.name);    // ForbiddenError
  console.error(error.code);    // INVALID_SOFTWARE
  console.error(error.status);  // 403
  console.error(error.message); // Invalid software
  console.error(error.details); // Respuesta completa del backend
}

Contrato esperado del backend

POST /activate

Request:

{
  "licenseKey": "PHOTO-TEST-0001-AAAA",
  "fingerprint": "...",
  "softwareName": "PhotoEditor",
  "softwareVersion": "2.5.1"
}

Respuesta exitosa:

{
  "success": true,
  "data": {
    "payload": {
      "licenseId": 1,
      "licenseKey": "PHOTO-TEST-0001-AAAA",
      "fingerprint": "...",
      "expiresAt": "2027-12-31",
      "softwareName": "PhotoEditor",
      "softwareVersion": "2.5.*"
    },
    "signature": "..."
  }
}

POST /validate

Request:

{
  "licenseId": 1,
  "fingerprint": "...",
  "softwareName": "PhotoEditor",
  "softwareVersion": "2.5.1"
}

Respuesta exitosa:

{
  "valid": true
}

Respuesta con error de negocio:

{
  "success": false,
  "error": {
    "code": "INVALID_SOFTWARE",
    "message": "Invalid software"
  }
}

Buenas practicas de integracion

  • Definir softwareName y softwareVersion como constantes de build de la aplicacion.
  • No guardar licencias recibidas del backend sin pasar por ActivationService.
  • Ejecutar validate() al iniciar la aplicacion.
  • Iniciar startWatchdog() manualmente solo cuando la aplicacion ya este lista para monitorear integridad.
  • En onWatchdogInvalid, cerrar la aplicacion, bloquear funcionalidades o limpiar sesion segun el modelo de seguridad.
  • Mostrar al usuario mensajes amigables, pero registrar error.code, error.status y error.details para soporte.
  • Tratar INVALID_SIGNATURE, INVALID_DEVICE e INVALID_SOFTWARE como errores definitivos.
  • Tratar NETWORK_ERROR y REQUEST_TIMEOUT como errores temporales.