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

@mqxym/cryptit

v2.2.8

Published

Modern, cross-platform AES-GCM 256 / XChaCha20Poly1305 + Argon2-id encryption for files and text.

Readme

@mqxym/cryptit

Base CI

Modern, cross-platform encryption for both files and text.

  • Node 22 / Bun 1 - native argon2 addon + WebCrypto
  • Browser (evergreen) - tiny WASM build of argon2-browser
  • CLI - stream encryption & decryption, zero memory bloat
  • TypeScript-first, tree-shakable, ESM & CJS builds
  • Format-agnostic decryption - one instance reads any registered scheme

Scheme Support

Currently there are 2 encryption schemes supported:

  • Scheme 0 (default): AES-GCM 256 (native via Crypto API) and Argon2id (single thread parallelism setup using argon2 or argon2-browser*)
  • Scheme 1: XChaCha20Poly1305 (via JavaScript engine @noble/ciphers) and and Argon2id (multi thread parallelism setup using argon2 or argon2-browser*)

* This means that for the same "difficulty" setting, the KDF will be significantly slower in the browser than in Node.js.

The library can support up to 8 schemes via a header info byte (3 bit allocated).

[!WARNING] Scheme 1 works with an extractable CryptoKey. If unsure use scheme 0.


Live Demo


Install

# Bun (recommended)
bun add @mqxym/cryptit

# npm / pnpm
yarn add @mqxym/cryptit           # or npm i / pnpm add

Quick start - Node / Bun

import { createCryptit } from "@mqxym/cryptit";

const crypt = createCryptit({ scheme: 1 });
const pass  = "correct horse battery staple";

// Encrypt: returns a ConvertibleOutput wrapper
const out = await crypt.encryptText("hello", pass);

// Pick your preferred representation
console.log(out.base64);      // Base64 container
console.log(out.hex);         // Hex container
const bytes = out.uint8array; // Uint8Array container

// Decrypt: accepts Base64, Uint8Array, or ConvertibleInput
const dec = await crypt.decryptText(out.base64, pass);
console.log(dec.text);        // "hello"

// Clean sensitive buffers when done
out.clear();
dec.clear();

Streaming files

import { createCryptit } from "@mqxym/cryptit";
import { createReadStream, createWriteStream } from "node:fs";

const crypt = createCryptit();
const pass  = "hunter2";

// encrypt → movie.enc
await createReadStream("movie.mkv")
  .pipeThrough(await crypt.createEncryptionStream(pass))
  .pipeTo(createWriteStream("movie.enc"));

// decrypt back
await createReadStream("movie.enc")
  .pipeThrough(await crypt.createDecryptionStream(pass))
  .pipeTo(createWriteStream("movie.mkv"));

Browser usage

<script>
  // This needs to be included before the actual importing of cryptit
  // IMPORTANT: host argon2.wasm where the fetch command points to
  window.loadArgon2WasmBinary = () =>
    fetch("/examples/assets/argon2.wasm")
      .then(r => r.arrayBuffer())
      .then(buf => new Uint8Array(buf));
</script>

<!-- app.ts / app.js -->
<script type="module">
  import { createCryptit } from "@mqxym/cryptit/browser";

  const crypt = createCryptit({ saltStrength: "high", verbose: 2 });

  async function enc() {
    const cipher = await crypt.encryptText("hello", "pw");
    console.log(cipher.base64);  // or .hex / .uint8array
    cipher.clear();
  }
  enc();
</script>

Use with a bundler or simply via <script type="module">.


API highlights

import { createCryptit, Cryptit } from "@mqxym/cryptit";
// Also available: ConvertibleInput / ConvertibleOutput
// import { ConvertibleInput, ConvertibleOutput } from "@mqxym/cryptit";

const c = createCryptit({ verbose: 1 });

// TEXT 
const enc: ConvertibleOutput =
  await c.encryptText(/* string | Uint8Array | ConvertibleInput */ "txt", pass);
// Choose your representation:
enc.base64; enc.hex; enc.uint8array; // and wipe when done:
enc.clear();

const dec: ConvertibleOutput =
  await c.decryptText(/* Base64 string | Uint8Array | ConvertibleInput */ enc.base64, pass);
