npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@key-warden/sdk

v1.3.0

Published

Official Node.js client for Key-Warden - validate software licences online (seat- and revocation-aware) or verify signed tokens offline against your embedded public key.

Downloads

782

Readme

@key-warden/sdk

The official Node.js client for Key-Warden. Validate a software licence online — seat-aware, revocation-aware — or verify a signed token offline against your embedded public key, with no network round-trip.

No dependencies. Node 16+.

npm install @key-warden/sdk

Validate online

The authoritative check. Ask the platform whether a licence is good right now.

const kw = require('@key-warden/sdk');

const res = await kw.validate(customerLicenceKey, {
  apimKey:   process.env.KW_APIM_KEY,     // your APIM subscription key
  clientKey: process.env.KW_CLIENT_KEY,   // your validation key
  machineId: kw.machineIdFrom(os.hostname(), userId), // stable, hashed on your side
});

if (!res.valid) throw new Error(`licence not valid: ${res.reason}`);
// res.token is a freshly signed proof — cache it for the offline path below.

A valid: false (e.g. revoked, expired, seat_limit_exceeded) is data, not an error. A wrong clientKey throws a KeyWardenError with code: 'unauthorized_client' — that's your auth failing, and your customer should never see it as a licence problem.

Verify offline

No connection? Verify a token you already hold against your public key — the 32-byte raw key from your vendor console. Pure and synchronous.

const check = kw.verifyToken(cachedToken, process.env.KW_PUBLIC_KEY);
if (!check.valid) lockFeatures(check.reason); // 'bad_signature' | 'expired' | ...

The token is header.body.signature (compact JWT style) and the Ed25519 signature covers the exact bytes header.body. This SDK verifies over those bytes — you never decode-then-reverify, which is the one mistake that silently breaks offline checks. Expiry is honoured within the offline grace window you set at mint time.

Online, with an offline fallback

The pattern most desktop apps want: online is authoritative; if the network is down, keep working within grace.

const res = await kw.validateOrVerify(customerLicenceKey, {
  apimKey, clientKey, machineId,
  cachedToken: lastGoodToken,        // from a previous validate()
  publicKey:   process.env.KW_PUBLIC_KEY,
});
// res.source === 'online' | 'offline'

A rejected clientKey (401) is never masked by the offline path — only a genuine reachability failure falls back.

Free trials

A trial licence is an ordinary Key-Warden key — you validate it exactly like any other. It just carries two extra claims: trial: true and an exp (unix seconds). When the trial ends, verifyToken()/validate() refuse it as expired on their own; you don't enforce anything yourself. The trial helpers are for display — showing "N days left" and switching to an expired state:

const res = kw.verifyToken(cachedToken, process.env.KW_PUBLIC_KEY);

if (res.valid) {
  const t = kw.trialInfo(res);            // { isTrial, expired, expiresAt, secondsRemaining, daysRemaining }
  if (t.isTrial) {
    showBanner(`Trial — ${t.daysRemaining} day(s) left`);
  }
  runApp();
} else if (res.reason === 'expired') {
  // trial (or paid licence) has run out — prompt for a purchased key
  showPaywall('Your trial has ended. Enter a licence key to continue.');
}

trialInfo() accepts a verifyToken()/validate() result or a raw claims object. isTrial(x) and daysRemaining(x) are shortcuts. daysRemaining is rounded up (so the last partial day still reads "1 day left") and is 0 once expired, null for a key with no exp. These helpers never grant access — always gate on verifyToken()/validate() first.

Trial keys are minted node-locked to one device (sites: 1), so use the same machineId you pass to validate().

API

| Function | Purpose | |---|---| | validate(key, opts) | Online check. Returns { valid, reason?, activeSeats?, token? }. | | verifyToken(token, rawPubB64) | Offline check. Returns { valid, reason?, claims? }. | | validateOrVerify(key, opts) | Online, falling back to a cached token when unreachable. | | machineIdFrom(...parts) | A stable SHA-256 machine id; raw parts never leave the machine. | | trialInfo(x, opts?) | Trial facts for display: { isTrial, expired, expiresAt, secondsRemaining, daysRemaining }. | | isTrial(x) | true when the licence carries trial: true. | | daysRemaining(x, opts?) | Whole days left (rounded up); 0 once expired; null if no exp. |

TypeScript definitions are bundled.

Security notes

  • Your private signing key never leaves Key-Warden's Key Vault. You embed only the 32-byte public half.
  • machineId is hashed by the platform, but send an opaque, stable id — not a raw MAC address or a hostname you wouldn't want logged. machineIdFrom() hashes on your side too.
  • Two independent credentials gate every online call: the APIM subscription key gets you to the gateway, the validation key authenticates you as the vendor. A leaked validation key can be rotated without reissuing a single customer licence.

Code protection (seal / unlock / unseal)

Lock part of your product so it only runs for a valid, activated licence. Turn it on and get your content key (base64) from the vendor console → Protect your code.

Build time — seal a file once:

const kw = require('@key-warden/sdk');
const fs = require('fs');
const blob = kw.seal(fs.readFileSync('secret-module.js'), MY_CONTENT_KEY_B64);
fs.writeFileSync('secret-module.sealed', blob);   // ship this instead

Runtime — get the key and decrypt. The key rides in the validate token as ck, machine-bound, so unlock with the SAME machineId you validate with:

const res = await kw.validate(licence, { apimKey, clientKey, machineId });
const key = kw.unlockFromToken(res.token, machineId);   // Buffer: content key
const code = kw.unseal(sealedBlob, key);                // your decrypted file

Prefer a live check every time (real-time revocation)? Use the online path instead of the token:

const key = await kw.unsealOnline(licence, { apimKey, machineId /*, product */ });

All AES-256-GCM. A revoked or lapsed licence stops getting the key.

Licence

MIT.