Skip to content

Offline verification

The offline artifact is a signed v3 lease issued by the Keylight API. The SDK reconstructs the exact signed payload (entitlements sorted, pipe-delimited) and verifies it with Ed25519 against the tenant’s trusted keyset, applying a 300-second clock-skew tolerance.

use keylight::KeylightConfig;
let cfg = KeylightConfig::builder("your-tenant", "your-product", "sdk_live_…")
// Pin trusted keys explicitly instead of fetching them:
.trusted_key("k1", "<raw ed25519 public key, base64>")
.max_offline_days(7) // omit to run offline as long as the lease itself is current
.build();

The trusted keyset can be fetched once from GET /{tenant}/.well-known/keylight-keys (keylight::keyset::fetch_keyset) or pinned at build time with .trusted_key(kid, pub_b64).

cached_lease() returns the lease only when it is kid-known, signature-valid, unexpired, and (if set) within max_offline_days of the last online validation. Entitlement checks read that gated lease, so they work with no network call:

if kl.has_entitlement("pro") {
// reads the gated cached lease — offline, synchronous
}

A forged or hand-edited lease cannot pass verification without the tenant’s private key — the security boundary is the signature, not at-rest secrecy.

state() additionally forces Invalid if the system clock has been rolled back past tolerance since the last server contact, closing the offline path to reviving an expired lease. The check is UTC-based (timezone changes don’t trip it) and self-heals on the next successful validate().

Encrypted lease and key material is stored device-bound with ChaCha20-Poly1305 (the key is derived from a per-device identity via BLAKE3) — copying the files to another machine won’t decrypt them.