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.
Pin or fetch the trusted keyset
Section titled “Pin or fetch the trusted keyset”#include <keylight/keylight.hpp>
keylight::Config cfg;cfg.tenantId = "your-tenant";cfg.productId = "your-product";cfg.sdkKey = "sdk_live_…";// Pin trusted keys explicitly instead of fetching them:cfg.trustedKeys = { { "k1", "<raw ed25519 public key, base64>" } };cfg.maxOfflineDays = 7; // omit to run offline as long as the lease itself is currentThe trusted keyset can be fetched once from GET /{tenant}/.well-known/keylight-keys (keylight::fetchKeyset) or pinned at construction time directly in Config::trustedKeys.
How the cached lease is gated
Section titled “How the cached lease is gated”validate() returns the cached lease only when it is kid-known, signature-valid, unexpired, and (if set) within maxOfflineDays of the last online validation. Entitlement checks read that gated result, so they work with no network call:
if (kl.hasEntitlement("pro")) { // reads the gated cached lease — offline, no network}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. Leases are signed, not encrypted.
state() additionally forces State::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 online validate().
Standalone verification with Verifier
Section titled “Standalone verification with Verifier”You can verify a lease without a Client instance — useful in server middleware, a license validator tool, or a second-opinion check:
#include <keylight/verifier.hpp>
// `lease` is a parsed keylight::Lease.// Build the canonical signed payload and verify directly:auto payload = keylight::canonical_payload(lease);
keylight::Verifier verifier;verifier.addKey("k1", "<raw ed25519 public key, base64>");
auto result = verifier.verify(lease, std::time(nullptr));// result.kidKnown — the lease's key ID is in the trusted set// result.signatureValid — Ed25519 signature checks out// result.expired — lease notAfter has passed (within 300s skew)
if (!result.kidKnown || !result.signatureValid) { // reject — forged or from an unknown key rotation}if (result.expired) { // reject — lease is past its validity window}canonical_payload returns the exact byte sequence the Keylight API signs: entitlements are sorted lexicographically and joined with | before the Ed25519 signature is applied. This byte-for-byte format is identical across the Swift, Rust, JavaScript, and C# SDKs — verified by the cross-SDK conformance vectors.
At-rest storage
Section titled “At-rest storage”The default FileStore writes leases to the platform-appropriate data directory (default_store_path(cfg)). The on-disk format is the raw signed JSON lease — it is not encrypted at rest. If your threat model requires at-rest secrecy, wrap FileStore with your own encrypted store by subclassing keylight::Store.
- Install — add the SDK and activate a license.
- How offline leases work