Guide

Getting Started

Install the package and hash one string. Every algorithm answers with the same result shape
The API and the tool list can still move. Pin exact versions if you build on it now.

Why this exists

Ask a model for the SHA-256 of a string. It gives you 64 hex characters, very confidently, and they're wrong. Hashing is the one thing a language model can't fake, so it should call something that actually hashes. @agntn/hashes is that something, and it's the same thing in your TypeScript and in your terminal.

It also has the hashes chains quietly depend on. Keccak-256 with its pre-SHA-3 padding, HASH160, double SHA-256, BLAKE2b cut to 32 or 28 bytes, SHA-512Half. Those are the ones people get subtly wrong, and subtly wrong means a different address.

No HTTP, no keys, no telemetry, no hashing dependency. Every digest is plain TypeScript in src/core/, and the library imports nothing from node:*. That's why this site can run it in your browser.

Install

shell
pnpm add @agntn/hashes

Node.js 26 or newer. It needs Uint8Array with native hex and base64, and 26 is where that stopped being a question.

First call

tsfirst.ts
import { create } from "@agntn/hashes";

const result = create("sha256").hash("abc");
result.digest; // "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
result.digestLength; // 32

create(name) wants the exact registry key and hands back one cached instance. resolveAlgorithm("SHA3_256") forgives case, spaces and underscores, then wants an exact match too. No prefixes, no fuzzy guess. A typo is an UnknownAlgorithmError, and its message lists every name that does exist.

The shape

Every hash() returns this, whatever the algorithm:

ts
interface HashResult {
  digest: string | Uint8Array; // bytes only with encoding "binary"
  algorithm: string; // "sha256"
  operation: "hash" | "hmac";
  encoding: "hex" | "base64" | "base64url" | "binary";
  digestLength: number; // bytes of the raw digest
  options: Record<string, unknown>; // what the digest depends on: encoding, salt, cost, seed
}

options is the part people skip and then regret. For scrypt it holds the salt it drew and the cost it ran with, and without those the digest is a string that looks random and that you can never reproduce.

What ships

algorithms()24 algorithms · listing order
OptionsSecurity
SHA-256sha256Cryptographic256-bitHMAC yesno options128-bit collision resistance
SHA-384sha384Cryptographic384-bitHMAC yesno options192-bit collision resistance
SHA-512sha512Cryptographic512-bitHMAC yesno options256-bit collision resistance
SHA-512Halfsha512-halfCryptographic256-bitHMAC nono options128-bit collision resistance
SHA3-256sha3-256Cryptographic256-bitHMAC yesno options128-bit collision resistance
SHA3-512sha3-512Cryptographic512-bitHMAC yesno options256-bit collision resistance
Keccak-256keccak256Cryptographic256-bitHMAC yesno options128-bit collision resistance
BLAKE2bblake2bCryptographic512-bitHMAC yesno options256-bit collision resistance
BLAKE2b-256blake2b-256Cryptographic256-bitHMAC nono options128-bit collision resistance
BLAKE2b-224blake2b-224Cryptographic224-bitHMAC nono options112-bit collision resistance
BLAKE2sblake2sCryptographic256-bitHMAC yesno options128-bit collision resistance
BLAKE3blake3Cryptographic256-bitHMAC nono options128-bit security against collisions and preimages
BLAKE-256blake256Cryptographic256-bitHMAC nono options128-bit collision resistance
RIPEMD-160ripemd160Cryptographic160-bitHMAC yesno options80-bit collision resistance
HASH160hash160Cryptographic160-bitHMAC nono options80-bit collision resistance
HASH256 (double SHA-256)hash256Cryptographic256-bitHMAC nono options128-bit collision resistance
MD5md5Legacy128-bitHMAC yesno optionsBROKEN: collision attacks known since 2004
SHA-1sha1Legacy160-bitHMAC yesno optionsBROKEN: practical collision attack (SHAttered)
CRC-32crc32Non-cryptographic32-bitHMAC nono optionsNOT for security: error-detection checksum only
CRC-16/XMODEMcrc16-xmodemNon-cryptographic16-bitHMAC nono optionsNOT for security
xxHash (XXH64)xxhashNon-cryptographic64-bitHMAC noseed?NOT for security: fast hash for hash tables
FNV-1a (64-bit)fnv1aNon-cryptographic64-bitHMAC nono optionsNOT for security: simple hash for hash tables
scryptscryptPasswordvariableHMAC nosalt?, N?, r?, p?, keyLength?Memory-hard KDF
PBKDF2pbkdf2PasswordvariableHMAC nosalt?, iterations?, digest?, keyLength?OWASP Password Storage Cheat Sheet: >=600000 iterations with HMAC-SHA256
read from the registry in your browser / no networka name with ? is optional

Every row comes from create(name).info(), read in your browser. The HMAC column says which ones take a key. The rest refuse it with an error, they don't quietly hash without it.

Errors

ts
import { HashError, InvalidOptionError, UnknownAlgorithmError, create } from "@agntn/hashes";

try {
  create("scrypt").hash("password", { N: 1000 });
} catch (error) {
  if (error instanceof InvalidOptionError) {
    error.option; // "N"
    error.reason; // "must be a power of 2 and >= 2"
  }
}

A name nobody registered is an UnknownAlgorithmError. A bad value is an InvalidOptionError with the option, the value and the reason. A required one left out is a MissingOptionError. Anything else an algorithm throws gets wrapped by normalizeError into a HashError that names the algorithm. The CLI prints the message on one line and exits 1. No stack trace, the input was wrong, not you.

Next