KDPika Licensing Sign inStart free
API v1
Show all languages
Browse documentation

Choose a page in the navigation drawer.

KDPika Licensing · API v1 Intermediate

JavaScript SDK

Install and configure the official JavaScript SDK with exact members, safe lifecycle examples, storage and release limitations.

On this page
Updated 2026-10-17 UTCDownload Markdown

Integrate the unchanged JavaScript SDK with an explicit validity gate and a stable private installation state.

What you will learn

  • Install the actual source package and configure every constructor option.
  • Use the complete session CLI and distinguish release success from feature access.
  • Understand every exposed member, storage/error/offline boundary and packaging limit.

Prerequisites

Runtime and package

Node18+ ESM with native fetch/crypto/filesystem APIs; the tested session program targets Node18.20+ syntax and runs on Node22.22.2. Intended for Node services or Electron main process, not ordinary browser JavaScript.

Package @kdpika/license-sdk, current label 1.0.0. Download kdpika-javascript-sdk-1.0.0.zip from the SDK overview and verify SHA-256 before extracting. This is a local source install, not a claim that a public registry release exists.

Install and use

Run the installation command in an empty application project with the extracted SDK directory. Set KDP_APP_ID and a private KDP_STATE_DIR. For PHP also set KDP_SDK_DIR to the extracted package directory. For activation set KDP_LICENSE_KEY and optional KDP_CUSTOMER_EMAIL from trusted user input/your secret environment, not source literals.

bashbash
npm install --ignore-scripts --save-exact ./kdpika-javascript-sdk

Expected: the exact local SDK installs/builds and its imports resolve. Python on Windows uses .venv\Scripts\python.exe instead of .venv/bin/python; the transcript is the tested macOS path. No registry release is implied by dependency restoration.

The session program is complete and runs one explicit action per invocation, preserving state across processes. Its SDK constructor deliberately sets SANDBOX. The key-prefix/URL checks are demonstration guards, not a replacement for server environment lookup or SDK verification. HTTPS is the default; local fixture HTTP requires KDP_ALLOW_LOOPBACK=1.

session.mjsjavascript
/** Sandbox session CLI; Node18.20+, official local SDK1.0.0 (ESM).
 * Run node session.mjs status|activate|check|validate|deactivate.
 * Set KDP_APP_ID,KDP_STATE_DIR; activate: KDP_LICENSE_KEY/optional customer email.
 * Expected safe JSON, no raw signed payload/activation token. Keep validity gate.
 */
