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

@sewdn/signed-license

v0.1.1

Published

Ed25519-signed token format for offline licenses and passwordless auth (verify + optional admin signing).

Readme

Signed License SDK (workspace: @signed-license/sdk)

TypeScript library for signed JSON tokens: base64url(payloadJson).base64url(signature). Signing and verification use node:crypto; the algorithm is selected per token (see Cryptography below). Ed25519 is the default when the payload omits sig.

  • Runtime: Node.js 20+ and Bun (uses node:crypto for verify/sign).
  • Imports: ESM only ("type": "module").

Names: monorepo vs npm

| Context | Package name | Import / install | | ---------------- | ----------------------- | ------------------------------------- | | This monorepo | @signed-license/sdk | import … from "@signed-license/sdk" | | npm (your scope) | @sewdn/signed-license | npm install @sewdn/signed-license |

Published tarballs are built with bun run build:npm from the repo root (see root README.md). Do not publish the workspace package directly.

Install (registry)

npm install @sewdn/signed-license

Cryptography (TokenSigV1)

The exact UTF-8 JSON of the payload (including optional sig) is what gets signed. Verifiers read sig from the decoded payload (or assume the default) so the token stays self-describing. PEM is always public SPKI + private PKCS#8 (Node’s usual export for these key types).

Default: If sig is omitted, verification behaves as { "kty": "ed25519" } (DEFAULT_TOKEN_SIG_V1). signTokenPayload embeds sig automatically: payload.sigoptions.sig → default.

