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

@keylight-dev/js

v0.1.3

Published

Official JavaScript/TypeScript SDK for Keylight licensing.

Readme

Keylight JavaScript SDK

npm License: MIT Types Runtimes Conformance

Open-source JavaScript/TypeScript SDK for Keylight — license your web, Node, Electron, and edge apps with online activation and offline Ed25519 license verification, from any JavaScript runtime.

In one line: a software-licensing SDK for JavaScript/TypeScript — license-key activation and validation, entitlement/feature gating, trials and free tiers, and tamper-resistant offline license verification (signed v3 lease, Ed25519 + clock-skew tolerance) for the browser, Node, Deno, Bun, and edge/Workers. Universal, dependency-light, and fully typed.

Why Keylight

Licensing shouldn't mean bolting a heavyweight, phone-home-or-die SDK onto your app.

  • Works offline. The license is a signed lease your app verifies locally with Ed25519 — no network round-trip to gate a feature, no lockout when the user is offline.
  • Tamper-resistant by design. Entitlements live inside the signature; a forged or hand-edited lease can't pass verification without the tenant's private key.
  • One SDK, every runtime. The same package runs in the browser, Node, Deno, Bun, and edge — and verifies licenses identically to the Swift and Rust SDKs (proven by shared conformance vectors).
  • Small and explicit. A handful of methods, no background daemons, no magic — you decide when to check in.

Table of Contents