import crypto from 'node:crypto';
import {LicenseClient} from '@kdpika/license-sdk';
function required(name){const value=(process.env[name]||'').trim();if(!value)throw Error('Missing configuration');return value;}
try{
 const action=process.argv.length===3?process.argv[2]:'';
 if(!['status','activate','check','validate','deactivate'].includes(action))throw Error('Choose an action');
 const base=(process.env.KDP_BASE_URL||'https://licensing.kdpika.com').replace(/\/+$/,'');const uri=new URL(base);
 const local=uri.protocol==='http:'&&['127.0.0.1','localhost'].includes(uri.hostname)&&process.env.KDP_ALLOW_LOOPBACK==='1';
 if(uri.username||uri.password||uri.pathname!=='/'||uri.search||uri.hash||(uri.protocol!=='https:'&&!local))throw Error('Use an HTTPS site origin');
 const client=new LicenseClient({product:required('KDP_APP_ID'),environment:'SANDBOX',baseUrl:base,
  applicationName:'KDPika SDK session example',applicationVersion:'1.0.0',storageDir:required('KDP_STATE_DIR')});
 if(action==='status')console.log(JSON.stringify({activated:client.activated,storage_security:'permission-protected-file',
  installation_hash:crypto.createHash('sha256').update(client.installationId).digest('hex')}));
 else{
  let result;
  if(action==='activate'){
   const key=required('KDP_LICENSE_KEY');if(!/^TEST[A-Z0-9]*-/i.test(key))throw Error('Use a Sandbox key');
   result=await client.activate(key,process.env.KDP_CUSTOMER_EMAIL||null);
  }else result=await client[action]();
  const active=action!=='deactivate'&&result.valid&&result.signatureValid;
  const limit=active?result.featureValue('seat_count'):null;
  console.log(JSON.stringify({valid:result.valid,source:result.source,error_code:result.errorCode,
   signature_valid:result.signatureValid,pdf_export_allowed:Boolean(active&&result.feature('pdf_export')),
   seat_count:Number.isSafeInteger(limit)&&limit>=0?limit:null,request_id:/^req_[a-f0-9]{24}$/.test(result.requestId||'')?result.requestId:null,
   message:action==='deactivate'&&result.valid?'This installation is deactivated.':result.valid?'Your license is ready.':'Your license could not be confirmed. Check your key or contact the software publisher.'}));
  if(!result.valid)process.exitCode=1;
 }
}catch{
 console.error('License check unavailable. Review configuration, connectivity and private storage permissions. Do not disable signature verification.');process.exitCode=2;
}
  1. Run node session.mjs status. Expected: activated=false for fresh storage, with only an installation hash and storage category.
  2. Run node session.mjs activate with a Sandbox key and matching email. Expected: valid=true/signature_valid=true or an explicit refusal, never a raw token/payload dump.
  3. Run node session.mjs check using the same state directory. Expected: current valid result from the eligible cache or online path; no feature is enabled on failure.
  4. Run node session.mjs validate. Expected: forced-online path, subject to the documented bounded transport-failure fallback.
  5. Run node session.mjs deactivate. Expected: the installation seat is released and local token/cache removed; pdf_export_allowed remains false for this release operation.
  6. Run node session.mjs status again. Expected: activated=false with the same installation hash. Cumulative activation_count is not reset.

The programs exit0 for an accepted operation/status, exit1 for a licensing refusal, exit2 for configuration/unexpected failure. They expose safe decisions only. seat_count is read only from a verified valid result and must be a nonnegative integer; an absent value remains null. Result feature helpers cannot substitute for the validity/signature gate.

Complete constructor configuration

OptionTypeDefaultMeaning
productstringrequiredPublic App ID; never the internal product ID or an lmp_ key.
environmentstringLIVE (Python: production maps to LIVE)Set SANDBOX explicitly for examples. production/test aliases normalize to LIVE/SANDBOX.
baseUrlstringhttps://licensing.kdpika.comSite origin, without /api/v1; default HTTPS. Loopback HTTP is for local testing only.
applicationNamestringLicensed ApplicationNames the default application storage directory and device fallback label.
applicationVersionstringempty stringApplication release sent at activation. Empty version skips the server version-range check.
verificationKeydictionary/array/objectnull/NoneOptional pinned key with key_id and raw base64url public_key; manual rotation.
storageDirpath/stringOS-derived application storage rootNamespace includes origin, product and environment; keep outside public web roots.
timeoutMsseconds / milliseconds15000 millisecondsTransport timeout, not an offline grace or validation interval.
fetchImplfetch-compatible functionglobalThis.fetchTest injection must preserve the actual response/error contract; never accept unsigned fixtures in release.
clockcallablecurrent UTC-compatible timeTesting clock; do not expose time control to untrusted application callers.

Timeout is an HTTP timeout, not a client-side way to configure offline grace or validation interval. Those times come from the verified signed payload and the product configuration. Defaults are actual source defaults; the runnable sample explicitly changes environment/storage choices for safe isolated tests.

Public members and result contract

This inventory includes constructors, fields/properties, lifecycle/result helpers and exposed low-level members. JavaScript helpers are language-public even when they are implementation details. Python non-underscore storage/transport classes are listed rather than silently hidden. The exact source signature is shown so a guide cannot invent a method such as has_feature().

