Ed25519 license keys
Ed25519 license keys are the asymmetric key pair behind Caisson's offline licensing: the issuer holds a private Ed25519 key and signs a buyer's exact license claims; Caisson's registry license gate verifies that signature offline against a baked-in public key, with no network call and no way to forge a license without the private key.
In code
export async function issueLicense(
signer: Signer,
claims: unknown,
): Promise<string> {
const parsed: LicenseClaims = licenseClaimsSchema.parse(claims);
// Sign EXACTLY what the verifier re-derives: canonicalize the PARSED object (key-order independent).
const payload = canonicalize(parsed as unknown as JsonValue);
const signature = await signer.sign(new TextEncoder().encode(payload));
if (signature.length !== ED25519_SIGNATURE_BYTES) {
throw new ValidationError(
`issued signature must be ${String(ED25519_SIGNATURE_BYTES)} bytes, got ${String(signature.length)}`,
);
}
return encodeToken({
prefix: WIRE_PREFIX,
tier: parsed.tier.toUpperCase(),
payload,
signature: Buffer.from(signature),
});
}How it holds
One shared claims schema, no drift
The issuer imports licenseClaimsSchema, the LicenseClaims type, and encodeToken straight from @caisson/license-verify instead of re-declaring them, so the signer and the verifier can never disagree on what a valid claim looks like.
The private key stays an opaque node:crypto KeyObject
Ed25519Signer holds the key in a #private field; node:crypto never exposes its bytes through enumeration, logging, or JSON.stringify, and crypto.sign performs the signature in-engine rather than in JS memory.
Signs the canonical bytes, not the raw input
issueLicense signs canonicalize(parsed claims), the identical byte sequence @caisson/license-verify re-derives and compares; a one-byte mismatch anywhere makes the verifier reject the signature outright.
KMS is a documented seam, not a v1 dependency
A KmsSigner interface implements the same Signer port for an AWS KMS asymmetric key whose private half never leaves the HSM, but no AWS SDK ships and no live KMS call exists in this version.