Licensing · Getting Started

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:

APIPurpose
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_KEYbundled 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:

<base64url(payload-json)>.<base64url(ed25519-signature)>

The payload must match LicenseClaims:

type LicenseTier = 'developer_seat' | 'team' | 'enterprise';
 
interface LicenseClaims {
  sub: string;
  tier: LicenseTier;
  iat: number;
  exp: number;
  seats: number;
}

seats must be a number greater than or equal to 1.

#Status model

evaluateLicense() returns one of:

StatusMeaning
licensedsignature verified and nowSec <= exp
gracesignature verified, expired, but still inside the grace window
expiredsignature verified and outside the grace window
missingno token and not marked noncommercial
tamperedtoken was malformed or failed signature verification
noncommercialno 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.warn unless a custom warn function 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 call runLicenseCheck() directly.
  • API reference — the full type surface.