ID@agntn/hashesv0.3.0

Hash it. Don't guess it.

A model will hand you 64 hex characters for any SHA-256 you ask about, very confidently, and they'll be wrong. This computes them. SHA-2 to scrypt, the hashes Bitcoin and Ethereum build addresses from, one call in TypeScript, in the terminal and in your agent. No network, no keys, nothing to configure.

Algorithms
24
in 4 families
With HMAC
11
4 agent tools on top
Network calls
0
every value computed in place
Install$ pnpm add @agntn/hashes
Callcreate("sha256").hash("hello world")cryptographic · 01 / 24
Hash / cryptographic

SHA-256

The default. Certificates, signatures, Bitcoin.

Avalanche

In11 bytes
Flip0x64 → 0x65
Out
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
Out
0fc30e735a0228a31cbbb969988b4f50e02e737f979f091d7d224b765443f5d4
Digest
256-bit · 32 bytes
Bits moved
140 of 256 · 54.7%
HMAC
takes a key
Family
cryptographic
family 16 / 24

Same call, every algorithm

create("sha256") gives you an object with one method that matters, hash. Pass a key and it's an HMAC, pass an encoding and the digest comes back in it. This file walks through 22 algorithms and none of it is a recording. Your browser computes every line, with the same TypeScript the package ships.

  • create(name) wants the exact key. resolveAlgorithm forgives case, spaces and underscores
  • Every result is { digest, algorithm, operation, encoding, digestLength, options }
  • Hex, base64, base64url or raw bytes. Text is UTF-8, a Uint8Array is hashed as it is
Read Hashing
Filesha256.tscryptographic · computed here

Digest

import { create, digestMatches } from "@agntn/hashes";// SHA-256, cryptographic, 256-bitconst hash = create("sha256");const result = hash.hash("hello world");result.digest;        // "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"result.digestLength;  // 32hash.hash("hello world", { encoding: "base64" }).digest;// "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek="const upper = "B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9";digestMatches(result, upper);  // true, hex ignores case

Verify compares bytes, not letters

Lowercasing a digest before comparing is the classic shortcut, and it's fine for hex. For base64 it's a bug, A and a are different bytes. The panel takes the current digest, uppercases the hex, lowercases the base64 and asks hash_verify about both. One passes. The other shouldn't, and doesn't.

  • digestMatches decodes both sides and compares every byte, even after the first difference
  • Hex ignores case. Base64 and base64url never do
  • hashes verify exits with 1 on a mismatch, so a script can branch on it
Read HMAC and verify
Callhash_verify("sha256", "hello world", …)hex · base64 · every byte
Verdict / sha256

same letters, other bytes

Hex ignores case, so the uppercase digest matches. Lowercase the base64 and it spells a different digest.

  1. hexB94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9match
  2. base64uu0nuznnpgillllx2n2r+sse7+n6u4dukij3rolvzek=mismatch
21 of 32 bytes differ

Twenty-four algorithms, four families

Sixteen cryptographic ones, from SHA-256 to BLAKE3, with the compositions chains actually use: Keccak-256 with its old padding, HASH160, double SHA-256, BLAKE2b cut to 32 and 28 bytes. MD5 and SHA-1 sit under legacy, broken and still everywhere. Four checksums for tables and files, and two KDFs that print the salt they drew. Eleven of them take a key. The rest say no instead of pretending.

Importimport { algorithms, create } from "@agntn/hashes"

Callalgorithms()24 names · 16 cryptographic · 2 legacy · 4 non-cryptographic · 2 password

legacy

non-cryptographic

password

Four tools, one executor

Ask a model for a digest and it answers from memory. Give it hash_compute and it answers from code. hashes mcp, the Pi and OMP extensions and @agntn/hashes/ai call the same executors, so they answer identically and a fix lands once. This page runs them too, so the dialog shows exactly what a model reads for "hello world".

  • hash_compute, hash_hmac, hash_verify, hash_algorithms
  • A misspelled argument is an error. salt_hex never turns into a random salt
  • Input as utf8, hex or base64, so a public key hashes as bytes
Read MCP, Pi, OMP and AI SDK
Callhash_compute("sha256", "hello world")
Tool / Cryptographic

SHA-256

The digest comes out of the executor, not out of the model. The second line says what it is, so the next call can check it.

digest
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
about
sha256, hex, 32 bytes
hmac
hash_hmac takes a key
MCP · Pi · OMPhashes mcp · stdio

Extend FixedHash, call register

Every built-in is a class, and yours is the same shape, one file. A fixed-length digest only has to turn bytes into bytes. FixedHash does the encodings, the result and the error wrapping, and says no to an HMAC key it can't honour. No plugin manifest.

  • A static key, an about block and digest(bytes). Encoding, input and errors are the base class's job
  • BlockHash with a Hasher gets you HMAC as well
  • register(Class) makes it visible to create, resolveAlgorithm and the tools
Read Custom algorithms
Filefnv1a-32.tsfolded · copy is whole
import { FixedHash, create, register } from "@agntn/hashes";class Fnv1a32 extends FixedHash {  static readonly key = "fnv1a-32";  protected readonly about = { /* label, family, digestLength */ };  protected digest(bytes: Uint8Array): Uint8Array {    let hash = 0x811c9dc5;    for (const byte of bytes) hash = Math.imul(hash ^ byte, 0x01000193);    const out = new Uint8Array(4);    new DataView(out.buffer).setUint32(0, hash >>> 0);    return out;  }}register(Fnv1a32);create("fnv1a-32").hash("a").digest; // "e40c292c"
Startpnpm add @agntn/hashesNode.js 26 or newer

Start with one command

One install gives you the library, the hashes CLI and the MCP server. Pick an algorithm by name, hand it text or bytes, and every one answers with the same result: the digest, its length and the options it depends on.

  • PinPre-1.0, so pin exact versions.
  • Passwordsscrypt and PBKDF2 reproduce a digest. They don't run your login.
  • OfflineNothing to fetch, no key to sign up for, nothing to configure.

First call

$ pnpm add @agntn/hashesimport { create } from "@agntn/hashes";create("sha256").hash("abc").digest;  // "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"create("sha256").hash("message", { key: "secret" }).digest;  // "8b5f48702995c1598c573db1e21866a9b825d4a794d169d7060a03605796360b"create("keccak256").hash("transfer(address,uint256)").digest;  // "a9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b"