MemberKindExact signature / fieldUse and return/error boundary
LicenseResult.constructorconstructorconstructor({valid, source, payload = {}, errorCode = null, message = "", requestId = null, signatureValid = false, storageSecurity = "permission-protected-file"})Configure this type using the exact signature. Client construction creates/loads its local installation namespace; result construction alone is not verification.
LicenseResult.featuremethodfeature(name, fallback = false)Boolean-style feature lookup only. Never authorizes invalid results; C# true/false fallback semantics differ from truthiness in the other SDKs.
LicenseResult.featureValuemethodfeatureValue(name, fallback = null)Read the typed feature value/default. Check valid and the actual feature type first.
LicenseClient.constructorconstructorconstructor({product, environment = "LIVE", baseUrl = "https://licensing.kdpika.com", applicationName = "Licensed Application", applicationVersion = "", verificationKey = null, storageDir = null, timeoutMs = 15000, fetchImpl = globalThis.fetch, clock = () => new Date()})Configure this type using the exact signature. Client construction creates/loads its local installation namespace; result construction alone is not verification.
LicenseClient.activatedpropertyget activated()Token-presence status only; not a current entitlement decision.
LicenseClient.activatemethodasync activate(licenseKey, email = null)Send activation; return a valid signed result or explicit refusal. Success stores the token and signed state; verification failure attempts best-effort deactivation.
LicenseClient.checkmethodasync check(forceOnline = false)Use verified cache when eligible or validate online. A transport failure can enter bounded offline policy; explicit server rejection is not offline permission.
LicenseClient.validatemethodvalidate()Force the check path online; transport failure can still attempt bounded verified offline fallback, not an unconditional online-only guarantee.
LicenseClient.deactivatemethodasync deactivate()Release this token seat online and clear token/cache on success. A successful release result does not grant access. Installation identity remains.
LicenseClient.featuremethodasync feature(name, fallback = false)Client helper may perform a new check but does not enforce valid itself. Prefer one accepted result, then result feature access. Never use the helper as the sole permission gate.
LicenseClient.offlinemethodasync offline()Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.verifiedmethodasync verified(signed, source, response = null)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.getVerificationKeymethodasync getVerificationKey(signed)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.saveStatemethodasync saveState(signed)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.checkpointmethodcheckpoint()Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.tokenmethodtoken()Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.bestEffortDeactivatemethodasync bestEffortDeactivate(token)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.requestmethodasync request(method, endpoint, body, headers)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.responseErrormethodresponseError(response)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.errormethoderror(errorCode,message,source,response=null,payload={},signatureValid=false)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.nowmethodnow()Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.readJsonmethodreadJson(name)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.writeJsonmethodwriteJson(name,value)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.atomicWritemethodatomicWrite(name,value)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
LicenseClient.removemethodremove(name)Language-public implementation helper. Not a supported permission shortcut; requires trusted arguments and the same signature/storage/time rules. Keep direct calls inside isolated conformance tests, not renderer or buyer-controlled interfaces.
module.canonicalJsonfunctionexport function canonicalJson(value)Exported current canonicalizer, with the documented integral-float limitation. Not a standalone verification or entitlement decision.
LicenseResult.validfieldvalidActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.sourcefieldsourceActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.payloadfieldpayloadActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.errorCodefielderrorCodeActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.messagefieldmessageActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.requestIdfieldrequestIdActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.signatureValidfieldsignatureValidActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseResult.storageSecurityfieldstorageSecurityActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.productattributeproductActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.environmentattributeenvironmentActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.baseUrlattributebaseUrlActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.applicationNameattributeapplicationNameActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.applicationVersionattributeapplicationVersionActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.verificationKeyattributeverificationKeyActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.timeoutMsattributetimeoutMsActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.fetchImplattributefetchImplActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.clockattributeclockActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.storageDirattributestorageDirActual exposed value/component; inspect the declared type. Do not treat writable/internal state or result payload presence as an authorization grant.
LicenseClient.installationIdattributeinstallationIdPersisted random installation identity; not a hardware fingerprint or secret developer API key.