dec.text;
dec.clear();

// RUNTIME TWEAKS
c.setDifficulty("high");  // Argon2id difficulty preset
c.setScheme(1);           // choose another registered format (scheme 1 = XChaCha20Poly1305)
c.setSaltDifficulty("low");

// HELPERS
Cryptit.isEncrypted(blobOrB64);   // ↦ boolean
Cryptit.decodeHeader(blobOrB64);  // ↦ meta {scheme, salt, …}
Cryptit.decodeData(blobOrB64);  // ↦ {isChunked, ivLength, tagLength, iv, tag, …}

Verbose levels:

| Level | Emits | | ----- | ----------------------------- | | 0 | errors only | | 1 | +start/finish notices | | 2 | +timings, key-derivation info | | 3 | +salt / scheme / KDF details | | 4 | wire-level debug |


CLI (cryptit)

# encrypt file → .enc
cryptit encrypt  <in> [-o out] [options]

# decrypt back
cryptit decrypt  <in> [-o out] [options]

# encrypt text
echo "secret" | cryptit encrypt-text  -p pw
cryptit encrypt-text "secret" -d high -S 1 # -> Prompt for password, Argon2id difficulty "high" and Scheme 1

# decrypt text
echo "…b64…" | cryptit decrypt-text -p pw

# inspect header, chunk and text details of Cryptit-encrypted payloads (no decryption)
cryptit decode movie.enc
cat movie.enc | cryptit decode

# output fake data (valid header) in base64 with random 32-byte tail
cryptit fake-data --base64 32

Docker CLI

docker pull ghcr.io/mqxym/cryptit-cli:latest

echo "AQVWgYDH/rkR6Ymxv1W9NzFWTsvTTXsnEaLHPx+NlATmuwcqea5RlljX1ly16Px716I2yGX/XsXHt7xG14DmnJ3Czu0A9/TM1sPJayRdHDYPckJ5eGfAGY5n5H8nNjKqhpY=" | docker run --rm -i cryptit:latest decode | jq

Common flags

| Flag | Default | Description | | ------------------------- | ------- | -------------------- | | -p, --pass <pw> | prompt | passphrase | | -d, --difficulty <l> | middle | Argon2 preset | | -S, --scheme <0-1>. | 0. | Scheme preset | | -s, --salt-strength <l> | high | 12 B vs 16 B salt | | -c, --chunk-size <n> | 524 288 | plaintext block size | | -v, --verbose | 0 … 4 | repeat to increase |

Exit codes: 0 success · 1 any failure (invalid header, auth, I/O …)

[!NOTE] Use the prompt password feature where ever possible, to not leak your password via history.


Versioned format

  • Header: 0x01 | infoByte | salt
  • Decryptors pick the engine by the header’s scheme ⇒ one CLI handles all registered schemes.

Additional Authenticated Data

  • Since version 1.0.0: Header data is authenticated.
  • Since version 2.2.0: encryptText() uses 8-bit padding before AEAD, which is also tagged in AAD.

Compatibility

  • To decrypt data from versions prior to 1.0.0, there is a temporary solution:

    const cryptit = createCryptit({ acceptUnauthenticatedHeader: true });
    • This option will be removed in future releases because the header must always be authenticated.
  • The padding tag for encrypted text in AAD is not required, so encrypted text from versions prior to 2.2.0 can still be decrypted with versions greater than 2.2.0.

    • This backward compatibility will also be removed in future releases.

Build from source

git clone https://github.com/mqxym/cryptit
cd cryptit
bun install && bun run build && bun test

Security

  • AES-GCM 256 / 12-byte IV / 128-bit tag
  • XChaCha20Poly1305 / 24-byte IV / 128-bit tag
  • Argon2-id presets (low / middle / high)
  • Salts generated per-ciphertext; never reused

[!IMPORTANT] DISCLAIMER This project was created in collaboration with OpenAI’s language models and me, @mqxym.


CLI Benchmarks (Bun Engine, MacOS, M3 Pro Chip)

