Skip to content

Casos de uso

Revocar, renovar y gestionar el vencimiento

Corta el acceso, extiende un plazo sin una clave nueva y conoce exactamente qué ve la aplicación al vencer, durante la gracia y tras la revocación — además de en qué se diferencia de una instancia autoalojada que se licencia a sí misma.

Objetivo. Necesitas terminar el acceso de un cliente ahora mismo (una devolución de cargo, un abuso, una emisión por error), extender el plazo de un cliente que paga sin reemitir su clave, y entender con precisión qué muestra la aplicación en ejecución a medida que un plazo se agota.

Cuándo usarlo

  • Úsalo si vendes licencias por plazo (anuales/suscripción) y las renuevas, o si alguna vez necesitas revocar una clave.
  • Úsalo si estás decidiendo con qué agresividad fallar — con qué rapidez una revocación o un vencimiento alcanza a una máquina que ahora está offline.
  • No lo uses si solo distribuyes licencias perpetuas que nunca vencen y nunca se revocan — entonces esta página es solo la vía de revocación «de emergencia». Mover un puesto entre máquinas es activación y desactivación, no revocación.

Dos ciclos de vida comparten esta página y no deben confundirse. El ciclo de vida de la licencia del cliente (las claves de tus usuarios finales) se comporta igual tanto si usas Cloud como autoalojado. El ciclo de vida de la instancia autoalojada es algo distinto — la propia clave de la instancia que le otorga el derecho a ejecutarse — y se trata al final y en Licenciamiento y ediciones.

Activa — licenciadavence prontoExpired → FreeIsExpiringSoon()expiryUtcrenew → extiende el vencimientomisma clave · sin reactivaciónrevocar — en cualquier momentola app baja a Free en la siguiente renovación del leaserapidez de aplicación = TTL del leaseen línea pronto; offline no más allá de la ventana de graciaUn plazo de cliente, leído por completo desde LicenseInfoDaysRemaining hace la cuenta atrás · IsPaid pasa a false al vencer/revocar · Status dice por qué
Una licencia por plazo a lo largo de su vida: activa, a punto de vencer, renovada (misma clave), o vencida/revocada — y con qué rapidez un cambio llega al dispositivo.

Qué haces tú (lado del proveedor)

Las tres acciones vuelven a firmar la licencia en el servidor; el cliente conserva la misma clave y simplemente se reactiva (normalmente de forma automática al arrancar) para recoger el cambio.

Revocar — cortar una clave:

curl -X POST $BASE/admin/licenses/LIC-…/revoke -H "X-Admin-Token: $TOKEN"
# the dashboard's per-row Revoke action does the same. Many at once: POST /admin/licenses/bulk-revoke

Renovar / extender un plazo — aleja el vencimiento (y opcionalmente cambia los puestos), sin clave nueva:

curl -X POST $BASE/admin/licenses/LIC-…/renew -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
  -d '{"expiryUtc":"2028-10-12T00:00:00Z"}'
# seats can be changed here too; a renewal re-signs the license in place. The key is unchanged.

Ambas viven en la propia página de la licencia: Renew (renovar) está en la fila de acciones y Revoke (revocar) está en la Danger zone (zona de peligro).

La página de la licencia de Stark Industries en Keyright: un botón Renew (junto a Add seats, License file y Send portal link) en la fila de acciones de la parte superior, y una tarjeta Danger zone abajo a la derecha con 'Transfer ownership…' y un botón rojo 'Revoke…'; la tarjeta Details muestra la edición pro, el plazo perpetuo y los dispositivos vinculados.
Renew (fila de acciones) extiende el plazo in situ sobre la misma clave; Revoke (Danger zone) la corta — ambas vuelven a firmar la licencia, y la aplicación recoge el cambio en su siguiente renovación del lease.

El vencimiento es simplemente la llegada del expiryUtc de la licencia sin renovación — tú no haces nada; el comportamiento descrito abajo se activa por sí solo. (Un reembolso/cancelación desde un webhook de tienda revoca automáticamente — consulta Emitir claves automáticamente desde una tienda.)

Cada estado de este ciclo de vida se muestra en la lista de Licenses, para que detectes de un vistazo una clave revocada, vencida o a punto de vencer:

La lista de Licenses de Keyright mostrando una mezcla de estados en la columna STATUS: etiquetas 'active' en Stark Industries, Globex Corporation y Acme Inc.; una etiqueta roja 'revoked' en Wayne Enterprises; una etiqueta gris 'expired' en Umbrella Ltd (vencimiento Mar 1, 2026); y una advertencia ámbar 'in 10 days' de vencimiento próximo en Initech LLC (vencimiento Oct 22, 2026), con fechas de vencimiento y número de puestos por fila.
La lista de Licenses muestra todo el ciclo de vida a la vez — active, revoked, expired y una advertencia de vencimiento próximo («in 10 days») — los mismos estados que tu aplicación lee en LicenseInfo.Status.

