license-exposed
v1.0.0
Published
TypeScript SDK for [License Exposed](https://license.exposed) — a platform for creating and validating software licenses.
Downloads
50
Readme
license-exposed
TypeScript SDK for License Exposed — a platform for creating and validating software licenses.
The SDK is lightweight, type-safe, and handles the full license lifecycle: machine attestation, periodic verification, and in-app access to the active license.
Installation
npm install license-exposedRequires Node.js 18+ (uses native fetch and crypto).
Prerequisites
Before using the SDK you need two values from the License Exposed dashboard:
accessToken— a Personal Access Token (PAT) scoped to your product.serverKey— the ECDH public key of your License Exposed server (base64-encoded), found in your product settings.
Quick Start
Wrap your application entry point with withLicense. On first run it will attest the machine (prompting the user to approve via a QR code / URL), then verify the license on every startup.
import { withLicense, License } from 'license-exposed';
async function main(license: License) {
console.log('License valid, starting app…');
}
withLicense(main, {
serverKey: '<YOUR_SERVER_KEY>',
accessToken: '<YOUR_ACCESS_TOKEN>',
});API
withLicense(app, options)
The primary entry point. Handles attestation (first run) and verification (every run), then calls app with the validated License.
withLicense(app: (license: License) => Promise<void> | void, options: Options & Partial<LicenseEvents>): Promise<void>Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| accessToken | string | required | Personal Access Token from the dashboard. |
| serverKey | string \| Buffer | required | Server ECDH public key (base64 string or raw Buffer). |
| path | string | ./license.key | File path where the attestation key is stored. |
| interval | number | 900000 (15 min) | Milliseconds between background re-verifications. |
| machineInfo | MachineInfo \| () => MachineInfo | auto-detected | Override machine metadata sent during verification. |
| version | string \| () => string | from package.json | Application version reported during verification. |
| interactive | boolean | true | When true, shows a QR code for manual approval. Set to false for headless/server environments. |
When interactive is false, you must also provide auth:
// Username/password auth
{ interactive: false, auth: { username: '[email protected]', password: 'secret' } }
// Token auth
{ interactive: false, auth: { token: 'pat_…' } }Event hooks (all optional)
| Hook | Signature | Description |
|------|-----------|-------------|
| onAttestation | () => void | Called after a new machine attestation is written to disk. |
| onVerify | (license: License) => void | Called after each successful license verification. |
| onError | (error: KeyedError) => void | Called on any error instead of throwing. Useful for custom error UX. |
withLicense(main, {
serverKey: '…',
accessToken: '…',
onAttestation: () => console.log('Machine registered!'),
onVerify: (license) => console.log('License verified', license),
onError: (err) => {
console.error(`[${err.key}] ${err.message}`);
process.exit(1);
},
});useLicense()
Access the active license from anywhere inside the withLicense call stack without passing it explicitly.
import { useLicense } from 'license-exposed';
function checkFeatureFlag() {
const license = useLicense();
// inspect license fields…
}Throws a KeyedError("no_valid_license", …) if called outside a licensed context.
verifyAttestation(options)
Low-level function that sends a signed verification request to the License Exposed API. Used internally by withLicense; exposed for advanced use cases where you manage attestation yourself.
import { verifyAttestation, LicenseAttestation } from 'license-exposed';
const attestation = LicenseAttestation.fromFile('./license.key');
const license = await verifyAttestation({
attestation,
serverKey: '<YOUR_SERVER_KEY>',
accessToken: '<YOUR_ACCESS_TOKEN>',
});Attestation Strategies
When a license.key file does not yet exist, withLicense runs attestation to register the machine. Three strategies are available:
Interactive (default)
Prints a QR code and approval URL to the terminal. The end user (or an operator) opens the URL in a browser to approve the machine. Suited for desktop CLIs and developer tools.
withLicense(main, {
serverKey: '…',
accessToken: '…',
// interactive: true ← default
});Non-interactive (username/password or token)
For server environments where no human is present. Pass credentials directly.
withLicense(main, {
serverKey: '…',
accessToken: '…',
interactive: false,
auth: { username: '[email protected]', password: 'secret' },
});Note: Login-based attestation is not yet fully implemented and will throw a
not_implementederror. Use the interactive strategy or CryptoLens migration for now.
CryptoLens migration
If you are migrating from Cryptolens, pass a cryptoLensKey alongside the standard options. The SDK will use your existing Cryptolens license key to bootstrap attestation.
withLicense(main, {
serverKey: '…',
accessToken: '…',
cryptoLensKey: '<YOUR_CRYPTOLENS_KEY>',
});Error Handling
All SDK errors are instances of KeyedError:
import { KeyedError } from 'license-exposed';
class KeyedError extends Error {
key: string; // machine-readable snake_case code
message: string;
cause?: Error;
}Common error keys:
| Key | Meaning |
|-----|---------|
| invalid_configuration | A required option is missing or invalid. |
| attestation_initiate_failed | Could not start the attestation flow with the server. |
| attestation_timeout | User did not approve the machine within ~4 minutes. |
| attestation_failed | Machine was rejected or attestation otherwise failed. |
| verification_failed | The server rejected the verification request. |
| no_valid_license | useLicense() was called outside a licensed context. |
Custom Machine Info
By default the SDK collects machineId, hostname, os, and arch automatically. Override any or all fields:
withLicense(main, {
serverKey: '…',
accessToken: '…',
machineInfo: {
machineId: 'my-custom-id',
os: 'linux',
arch: 'x64',
},
// or lazily:
// machineInfo: () => fetchMachineInfoFromEnv(),
});License Key File
The attestation key is written to ./license.key (configurable via path). This file contains the machine's private ECDH key and attestation ID. It should be:
- Excluded from version control (add to
.gitignore). - Kept on disk between runs — deleting it triggers a new attestation flow.
- Not shared between machines — each machine needs its own key.
