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

alterion-encrypt

v1.1.0

Published

X25519 ECDH key exchange, AES-256-GCM session encryption, and a MessagePack + Deflate request/response pipeline — framework-agnostic client-side counterpart to the alterion-encrypt Rust crate.

Readme

License: GPL-3.0 npm TypeScript ESM + CJS GitHub

The JavaScript/TypeScript frontend/client-side counterpart to alterion-encrypt — X25519 ECDH key exchange, AES-256-GCM session encryption, and a MessagePack + Deflate request/response pipeline, all in a framework-agnostic package.

Frontend only. This package is intended for use in browser environments and client-side runtimes. The server-side implementation lives in the alterion-encrypt Rust crate. Do not use this package as a server-side encryption backend.


What it does

Each request to the server is packaged as a Request:

Client → Request { data: AES-256-GCM ciphertext, kx, client_pk: ephemeral X25519, key_id, ts }

Request path (buildRequestPacket):

  1. JSON-serialise the payload and deflate-compress it.
  2. Generate a random 32-byte AES-256 enc_key per request.
  3. AES-256-GCM encrypt the payload with enc_key.
  4. Generate an ephemeral X25519 key pair, perform ECDH against the server's public key.
  5. Derive a wrap_key via HKDF-SHA256, use it to AES-GCM wrap enc_keykx.
  6. Encode a Request { data, kx, client_pk, key_id, ts } with MessagePack.
  7. Store enc_key client-side keyed by request ID — it is never sent in plaintext.

Response path (decodeResponsePacket):

  1. MessagePack-decode the Response { payload, hmac }.
  2. Derive the HMAC key from enc_key via HKDF-SHA256 ("alterion-response-mac" info label).
  3. Constant-time verify the HMAC — reject if invalid.
  4. AES-256-GCM decrypt payload with enc_key.
  5. MessagePack-decode → deflate-decompress → JSON-parse → typed result.

No second ECDH round-trip is needed for the response; the server re-uses the enc_key it unwrapped from the request. The server side is implemented in alterion-encrypt.


Package layout

alterion-encrypt (JS)
├── serializer   buildRequestPacket / decodeResponsePacket — the main public API
├── crypt        aesEncrypt / aesDecrypt — AES-256-GCM with prepended nonce
├── compress     compress / decompress — deflate-raw via CompressionStream
└── keys         deriveWrapKey / deriveResponseMacKey — HKDF-SHA256 key derivation

Quick start

1. Add the dependency

npm install alterion-encrypt

2. Fetch the server's public key

The server exposes a POST /api/ecdh/init endpoint (from the alterion-encrypt Rust crate) that returns a one-time ephemeral key pair:

const { handshake_id, public_key } = await fetch("/api/ecdh/init", { method: "POST" })
    .then(r => r.json());

const serverPk = Uint8Array.from(atob(public_key), c => c.charCodeAt(0));
const keyId    = handshake_id;

3. Encrypt a request

import { buildRequestPacket } from "alterion-encrypt";

const { wireBytes, encKey } = await buildRequestPacket(
    { username: "alice", action: "login" },
    serverPk,
    keyId,
);

// Store encKey client-side, keyed by request ID, to decrypt the response.
// Send wireBytes as application/octet-stream body.

4. Decrypt the response

import { decodeResponsePacket } from "alterion-encrypt";

const rawResponse = await fetch("/api/example", {
    method:  "POST",
    body:    wireBytes,
    headers: { "Content-Type": "application/octet-stream" },
});
const bytes = new Uint8Array(await rawResponse.arrayBuffer());

const result = await decodeResponsePacket<{ token: string }>(bytes, encKey);
console.log(result.token);

API

buildRequestPacket

function buildRequestPacket(
    value:    unknown,
    serverPk: Uint8Array,
    keyId:    string,
): Promise<{ wireBytes: Uint8Array; encKey: Uint8Array }>

Encrypts value and returns the wire bytes to send and the enc_key to hold client-side.

| Parameter | Description | |---|---| | value | Any JSON-serialisable payload | | serverPk | Server's 32-byte X25519 public key (base64-decoded from the key endpoint) | | keyId | Key identifier returned alongside the server's public key |

Returns { wireBytes, encKey }. Store encKey indexed by request ID and pass it to decodeResponsePacket when the response arrives.


decodeResponsePacket

function decodeResponsePacket<T = unknown>(
    wireBytes: Uint8Array,
    encKey:    Uint8Array,
): Promise<T>

Verifies and decodes a server response. Throws if the HMAC is invalid or decryption fails.

| Parameter | Description | |---|---| | wireBytes | Raw bytes from the server response body | | encKey | The AES key returned by the matching buildRequestPacket call |


Lower-level exports

import { aesEncrypt, aesDecrypt }         from "alterion-encrypt";
import { compress, decompress }            from "alterion-encrypt";
import { deriveWrapKey, deriveResponseMacKey } from "alterion-encrypt";

| Function | Description | |---|---| | aesEncrypt(plaintext, key) | AES-256-GCM encrypt — 12-byte nonce prepended to output | | aesDecrypt(data, key) | AES-256-GCM decrypt — reads nonce from first 12 bytes | | compress(data) | Deflate-raw compress via CompressionStream | | decompress(data) | Deflate-raw decompress via DecompressionStream | | deriveWrapKey(sharedSecret, clientPk, serverPk) | HKDF-SHA256 wrap key — salt = clientPk ‖ serverPk, info = "alterion-wrap" | | deriveResponseMacKey(encKey) | HKDF-SHA256 HMAC key — info = "alterion-response-mac" |


Pipelines

Client request (buildRequestPacket)

Any JSON-serialisable value
        │
        ▼
  JSON.stringify → TextEncoder
        │
        ▼
  deflate-raw compress (CompressionStream)
        │
        ▼
  MessagePack encode  ──→  Uint8Array
        │
        ▼
  AES-256-GCM encrypt  (random enc_key — stored client-side by request ID)
        │
        ▼
  Ephemeral X25519 keygen  ──→  ECDH(client_sk, server_pk)  ──→  HKDF-SHA256  ──→  wrap_key
        │
        ▼
  AES-256-GCM wrap enc_key  (wrap_key)  ──→  kx
        │
        ▼
  Request { data, kx, client_pk, key_id, ts }
        │
        ▼
  MessagePack encode  ──→  wire bytes  ──→  sent to server

enc_key is returned to the caller and must be stored client-side (e.g. keyed by request ID). kx lets the server recover enc_key via ECDH without it ever appearing in plaintext on the wire.

Server response (decodeResponsePacket)

wire bytes received from server
        │
        ▼
  MessagePack decode  ──→  Response { payload, hmac }
        │
        ▼
  HKDF-SHA256(enc_key, info="alterion-response-mac")  ──→  mac_key
        │
        ▼
  HMAC-SHA256 verify (mac_key, payload)  ──  reject if invalid
        │
        ▼
  AES-256-GCM decrypt payload  (enc_key)
        │
        ▼
  MessagePack decode  ──→  Uint8Array
        │
        ▼
  deflate-raw decompress (DecompressionStream)
        │
        ▼
  JSON.parse  ──→  T

Compatibility

Requires CompressionStream / DecompressionStream — available in all modern browsers and Node.js 18+.


Contributing

See CONTRIBUTING.md. Open an issue before writing any code.


License

GNU General Public License v3.0 — see LICENSE.


Made with ❤️ by the Alterion Software team

Discord Website GitHub