mrkeyoor.com_
Sat 08 Aug 21:58 UTC
npmSecurityupdated 08 Aug 2026

libsodium

This npm package is the raw Emscripten build of the Sodium cryptography library, with WebAssembly and a pure JavaScript fallback. It is not the application-facing API most developers mean when they ask for libsodium in JavaScript. The package metadata says “raw library, no wrappers,” and the repository tells applications to load libsodium-wrappers, which installs this package transitively and adds readiness handling, Uint8Array conversions, generated TypeScript declarations, constants, and callable high-level crypto functions. Treat libsodium as an implementation artifact unless you are maintaining a wrapper.

Verdict

Do not install the bare libsodium package for application code. Install libsodium-wrappers and let it bring this raw engine transitively; choose the sumo wrapper only for a verified missing primitive.

API stability2/5The underlying Sodium C API is intentionally stable, and wrappers generate a familiar buffer-oriented surface from it. The bare npm package, however, exposes a generated Emscripten module full of underscored functions, heap operations, pointers, and runtime initialization details. Its metadata explicitly says it has no wrappers, and it publishes no declarations. That raw surface is an implementation boundary, not a dependable application API.
Docs3/5The repository README is unusually candid about which package applications should load, standard versus sumo contents, supported runtimes, readiness, binary conversions, return shapes, browser UTF-8 failure modes, and manual state cleanup. The documentation is excellent for libsodium-wrappers but intentionally does not teach direct use of the bare libsodium module. A guide for this exact npm name therefore has to redirect readers rather than document a supported raw API.
Maintenance5/5Version 0.8.4 was published in April 2026, the repository was pushed in July 2026, and GitHub reports zero open issues and pull requests. The same repository builds the raw standard and sumo modules, their wrapper packages, browser artifacts, generated API declarations, and tests. Current Node and Bun support plus recent packaging work show active maintenance across both the compiled engine and supported wrapper layer.
Ecosystem4/5The raw package recorded 3,384,103 downloads in the measured week largely because the supported wrappers depend on it. The project targets browsers, Node, Bun, and pure JavaScript fallback environments, and its cryptographic formats follow the widely deployed native Sodium library. The npm package itself has almost no direct developer ecosystem because applications correctly gather around libsodium-wrappers, sumo, native bindings, and platform-specific adapters.

Use it if

  • You maintain libsodium-wrappers or another binding that needs the raw compiled module as its engine
  • It appears transitively under libsodium-wrappers in your lockfile and you are deciding whether it is legitimate
  • You are writing a specialized Emscripten binding and are prepared to manage raw memory pointers, underscored C exports, initialization, and version coupling yourself
Skip it if

Setup reality

Do not add libsodium directly to an application. Install libsodium-wrappers, which declares libsodium as its dependency, then import the wrapper's default export and await sodium.ready before touching any crypto function or runtime-added constant. Crypto functions are attached dynamically after initialization, so they cannot be imported as ESM named exports; only helpers such as ready, free, from_hex, to_hex, from_string, and to_string have named exports. There are no native builds for consumers and no credentials or config file, because the published artifact includes WebAssembly plus a pure JavaScript fallback. That convenience costs size: the README puts the standard distribution around 290 KB gzipped and sumo around 375 KB. Use libsodium-wrappers-sumo only when the required primitive, notably crypto_pwhash, is absent from standard. Binary values are Uint8Array objects, so define one storage encoding and include every nonce, salt, header, algorithm version, and public key needed for later decryption or verification. Nonces must never repeat under the same key; use randombytes_buf at the library's required size instead of counters unless you have a reviewed protocol. Authentication failure must stop processing rather than return partial plaintext. Long-running state APIs allocate opaque WebAssembly handles; the README requires sodium.free() for APIs without a finalizer, including secretstream and XOF state. Browser script users must serve or inline with UTF-8 metadata or the embedded WebAssembly can be corrupted. For secrets, memzero() only clears the specific Uint8Array you pass, not copies created by your application or garbage collector.

Patterns

Install the application-facing packageinstall-supported-wrapper

npm uninstall libsodium
npm install libsodium-wrappers

libsodium-wrappers installs libsodium transitively; keeping a separate direct dependency on the raw package usually adds version ambiguity without adding an API.

Wait for Sodium before using runtime exportsinitialize-wrapper

import sodium from 'libsodium-wrappers';

await sodium.ready;
console.log(sodium.SODIUM_VERSION_STRING);

Crypto functions and constants are attached dynamically after ready resolves, so they cannot be imported as named ESM exports.

Generate a URL-safe random tokengenerate-random-token

await sodium.ready;

const bytes = sodium.randombytes_buf(32);
const token = sodium.to_base64(
  bytes,
  sodium.base64_variants.URLSAFE_NO_PADDING
);

Store or transmit the encoding label with long-lived values; the wrapper's default base64 variant is URL-safe without padding, which may differ from other systems.

Encrypt and prepend a fresh secretbox nonceencrypt-secretbox

const key = sodium.crypto_secretbox_keygen();

function encrypt(message) {
  const nonce = sodium.randombytes_buf(sodium.crypto_secretbox_NONCEBYTES);
  const ciphertext = sodium.crypto_secretbox_easy(message, nonce, key);
  const packed = new Uint8Array(nonce.length + ciphertext.length);
  packed.set(nonce);
  packed.set(ciphertext, nonce.length);
  return packed;
}

