Introduction
@threadplane/licensing is the shared license-check helper used by @threadplane/chat and by custom integrations that opt into the same warning behavior. It verifies compact Ed25519-signed tokens offline, evaluates the result into a small status set, and emits non-blocking warnings when appropriate.
The package itself is MIT licensed. COMMERCIAL.md says the libraries in this repository are free to use, modify, and distribute in commercial and noncommercial projects. The proprietary part it calls out is the internal minting service, not this package.
#Public API shape
The main entry point exports:
| API | Purpose |
|---|---|
verifyLicense() | verifies token signature against a public key |
evaluateLicense() | turns a verify result and current time into a status |
runLicenseCheck() | verifies, evaluates, and warns once |
emitNag() | emits the warning for non-licensed statuses |
signLicense() | signs claims with an Ed25519 private key |
inferNoncommercial() | returns a default noncommercial hint from NODE_ENV |
LICENSE_PUBLIC_KEY | bundled public key |
This table covers the functions and the bundled key, not the full export list. The package also exports the supporting types — LicenseClaims, LicenseTier, LicenseStatus, VerifyResult/VerifyReason, EvaluateResult/EvaluateOptions, RunLicenseCheckOptions, and EmitNagOptions. See the API reference for the full type surface.
@noble/ed25519 is the only peer dependency.
#Token model
A token is:
The payload must match LicenseClaims:
seats must be a number greater than or equal to 1.
#Status model
evaluateLicense() returns one of:
| Status | Meaning |
|---|---|
licensed | signature verified and nowSec <= exp |
grace | signature verified, expired, but still inside the grace window |
expired | signature verified and outside the grace window |
missing | no token and not marked noncommercial |
tampered | token was malformed or failed signature verification |
noncommercial | no token and isNoncommercial was true |
The default grace window is 14 days.
#Runtime posture
The higher-level check is built not to block app startup:
- signature verification is local;
- warning output goes through
console.warnunless a customwarnfunction is supplied; - no network request is made by the licensing check.
The code returns statuses instead of throwing for normal license states. For me, that's the right default here: you get to decide what a missing or expired license means for your app instead of having the check make that call for you. The tradeoff is that nothing stops a consumer from ignoring the status entirely. @threadplane/chat treats it as a warning and visibility mechanism, not an app kill switch.
#Next steps
- Quickstart — a runnable round-trip: sign, verify, and evaluate a token end to end.
- Setup — consume the check via
provideChat()or callrunLicenseCheck()directly. - API reference — the full type surface.