Skip to content

Node.js SDK

@signox/sdk — the official SDK for Node.js (servers, CLIs, Electron). Zero runtime dependencies, supports Node 14+.

  • Every server response is trusted only after its Ed25519 signature is verified with the product public key (tampering/replay defense).
  • The hardware ID (hwid) is generated internally by the SDK — callers never deal with it.
  • Verdicts are delivered as LicenseResult.code values, not exceptions. Network failure (NETWORK_ERROR) is always distinguished from an invalid verdict.
  • Supports both an online validation cache and fully offline (.lic) verification.
Terminal window
npm install @signox/sdk
import { SignoxClient } from '@signox/sdk';
const client = new SignoxClient({
// Product public key (SPKI PEM) — must be embedded in your app. See "Embedding the public key" below.
productPublicKey: `-----BEGIN PUBLIC KEY-----
MCowBQYDK2Vw...
-----END PUBLIC KEY-----`,
// baseUrl defaults to https://api.signox.kr
});
const licenseKey = 'S4K2MB-7IWQ3F-9XG5JN-P2QN8R-QW3ZOD-8T7V2X';
// 1) Activate the device (once, or when moving devices) — idempotent
const activated = await client.activate(licenseKey, { name: "John's laptop" });
if (!activated.valid) {
console.error('Activation failed:', activated.code); // e.g. DEVICE_LIMIT_REACHED, VM_NOT_ALLOWED
}
// 2) Validate (on app start / periodically)
const result = await client.validate(licenseKey);
if (result.valid) {
// VALID or IN_GRACE_PERIOD
console.log('Features:', result.features); // { pro_export: true, max_seats: 10, ... }
} else if (result.code === 'NETWORK_ERROR') {
// Server unreachable + no valid cache — not an invalid verdict. Retry or apply grace UX.
} else {
console.warn('Invalid:', result.code); // EXPIRED, REVOKED, DEVICE_NOT_ACTIVATED ...
}

activate automatically collects and sends the current platform, hwid, and anomaly signals (vm, etc.).

client.validate(licenseKey) // Validate (successful responses are cached automatically)
client.activate(licenseKey, { name?, platform? }) // Activate this device
client.deactivate(licenseKey) // Deactivate (returns the slot)
client.heartbeat(licenseKey) // Liveness signal (telemetry)
client.reportUsage(licenseKey, feature, value?) // Report metered usage (default 1)
client.requestOfflineFile(licenseKey) // Issue a .lic → result.file.content
client.validateOffline(licContent) // Fully local verification (synchronous)
client.getHwid() // Diagnostic hwid lookup
Field Description
valid true only for VALID and IN_GRACE_PERIOD
code Verdict code (below)
features Policy feature-value map (bool/int/string) — {} when invalid
license / product License/product info (on successful validation)
device Device info on successful activate
usage Metered usage on reportUsage
file .lic content on successful requestOfflineFile
fromCache Returned from a valid cache without network
stale Fell back to an expired cache due to network failure
  • State: VALID, IN_GRACE_PERIOD, EXPIRED, SUSPENDED, REVOKED
  • Request/device: NOT_FOUND, PRODUCT_INACTIVE, HWID_REQUIRED, DEVICE_NOT_ACTIVATED, DEVICE_LIMIT_REACHED, VM_NOT_ALLOWED, FEATURE_NOT_FOUND, USAGE_LIMIT_REACHED
  • SDK-local: SIGNATURE_INVALID, NONCE_MISMATCH, OFFLINE_FILE_INVALID, HWID_MISMATCH, NETWORK_ERROR

See the online activation guide for what each code means and the recommended UX.

Option Default Description
productPublicKey (required) Product public key, SPKI PEM
baseUrl https://api.signox.kr API base URL
timeout 10000 Request timeout (ms)
cacheDir (none) If set, caches the signed raw responses on disk. In-memory only otherwise
tsToleranceSec 300 Allowed clock skew for the response meta.ts (seconds)
offlineGraceDays 7 Days a stale cache may be used as fallback on network failure, counted from when it was stored. 0 = no fallback
  • A successful validate response is trusted for the server-provided cache.ttl, skipping re-validation.
  • The cache stores only the signed raw response and re-verifies the signature on every read (tamper defense).
  • On network failure, a cache within offlineGraceDays is used as the verdict, with stale set to true.
  • With cacheDir set, the cache survives process restarts.

Machines without internet access use a .lic file issued from the portal or dashboard. See offline activation for the full flow.

// Issue and save on an online device
const res = await client.requestOfflineFile(licenseKey);
if (res.file) fs.writeFileSync('license.lic', res.file.content);
// Verify on the offline device (no network)
const offline = client.validateOffline(fs.readFileSync('license.lic', 'utf8'));
if (offline.valid) {
// VALID (perpetual/valid) or IN_GRACE_PERIOD
}

Always embed productPublicKey in your app binary. The SDK deliberately provides no way to fetch the product public key from the server — receiving the key over the network would let a man-in-the-middle swap in their own key and make forged responses look valid. This embedded key is the single root of trust for signature verification, so do not add lookup or config-injection bypasses around it. Never put the private key in a client (signing is server-only).