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

tether-sdk

v0.1.0

Published

Tether SDK — deployment license verification, enforcement, and health reporting (open source).

Readme

@tether/sdk

Open-source client SDK for Tether — deployment license verification, enforcement, and health reporting for software you sell to clients.

On startup (and on an interval) the SDK builds a deployment fingerprint, sends a signed check-in to your control server, and verifies the server's Ed25519-signed verdict with an embedded public key. If the deployment is not authorized, the SDK locks the app — it refuses to run.

Enforcement is disable-only. The strongest thing this SDK ever does is throw LicenseInvalidError so your app refuses to start. It never deletes data, mutates the database, or harms the host, and the lock reverses automatically the moment the operator authorizes the server. Tether is meant to be disclosed to clients as a license-validation mechanism, not run as a covert kill switch.

Install

npm install @tether/sdk
# or: pnpm add @tether/sdk

Requires Node.js ≥ 18.

Quick start

Call guard() as early as possible in your app's startup. If the deployment is unauthorized it throws and the app stops.

import { guard } from '@tether/sdk';

await guard({
  licenseKey: process.env.TETHER_LICENSE_KEY!, // "tth_<keyId>_<secret>"
  serverUrl: 'https://tether.example.com', // your control server
  serverPublicKey: process.env.TETHER_SERVER_PUBLIC_KEY!, // base64 Ed25519 public key
});

// Reaching this line means the deployment is authorized (or fail-open is configured).
startMyApp();

Long-running apps: guard + monitor

Keep a Tether instance so you can also emit heartbeats and report crashes:

import { createTether, LicenseInvalidError } from '@tether/sdk';

const tether = createTether({
  licenseKey: process.env.TETHER_LICENSE_KEY!,
  serverUrl: 'https://tether.example.com',
  serverPublicKey: process.env.TETHER_SERVER_PUBLIC_KEY!,
  appId: 'acme-checkout',
});

try {
  await tether.guard(); // verify + enforce once at startup
} catch (err) {
  if (err instanceof LicenseInvalidError) {
    console.error('License invalid:', err.reason, '\nFingerprint:', err.fingerprint);
    process.exit(1); // refuse to run — disable, never destroy
  }
  throw err;
}

// Periodic heartbeats (dead-man's-switch on the server) + crash reporting.
tether.startMonitoring();

License key format

A license key is a single string tth_<keyId>_<secret>:

  • keyId — a public identifier sent on every request so the server can look up the license.
  • secret — the HMAC key used to sign requests. It is never transmitted; possession is proven by the request signature.

How verdicts are trusted

  1. The SDK signs each request with HMAC-SHA256 over method + path + timestamp + nonce + body-hash (replay-resistant).
  2. The server returns a verdict plus an Ed25519 signature over its canonical JSON.
  3. The SDK verifies that signature with serverPublicKey before trusting any field, then checks that the verdict is bound to this license (keyId), this deployment (fingerprint), and this request (nonce), and that it is fresh (issuedAt / expiresAt). A forged or replayed verdict is rejected.

Failure modes

When no verified verdict is available (network error, timeout, bad signature), the failureMode option decides:

| Mode | Behaviour on error | Use when | | --------------------- | ------------------------------ | --------------------------- | | fail-open (default) | app keeps running (degraded) | availability matters most | | fail-closed | app locks | you want strict enforcement |

Only an explicit, signature-verified unauthorized verdict locks the app in either mode. fail-open ensures a control-server outage can never take down a paying client.

Configuration

| Option | Type | Default | Description | | ---------------------- | ------------------------------ | ----------- | -------------------------------------------- | | licenseKey | string | — | tth_<keyId>_<secret> (required) | | serverUrl | string | — | Control server base URL (required) | | serverPublicKey | string | — | base64 Ed25519 public key (required) | | appId | string | — | App identifier reported on each call | | failureMode | 'fail-open' \| 'fail-closed' | fail-open | Behaviour when no verified verdict | | heartbeatIntervalSec | number | 60 | Heartbeat interval (server may override) | | requestTimeoutMs | number | 5000 | Per-request network timeout | | clockSkewMs | number | 60000 | Allowed skew when checking verdict freshness | | captureErrors | boolean | true | Capture & report uncaught errors | | logLevel | LogLevel | info | silent \| error \| warn \| info \| debug |

API surface

  • guard(config, deps?) — one-shot verify + enforce (throws LicenseInvalidError).
  • createTether(config, deps?) / new Tether(...) — orchestrator with:
    • verify() — run a check-in, return a decision (never throws on lock).
    • guard() — verify + enforce (throws on lock).
    • startMonitoring(opts?) / stopMonitoring() — heartbeats + error capture.
    • sendHeartbeat() / reportError(kind, error, ctx?) — manual, best-effort.
  • collectFingerprint(opts?) and the pure helpers computeFingerprint, normalizeMac, pickPrimaryMac.
  • Crypto helpers: verifyEd25519, signEd25519, canonicalize, generateEd25519KeyPair.

All transport and clock dependencies are injectable (deps.fetcher, deps.now, deps.fingerprintProvider), so the SDK is fully testable offline.

Honest limitation

Client-side enforcement is inherently bypassable — the client has the code and can edit it. Tether deters casual reselling and honest-but-tempted clients; it does not stop a determined developer. The strongest protection is keeping the truly valuable logic server-side, behind the control plane.

License

Apache-2.0