Con qué rapidez un cambio llega a la máquina

Una revocación o una renovación se aplica en la siguiente comprobación en línea del dispositivo, porque entre comprobaciones la aplicación funciona offline contra un lease en caché. El TTL del lease (por defecto 14 días, KEYRIGHT_LEASE_TTL_DAYS) es exactamente el mando de control: una aplicación conectada que se reactiva al arrancar recoge una revocación dentro de su cadencia de renovación; una máquina que ha estado offline no la verá hasta que vuelva, y no más tarde de cuando expire su ventana de gracia del lease y se vea obligada a volver a comprobar. Acorta el TTL para que las revocaciones y los cambios de puestos surtan efecto antes, a costa de menos tolerancia offline. Para la aplicación offline sin ninguna red, distribuye una lista de revocación firmada con tu compilación (RevocationListJson / RevocationListPath) para que una aplicación puramente offline siga respetando una revocación.

En tu código

Tu aplicación lo lee todo desde LicenseInfo — nunca analiza una clave. Los miembros relevantes, en cada SDK y por HTTP en bruto:

.NET

LicenseInfo info = await License.ActivateAsync(key, ct);   // or License.Validate() offline; both fail closed

// Status is the single source of truth — branch on it.
switch (info.Status)
{
    case LicenseStatus.Valid:    break;                     // licensed; fall through to the badge below
    case LicenseStatus.Expired:  return ShowRenewPrompt(info.Message);   // term ended, or trial over
    case LicenseStatus.Revoked:  return ShowUnlicensed(info.Message);    // key was revoked
    default:                     return ShowUnlicensed(info.Message);    // NoLicense, SeatLimit, SignatureInvalid, …
}

// While valid, surface the countdown and nudge before renewal.
if (info.IsExpiringSoon())                                   // true inside the renewal window
    ShowRenewBanner($"{info.DaysRemaining} days left");      // DaysRemaining counts down to expiryUtc
string badge = info.ExpiryUtc is null
    ? "Licensed — perpetual"                                 // no expiry
    : $"Licensed — expires {info.ExpiryUtc:d}";

Node.js

const { info } = license.validate();                 // or: info = await license.activate(key)
switch (info.status) {
  case 'valid':    break;                                      // licensed
  case 'expired':  return showRenewPrompt(info.message);       // term ended, or trial over
  case 'revoked':  return showUnlicensed(info.message);        // key was revoked
  default:         return showUnlicensed(info.message);        // no_license, machine_mismatch, …
}
if (info.daysRemaining != null && info.daysRemaining <= 14)    // nudge before renewal
  showRenewBanner(`${info.daysRemaining} days left`);
const badge = info.expiryUtc ? `Licensed — expires ${info.expiryUtc.toLocaleDateString()}`
                             : 'Licensed — perpetual';

Python

info = license.validate_info()                       # or: info = license.activate(key)
if info.status == LicenseStatus.VALID:
    pass                                             # licensed
elif info.status == LicenseStatus.EXPIRED:
    return show_renew_prompt(info.message)           # term ended, or trial over
elif info.status == LicenseStatus.REVOKED:
    return show_unlicensed(info.message)             # key was revoked
else:
    return show_unlicensed(info.message)             # NO_LICENSE, MACHINE_MISMATCH, …
if info.days_remaining is not None and info.days_remaining <= 14:
    show_renew_banner(f"{info.days_remaining} days left")
badge = "Licensed — perpetual" if info.expiry_utc is None else f"Licensed — expires {info.expiry_utc:%x}"

Java

LicenseInfo info = client.validateInfo();            // or: info = client.activate(key)
switch (info.status) {
    case VALID:   break;                                   // licensed
    case EXPIRED: return showRenewPrompt(info.message);    // term ended, or trial over
    case REVOKED: return showUnlicensed(info.message);     // key was revoked
    default:      return showUnlicensed(info.message);     // NO_LICENSE, MACHINE_MISMATCH, …
}
if (info.isExpiringSoon(14))                               // nudge before renewal
    showRenewBanner(info.daysRemaining() + " days left");
String badge = info.expiryUtc == null ? "Licensed — perpetual"
                                      : "Licensed — expires " + info.expiryUtc;

Cualquier lenguaje (HTTP en bruto)

curl -s -X POST "$BASE/v1/validate" -H "content-type: application/json" \
  -d '{"key":"LIC-…","machineId":"<stable-machine-id>","product":"acme-app"}'
# status "revoked" -> drop to Free; "expired" -> prompt to renew; "ok" -> verify the lease and read
# licenseExpiryUtc for the countdown. Fail closed on anything but a valid, in-date lease.

