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
$ pnpm add @agntn/hashesSHA-256
The default. Certificates, signatures, Bitcoin.
Avalanche
- Out
- b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
- Out
- 0fc30e735a0228a31cbbb969988b4f50e02e737f979f091d7d224b765443f5d4
- Digest
- 256-bit · 32 bytes
- Bits moved
- 140 of 256 · 54.7%
- HMAC
- takes a key
- Family
- cryptographic
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
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 caseVerify 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
same letters, other bytes
Hex ignores case, so the uppercase digest matches. Lowercase the base64 and it spells a different digest.
- hexB94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9match
- base64uu0nuznnpgillllx2n2r+sse7+n6u4dukij3rolvzek=mismatch
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"
cryptographic
non-cryptographic
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
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
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
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"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"