Features

  • License Lifecycle — Activate, validate, and deactivate license keys with a small, explicit API.
  • Offline Verification — The single offline artifact is a signed v3 lease, verified with Ed25519 (@noble/ed25519) and a 300-second clock-skew tolerance. An optional maxOfflineDays grace caps how long a device may run without checking in.
  • Universal — One package for browser, Node ≥ 18, Deno, Bun, and edge / Cloudflare Workers. Uses only fetch + @noble/* — no Node-specific APIs in the core.
  • Synchronous reads — Network calls are async, but state(), hasEntitlement(), and the cached getters are synchronous (backed by an in-memory cache hydrated by load()).
  • Entitlements — Feature gating from the cached lease: hasEntitlement("pro").
  • Trials & Free Tier — Built-in local trial timer, free-tier mode, and an anonymous "keyless" usage beacon.
  • Lifecycle Eventson(event, fn) for Renewed / Cancelled / Expired / Restored, plus subscribe(fn) for any state change.
  • Clock-Manipulation Detection — Flags backward/forward system-clock tampering.
  • Device Telemetry — Auto-attaches sdk_version, platform, and (optional) app_version (clamped to the backend's limits).
  • Network Resilience — Automatic retry with exponential backoff + jitter; honors Retry-After.
  • Pluggable — Swap the storage backend (LicenseStore) or HTTP transport (Transport) via interfaces for tests or custom platforms.
  • Fully typed — Ships ESM, CJS, an IIFE browser bundle, and complete .d.ts.

Runtime support

Universal: browser, Node ≥ 18, Deno, Bun, edge / Cloudflare Workers. The SDK requires only fetch and @noble/ed25519 + @noble/hashes (bundled dependencies). Ed25519 verify is synchronous, so license state can be read without await.

Quick Start

npm install @keylight-dev/js
import { Keylight, fetchKeyset, FetchTransport } from "@keylight-dev/js";

// Optionally fetch the tenant's trusted Ed25519 keyset so leases verify offline.
// (You can also pin keys explicitly via `trustedKeys`.)
const ks = await fetchKeyset(new FetchTransport(), "https://api.keylight.dev", "your-tenant");

const kl = new Keylight({
  tenantId: "your-tenant",
  productId: "your-product",
  appVersion: "1.0.0",
  maxOfflineDays: 7,            // optional offline grace window
  trustedKeys: ks?.keys ?? {},  // for offline lease verification
});

// Hydrate the in-memory cache from the persistent store (async, idempotent).
await kl.load();

// Activate a license key (online). The returned lease is Ed25519-verified
// *before* anything is persisted.
const res = await kl.activate("USER-LICENSE-KEY");
if (!res.activated) console.error(res.error);

// Gate features on entitlements — synchronous, from the cached lease.
if (kl.hasEntitlement("pro")) {
  // unlock pro features
}

// Release the seat when uninstalling / switching devices.
await kl.deactivate();

Call await kl.load() once at startup — it hydrates the cache that backs the synchronous reads (state(), hasEntitlement(), cached*). The licensing methods also await it internally.

For a browser <script> tag, the IIFE bundle exposes the namespace as window.KeylightSDK (new KeylightSDK.Keylight({ … })).

License Lifecycle

┌─────────────┐     ┌─────────────┐     ┌──────────────┐
│  activate   │────▶│  validate   │────▶│  deactivate  │
└─────────────┘     └─────────────┘     └──────────────┘
                          ▲
                          │ on launch / on events (no background timers)
                  ┌────────────────────┐
                  │  refreshIfNeeded   │
                  └────────────────────┘

| Method | Description | |--------|-------------| | activate(key) → ActivationResult | Activates a key on this device. Verifies the returned lease before persisting; returns instanceId, the lease, and expiry. | | validate() → ValidationResult | Re-checks the stored license online. Decodes hard-expiry (422) responses and preserves fallback/expired leases so state can resolve. | | deactivate() | Releases the seat and clears local license state (even if the network call fails). Call on uninstall or device switch. | | refreshIfNeeded() → ValidationResult \| null | Validates only if due (debounce 5 min, stale 6 h, or within 24 h of expiry). Safe to call often. | | checkOnLaunch() | Convenience: refresh if a license is stored, else no-op. |

License States

state() resolves a single high-level status from the cached lease, trial, and free-tier config (no network). It is a discriminated union on .kind:

| State | Meaning | |-------|---------| | Licensed | Current, signature-valid active lease. | | Limited | Signature-valid fallback lease (grace mode). | | Trial (carries daysLeft) | No license, but a local trial is active. | | FreeTier | No license, free tier enabled. | | Expired | Lease expired, or a license was stored but is no longer current. | | Invalid | No license, no trial, no free tier. |

const s = kl.state();
if (s.kind === "Trial") console.log(`${s.daysLeft} days left`);

Entitlements

Entitlements are feature keys carried inside the signed lease and checked offline:

if (kl.hasEntitlement("cloud-sync")) {
  enableCloudSync();
}

hasEntitlement returns true only when the cached lease is signature-valid, unexpired, not expired-status, and within maxOfflineDays — so offline feature gating never disagrees with the resolved Expired state.

Offline Validation

The offline artifact is a signed v3 lease issued by the Keylight API. The SDK reconstructs the exact signed payload (entitlements sorted, pipe-delimited) and verifies it with Ed25519 against the tenant's trusted keyset, applying a 300-second clock-skew tolerance.

import { Keylight } from "@keylight-dev/js";

const kl = new Keylight({
  tenantId: "your-tenant",
  productId: "your-product",
  // Pin trusted keys explicitly instead of fetching them:
  trustedKeys: { k1: "<raw ed25519 public key, base64>" },
  maxOfflineDays: 7, // omit to run offline as long as the lease itself is current
});
  • The trusted keyset can be fetched once from GET /{tenant}/.well-known/keylight-keys (fetchKeyset) or pinned at construction time.
  • cachedLease returns the lease only when it is kid-known, signature-valid, unexpired, and (if set) within maxOfflineDays of the last online validation.
  • You can verify a lease standalone (e.g. in server middleware) without a client instance:
import { verifyLease, isTrusted, SKEW_SECONDS } from "@keylight-dev/js";

const r = verifyLease(lease, { [kid]: base64PubKey }, Math.floor(Date.now() / 1000));
if (!isTrusted(r)) throw new Error("Untrusted lease");
if (r.expired) throw new Error("Lease expired");

Storage & security note: the at-rest license cache is plaintext (localStorage in the browser, a JSON file in Node). The security boundary is the Ed25519-signed lease, not at-rest secrecy — a tampered or forged lease cannot pass isTrusted() without the tenant's private key. In a browser, any key the SDK could use to "encrypt" the cache would have to live next to it, so encryption there would be theater; integrity (the signature) is what protects you.

Refresh, Trials & Free Tier

There are no background timers. The host drives refresh on launch and on meaningful events:

await kl.checkOnLaunch();     // validate if due, on startup
await kl.refreshIfNeeded();   // call again on focus / purchase / resume

Trials and free tier are local and offline-first:

await kl.startTrial();        // begins the trial clock once
const t = kl.checkTrial();    // { kind: "not_started" | "active", daysLeft } | { kind: "expired" }
if (t.kind === "active") console.log(`${t.daysLeft} days left`);

// Anonymous, debounced usage beacon for trial / free-tier / expired devices:
await kl.reportKeylessState("trial");

// Tamper check and a pre-filled hosted upgrade link:
const tampered = kl.isClockManipulated();
if (kl.upgradeUrl) console.log(`Upgrade: ${kl.upgradeUrl}`);

Lifecycle Events

Subscribe to react when the resolved state crosses a transition. on(event, fn) targets a specific event; subscribe(fn) fires on every state change with the new LicenseState. Both return an unsubscribe function.

const off = kl.on("Cancelled", () => showUpgradePrompt());
kl.subscribe((state) => render(state));
// ...later
off();

| Event | Fires when | |-------|-----------| | Renewed | Stayed Licensed and the expiry moved later. | | Cancelled | LicensedLimited or Expired. | | Expired | Any state → Expired. | | Restored | Expired/Limited/InvalidLicensed. |

Events are evaluated during validate() and re-derive the previous state from the persisted lease, so a transition won't re-fire across restarts.

Configuration Reference

Pass KeylightOptions to new Keylight({ … }):

| Option | Type | Default | Description | |--------|------|---------|-------------| | tenantId | string | — | Your Keylight tenant (required). | | productId | string | — | Your product (required). | | sdkKey | string | — | Optional SDK key, sent as X-Keylight-SDK-Key. | | trustedKeys | Record<string,string> | {} | Trusted Ed25519 public keys (kid → base64) for offline verification. | | maxOfflineDays | number | — | Offline grace window since last online validation. Omit = until the lease itself expires. | | trialDurationDays | number | 14 | Local trial length. | | freeTierEnabled | boolean | false | Resolve to FreeTier when there's no license/trial. | | appVersion | string | — | Reported in telemetry. | | baseUrl | string | https://api.keylight.dev | API base URL. | | keyPrefix | string | — | Client-side key-format check (e.g. "PROD"). | | deviceId | string | generated | Override the persisted free-tier/keyless instance id. | | transport | Transport | FetchTransport | Injectable HTTP transport. | | store | LicenseStore | auto-detected | Injectable persistence layer. |

A small set of Swift-parity aliases is also available for teams porting between platforms: isEntitled, productFreeTierEnabled(), isValidKeyFormat(key), refresh(force?), freeTierInstanceIdIfPresent(), reportFreeTier().

Pluggable Transport & Storage

Implement Transport to swap the HTTP layer (mock in tests, add headers, proxy through a service worker). Implement LicenseStore (get/set/remove, all async) to back the SDK with any persistence layer:

import { MemoryStore, LocalStorageStore, FsStore, makeDefaultStore } from "@keylight-dev/js";

| Store | Runtime | Notes | |-------|---------|-------| | MemoryStore | All | In-process only; state lost on reload. | | LocalStorageStore | Browser | Namespaced under a keylight_ prefix by default. | | FsStore | Node / Bun | One JSON file at ~/.keylight.json by default. | | makeDefaultStore() | All | Auto-selects: LocalStorageStoreFsStoreMemoryStore. |

Demo

The demo/ "Keylight Notes" app shows entitlement gating end-to-end (free = 3 notes; the pro entitlement unlocks unlimited notes + export) against the live public demo tenant:

npx tsx demo/notes.ts                       # free tier (3-note limit) — works offline
npx tsx demo/notes.ts NOTES-PRO0-0000-0001  # pro (unlimited + export) — activates online

Conformance

The security-critical lease verifier is gated by Keylight's frozen cross-SDK conformance vectors (tests/conformance.test.ts). The JavaScript verifier must agree with every vector on { kidKnown, signatureValid, expired }, which keeps offline verification behavior identical across the Keylight SDK family (Swift, Rust, JavaScript, …).

npm test                                  # full suite (live tests skipped)
npx vitest run tests/conformance.test.ts  # just the conformance vectors
KEYLIGHT_LIVE=1 npm run test:live         # opt-in live tests vs the demo tenant

Documentation

Other SDKs

| Platform | Status | Repository | |----------|--------|------------| | Swift (macOS/iOS) | Available | keylight-swift | | Rust (CLIs/daemons/Tauri) | Available | keylight-rust | | JavaScript (this repo) | Available | keylight-js | | C# · C++ | Planned | unified by the same cross-SDK conformance vectors |

License

MIT License. See LICENSE for details.


Keylight JavaScript SDK — software licensing for JS/TS: license-key activation & validation, offline Ed25519 lease verification, entitlement/feature gating, trials and free tiers, lifecycle events, and pluggable storage/transport — for the browser, Node, Deno, Bun, and edge runtimes.