npm package: @software-authority/license-service-sdk
Description: Node SDK for the License Manager service API
Latest version: 1.2.1

# License Service SDK

Node SDK for the Laravel license service exposed by this repository.

## Features

- Validate licenses with `license_key` or encrypted `.lic` payloads
- Activate licenses with device context
- Deactivate licenses
- Download encrypted `.lic` files
- Verify signed `.lic` files locally using a public key
- Keep backward compatibility with legacy encrypted `.lic` files using `LICENSE_FILE_KEY`
- Validate and activate `.lic` files offline without calling the license service

## Install

```bash
npm install @software-authority/license-service-sdk@1.2.1
```

For consumers on other software, follow the major version you support:

```bash
npm install @software-authority/license-service-sdk@^1.2.1
```

Use 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:

```bash
npm publish
```

`publishConfig.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.x` if your software is built against the current API contract.
- Upgrade to `2.x` only when you intentionally adopt a breaking SDK change.
- Prefer `^1.2.1` in consuming software for safe backward-compatible updates.
- Prefer exact `1.2.1` when you need fully locked deployments.

Use one of these commands before publishing an update:

```bash
npm run release:patch
npm run release:minor
npm run release:major
```

Typical release flow:

```bash
npm test
npm run build
npm run release:patch
npm run publish:npm
```

Version updates are stored in [package.json](c:\Naga\LicenseManager\packages\license-service-sdk\package.json) and [package-lock.json](c:\Naga\LicenseManager\packages\license-service-sdk\package-lock.json), so future releases keep a clear version history.

## Usage

```ts
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.

```ts
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:

```ts
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 `.lic` files 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`.

```ts
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:

```ts
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 `.lic` files use the product's current active signing key
- older `.lic` files 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:

1. Add the new public key to the local trust bundle instead of replacing the old one immediately.
2. Mark the old key as retired, but keep it available for historical offline files.
3. Use `key_id` from the `.lic` envelope to select the correct verification key.
4. Remove old keys only when you are certain no installed software still depends on old offline files.

Offline helper behavior:

- Verifies signed `.lic` payloads locally with a public key.
- Checks `status`, `expires_at`, and `offline_file_valid_until` locally.
- 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`: sends `Authorization: Bearer <key>`
- `appId` and `appSecret`: sends `X-App-Id` and `X-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.