Qué expone cada uno a medida que el plazo se agota:

  • DaysRemaining hace la cuenta atrás hasta expiryUtc; IsExpiringSoon() pasa a verdadero dentro de la ventana de renovación, para que puedas avisar antes de que termine el acceso.
  • Al vencer o tras una revocación, IsPaid pasa a false y la aplicación baja a su estado Free/sin licencia — Keyright falla de forma cerrada, no lanza excepciones. Status te dice cuál es el caso (Expired frente a Revoked) para que puedas redactar el aviso («renueva» frente a «contáctanos»).
  • Tras una renovación, el siguiente ActivateAsync/Validate() devuelve el ExpiryUtc posterior — sin reintroducir la clave, sin diálogo de reactivación.

No hay una «ventana de gracia» en la aplicación para una licencia por plazo: en el momento en que pasa expiryUtc (y la máquina vuelve a validar), la aplicación está en Expired. La ventana de 14 días del lease es un colchón de conectividad — permite que una máquina funcione offline contra un plazo todavía válido — no una gracia adicional después de que termine el plazo pagado. Si quieres que las versiones antiguas sigan funcionando tras caducar una suscripción, eso es la suscripción con fallback de la cobertura de versiones, no una gracia de vencimiento.

Los consumidores de HTTP en bruto ven el mismo ciclo de vida a través del campo status de /v1/validate (revoked, expired) y el licenseExpiryUtc del lease — verifica el lease y falla de forma cerrada ante cualquier cosa que no sea un lease válido y vigente, como en Usar la API HTTP directamente.

El ciclo de vida de la licencia del cliente frente al ciclo de vida de la instancia autoalojada

Son dos claves distintas con reglas distintas. Mantenlas separadas:

Licencia del cliente (tus usuarios finales)Licencia de la instancia autoalojada (tu propia clave)
De quiénLas claves de tus clientes, emitidas por tiEl derecho de tu instancia a ejecutarse, emitido por Delta1
Se aplica aCloud y autoalojado por igualSolo autoalojado
Gracia al vencerNinguna — Expired en el momento en que pasa el plazoDe pago: 15 días de gracia de solo lectura tras el vencimiento; prueba: ninguna
Al detenerseLa aplicación falla de forma cerrada a FreeLa instancia se bloquea — 402 en la mayoría de rutas; los endpoints de estado siguen activos
Lo controlas con/revoke, /renew, expiryUtcSustituir KEYRIGHT_SELF_LICENSE + reiniciar

Todo lo que hay por encima de la tabla es el ciclo de vida del cliente — idéntico en Cloud y autoalojado. La propia clave de la instancia es otra historia: un despliegue autoalojado se licencia a sí mismo, una instancia de pago obtiene 15 días de gracia de solo lectura tras su propio vencimiento antes de bloquearse, y una instancia de prueba se detiene en el momento en que vence. Ese ciclo de vida — instalar, renovar, el campo phase de /admin/self-license y qué significa «bloqueada» — se trata por completo en Licenciamiento y ediciones. No confundas que caduque la suscripción de un cliente (su aplicación pasa a Free) con que caduque la licencia de tu instancia (todo tu servicio de emisión pasa a solo lectura y luego se detiene).

Cloud frente a autoalojado

  • $BASE / $TOKEN. Cloud: https://keyright.delta1labs.com + un token de sesión. Autoalojado: la URL de tu instancia + KEYRIGHT_ADMIN_TOKEN.
  • Revocar, renovar y el vencimiento de las licencias de clientes son idénticos en ambos modos — mismos endpoints, mismo comportamiento del SDK.
  • El TTL del lease lo ajustas tú en una instancia autoalojada (KEYRIGHT_LEASE_TTL_DAYS); en Cloud es el valor predeterminado del servicio.
  • El ciclo de vida de la instancia solo existe en autoalojado (ver arriba).

Verificar

  1. Emítete una clave por plazo que venza en uno o dos días; actívala. La insignia muestra el vencimiento y DaysRemaining.
  2. Revócala, reconecta y deja que el lease se renueve (o llama a ActivateAsync): la aplicación baja a Free con Status == Revoked.
  3. Emite una clave nueva, actívala y luego renuévala a una fecha posterior; reactiva — ExpiryUtc se aleja, misma clave, sin diálogo.
  4. Deja que una clave de plazo corto venza y revalida: Status == Expired, IsPaid == false, aparece el aviso de renovación.
  5. Temporización offline: revoca una clave mientras esa máquina está offline — confirma que sigue funcionando hasta que reconecta (o hasta que termina su ventana de lease) y entonces baja a Free. Acorta KEYRIGHT_LEASE_TTL_DAYS si te parece demasiado lento.

Véase también