Skip to content

Casos de uso

Dar soporte a clientes offline y aislados

Licencia máquinas sin internet: archivos de licencia offline independientes, leases aislados de larga duración, importación de un bloque de cobertura firmado por archivo, y cuándo elegir offline en lugar de la activación en línea.

Objetivo. Licenciar una máquina que no puede — o no quiere — llegar a tu servicio: una estación de trabajo desconectada, una red empresarial securizada, un equipo embebido. Todo lo que el cliente necesita llega como un archivo firmado, se verifica localmente y nada «llama a casa».

Cuándo usarlo

  • Úsalo si algunos clientes operan en redes aisladas, o vendes en entornos regulados donde las conexiones salientes están prohibidas.
  • Úsalo si un cliente normalmente en línea debe sobrevivir a una interrupción larga y la ventana de lease de 14 días no es suficiente.
  • No lo uses si tus clientes están conectados habitualmente — la activación en línea normal ya funciona offline entre renovaciones y te da aplicación de puestos y revocación en vivo. El licenciamiento offline renuncia a eso a cambio de independencia de la red.

Hay tres bloques de construcción offline, y son independientes — elige lo que la situación necesite:

Bloque de construcciónQué es¿Puestos?¿Revocable?
Archivo de licencia offline (license.json)Una licencia firmada independiente — nunca hay un viaje de ida y vuelta de activaciónSin seguimiento de puestosSolo mediante una lista de revocación distribuida
Lease offline (aislado)Un lease firmado ordinario, acuñado para una máquina, con un TTL largoSí — confirma un puesto al emitirseEn la siguiente comprobación (ninguna, si nunca está en línea)
Archivo de bloque de coberturaEl bloque firmado de cobertura de versiones, entregado como archivo—Se sustituye por un archivo más nuevo

Los tres se verifican con la misma clave pública integrada que tu aplicación ya lleva, usando las mismas comprobaciones RSA/SHA-256 que los leases en línea — de modo que «offline» cambia solo cómo se entrega el documento firmado, nunca cómo se confía en él. La profundidad del modelo de lease/anti-retroceso vive en Seguridad y el modelo de leases; esta página es la tarea.

Archivo de licencia offlinelicense.json firmado · sin puestose distribuye con el productoLease aisladomachine-id → el proveedor firmaTTL largo, vinculado a una máquinaconfirma un puesto al emitirArchivo de bloque de coberturaexport → importel nº de secuencia crece (anti-retroceso)Una clave pública integradaRSA / SHA-256 · verificación localproduct · machine · expiry · seqLicenciado, totalmente offlinenunca llama a casaVinculado a la huella de una máquina, un archivo es inútil si se copia a otra; un número de secuencia menor se rechaza.Un bloque ausente nunca es «sin restricciones» — solo un documento firmado otorga algo.
Tres rutas de entrega offline, una verificación local: cada archivo firmado se comprueba contra la clave que ya está en tu binario.

Cuándo elegir offline frente a en línea

Recurre a la activación en línea por defecto — te da aplicación de puestos y revocación en vivo, y aun así funciona offline durante toda la ventana del lease entre renovaciones. Elige una ruta offline cuando:

  • La máquina puede llegar a ti ocasionalmente pero no de forma fiable → la activación en línea con un TTL de lease ampliado suele bastar; pasa a un lease aislado (365 días por defecto) solo si puede quedarse a oscuras más tiempo del que cualquier ventana de lease te resulte cómoda.
  • La máquina nunca llegará a ti → un lease aislado (conserva la vinculación por máquina y el recuento de puestos) o un archivo de licencia independiente (sin red ni para ti ni para el cliente, sin seguimiento de puestos — lo mejor para despliegues embebidos/verdaderamente aislados).

Qué haces tú (lado del proveedor)

Lease offline (aislado) — vinculado a una máquina

El cliente te envía su id de máquina; tú acuñas un lease de larga duración para ella:

# the customer reads their machine id first:  keyright machine-id   (or MachineFingerprint.Current().ToBoundString())
curl -X POST $BASE/admin/licenses/LIC-…/offline-lease -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
  -d '{"machineId":"KRM1:…","days":365}'  > offline-lease.json
# -> { "machineId":"KRM1:…", "ttlDays":365, "lease":"{…signed lease JSON…}" }

Acuñar un lease offline confirma un puesto para esa máquina, así que las activaciones aisladas siguen contando contra el límite de puestos — no puedes acuñar leases offline ilimitados más allá del número de puestos. El lease está vinculado a esa única huella, de modo que el archivo es inútil en cualquier otra máquina (MachineMismatch).

En el panel esto es la acción License file (archivo de licencia) de la licencia — firma el lease offline para el id de máquina que indiques y te entrega el JSON para que lo hagas llegar:

