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

@pksuite/crypto

v0.2.0

Published

Browser and Node cryptographic primitives for the pk suite.

Readme

@pksuite/crypto

Browser and Node cryptographic primitives shared by the pk suite. Version 0.1.0 provides canonical age/X25519 identities and the streaming PKFILE1 file envelope. It has no workflow, storage-provider, or network concepts. The ESM browser entry is intended for an application bundler. Its declared @noble/* dependencies remain external in both ESM and CommonJS artifacts so consumer lockfile updates change the code that actually executes.

import {
  createRecipientIdentity,
  createFileEncryption,
  disposeEnvelope,
  encryptChunk,
  finalizeEnvelope,
  openFileEnvelope,
  decryptChunk,
} from "@pksuite/crypto";

const recipient = createRecipientIdentity();
const encryption = createFileEncryption({
  recipient: recipient.recipient,
  plaintextSize: file.size,
  metadata: {
    name: file.name,
    mediaType: file.type || "application/octet-stream",
    lastModified: file.lastModified,
  },
});

for (let index = 0; index < encryption.chunkCount; index++) {
  const start = index * encryption.chunkSize;
  const plaintext = new Uint8Array(await file.slice(start, start + encryption.chunkSize).arrayBuffer());
  const ciphertext = encryptChunk({ state: encryption, index, plaintext });
  // Persist or upload this independently authenticated chunk before reading
  // the next slice. Retrying an index produces a fresh XChaCha nonce.
  await storeChunk(index, ciphertext);
}
finalizeEnvelope(encryption);

const decryption = openFileEnvelope({ identity: recipient.identity, header: encryption.header });
for (let index = 0; index < decryption.chunkCount; index++) {
  const plaintext = decryptChunk({ state: decryption, index, ciphertext: await loadChunk(index) });
  await consumePlaintext(plaintext);
}
finalizeEnvelope(decryption);

When the identity scalar is held elsewhere (e.g. an agent process that never releases it), pass unwrapFileKey instead of identity: the callback receives a copy of the envelope's age header and returns the 16-byte file key; the package copies the result, so the callback may wipe its own buffer.

finalizeEnvelope best-effort overwrites the file key retained by its state and makes that state unusable. Call disposeEnvelope(state) on cancellation or any abandoned upload/decryption. JavaScript cannot guarantee erasure of engine or cryptographic-library internal copies.

Metadata names are encrypted portable filename components, not paths. A consumer that writes files must still join the name beneath an explicitly chosen directory and must not decode or reinterpret it as path syntax.

The encrypted header exposes file size, chunk geometry, recipient count, and header length. Filename, media type, and modification time are encrypted. See dist/SPEC-FILE.md in the package, or spec/SPEC-FILE.md in the repository, for the implementation-independent wire format and threat contract.

vectors/file-v1.json contains fixed identity, header, metadata, nonce, AAD, and chunk outputs. An independent Node reference reader opens that frozen vector in the test suite. Every structured case in vectors/file-v1-tampering.json is executed by the conformance tests. The frozen age-cli-v1.age artifact was written by the external age CLI.

The mobile release gate is recorded under test/mobile/reports/ for Android Chrome and iOS Safari. Both devices completed foreground and background-resume runs with matching byte counts. Safari does not expose performance.memory; the harness records that measurement as unavailable.