TL;DRScheme 0 (AES‑GCM/SubtleCrypto + XChaCha20‑Poly1305) is much faster for streaming: peak ~1,810 MiB/s (stdin→stdout, 256 MiB, middle). • Scheme 1 (XChaCha20‑Poly1305) peaks ~170 MiB/s. • KDF cost is now measured separately and not included in “stream‑only” throughput below.


KDF Baseline (avg of 3, encrypt‑text 16 B payload)

| Difficulty | KDF avg (Scheme 0) | KDF avg (Scheme 1) | | :--------: | -----------------: | -----------------: | | low |  174.29 ms  |  167.05 ms  | | middle |  540.41 ms  |  442.74 ms  | | high |  1013.10 ms  |  825.44 ms  |

Higher is better (MiB/s).

|  Size  |  Difficulty  |  enc f→f  |  dec f→out  |  enc in→out  |  dec in→out  | | :-------: | :----------: | --------: | ----------: | ------------: | -----------: | |  256 MiB  |  low     |  868.33  |  910.05  |  1709.13  |  1519.16  | |  256 MiB  |  middle  |  995.48  |  910.46  |  1810.27  |  1585.49  | |  256 MiB  |  high    |  899.43  |  866.88  |  1658.29  |  1409.89  |

Higher is better (MiB/s).

|  Size  |  Difficulty  |  enc f→f  |  dec f→out  |  enc in→out  |  dec in→out  | | :-------: | :----------: | --------: | ----------: | -----------: | -----------: | |  256 MiB  |  low     |  149.33  |  153.01  |  167.36  |  164.99  | |  256 MiB  |  middle  |  154.23  |  154.72  |  169.55  |  162.91  | |  256 MiB  |  high    |  149.83  |  151.26  |  157.55  |  163.05  |

Lower is better (ms / s). Decode columns show latency (ms).

|  Size  |  Difficulty  |  enc f→f  |  dec f→out  |  enc in→out  |  dec in→out  |  decode file (ms)  |  decode stdin (ms)  | | :-------: | :----------: | -----------------: | -----------------: | -----------------: | -----------------: | -----------------: | ------------------: | |  256 MiB  |  low     |  469 ms / 0.47 s  |  456 ms / 0.46 s  |  324 ms / 0.32 s  |  343 ms / 0.34 s  |  45  |  153  | |  256 MiB  |  middle  |  798 ms / 0.80 s  |  822 ms / 0.82 s  |  682 ms / 0.68 s  |  702 ms / 0.70 s  |  46  |  190  | |  256 MiB  |  high    |  1298 ms / 1.30 s  |  1308 ms / 1.31 s  |  1167 ms / 1.17 s  |  1195 ms / 1.19 s  |  45  |  178  |

Lower is better (ms / s). Decode columns show latency (ms).

|  Size  |  Difficulty  |  enc f→f  |  dec f→out  |  enc in→out  |  dec in→out  |  decode file (ms)  |  decode stdin (ms)  | | :-------: | :----------: | -----------------: | -----------------: | -----------------: | -----------------: | -----------------: | ------------------: | |  256 MiB  |  low     |  1881 ms / 1.88 s  |  1840 ms / 1.84 s  |  1697 ms / 1.70 s  |  1719 ms / 1.72 s  |  48  |  133  | |  256 MiB  |  middle  |  2103 ms / 2.10 s  |  2097 ms / 2.10 s  |  1953 ms / 1.95 s  |  2014 ms / 2.01 s  |  48  |  180  | |  256 MiB  |  high    |  2534 ms / 2.53 s  |  2518 ms / 2.52 s  |  2450 ms / 2.45 s  |  2396 ms / 2.40 s  |  49  |  190  |

Legend enc f→f = encrypt file→file • dec f→out = decrypt file→stdout • enc in→out = encrypt stdin→stdout • dec in→out = decrypt stdin→stdout.

Method notes • CLI: bun run cli:run  • Difficulties: low/middle/high  • Size: 256 MiB  • Repeats: 1 • KDF repeats = 3, payload = 16 bytes. “Stream‑only” removes the measured KDF baseline for the respective difficulty; wall‑clock shows full end‑to‑end time.


License

MIT