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.codevalues, not exceptions. Network failure (NETWORK_ERROR) is always distinguished from an invalid verdict. - Supports both an online validation cache and fully offline (
.lic) verification.
Installation
Section titled “Installation”npm install @signox/sdkQuickstart
Section titled “Quickstart”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) — idempotentconst 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 deviceclient.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.contentclient.validateOffline(licContent) // Fully local verification (synchronous)client.getHwid() // Diagnostic hwid lookupThe verdict (LicenseResult)
Section titled “The verdict (LicenseResult)”| 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 |
Verdict codes
Section titled “Verdict codes”- 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.
Configuration (SignoxConfig)
Section titled “Configuration (SignoxConfig)”| 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 |
Caching and offline fallback
Section titled “Caching and offline fallback”- A successful
validateresponse is trusted for the server-providedcache.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
offlineGraceDaysis used as the verdict, withstaleset totrue. - With
cacheDirset, the cache survives process restarts.
Offline verification (.lic)
Section titled “Offline verification (.lic)”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 deviceconst 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}Embedding the public key (important)
Section titled “Embedding the public key (important)”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).