A nonce is public but must never repeat with the same key; generating it at NONCEBYTES and storing it beside the ciphertext is the README's recommended shape.

Split and authenticate a secretbox payloaddecrypt-secretbox

function decrypt(packed) {
  const min = sodium.crypto_secretbox_NONCEBYTES + sodium.crypto_secretbox_MACBYTES;
  if (packed.length < min) throw new Error('Ciphertext is too short');

  const nonce = packed.slice(0, sodium.crypto_secretbox_NONCEBYTES);
  const ciphertext = packed.slice(sodium.crypto_secretbox_NONCEBYTES);
  const plaintext = sodium.crypto_secretbox_open_easy(ciphertext, nonce, key);
  return sodium.to_string(plaintext);
}

open_easy authenticates before returning plaintext and throws when verification fails; catch that only to reject the message, never to continue with guessed data.

Create and verify a detached signaturesign-detached-message

const { publicKey, privateKey } = sodium.crypto_sign_keypair();
const message = sodium.from_string('release manifest');
const signature = sodium.crypto_sign_detached(message, privateKey);

const valid = sodium.crypto_sign_verify_detached(
  signature,
  message,
  publicKey
);
if (!valid) throw new Error('Invalid signature');

A signature proves possession of the private key but does not encrypt the message; distribute public keys through an authenticated channel.

Encrypt anonymously to a recipient public keyseal-for-recipient

const recipient = sodium.crypto_box_keypair();
const sealed = sodium.crypto_box_seal(
  'private payload',
  recipient.publicKey
);

const opened = sodium.crypto_box_seal_open(
  sealed,
  recipient.publicKey,
  recipient.privateKey
);
console.log(sodium.to_string(opened));

Sealed boxes hide the message for the recipient but do not authenticate a sender; add a signature or authenticated protocol when sender identity matters.

Hash content with the generic hash APIhash-content

const digest = sodium.crypto_generichash(
  sodium.crypto_generichash_BYTES,
  sodium.from_string('content to fingerprint'),
  null
);
const hex = sodium.to_hex(digest);

An unkeyed generic hash is suitable for fingerprints, not password storage; use the sumo password-hashing API for human passwords.

Store and verify a password with the sumo wrapperhash-password

import sodium from 'libsodium-wrappers-sumo';
await sodium.ready;

const stored = sodium.crypto_pwhash_str(
  password,
  sodium.crypto_pwhash_OPSLIMIT_MODERATE,
  sodium.crypto_pwhash_MEMLIMIT_MODERATE
);

if (!sodium.crypto_pwhash_str_verify(stored, candidate)) {
  throw new Error('Invalid credentials');
}

crypto_pwhash is sumo-only and deliberately memory intensive; benchmark the chosen limit on production hardware and enforce request rate limits.

Detect hashes that need stronger current limitsrehash-password

const needsUpgrade = sodium.crypto_pwhash_str_needs_rehash(
  stored,
  sodium.crypto_pwhash_OPSLIMIT_MODERATE,
  sodium.crypto_pwhash_MEMLIMIT_MODERATE
);

if (needsUpgrade && sodium.crypto_pwhash_str_verify(stored, candidate)) {
  stored = sodium.crypto_pwhash_str(
    candidate,
    sodium.crypto_pwhash_OPSLIMIT_MODERATE,
    sodium.crypto_pwhash_MEMLIMIT_MODERATE
  );
}

Run the upgrade after a successful login so the plaintext password is available; this pattern also requires libsodium-wrappers-sumo.

Encrypt an ordered stream and free its statesencrypt-stream-chunks

const key = sodium.crypto_secretstream_xchacha20poly1305_keygen();
const pushed = sodium.crypto_secretstream_xchacha20poly1305_init_push(key);
const c1 = sodium.crypto_secretstream_xchacha20poly1305_push(
  pushed.state, 'chunk one', null,
  sodium.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE
);
const c2 = sodium.crypto_secretstream_xchacha20poly1305_push(
  pushed.state, 'chunk two', null,
  sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL
);

const pulled = sodium.crypto_secretstream_xchacha20poly1305_init_pull(pushed.header, key);
const r1 = sodium.crypto_secretstream_xchacha20poly1305_pull(pulled, c1, null);
const r2 = sodium.crypto_secretstream_xchacha20poly1305_pull(pulled, c2, null);
if (!r1 || !r2 || r2.tag !== sodium.crypto_secretstream_xchacha20poly1305_TAG_FINAL) {
  throw new Error('Invalid or truncated stream');
}
sodium.free(pushed.state);
sodium.free(pulled);

Transmit the header before ciphertext chunks, preserve chunk order, require a final tag, and free both opaque WebAssembly states after the last operation.

Compare equal-length secrets and clear a key buffercompare-and-wipe

if (expected.length !== received.length || !sodium.memcmp(expected, received)) {
  throw new Error('Authentication failed');
}

sodium.memzero(key);

memcmp is constant-time for equal-length buffers; memzero clears only that Uint8Array, so avoid creating extra secret copies that remain managed by JavaScript's garbage collector.

Alternatives

PackageRegistryPick it when
libsodium-wrappersnpmThe correct default for browser, Node, or Bun application code using standard Sodium functions
libsodium-wrappers-sumonpmYou specifically need password hashing or another function omitted from the standard build and accept the larger, riskier surface
sodium-nativenpmNode-only code can accept native installation constraints in exchange for native bindings and no browser payload