Skip to content

Guide

Security & the lease model

How Keyright licenses are secured: what a key actually is, why a single-seat license cannot run on two machines even offline, what "tamper-proof" really means, and exactly how the short-lived signed lease works — renewal, going offline, and why editing a lease file cannot extend it.

The honest version of how Keyright keeps licenses secure — what the cryptography does and does not protect, and how the lease works. If you’re evaluating Keyright for a real product, read this page before you ship.

What is actually secured — the key, or something else?

The license key (LIC-…) is just an identifier, not the secret. It carries no entitlements and grants nothing on its own. The thing your app trusts is the signed document the service returns on activation — the lease (online) or a signed license file (offline). Both are signed with your product’s RSA-2048 private key, which lives only on the issuing service and is stored encrypted at rest (AES-256-GCM under a key-encryption-key; in production the service refuses to start without one). Your app embeds only the matching public key and verifies the signature (PKCS#1 v1.5 / SHA-256).

So:

  • A leaked key lets someone attempt an activation against your seat limit — nothing more. They can’t grant themselves a higher tier, more seats, or a later expiry, because all of that is inside the signed payload they can’t produce.
  • If a key is abused, you revoke it (dashboard or /admin/licenses/{id}/revoke). Revocations propagate online immediately and offline via a signed revocation list the client checks.
  • Your app never trusts a key string it hasn’t seen the service sign for. It fails closed: no valid signature ⇒ not licensed.

Why a single-seat license can’t run on two machines — even offline {#seats}

Two independent controls, and the offline one is the important part:

  1. Server-side seat enforcement (online). Activation counts distinct machines against the seat count. A new machine beyond the limit gets SeatLimit. Re-activating the same machine is idempotent, so it never inflates the count.
  2. Node-lock binding (works with no network). Every signed lease/offline-file is bound to a machine fingerprint (KRM1:…) derived from stable hardware/OS identifiers, salted and hashed. The fingerprint is inside the signed payload, so when the client validates, it recomputes this machine’s fingerprint and checks it matches. Copy an offline lease to a second machine and validation returns MachineMismatch — the signature is fine, but it isn’t this machine.

That’s why offline doesn’t become a loophole: an offline lease is minted for one machine ID (and still confirms a seat when issued), so it can’t be shared. Keyright tolerates one “soft” component changing (e.g. a machine rename) so ordinary maintenance doesn’t lock a legitimate user out, while the hardware anchor must always match.

How “unbreakable” should be read

Be precise about the two different threats:

  • Forgery / tampering — this is cryptographically closed. No one can create a valid license, upgrade a tier, add seats, or push out an expiry without your private key, and that key never leaves the server. Change a single field in a lease or license file and the RSA signature no longer verifies, so the client rejects it. In that sense the documents are unforgeable and tamper-evident.
  • A determined attacker on a machine they fully control is a different problem, and no licensing library alone solves it. Someone who can patch your compiled binary can, in principle, bypass the if (!info.IsValid) check itself — that’s true of every licensing product, because the check runs on their CPU. Keyright makes the license unforgeable; protecting the enforcement code is a separate layer. Pair Keyright with Nebula.NET (obfuscation, anti-tamper, method encryption) when that matters. We’d rather tell you this than pretend a signature stops a debugger.

Other things people ask about security, stability & scale

  • Stability / offline resilience. Validation is local and stateless — after activation your app doesn’t depend on the service being up to keep working (that’s the whole point of the lease). A service outage doesn’t strand activated customers within their grace window.
  • Scalability. The issuing service is stateless per request and horizontally scalable; the hot path (/v1/activate, /v1/validate) is a signature op plus a seat check. Because clients validate offline between activations, you are not taking a request per app launch.
  • Multi-tenant isolation. Each tenant has its own signing key and data is isolated per tenant, so one vendor’s key or licenses can never verify or touch another’s.
  • Abuse resistance. Client endpoints are rate-limited; admin endpoints require a token and are role-scoped (Read < Write < Manage < Own) with an audit trail.
  • Clock tampering. Trials and expiries are checked against a persisted first-seen timestamp; rolling the system clock backward is detected (ClockTampered) rather than rewarded.

The lease, explained

Why issue a lease at all?

A lease is a short-lived, signed grace token (default 14 days) the service hands back on activation. It’s the balance between two bad extremes: phoning home on every launch (fragile, privacy-unfriendly, breaks when your service blips) versus trusting a machine forever after one activation (uncontrollable). The lease lets an activated machine run offline for the grace window, then re-checks.

Does the product have to come back to Keyright to renew the lease?

Yes — but invisibly, and only occasionally. The recommended pattern is to call ActivateAsync at startup: when the machine is online it silently refreshes the 14-day lease (idempotent, no extra seat); when it’s offline the SDK keeps validating against the cached lease. So a normally-connected machine renews without anyone noticing, and the 14 days is simply how long it can stay completely offline before it must reach the service once. You can widen or narrow that window (KEYRIGHT_LEASE_TTL_DAYS) to trade offline tolerance against how quickly a revocation or seat change takes effect.

What happens if a machine goes offline after activation?

It keeps working until the lease’s leaseExpiresUtc passes — the grace window. Reconnect any time before then and the lease refreshes for another window. If the window elapses with no contact, Validate() returns Expired (“reconnect to re-validate”) and your app fails closed. For machines that will be offline for a long time or forever, don’t rely on the 14-day lease — issue a long-TTL offline lease (default 365 days) or ship a standalone offline license file. See Activation & deactivation.

Can a user just edit the lease file to extend it forever?

No. The expiry (leaseExpiresUtc) is part of the signed payload, not a separate setting. Change it — or the tier, the seats, the machine binding, anything — and the RSA signature no longer matches, so the client rejects the lease as SignatureInvalid and falls back to unlicensed. There is no field a user can flip. Rolling the clock back to sit inside an old window doesn’t help either: that’s what clock-tamper detection catches. The only way to get a longer lease is for you to issue one.

How does offline activation work, in one paragraph?

The offline machine emits its fingerprint; you (with connectivity and an admin token) mint a lease bound to that fingerprint with whatever TTL you choose; the machine imports it and validates locally forever-until-TTL, with no network. It’s the same signed-lease mechanism as online activation — just delivered by hand instead of over HTTP, and bound to exactly one machine so it can’t be shared. Step-by-step: Offline / air-gapped activation.