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

@cuilabs/qnsp

v0.3.2

Published

Official Node.js / TypeScript SDK for the QNSP Quantum-Native Security Platform by CUI Labs. Single package covering vault, kms, audit, auth, tenant, access-control, billing, crypto-inventory, storage, search, ai-orchestrator + local PQC primitives + webh

Readme

@cuilabs/qnsp — Node.js / TypeScript SDK for the Quantum-Native Security Platform

npm version License

The official single-package Node.js / TypeScript SDK for QNSP. Covers the full customer-facing platform — vault, KMS, audit, auth, tenant, access-control, billing, crypto-inventory, storage, search, and AI orchestrator — plus webhook signature verification. Mirrors the shape of the qnsp Python / Go / Rust SDKs byte-for-byte: same wire contracts, same algorithm names, same FIPS 203 / 204 / 205 posture.

Free tier available. Free-forever account at https://cloud.qnsp.cuilabs.io/auth — 60-second signup, no credit card. Includes 10 GB PQC storage, 50 000 API calls/month, 20 KMS keys, 25 vault secrets.

Why one package?

Previous TypeScript consumers had to install up to 11 separate @cuilabs/qnsp-*-sdk packages and keep their versions in sync. @cuilabs/qnsp collapses that into a single dependency with sub-namespaces:

import { QnspClient } from "@cuilabs/qnsp";

const qnsp = new QnspClient({ apiKey: process.env.QNSP_API_KEY! });

await qnsp.vault.createSecret({ ... });   // was @cuilabs/qnsp-vault-sdk
await qnsp.kms.sign(keyId, data);          // was @cuilabs/qnsp-kms-client
await qnsp.audit.logEvent({ ... });        // was @cuilabs/qnsp-audit-sdk
await qnsp.tenant.getTenant(tenantId);     // was @cuilabs/qnsp-tenant-sdk
// ...

One activation handshake on first use, shared across all 11 sub-clients. One version bump per QNSP release. One CHANGELOG. One source of truth.

Install

pnpm add @cuilabs/qnsp
# or
npm install @cuilabs/qnsp
# or
yarn add @cuilabs/qnsp

Requires Node.js ≥ 22.0.0. ESM-first; CommonJS consumers can await import("@cuilabs/qnsp").

Quick start

import { QnspClient } from "@cuilabs/qnsp";

const qnsp = new QnspClient({ apiKey: process.env.QNSP_API_KEY! });

// Vault — PQC-encrypted secret storage
const secret = await qnsp.vault.createSecret({
  name: "openai-api-key",
  payloadB64: Buffer.from("sk-...").toString("base64"),
  algorithm: "ml-kem-768",
});

// KMS — server-side PQC keys
const key = await qnsp.kms.createKey({ algorithm: "ml-dsa-65", purpose: "signing" });
const sig = await qnsp.kms.sign(key.keyId as string, new TextEncoder().encode("hello"));
const ok  = await qnsp.kms.verify(key.keyId as string, new TextEncoder().encode("hello"), sig);

// Audit — immutable, hash-chained event log
await qnsp.audit.logEvent({
  eventType: "model.inference",
  payload: { modelId: "gpt-4o", latencyMs: 412 },
});

// Tenant, access, billing, crypto-inventory, storage, search, ai, auth — all on one client
await qnsp.tenant.getTenant(await qnsp.tenantId());
await qnsp.access.checkPermission({ subjectId: "user-1", permission: "vault.read" });
await qnsp.billing.getEntitlements();

Modules

Each sub-namespace wraps one QNSP backend service:

| Sub-client | Wraps | Key methods | |---|---|---| | qnsp.vault | apps/vault-service (/vault/v1) | createSecret, getSecret, getSecretVersion, rotateSecret, deleteSecret, listSecretVersions | | qnsp.kms | apps/kms-service (/kms/v1) | createKey, listKeys, getKey, rotateKey, deleteKey, sign, verify, wrap, unwrap | | qnsp.audit | apps/audit-service (/audit/v1) | logEvent, ingestEvents, listEvents | | qnsp.auth | apps/auth-service (/auth/v1) | login, refreshToken, revoke, WebAuthn passkey lifecycle, mfaChallenge / mfaVerify, federateSAML / federateOIDC, evaluateRisk | | qnsp.tenant | apps/tenant-service (/tenant/v1) | createTenant, getTenant, updateTenant, listTenants, getCryptoPolicy, upsertCryptoPolicy, getCurrentHealth, getCurrentQuotas | | qnsp.access | apps/access-control-service (/access/v1) | createRole, getRole, listRoles, deleteRole, assignRole, revokeRoleAssignment, checkPermission | | qnsp.billing | apps/billing-service (/billing/v1) | getEntitlements, ingestMeter, ingestMeters, listInvoices, getInvoice, getCreditBalance | | qnsp.cryptoInventory | apps/crypto-inventory-service (/crypto/v1) | listAssets, getAsset, getAssetStats, discoverAssets, getReadinessScore | | qnsp.storage | apps/storage-service (/storage/storage/v1) | putObject, getObject (returns [bytes, descriptor]), deleteObject, listObjects, listBuckets | | qnsp.search | apps/search-service (/search/v1) | createIndex, listIndexes, deleteIndex, upsertVectors, query | | qnsp.ai | apps/ai-orchestrator (/ai/v1) | model registry (registerModel, listModels, getModel, updateModel, activateModel, deployModel), workloads (submitWorkload, getWorkload, listWorkloads, cancelWorkload), invokeInference, registerArtifact |