La página de la licencia de Stark Industries en Keyright con un botón 'License file' en la fila de acciones (junto a Renew, Add seats y Send portal link) — el control que firma un lease offline de larga duración para una máquina aislada — sobre el panel Devices que lista las máquinas y los puestos vinculados, y una tarjeta Details que muestra la edición pro y sus derechos.
La acción License file de una licencia acuña el lease offline firmado para un id de máquina dado — el equivalente en el panel de la llamada offline-lease de arriba — que entregas por archivo al equipo aislado.

Archivo de licencia offline independiente — sin servicio alguno

Para despliegues genuinamente aislados o embebidos, firma un archivo de licencia con la CLI y distribúyelo con el producto — sin activación, nunca:

keyright license --sign --licensee "Acme Corp" --product acme-app --tier pro \
  --private-key acme-app.private.json --expiry 2027-12-31 --out license.json

En este modo no hay seguimiento de puestos; es lo adecuado para equipos aislados y embebidos. Para cortar una compilación así, distribuyes una lista de revocación firmada (GET /admin/revocation-list) con tu siguiente actualización.

Archivo de bloque de cobertura — para máquinas aisladas que aplican la cobertura de versiones

Una máquina desconectada no puede recibir su bloque de cobertura en la activación, así que entrégalo como archivo:

# one license
curl $BASE/admin/licenses/LIC-…/coverage/block -H "X-Admin-Token: $TOKEN"
# -> { "block":"…signed envelope…" }

# every license of a product, for bulk air-gapped delivery
curl $BASE/admin/products/acme-app/coverage/export -H "X-Admin-Token: $TOKEN"
# -> { "product":"acme-app", "count":5, "items":[ { "licenseId":"…", "coverageBlock":"…" }, … ] }

Una renovación para un cliente aislado es simplemente un archivo de cobertura más nuevo: lleva un número de secuencia mayor, así que sustituye al que está en caché, mientras que un archivo más antiguo se rechaza (anti-retroceso). La exportación respeta tu derecho exactamente igual que la activación, así que nunca puede dejar fuera a nadie.

En tu código

Todo lo que sigue se verifica localmente, sin red, contra la clave pública que el cliente ya integra. Las tres rutas se muestran en cada SDK (la importación del bloque de cobertura es hoy una llamada de .NET — los demás SDK usan la ruta del lease, o verifican el sobre por HTTP en bruto):

.NET

using Keyright.Client;
using Keyright.Coverage;

// (A) Standalone license file / env var: just validate what's present — Validate() discovers it by
//     explicit path, the KEYRIGHT_LICENSE env var, ConfigLicensePath, or the OS app-data location.
LicenseInfo info = License.Validate();          // offline, never throws; fails closed to Free on any problem
if (info.IsPaid) { /* licensed */ }

// (B) Air-gapped lease: import the file the vendor signed for THIS machine. This one DOES throw if invalid.
try
{
    LicenseInfo leased = License.ImportOfflineLease(File.ReadAllText("offline-lease.json"));
    // leased.Status == Valid, fully offline; wrong machine/product/signature/expiry -> throws
}
catch (InvalidOperationException ex) { ShowOfflineLeaseError(ex.Message); }

// (C) Coverage block file: import the signed envelope; verifies, binds to the license, enforces anti-rollback.
CoverageVerifyResult r = License.ImportCoverageBlock(File.ReadAllText("acme.coverage.json"));
if (!r.Ok)
{
    // r.Status: Malformed | UnknownKey | BadSignature | RolledBack | WrongBinding | UnsupportedVersion
    ShowImportError(r.Status);
}

Node.js

const fs = require('fs');

// (A) Offline validate — synchronous, no network, never throws; failures resolve to 'free'.
const { info } = license.validate();
if (info.isPaid) { /* licensed */ }

// (B) Air-gapped lease: import the file the vendor signed for THIS machine. Throws if invalid.
try {
  const leased = license.importOfflineLease(fs.readFileSync('offline-lease.json', 'utf8'));
  // leased.isValid === true, fully offline
} catch (err) { showOfflineLeaseError(err.message); }

// (C) Coverage block file: no SDK call in Node — import it with the .NET SDK, or verify the
//     signed envelope yourself over raw HTTP (below).

Python

# (A) Offline validate — no network, never raises; failures resolve to Edition.FREE.
info = license.validate_info()
if info.is_paid:
    ...  # licensed

# (B) Air-gapped lease: import the file the vendor signed for THIS machine. Raises if invalid.
try:
    with open("offline-lease.json", encoding="utf-8") as f:
        leased = license.import_offline_lease(f.read())   # leased.is_valid, fully offline
except ValueError as ex:
    show_offline_lease_error(str(ex))

# (C) Coverage block file: no SDK call in Python — use the .NET SDK, or verify the envelope over raw HTTP.

Java

import java.nio.file.Files;
import java.nio.file.Path;

// (A) Offline validate — no network, never throws; failures resolve to Edition.FREE.
LicenseInfo info = client.validateInfo();
if (info.isPaid()) { /* licensed */ }

