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

@quantumchat/bot-sdk

v0.1.0

Published

Node SDK for QuantumChat bot operators — hold the bot's PQ identity, receive PQ-encrypted envelopes, and reply through the relay. See ENG-1891.

Downloads

182

Readme

@quantumchat/bot-sdk

Node SDK for QuantumChat bot operators — build a Telegram-style bot that is end-to-end post-quantum encrypted underneath. Your bot server holds the bot's PQ messaging identity, receives encrypted messages addressed to the bot, and replies through the relay. Keys never leave your machine.

  • Post-quantum E2E — ML-KEM-1024 + ML-DSA-65 + PQXDH via the native @quantumchat/pqchat-node binding (same core as the QuantumChat clients).
  • BotFather-style — a user creates the bot in-app, you get an API token, paste it into your server; the SDK binds the bot's keys and runs.
  • Batteries included — message + command routing, rich replies, mini-app data, retry/backoff, outbound rate-limiting, and launch-payload verification.

Requirements

  • Node ≥ 18 (uses global fetch, a WebSocket-style transport, crypto.randomUUID).
  • The native @quantumchat/pqchat-node binding installed for your platform (provides ML-KEM/ML-DSA/PQXDH/AES). See Installation.
  • A running QuantumChat relay and a bot API token (from Bot Maker, or minted headlessly for dev — see below).

Installation

npm install @quantumchat/bot-sdk
# peer native binding (Rust/N-API) + shared types:
npm install @quantumchat/pqchat-node @quantumchat/webapp-types

Monorepo note: inside this repository the SDK consumes @quantumchat/pqchat-node and @quantumchat/webapp-types as workspace:* dependencies. For external distribution those must be published (or bundled) first — see Deployment at the bottom.

Quick start

1. Get a bot + token

A QuantumChat user creates the bot in the in-app Bot Maker (BotFather flow) and copies the API token shown once. Paste it into your server's env (BOT_API_TOKEN). For local development you can skip Bot Maker and mint a bot headlessly with createAndBindBot (see below).

2. Bind keys + run

import {
  generateBotIdentity,
  bindBot,
  loadCredential,
  saveCredential,
  QuantumChatBot,
} from '@quantumchat/bot-sdk';
import { existsSync } from 'node:fs';
import { echoBotManifest } from './manifest'; // your signed manifest

const CRED = './bot-credential.json';
const relayUrl = process.env.RELAY_URL ?? 'http://localhost:8080';

// First run: generate the bot's identity locally (the mnemonic never leaves the
// machine) and bind its public keys to the pending bot using the API token.
const credential = existsSync(CRED)
  ? await loadCredential(CRED)
  : await (async () => {
      const identity = generateBotIdentity();
      const cred = await bindBot(echoBotManifest, {
        relayUrl,
        apiToken: process.env.BOT_API_TOKEN!, // from Bot Maker
        identity,
      });
      await saveCredential(CRED, cred); // mode 0600
      return cred;
    })();

const bot = new QuantumChatBot({ credential, manifest: echoBotManifest });

bot.onMessage(async (msg, ctx) => {
  if (msg.command === '/start') return ctx.reply({ text: 'Hi! I echo what you say.' });
  await ctx.reply({ text: `echo: ${msg.text ?? ''}` });
});

bot.onWebAppData(async (data, ctx) => {
  await ctx.reply({ text: `got web-app data: ${JSON.stringify(data.data)}` });
});

await bot.start(); // restores identity, re-uploads prekeys, subscribes over WSS

Dev (headless, no Bot Maker)

import { generateBotIdentity, createAndBindBot } from '@quantumchat/bot-sdk';

// Registers an owner, mints a bot token, and binds — DEV/E2E ONLY.
const credential = await createAndBindBot(echoBotManifest, {
  relayUrl,
  ownerEmail: process.env.OWNER_EMAIL!,
  ownerPassword: process.env.OWNER_PASSWORD!,
  identity: generateBotIdentity(),
});

A runnable end-to-end example lives in examples/bot-server-stub.


Core concepts