Verifying inbound webhooks

QNSP signs every webhook with HMAC-SHA-256. Verify the raw body before parsing JSON:

import { parseQnspWebhook, QnspWebhookError } from "@cuilabs/qnsp";

app.post("/webhooks/qnsp", express.raw({ type: "application/json" }), (req, res) => {
  try {
    const event = parseQnspWebhook({
      body: req.body, // raw Buffer
      signatureHeader: req.header("x-qnsp-signature") ?? "",
      timestampHeader: req.header("x-qnsp-timestamp"),
      secret: process.env.QNSP_WEBHOOK_SECRET!,
    });
    if (event.eventType === "key.rotated") {
      // ...
    }
    res.sendStatus(200);
  } catch (err) {
    if (err instanceof QnspWebhookError) {
      res.status(400).send(err.reason);
    } else {
      throw err;
    }
  }
});

Constant-time HMAC comparison, 5-minute replay window by default (MAX_WEBHOOK_SKEW_MS), refuses payloads missing required fields.

Error handling

All errors descend from QnspError:

| Class | When | |---|---| | QnspNetworkError | DNS, TLS, timeout, or connection failure | | QnspAuthError | API key rejected at activation | | QnspApiError | A service returned 4xx/5xx with a structured body | | QnspWebhookError | HMAC mismatch, expired timestamp, malformed body, etc. |

import { QnspApiError, QnspNetworkError } from "@cuilabs/qnsp";

try {
  await qnsp.vault.getSecret("missing");
} catch (err) {
  if (err instanceof QnspApiError) console.log("HTTP", err.statusCode, err.code);
  else if (err instanceof QnspNetworkError) console.log("could not reach QNSP:", err.message);
  else throw err;
}

Activation + tier introspection

QnspClient performs a one-shot handshake against /billing/v1/sdk/activate on first use. The result is cached in memory; subsequent calls reuse it until ~60 s before expiry. You can inspect the current activation:

await qnsp.tenantId();      // resolved tenant
await qnsp.tier();          // plan tier
await qnsp.limits();        // full limits dict
await qnsp.hasFeature("sseEnabled");  // convenience boolean

// Force the handshake at startup so you fail fast on a bad key:
await qnsp.ensureActivated();

If the activation token is rotated server-side, the SDK invalidates its cache and retries the originating request once on a 401.

Migration from per-service SDKs

The per-service @cuilabs/qnsp-*-sdk packages on npm are now deprecated in favour of @cuilabs/qnsp. They continue to install and work, but new code should use this package.

| Before | After | |---|---| | import { VaultClient } from "@cuilabs/qnsp-vault-sdk" | import { QnspClient } from "@cuilabs/qnsp" then qnsp.vault | | import { KmsClient } from "@cuilabs/qnsp-kms-client" | qnsp.kms | | import { AuthClient } from "@cuilabs/qnsp-auth-sdk" | qnsp.auth | | import { TenantClient } from "@cuilabs/qnsp-tenant-sdk" | qnsp.tenant | | import { AccessControlClient } from "@cuilabs/qnsp-access-control-sdk" | qnsp.access | | import { BillingClient } from "@cuilabs/qnsp-billing-sdk" | qnsp.billing | | import { CryptoInventoryClient } from "@cuilabs/qnsp-crypto-inventory-sdk" | qnsp.cryptoInventory | | import { StorageClient } from "@cuilabs/qnsp-storage-sdk" | qnsp.storage | | import { SearchClient } from "@cuilabs/qnsp-search-sdk" | qnsp.search | | import { AiOrchestratorClient } from "@cuilabs/qnsp-ai-sdk" | qnsp.ai | | import { AuditClient } from "@cuilabs/qnsp-audit-sdk" | qnsp.audit |

The constructor signature is simpler — one apiKey for everything, instead of a per-service config:

// Before — 11 packages, 11 activation handshakes, 11 versions to keep in sync
import { VaultClient } from "@cuilabs/qnsp-vault-sdk";
import { KmsClient } from "@cuilabs/qnsp-kms-client";
import { AuditClient } from "@cuilabs/qnsp-audit-sdk";

const vault = new VaultClient({ apiKey, baseUrl: "https://api.qnsp.cuilabs.io/proxy/vault", tier });
const kms   = new KmsClient({   apiKey, baseUrl: "https://api.qnsp.cuilabs.io/proxy/kms",   tier });
const audit = new AuditClient({ apiKey, baseUrl: "https://api.qnsp.cuilabs.io/proxy/audit", tier });

// After — one package, one activation, one client
import { QnspClient } from "@cuilabs/qnsp";

const qnsp = new QnspClient({ apiKey });
// qnsp.vault, qnsp.kms, qnsp.audit, ... all share one connection pool + one activation cache

The wire contracts are identical, so migrating method-by-method is mechanical.

License

Apache-2.0. See LICENSE.