Offline license verification
Offline license verification is checking a signed license token's authenticity and entitlements with no network call, against a public key baked into the software itself. Caisson's license-verify package verifies the Ed25519 signature over canonicalized claims, then trusts only the signed tier; any failure, from a missing token to an elapsed expiry, resolves safely to the free community tier.
In code
export function verifyLicenseWithKey(
token: string | null | undefined,
publicKey: KeyObject,
now: Date = new Date(),
): VerifiedLicense {
if (token === null || token === undefined || token === "") {
return COMMUNITY;
}
try {
const decoded = decodeToken(token);
const signedBytes = Buffer.from(decoded.payload, "utf8");
// Asymmetric verify over the EXACT signed bytes (Ed25519: algorithm = null). `crypto.verify`,
// not `timingSafeEqual`: a signature check is not a secret comparison (ADR-0010).
if (!cryptoVerify(null, signedBytes, publicKey, decoded.signature)) {
return COMMUNITY;
}
const parsed = licenseClaimsSchema.safeParse(
JSON.parse(decoded.payload) as unknown,
);
if (!parsed.success) {
return COMMUNITY;
}
const claims = parsed.data;
// ... format-conformance (canonicalize(claims) === decoded.payload) and expiry checks follow,
// each failing safe to COMMUNITY too ...
return {
valid: true,
tier: claims.tier,
entitlements: claims.entitlements,
eval: claims.eval === true, // the eval-license discriminator (watermarking, no-redistribution)
claims,
};
} catch {
// Any unexpected throw (JSON parse, codec edge, crypto) → community. The verifier never raises.
return COMMUNITY;
}
}How it holds
Baked-in key, zero network dependency
The production verifier pins to one Ed25519 public key (LICENSE_PUBLIC_KEY_SPKI_B64) compiled straight into the package. No request ever leaves the install to check a license; verification is a local crypto.verify() call against that fixed key.
Fail-safe-to-community on every error path
A null or absent token, a bad signature, a claims payload that fails strict Zod parsing, a non-canonical signed payload, or an elapsed expiry all resolve to the identical free COMMUNITY result. verifyLicenseWithKey has no throwing path a caller has to guard against.
Canonical-bytes check closes a forging gap
The decoded payload must equal canonicalize(claims) exactly, so a signature that would verify over some other serialization of the same fields (reordered keys, different whitespace) is still rejected. The issuer always signs canonical bytes; anything else is treated as crafted.
Perpetual-per-major expiry, never silently extended
claims.expiry of null means the license never lapses for the major version it was signed against; a set expiry is checked against the caller-supplied clock. A license never auto-extends itself into a later major it wasn't issued for.