@software-authority/license-service-sdk
v1.2.1
Published
Node SDK for the License Manager service API
Readme
License Service SDK
Node SDK for the Laravel license service exposed by this repository.
Features
- Validate licenses with
license_keyor encrypted.licpayloads - Activate licenses with device context
- Deactivate licenses
- Download encrypted
.licfiles - Verify signed
.licfiles locally using a public key - Keep backward compatibility with legacy encrypted
.licfiles usingLICENSE_FILE_KEY - Validate and activate
.licfiles offline without calling the license service
Install
npm install @software-authority/[email protected]For consumers on other software, follow the major version you support:
npm install @software-authority/license-service-sdk@^1.2.1Use exact 1.2.1 if you want to lock to one release. Use ^1.2.1 if you want future non-breaking updates in the 1.x line.
Publish
For a scoped npm package, publish it from this package folder:
npm publishpublishConfig.access is already set to public in the package manifest, so future publishes use the correct access level.
Versioning
Current version: v1.2.1.
Consumer guidance:
- Stay on
1.xif your software is built against the current API contract. - Upgrade to
2.xonly when you intentionally adopt a breaking SDK change. - Prefer
^1.2.1in consuming software for safe backward-compatible updates. - Prefer exact
1.2.1when you need fully locked deployments.
Use one of these commands before publishing an update:
npm run release:patch
npm run release:minor
npm run release:majorTypical release flow:
npm test
npm run build
npm run release:patch
npm run publish:npmVersion updates are stored in package.json and package-lock.json, so future releases keep a clear version history.
Usage
import { LicenseServiceClient } from '@software-authority/license-service-sdk';
const client = new LicenseServiceClient({
baseUrl: 'https://example.com/api/licenses',
auth: {
appId: 'app_example',
appSecret: 'sec_example',
},
});
const result = await client.activate({
licenseKey: 'ABCD-EFGH-IJKL-MNOP',
machineId: 'machine-001',
instanceName: 'pos-main',
});Offline usage
Use the offline helpers in a Node or Electron service process when the software needs to work from a .lic file without contacting the server.
import { activateOfflineLicense, validateOfflineLicense } from '@software-authority/license-service-sdk';
const validation = validateOfflineLicense({
licenseFile: encryptedLicenseFile,
publicKey: process.env.LICENSE_FILE_PUBLIC_KEY!,
});
if (!validation.valid) {
throw new Error(validation.message);
}
const activation = activateOfflineLicense({
licenseFile: encryptedLicenseFile,
publicKey: process.env.LICENSE_FILE_PUBLIC_KEY!,
machineId: 'machine-001',
instanceName: 'pos-main',
meta: {
machine_id: 'machine-001',
},
});
console.log(activation.license?.effective_rules);
console.log(activation.activation);Selecting the correct public key bundle
When the license provider uses different signing keys per product, the consuming software should keep a local trust bundle instead of a single public key string.
Recommended trust bundle shape:
type ProductTrustBundle = {
productId: string;
activeKeyId: string;
keys: Array<{
keyId: string;
publicKey: string;
retiredAt?: string | null;
}>;
};Recommended local storage per product:
- the current active public key
- older public keys kept for historical
.licfiles signed before rotation - the product identifier or software identifier that owns the bundle
Use parseLicenseFileEnvelope(...) first to inspect the signed file and choose the matching key by key_id.
import {
resolvePublicKeyFromTrustBundle,
validateOfflineLicense,
} from '@software-authority/license-service-sdk';
const publicKey = resolvePublicKeyFromTrustBundle(licenseFile, trustBundle);
const result = validateOfflineLicense({
licenseFile,
publicKey,
});The provider's Offline Trust download now returns this JSON shape directly:
type ProviderProductTrustBundle = {
product: {
id: string | number;
slug: string | null;
name: string | null;
};
active_key_id: string;
keys: Array<{
key_id: string;
public_key: string;
activated_at?: string | null;
retired_at?: string | null;
}>;
};resolvePublicKeyFromTrustBundle(...) accepts both the normalized camelCase bundle and the provider-exported snake_case bundle.
Why this matters:
- new
.licfiles use the product's current active signing key - older
.licfiles may have been signed before a key rotation - keeping the historical keys in the local trust bundle allows both old and new files to verify correctly
Recommended provider handoff per product:
- active public key PEM
- active key ID
- historical public keys when older offline files must remain usable
- product or software identifier so the bundle is not mixed across products
Recommended consumer behavior after key rotation:
- Add the new public key to the local trust bundle instead of replacing the old one immediately.
- Mark the old key as retired, but keep it available for historical offline files.
- Use
key_idfrom the.licenvelope to select the correct verification key. - Remove old keys only when you are certain no installed software still depends on old offline files.
Offline helper behavior:
- Verifies signed
.licpayloads locally with a public key. - Checks
status,expires_at, andoffline_file_valid_untillocally. - Returns rule data from the signed payload without calling the server.
- Produces an activation-shaped response for offline service processes.
- Does not persist activation state back to the server.
For legacy LM1. files, pass fileEncryptionKey instead of publicKey.
Auth modes
Use one of the following:
apiKey: sendsAuthorization: Bearer <key>appIdandappSecret: sendsX-App-IdandX-App-Secret
Notes
- This SDK is intended for trusted Node service processes.
- Do not embed app secrets in browser code.
- Offline parsing and activation are intended for local/offline execution paths and do not replace server-side activation tracking.