A LicenseResult carries valid/source/payload/error code/message/request ID/signature status/storage category under the language-specific spelling above. Lifecycle calls return this result type. Unknown codes fail closed. HttpResponse/custom transport status is not an access grant. A field in a constructed result has not been verified merely because it is present.

The exact runnable session covers the real Sandbox lifecycle and safe output. Representative local/model/helper assertions execute every inventoried public member; the optional Python dialog has four genuine offscreen Qt cases. These are not claims that every branch, injected cancellation, native vault, framework or target OS was executed. The full app examples record actual packaged/native versus cross-build/controller boundaries explicitly.

Runnable member examples

This separate complete program exercises the public local/result/helper surface with explicit assertions and no access grant. It is an isolated conformance exercise, not an alternative licensing implementation. Run it beside the installed source package using node members.mjs. Expected: result=PASS and a list of exercised members, without credentials or raw customer payloads.

Python makes one read-only public-key GET to KDP_BASE_URL (default HTTPS production origin); its product is an unusable example App ID, and it does not activate. Its optional PyQt dialog is tested separately in four offscreen cases. PHP and C# use only synthetic local objects and no HTTP. JavaScript uses an ephemeral test signing key and injected synthetic transport, never a real KDPika credential or network response. The normal session example above is separately tested against the real isolated Sandbox.

These examples use their own fresh temporary directories. They never inspect or delete an existing installation. Result fields are constructed as invalid deliberately: a true feature helper cannot override valid=false. Typed return assertions demonstrate behavior, not complete branch, native vault or target-OS coverage.

members.mjsjavascript
// Isolated public-member conformance exercise, NOT a production access client.
// The ephemeral key signs synthetic in-memory data for an injected transport.
// It is never trusted by KDPika, written to disk, printed or used on the network.
import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {LicenseClient, LicenseResult, canonicalJson} from "@kdpika/license-sdk";