| Concept | What it is | |---|---| | BotIdentity | The bot's PQ messaging identity (mnemonic-derived ML-KEM + ML-DSA + prekeys) plus a standalone ML-DSA key for manifest signing. Created by generateBotIdentity(); the mnemonic never leaves your server. | | BotCredential | What your server persists to run the bot: { botIdentityId, slug, apiToken, mnemonic, relayUrl }. Save/load with saveCredential / loadCredential (mode 0600). | | Create → Bind → Run | A user creates the bot in-app (reserves the slug + issues the token). Your SDK binds its locally-generated public keys (bindBot), flipping the bot active. Then it runs (QuantumChatBot.start()). | | Token | The API token is a long-lived relay session bearer. The owner can revoke/rotate it from Bot Maker; a revoked token makes start() fail fast with a clear error. |

API reference

Identity & credential

  • generateBotIdentity(accountId?)BotIdentity — generate locally.
  • saveIdentity(path, id) / loadIdentity(path) — persist the identity (mode 0600).
  • saveCredential(path, c) / loadCredential(path) — persist the BotCredential (mode 0600).

Registration

  • bindBot(manifest, { relayUrl, apiToken, identity })Promise<BotCredential> — bind keys to a pending bot created in Bot Maker.
  • createAndBindBot(manifest, { relayUrl, ownerEmail, ownerPassword, identity })Promise<BotCredential>dev/E2E only: register an owner, mint a token, then bind.
  • signManifest(manifest, identity){ payload, signature } — ML-DSA-65 sign a manifest.

The bot runtime

  • new QuantumChatBot({ credential, manifest, onRetry? })
  • .onMessage((msg, ctx) => …) — inbound chat; msg.command / msg.args are pre-parsed.
  • .onWebAppData((data, ctx) => …) — a mini-app's WebApp.sendData(...).
  • .onStartChat(handler) — fires once on a sender's first message.
  • .onBlock / .onPermGrant / .onPermRevoke / .onWebAppOpen — lifecycle hooks (payload shapes per the webhook event spec).
  • .start() / .stop() — connect / disconnect the relay transport. .isRunning.
  • ctx.reply({ text?, attachments?, inline_keyboard?, web_app? }) — encrypt + send to the sender.

Mini-app launch verification

  • verifyInitData(initData, { relayPublicKey, maxAgeMs?, maxClockSkewMs? }){ valid, data?, reason? } — verify a relay-signed mini-app initData payload (ML-DSA-65) before trusting user_id_hmac / user.
  • fetchRelayInitDataPubkey(relayUrl) → the relay's verification public key (base64).

Utilities

  • parseCommand(text){ command, args }.
  • withRetry(fn, opts) / isRelayRetriable(err) — exponential backoff + jitter; RelayRateLimitError on exhaustion.
  • createRateLimiter({ perUserPerMinute, globalPerMinute }) — outbound token bucket.
  • Errors: BotConfigError, RelayError, RelayRateLimitError.

Configuration (env used by the example server)

| Var | Purpose | |---|---| | RELAY_URL | Relay base URL (default http://localhost:8080). | | BOT_API_TOKEN | API token from Bot Maker (production path). | | OWNER_EMAIL / OWNER_PASSWORD | Dev headless fallback (createAndBindBot). |

Security

  • The mnemonic and API token are secrets. saveCredential / saveIdentity write with file mode 0600; never commit bot-credential.json (gitignore it), never log the mnemonic or token.
  • A revoked/expired token makes start() throw BotConfigError: bot API token revoked or expired — re-issue from Bot Maker.
  • Inbound envelopes are verified cryptographically; a spoofed or stale frame is dropped, never tearing down a working session.
  • Verify mini-app launch payloads with verifyInitData before trusting any user fields — a WebView page cannot forge a relay signature.

Resources

Deployment

Builds to dist/ (ESM + CJS + .d.ts) via npm run build. Before publishing to a registry, the two workspace:* dependencies must be resolvable by consumers:

  • @quantumchat/webapp-types — pure TypeScript types; publish as-is.
  • @quantumchat/pqchat-nodenative (Rust/N-API); needs per-platform prebuilt binaries (napi-rs optionalDependencies) before it can be installed off-registry.

Until both are published, the SDK is consumed workspace-locally within this monorepo (the example server imports it directly).

License

UNLICENSED — internal QuantumChat package.