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

@whisper-protocol/sdk

v0.8.0

Published

Client SDK for the Whisper Protocol — encrypt, decrypt, and post envelopes over Sui.

Readme

@whisper-protocol/sdk

npm version provenance

Client SDK for the Whisper Protocol — Sui-based private messaging where the ciphertext, sender, recipient, schema, and key version are public on chain but the plaintext is only legible to the addressed recipient.

The SDK exposes pure primitives — encryption, decryption, transaction building, registry queries — and one optional convenience facade (WhisperClient). It holds no secrets; every encryption call takes the caller's keys as explicit input.

Read the writeup

The design rationale and threat model live at:

Decentralized Pairwise Secret Communication Protocol over SUI Blockchain

This README assumes you've skimmed it and just want to integrate.

Live demo

Try it without installing anything: https://0xergod.github.io/whisper-protocol/ (Sui testnet). You will need a Sui wallet (Slush, Suiet, etc.) set to Testnet.

Install

npm install @whisper-protocol/sdk
# or
pnpm add @whisper-protocol/sdk

Peer-required: a SuiClient from @mysten/sui and a way to sign transactions (a wallet adapter, dapp-kit, or a raw Keypair).

Quick start

import { SuiClient } from "@mysten/sui/client";
import { WhisperClient } from "@whisper-protocol/sdk";
import { TESTNET } from "@whisper-protocol/sdk/networks";

const suiClient = new SuiClient({ url: TESTNET.rpcUrl });
const whisper = new WhisperClient({
  suiClient,
  packageId: TESTNET.packageId!,
  registryId: TESTNET.registryId!,
});

// Optional but recommended: assert the deployed Move package's
// protocol_version() matches what this SDK was built for.
await whisper.assertProtocolCompatible();

// Build an unsigned transaction that posts an encrypted secret to the
// recipient. Caller signs and executes it via their wallet.
const { tx, payload, keyVersion } = await whisper.prepareSend({
  senderAddress: myAddress,
  recipientAddress: bobAddress,
  plaintext: "fortress at x=42 y=9",
});

const result = await wallet.signAndExecuteTransaction({
  transaction: tx,
  chain: "sui:testnet",
});

// Recipient side: pull the inbox and decrypt envelopes addressed to you.
const inbox = await whisper.fetchInbox(myAddress);
for (const env of inbox) {
  const text = whisper.decryptEnvelopeUtf8({
    envelope: env,
    recipientAddress: myAddress,
    recipientPrivateKey: myX25519PrivateKey,
  });
  if (text !== null) console.log(`${env.sender}: ${text}`);
}

Where do encryption keys come from?

The SDK is agnostic about how you produce an X25519 keypair. Three common paths:

  • Wallet-signature derivation (recommended for end-user dApps). The user's wallet signs a fixed canonical message; HKDF-SHA256 over the signature bytes produces a stable X25519 seed. Use @whisper-protocol/wallet-derived-keys.
  • From an Ed25519 seed (scripted demos / integration tests). Derive X25519 from the Ed25519 seed via SHA-512 + Curve25519 clamping.
  • Random ephemeral keys (one-shot conversations). Generate fresh keys per session and discard.

Whichever path you pick, register the public key on the on-chain KeyRegistry so other senders can address you. Use whisper.buildRegisterKeyTx(publicKey) and have your wallet sign it.

API surface

Convenience facade

new WhisperClient({ suiClient, packageId, registryId, protocolVersion? })

| Method | Returns | Notes | |---|---|---| | assertProtocolCompatible() | Promise<number> | Reads on-chain protocol_version(), throws on mismatch. Memoised. | | prepareSend({ senderAddress, recipientAddress, plaintext, schema?, context? }) | Promise<PreparedSend> | Looks up recipient, encrypts, builds unsigned tx. | | decryptEnvelope({ envelope, recipientAddress, recipientPrivateKey }) | Uint8Array \| null | Returns null on any failure. | | decryptEnvelopeUtf8(...) | string \| null | Same, decodes as UTF-8. | | fetchRegistry() | Promise<RegistryEntry[]> | All registered keys. | | fetchRegistryEntry(account) | Promise<RegistryEntry \| null> | One account's entry. | | fetchInbox(ownerAddress) | Promise<OnChainEnvelope[]> | Owned envelopes for an address. | | fetchEnvelope(envelopeId) | Promise<OnChainEnvelope \| null> | Single envelope by id. | | buildRegisterKeyTx(publicKey, scheme?) | Transaction | Unsigned. |

Pure primitives (no client needed)

import {
  encryptForRecipient, tryDecrypt, tryDecryptUtf8,
  buildPostEnvelopeTx, buildRegisterKeyTx,
  fetchRegistryEntries, fetchRegistryEntry,
  fetchInbox, fetchEnvelope,
  readOnChainProtocolVersion,
  normalizeAddress, shortAddress,
} from "@whisper-protocol/sdk";

Sub-exports

  • @whisper-protocol/sdk — main entry: client, primitives, address utils
  • @whisper-protocol/sdk/networksLOCALNET / TESTNET / MAINNET deployment constants
  • @whisper-protocol/sdk/feedfetchFeed, gas enrichment helpers (UI-oriented; optional)

Versioning

The SDK ships an SDK_PROTOCOL_VERSION constant. The deployed Move module exposes a matching protocol_version() view function. Call WhisperClient.assertProtocolCompatible() at startup to verify the SDK and the deployed package agree on wire format — bumping either side without the other will throw.

| SDK | Contract protocol_version | Notes | | --- | --------------------------- | ------------------ | | 0.1.x | 1 | Initial release. |

Threat model summary

Whisper provides:

  • Payload confidentiality against public chain observers, indexers, and other users.
  • Sender-authenticated delivery via Sui transaction signing.
  • Recipient-only decryption.

Whisper does not provide:

  • Forward secrecy (a wallet compromise reveals every past encryption key).
  • Recipient anonymity (recipient address is public on the envelope).
  • Deniability (the keypair is wallet-attributable).
  • Metadata privacy (sender, recipient, schema, key version, sizes, and timestamps are all public).

See the writeup and the protocol specs for the full accounting.

Repository

Source, issues, and protocol specs: https://github.com/0xErgod/whisper-protocol

License

MIT.