const covered = [];
const check = (member, condition) => { assert.ok(condition, `Member example failed: ${member}`); covered.push(member); };
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "kdpika-members-"));
fs.chmodSync(temporary, 0o700);
try {
  let now = new Date("2026-10-17T12:00:00Z"), mode = "online", releaseCalls = 0;
  const pair = crypto.generateKeyPairSync("ed25519");
  const publicBytes = pair.publicKey.export({type: "spki", format: "der"}).subarray(-32);
  const key = {key_id: "ed25519_example", algorithm: "Ed25519", public_key: publicBytes.toString("base64url")};
  let client;
  const signed = () => {
    const payload = {product_id: "YOUR_APP_ID", environment: "sandbox", status: "active",
      device: {installation_hash: crypto.createHash("sha256").update(client.installationId).digest("hex")},
      features: {pdf_export: true, seat_count: 2}, server_time: "2026-10-17T12:00:00Z",
      next_validation_at: "2026-10-18T12:00:00Z", offline_grace_until: "2026-10-20T12:00:00Z",
      expires_at: "2026-11-17T12:00:00Z"};
    return {payload, signature: crypto.sign(null, Buffer.from(canonicalJson(payload)), pair.privateKey).toString("base64url"),
      algorithm: "Ed25519", key_id: key.key_id};
  };
  const fetchImpl = async (url, options) => {
    const endpoint = new URL(url).pathname;
    assert.equal(new URL(url).origin, "https://licenses.example.invalid");
    assert.ok(options.signal instanceof AbortSignal);
    if (mode === "network") throw new Error("Synthetic transport outage");
    let body;
    if (endpoint === "/api/v1/public-key") body = {success: true, key};
    else if (endpoint === "/api/v1/licenses/deactivate") { releaseCalls++; body = {success: true, deactivated: true}; }
    else if (mode === "rejected") return {ok: false, status: 403, json: async () => ({success: false, error: {code: "LICENSE_REVOKED", message: "Example refusal"}})};
    else body = {success: true, activation_token: "UNUSABLE_EXAMPLE_TOKEN", license: signed(), request_id: "req_example"};
    // The actual SDK deliberately requires act_ at activation. This unusable,
    // local-only test value cannot be accepted by a real KDPika token lookup.
    if (endpoint === "/api/v1/licenses/activate") body.activation_token = "act_example_not_a_real_credential";
    return {ok: true, status: 200, json: async () => body};
  };
  client = new LicenseClient({product: "YOUR_APP_ID", environment: "SANDBOX", baseUrl: "https://licenses.example.invalid",
    storageDir: temporary, verificationKey: key, fetchImpl, clock: () => now});
  check("LicenseClient.constructor", client instanceof LicenseClient);
  for (const name of ["product", "environment", "baseUrl", "applicationName", "applicationVersion", "verificationKey",
    "timeoutMs", "fetchImpl", "clock", "storageDir", "installationId"]) check(`LicenseClient.${name}`, client[name] !== undefined);
  check("LicenseClient.activated", !client.activated);
  check("LicenseClient.token", client.token() === null);
  check("LicenseClient.now", client.now().toISOString() === now.toISOString());
  check("module.canonicalJson", canonicalJson({b: [1, true], a: "value"}) === '{"a":"value","b":[1,true]}');
  const example = new LicenseResult({valid: false, source: "example", payload: {features: {pdf_export: true, seat_count: 2}},
    errorCode: "EXAMPLE_ONLY", message: "Example", requestId: "req_example", signatureValid: false, storageSecurity: "unknown"});
  check("LicenseResult.constructor", Object.isFrozen(example));
  for (const [name, expected] of Object.entries({valid: false, source: "example", errorCode: "EXAMPLE_ONLY", message: "Example",
    requestId: "req_example", signatureValid: false, storageSecurity: "unknown"})) check(`LicenseResult.${name}`, example[name] === expected);
  check("LicenseResult.payload", example.payload.features.pdf_export === true);
  check("LicenseResult.feature", example.feature("pdf_export") && !example.feature("missing"));
  check("LicenseResult.featureValue", example.featureValue("seat_count") === 2 && example.featureValue("missing", 7) === 7);
  check("application.fail_closed", !(example.valid && example.signatureValid && example.feature("pdf_export")));
  check("LicenseClient.error", client.error("EXAMPLE_ONLY", "Example", "local").valid === false);
  check("LicenseClient.responseError", client.responseError({body: {error: {code: "INVALID_TOKEN", message: "Example"}}}).errorCode === "INVALID_TOKEN");
  const response = await client.request("GET", "/api/v1/public-key", null, {});
  check("LicenseClient.request", response.ok && response.status === 200 && response.body.key.algorithm === "Ed25519");
  check("LicenseClient.getVerificationKey", (await client.getVerificationKey(signed())).key_id === key.key_id);
  check("LicenseClient.verified", (await client.verified(signed(), "online")).signatureValid);
  await client.saveState(signed());
  check("LicenseClient.saveState", client.readJson("signed-state.json").signed_license.algorithm === "Ed25519");
  client.checkpoint();
  check("LicenseClient.checkpoint", client.readJson("signed-state.json").last_seen_at === now.toISOString());
  check("LicenseClient.offline", (await client.offline()).valid);
  client.writeJson("example.json", {local_example: true});
  check("LicenseClient.writeJson", fs.existsSync(path.join(client.storageDir, "example.json")));
  check("LicenseClient.readJson", client.readJson("example.json").local_example === true);
  client.atomicWrite("example.txt", "unusable local text");
  check("LicenseClient.atomicWrite", fs.readFileSync(path.join(client.storageDir, "example.txt"), "utf8") === "unusable local text");
  client.remove("example.txt");
  check("LicenseClient.remove", !fs.existsSync(path.join(client.storageDir, "example.txt")));
  const activated = await client.activate("TEST-UNUSABLE-EXAMPLE", "synthetic@example.invalid");
  check("LicenseClient.activate", activated.valid && activated.signatureValid && client.activated);
  check("LicenseClient.check", (await client.check()).source === "cache");
  check("LicenseClient.validate", (await client.validate()).source === "online");
  check("LicenseClient.feature", await client.feature("pdf_export"));
  const beforeRelease = releaseCalls;
  await client.bestEffortDeactivate("UNUSABLE_EXAMPLE_TOKEN");
  check("LicenseClient.bestEffortDeactivate", releaseCalls === beforeRelease + 1);
  now = new Date("2026-10-18T13:00:00Z"); mode = "network";
  assert.equal((await client.validate()).source, "offline");
  now = new Date("2026-10-21T12:00:00Z");
  assert.equal((await client.validate()).errorCode, "OFFLINE_GRACE_EXPIRED");
  now = new Date("2026-10-17T11:54:59Z");
  assert.equal((await client.offline()).errorCode, "CLOCK_ROLLBACK_DETECTED");
  now = new Date("2026-10-19T12:00:00Z"); mode = "rejected";
  assert.equal((await client.validate()).errorCode, "LICENSE_REVOKED");
  assert.equal(client.activated, false);
  mode = "online";
  assert.equal((await client.activate("TEST-UNUSABLE-EXAMPLE")).valid, true);
  const released = await client.deactivate();
  check("LicenseClient.deactivate", released.valid && !released.signatureValid && !client.activated);
  console.log(JSON.stringify({result: "PASS", members: covered, mode: "injected synthetic signing/transport; zero HTTP",
    access_granted: false, additional_cases: ["offline", "grace-expired", "clock-rollback", "explicit-revocation"]}));
} finally {
  // Only the exact fresh scratch directory from mkdtempSync is removed.
  fs.rmSync(temporary, {recursive: true});
}