// (B) Air-gapped lease: import the file the vendor signed for THIS machine. Throws if invalid.
try {
    LicenseInfo leased = client.importOfflineLease(Files.readString(Path.of("offline-lease.json")));
} catch (IllegalArgumentException | IllegalStateException ex) {
    showOfflineLeaseError(ex.getMessage());
}

// (C) Coverage block file: no SDK call in Java — use the .NET SDK, or verify the envelope over raw HTTP.

Cualquier lenguaje (HTTP en bruto)

# (A/B) An offline lease is an ordinary signed lease with a long leaseExpiresUtc — verify it with the
#       EXACT same code as an online lease: rebuild the canonical payload, RSA-verify, then check
#       product / machine / expiry, failing closed on anything. See "Use the HTTP API directly".
#       (Offline leases are never marked trial, so no |trial suffix applies.)
# (C)   A coverage block file carries its own signed envelope and a monotonic sequence number:
#       verify the signature the same way, bind it to the license, and reject a sequence number
#       lower than the one the machine already holds (anti-rollback).

Algunas cosas que el SDK garantiza aquí, para que tu UX sea correcta:

  • Validate() y las comprobaciones de cobertura nunca lanzan excepciones por condiciones offline/de red; ImportOfflineLease sí lanza una excepción ante un lease inválido, así que envuélvelo.
  • Un bloque ausente nunca es «sin restricciones». Eliminar un bloque de cobertura de una máquina produce UnavailableOffline (en una compilación que aplica cobertura) o ninguna cobertura — nunca un pase silencioso. CheckCoverageAsync devuelve UnavailableOffline cuando no hay nada en caché y no se puede llegar al servicio — la señal para mostrar «importa un archivo de licencia actualizado».
  • El anti-retroceso es un número de secuencia. Una importación RolledBack significa que el archivo es más antiguo que lo que la máquina ya tiene y se ignoró; WrongBinding significa que se emitió para otra licencia/producto; UnknownKey significa una clave que tu compilación no integra (durante una rotación de claves, conserva la clave pública anterior en AdditionalPublicKeysBase64).

Los consumidores de HTTP en bruto verifican un lease offline con exactamente el mismo código que un lease en línea — es un lease firmado ordinario con un leaseExpiresUtc largo — usando la receta de carga útil canónica de Usar la API HTTP directamente.

Anti-retroceso y el modelo de leases, en breve

Cada documento offline está vinculado a la huella de una máquina (dentro de la carga útil firmada, de modo que una copia a otra máquina falla con MachineMismatch), y los bloques de cobertura llevan un número de secuencia monótono para que un archivo antiguo no pueda reproducirse para restaurar una cobertura caducada. El vencimiento forma parte de los bytes firmados — editar una fecha rompe la firma — y retrasar el reloj lo detecta la detección de manipulación del reloj en pruebas y licencias por plazo. Ese es el resumen; el tratamiento completo, incluido por qué una licencia de un solo puesto no puede ejecutarse en dos máquinas ni siquiera offline, está en Seguridad y el modelo de leases.

Cloud frente a autoalojado

  • Los flujos offline son idénticos — mismos endpoints, mismos archivos, misma verificación local. La diferencia es solo $BASE/$TOKEN.
  • Los archivos de licencia independientes mediante la CLI necesitan la clave privada del producto para firmar; eso es natural en autoalojado (tú la tienes), y en Cloud normalmente usarías en su lugar la ruta del lease aislado (acuñado en el servidor), ya que la clave privada nunca sale del servicio alojado.
  • Una nota sobre la desactivación offline: una máquina aislada no puede devolver su puesto por sí misma. Libéralo del lado del proveedor una vez que tu backend tenga conectividad, o emite leases offline con un TTL lo bastante corto para que el puesto de una máquina retirada vuelva por sí solo.

Verificar

  1. Archivo de licencia: firma un license.json, apunta KEYRIGHT_LICENSE a él en una máquina desconectada, ejecuta la aplicación — Validate() devuelve licenciada sin red.
  2. Lease aislado: lee el id de una máquina de prueba, acuña un lease offline de 365 días para ella, ejecuta ImportOfflineLease en esa máquina — Valid, offline. Copia el archivo a una segunda máquina — MachineMismatch.
  3. Archivo de cobertura: exporta el bloque de cobertura de una licencia, impórtalo con ImportCoverageBlock en una compilación que aplique cobertura, confirma que la versión cubierta se ejecuta y que una versión fuera de rango muestra el mensaje de importar/renovar.
  4. Anti-retroceso: importa un archivo de cobertura más antiguo después de uno más nuevo — espera RolledBack y ningún cambio.
  5. Contabilidad de puestos: confirma que acuñar el lease offline consumió un puesto (el contador used de la licencia subió), demostrando que offline no es una escapatoria de puestos.

Véase también