Offline verification
A license token is a short, signed string you ship with your software. Your app verifies it at runtime using the account’s Ed25519 public key — no network call required. Verification proves the token is authentic and unexpired. It does not prove live revocation or current device count; use the online validate endpoint for that.
Token format
A token is three dot-separated segments. It is deliberately JWT-shaped but dependency-free, so you can verify it with the standard library in any language:
v1.<payload>.<signature>payload=base64url(JSON.stringify(claims))- signing input = the ASCII bytes of the string
v1.<payload> signature=base64url(Ed25519_sign(signing input))
The claims for a node-locked license look like this:
{
"kid": "clx0account00000000000000", // account id: selects the verifying public key
"key": "SEAL-XXXX-XXXX-XXXX", // the license key
"productId": "clx0product0000000000000",
"licenseType": "SUBSCRIPTION", // PERPETUAL | SUBSCRIPTION | TRIAL
"maxDevices": 3,
"status": "ACTIVE",
"iat": 1785958000, // issued-at, unix seconds
"exp": 1785958300 // expires 300 seconds after iat
}Fetch the account public key
The public key is served openly and is safe to cache. It is base64-encoded SPKI DER.
curl -s https://sealbind.io/api/v1/accounts/ACCOUNT_ID/public-key{
"accountId": "clx0account00000000000000",
"algorithm": "ed25519",
"format": "spki-der-base64",
"publicKey": "MCowBQYDK2VwAyEA<...base64 SPKI DER...>"
}Verify in Node
Pure standard library — no dependencies. Pass the token and the account public key (the publicKey field above). Returns the decoded claims on success, or null if the signature or format is bad.
import { createPublicKey, verify } from "node:crypto";
export function verifyLicenseToken(token, publicKeyB64) {
const parts = token.split(".");
if (parts.length !== 3 || parts[0] !== "v1") return null;
const [, payloadB64, signatureB64] = parts;
const publicKey = createPublicKey({
key: Buffer.from(publicKeyB64, "base64"),
format: "der",
type: "spki",
});
const signingInput = Buffer.from("v1." + payloadB64, "ascii");
const signature = Buffer.from(signatureB64, "base64url");
// Ed25519 uses a null digest algorithm in Node's crypto.verify.
if (!verify(null, signingInput, publicKey, signature)) return null;
const claims = JSON.parse(
Buffer.from(payloadB64, "base64url").toString("utf8"),
);
if (claims.exp && claims.exp * 1000 < Date.now()) return null;
return claims;
}Online validation (authoritative)
When you need the live answer — revocation, suspension, device-count — call the validate endpoint with a vendor API key. This is the source of truth; offline verification is the fast path.
curl -s -X POST https://sealbind.io/api/v1/licenses/LICENSE_KEY/validate \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"fingerprint": "device-abc-123"}'{
"result": "VALID",
"license": {
"key": "SEAL-XXXX-XXXX-XXXX",
"status": "ACTIVE",
"licenseType": "SUBSCRIPTION",
"productId": "clx0product0000000000000",
"maxDevices": 3,
"expiresAt": "2027-08-05T00:00:00.000Z"
},
"token": "v1.eyJ....Ab12..."
}result is one of VALID, INVALID, EXPIRED, REVOKED, or DEVICE_LIMIT_EXCEEDED. A fresh offline-verifiable token is returned only when result is VALID. Every validation token expires 300 seconds (five minutes) after issuance, including tokens for perpetual licences.