Errors and exceptions

Constructor validation throws Error. Lifecycle methods normally resolve LicenseResult; storage/custom-fetch/unexpected JSON failures can reject a Promise. Catch at your boundary and never show raw error text or payload. No built-in application logger or trace-redaction pipeline exists. The request helper uses AbortController; transport throws enter the documented offline/refusal path.

Use the complete error table for code/namespace/cause/correction and safe user messages. Log only application version, UTC time, action, code, source and a validated req_ correlation value. Never log the key, token, Authorization header, customer payload or an unredacted state file. No debug flag changes the signature or validity policy.

Storage and installation identity

Permission-protected files in a namespaced directory, with0700/0600 attempts where supported. No built-in Electron safeStorage, Windows Credential Manager or Secret Service adapter. Keep tokens in the main process and use the storage hardening guide. A JavaScript property or local payload is not trusted by itself.

Storage namespace is based on origin/product/environment. Keep those stable across app updates; changing them can create a separate identity/seat. Reinstalling or deleting identity can consume another cumulative activation even after a seat was released. Do not use machine serials/MAC addresses as a substitute for the persistent random ID.

Offline and clock policy

A new activation requires network. check uses verified signed state before next_validation_at when eligible. After that point it validates online; only a transport failure attempts the verified offline path. Explicit server rejection clears signed cache and selected invalid-token/device/revocation/expiry/cancellation errors also remove the stored credential. Offline/cache paths require active status, valid signature, product/environment/installation binding, expiry/grace and clock policy.

Clock rollback tolerance is300 seconds behind the latest trusted checkpoint/server time. A user controlling the host can patch the program or roll back a full machine snapshot; local checks are not absolute anti-tamper guarantees. Some malformed/missing signed time cases and current canonicalization issues are recorded limitations, not intended feature grants.

Validation interval/offline grace are not constructor options. Configure the product on KDPika, then verify what the signed server_time/next_validation_at/offline_grace_until actually say. Offline grace is based on server_time, not added after next_validation_at. Do not calculate a second extension or treat a failed check as permission.