| Preset | sig on the wire (JSON) | Key generation | sign / verify (digest or scheme) | | ------------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | Ed25519 (default) | { "kty": "ed25519" } | generateKeyPairSync("ed25519", …) | EdDSA, algorithm: null | | Ed448 | { "kty": "ed448" } | generateKeyPairSync("ed448", …) | EdDSA, algorithm: null | | ECDSA (NIST P-curves) | { "kty": "ec", "crv": "P-256" \| "P-384" \| "P-521" } | generateKeyPairSync("ec", { namedCurve: … }) — maps to prime256v1 / secp384r1 / secp521r1 | sha256 / sha384 / sha512 (matched to curve) | | RSA (PKCS#1 v1.5) | { "kty": "rsa", "bits": 2048 \| 3072 \| 4096, "hash": "sha256" \| "sha384" \| "sha512" } | generateKeyPairSync("rsa", { modulusLength: bits, … }) | RSA-SHA256, RSA-SHA384, or RSA-SHA512 |

API: generateKeyPairPem(sig?) — generate a key pair for a given preset (defaults to Ed25519). parseTokenPayloadJson rejects a payload if sig is present but not a valid TokenSigV1.

Structured presets: TOKEN_SIG_V1_PRESET_ENTRIES (and findTokenSigPresetById, DEFAULT_TOKEN_SIG_PRESET_ID) in the SDK list every supported variant with stable id strings and labels — used by the signed-license CLI and reusable for docs or tooling.

Env keys: SIGNED_LICENSE_PUBLIC_KEY_PEM / SIGNED_LICENSE_PRIVATE_KEY hold PEM material for whichever algorithm you mint with; the verifier must use the matching public key.

Application service (recommended)

Use createSignedLicenseService for verify, mint, and keygen with env-based keys. Cryptographic verification is the same for every token; business rules (e.g. “this app requires sub”, “this CLI only accepts tokens without sub”) belong in verifyAndNarrow or verifySignedTokenAs, not in the service config.

import { createSignedLicenseService } from "@sewdn/signed-license";

const signedLicense = createSignedLicenseService();
// Project-specific payload: createSignedLicenseService<MyPayload>({ ... })

const result = signedLicense.verifyToken(token); // uses SIGNED_LICENSE_PUBLIC_KEY_PEM if no PEM passed
const strict = signedLicense.verifyAndNarrow(token, (json) => myParse(json)); // your policy + domain types
const t = signedLicense.mintToken({
  v: 1,
  sub: "[email protected]",
  scopes: ["a"],
  ver: "1",
  iat: Math.floor(Date.now() / 1e3),
});

Tree-shake by subpath: @sewdn/signed-license/service (createSignedLicenseService), @sewdn/signed-license/admin (signing, keygen, sanitizePemValue — no env), @sewdn/signed-license/env (resolveSignedLicensePrivateKeyPemFromEnv / resolveSignedLicensePublicKeyPemFromEnv from process.env).

Browser / shared parsing: import payload types and parseTokenPayloadJson from @sewdn/signed-license/payload so bundlers do not pull in node:crypto from the main entry.

verifySignedTokenWithRawPayload — use when you must parse the exact signed JSON string with app-specific rules (roles enums, etc.) without splitting the token yourself.

Project-specific payload types

Wire format stays TokenPayloadV1 (or extensions that still serialize as compatible JSON). For typed verification:

  • TokenPayloadParser<TPayload>(payloadJson: string) => TPayload | undefined (return undefined to reject).
  • verifySignedTokenAs(token, pem, parsePayload)VerifySignedTokenResult<TPayload>.
  • verifySignedTokenWithRawPayloadAs — same, plus payloadJson on success.
  • readTokenPayloadAs — decode only (no signature check); use for previews/debug.
  • createSignedLicenseService<TPayload>()SignedLicenseService<TPayload>; verifyToken, verifyTokenWithRawPayload, verifyAndNarrow, and mintToken all use the same TPayload (default TokenPayloadV1).
  • signTokenPayload<TPayload extends TokenPayloadV1>(payload, pem) when minting outside the service.
import {
  parseTokenPayloadJson,
  verifySignedTokenAs,
  type TokenPayloadV1,
} from "@sewdn/signed-license";

type MyPayload = TokenPayloadV1 & { readonly tenantId: string };

function parseMyPayload(json: string): MyPayload | undefined {
  const o = JSON.parse(json) as Record<string, unknown>;
  const base = parseTokenPayloadJson(json);
  if (!base || typeof o.tenantId !== "string") return undefined;
  return { ...base, tenantId: o.tenantId };
}

const out = verifySignedTokenAs(token, publicKeyPem, parseMyPayload);
if (out.ok) out.payload.tenantId;

Keeping integrations small (suggestions)

  1. Prefer one dependency — Depend only on @sewdn/signed-license; avoid a second “wrapper” package in each monorepo unless it adds real domain types.
  2. Subpath imports — Use @sewdn/signed-license/service, /admin, or /env so bundlers drop minting or env wiring from builds when possible.
  3. Narrow once — Pass verifyAndNarrow(..., parseYourPayload) (or verifySignedTokenWithRawPayload + your parser) for product-specific rules; the shared service does not distinguish “license” vs “auth” modes.
  4. Browser vs Node — Keep Web Crypto verification in the frontend only; use createSignedLicenseService / verifySignedToken on the server. A future @sewdn/signed-license/browser subpath could isolate crypto.subtle without pulling node:crypto if needed.

Verification (consumer apps)

import {
  verifySignedToken,
  verifyWithPublicKeyFromEnv,
  type TokenPayloadV1,
} from "@sewdn/signed-license";

const result = verifySignedToken(token, publicKeyPem);
if (result.ok) {
  const payload: TokenPayloadV1 = result.payload;
  // license-style: optional label/exp
  // auth-style: check payload.sub, payload.scopes, etc.
}

In this repo, use @signed-license/sdk in imports instead of @sewdn/signed-license.

  • readTokenPayload(token) — decode JSON from the first segment without verifying the signature (debug/preview only).
  • isExpired(payload) — compares exp to current time when present.
  • isAuthStylePayload(payload)true when sub is present (non-empty).

Set SIGNED_LICENSE_PUBLIC_KEY_PEM at runtime (deployment env, container secret, etc.) to the public PEM that matches your tokens’ signing keys, then use verifyWithPublicKeyFromEnv(token) (returns a result with an error when the key is missing). resolveSignedLicensePublicKeyPemFromEnv() (from @sewdn/signed-license or @sewdn/signed-license/env) returns the PEM string or throws if unset or empty after normalization. Verification uses the Signed License env namespace only (no per-project aliases).

Signing (minting tools only)

@sewdn/signed-license/admin — pure signing and PEM helpers (no process.env):

import { generateKeyPairPem, signTokenPayload } from "@sewdn/signed-license/admin";

@sewdn/signed-license/env — read SIGNED_LICENSE_PRIVATE_KEY / SIGNED_LICENSE_PUBLIC_KEY_PEM (throws if unset or empty after sanitizePemValue):

import { resolveSignedLicensePrivateKeyPemFromEnv } from "@sewdn/signed-license/env";

const pem = resolveSignedLicensePrivateKeyPemFromEnv();
const token = signTokenPayload({ v: 1, iat: Math.floor(Date.now() / 1e3) }, pem);

Private key for minting: SIGNED_LICENSE_PRIVATE_KEY (PKCS#8 PEM), same namespace as SIGNED_LICENSE_PUBLIC_KEY_PEM for verify. Use a key pair consistent with the sig field you embed when minting (see Cryptography).

Payload shape (TokenPayloadV1)

| Field | License-style (offline / entitlement) | Auth-style (sign-in) | | -------- | --------------------------------------------------------------- | ---------------------------- | | v | 1 | 1 | | ver | optional version label | optional | | label | optional human-readable note | optional | | exp | optional (recommended when minting) | optional | | iat | optional (recommended when minting) | optional | | sub | omitted | set (subject, e.g. email) | | scopes | optional string capabilities | optional string capabilities | | sig | optional TokenSigV1 (algorithm); omit for default Ed25519 | same |

Testing

From packages/sdk, run bun test. Typings for bun:test come from the bun-types dev dependency; the default tsconfig.json includes *.test.ts and lists bun-types so the editor resolves bun:test. Run bun run typecheck for tsc --noEmit on all sources including tests.

Migrating from internal packages

| Old | New | | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | @scaffold-v2/svc-license / @procertus-docs/svc-license | @sewdn/signed-license (npm) / @signed-license/sdk (monorepo) | | verifyLicenseToken | verifySignedToken | | Earlier docs referring to LicensePayloadV1 / parseLicensePayloadJson | TokenPayloadV1 / parseTokenPayloadJson | | SIGNET_* env vars (pre–Signed License rename) | SIGNED_LICENSE_* (SIGNED_LICENSE_PRIVATE_KEY, SIGNED_LICENSE_PUBLIC_KEY_PEM) | | Legacy multi-name private-key env vars | SIGNED_LICENSE_PRIVATE_KEY only | | SIGNET_EMBEDDED_PUBLIC_KEY_PEM / legacy *_EMBEDDED_* aliases | SIGNED_LICENSE_PUBLIC_KEY_PEM only | | verifyWithEmbeddedPublicKey / resolveEmbeddedPublicKeyPemFromEnv | verifyWithPublicKeyFromEnv / resolveSignedLicensePublicKeyPemFromEnv | | resolvePrivateKeyPemFromEnv | resolveSignedLicensePrivateKeyPemFromEnv |