Read the full offline guide and test vectors. Known Python separator/exponent, JavaScript integral-float and C# cached-Unicode compatibility failures remain unchanged. A successful narrow local lifecycle is not universal canonicalization conformance.

Threading, asynchronous work and lifetime

Async methods return Promises, but filesystem operations are synchronous and there is no shared-instance/thread/process locking. Serialize lifecycle operations and do not race activation/deactivation across workers. Node worker threads and multiple Electron instances must not write one namespace concurrently without your own coordination.

Application and framework integration

Electron: construct the client only in the main process, keep contextIsolation enabled, expose narrow application actions through preload/IPC, and never return tokens/raw cache/arbitrary SDK methods to the renderer. The package is ESM only; CommonJS callers can use awaited dynamic import in an async entry point, not assume require() is an official CJS export. A web frontend must use its own authenticated backend and must not receive lmp_ developer keys.

Packaging and distribution

The complete Electron app uses pinned Electron44.9.0/electron-builder26.15.3 and includes the exact local SDK directory. npm ci restores the locked project; npm start runs the source window and npm run package builds its directory artifact. Both source and unsigned macOS packaged windows pass real Sandbox lifecycle/offline/refusal tests. Keep storageDir outside ASAR/installation paths. Code signing, notarization, other native OS packages and real safeStorage backends remain separate deployment tests; this SDK does not automatically encrypt its token files.

Upgrade and uninstall

Pin the current source checksum and runtime/dependency lock. Before changing a version, compare constructor/public members, storage namespace, canonical bytes, time policy and errors. Run activation/cache reload/offline/explicit refusal/deactivation on a separate Sandbox installation. No historical version or compatibility window is invented by this guide.

Deactivate while online before intentionally removing a deployed installation. Removing the package alone does not release a server seat or erase your application storage. Retaining storage may preserve identity for a later reinstall; deleting it can create another installation. Follow your application privacy/retention policy rather than recursively deleting a broad user directory.

The language-specific removal command applies only if the package/reference was installed into that application project:

bashbash
npm uninstall @kdpika/license-sdk

For a direct copied PHP source directory, removing a Composer root package is not the same as composer remove in a consuming application. Remove only the exact unused reference/files after backing up required application state; no broad deletion command is prescribed.

Expected result

Your application handles explicit refusal without enabling features, preserves the installation identity, and deactivates deliberately when releasing a seat. Check the real signed result, not only a transport status.

Common mistakes

  • Using activated/token presence or a feature helper as a validity proof.
  • Putting /api/v1 in the SDK site-origin constructor or omitting SANDBOX in a test.
  • Deleting identity/cache and assuming a server seat or cumulative activation was reset.
  • Suppressing a signature mismatch instead of testing the actual release limitation.

Security notes

Keep credentials off renderer/browser surfaces, retain TLS/signature verification, and use a verified valid result for the specific access action. A successful deactivate/status operation never grants a feature. Use private storage and safe correlation metadata only.

Troubleshooting

SymptomCauseAction
NOT_ACTIVATEDNo stored credential in this namespaceActivate a Sandbox installation; keep origin/product/environment/storage stable
INVALID_LICENSEWrong key/email/App ID/environmentCorrect the input; do not retry unchanged requests blindly
SIGNATURE_INVALID or cache rejectionTampering, trust mismatch or current canonicalization limitationFail closed and run real response/cache/vector checks
State disappeared after updateApplication name/origin/storage namespace changedRestore the intended stable namespace; reconcile any extra seat
Network failure at startupActivation requires network or cache is unavailable/invalidReconnect or show a clear retry/support path without unlocking

Next steps

Review the Sandbox quickstart, signed responses and error handling.

Was this page helpful?

Choose an answer

Do not include personal data, license keys or credentials. Retained for up to 30 days. This is not a support inbox.

Search documentation

Arrow keys to move, Enter to open, Escape to close. Search stays in your browser.

Cookie settings