Introduction
unsecure is a set of cryptographic utilities built on the Web Crypto API: zero dependencies, one entry point per module, and one typed error.
unsecure is a collection of runtime-agnostic, cryptographically secure utilities. The name is a joke. The defaults are not.
Everything here is built on the Web Crypto API and the platform's own CSPRNG. There are no runtime dependencies, no Node built-ins, and no WebAssembly, so the same code runs in Node, Bun, Deno, Cloudflare Workers and browsers.
#What is in the box
| Family | Functions |
|---|---|
| Hashing and MAC | hash, hmac, hmacVerify, importHmacKey |
| Key derivation | hkdf, importHkdfKey |
| Password hashing | argon2, argon2Hash, argon2Verify, argon2NeedsRehash |
| One-time passwords | hotp, totp, their verifies, generateOTPSecret, otpauthURI |
| UUID | uuidv4, uuidv7, createUUIDv7Generator, uuidv7Timestamp, the type guards |
| Secrets | secureGenerate, secureCompare, entropy |
| Sanitize | sanitizeObject, sanitizeObjectCopy, safeJsonParse |
| Random | secureRandomNumber, secureRandomBytes, secureShuffle, randomJitter |
| Codecs | Hex, Base64, Base32 and their flat stringify / parse functions |
| Errors | UnsecureError and its code set |
#Two rules that run through everything
Verification never throws on untrusted input. secureCompare, hmacVerify, hotpVerify, totpVerify and argon2Verify return false for a missing header, a malformed signature or a wrong-typed value from a JSON body. They throw only for something you control: an empty secret, an unsupported algorithm, an out-of-range window. So a try / catch around a verify is about your own configuration, never about the request.
One error class, one code set. Every failure is an UnsecureError with a code you can branch on. A native TypeError escaping the library is a bug.
#What it looks like
import { argon2Hash, argon2Verify } from "unsecure/argon2";
import { hmacVerify } from "unsecure/hmac";
import { uuidv7 } from "unsecure/uuid";
const stored = await argon2Hash("correct horse battery staple");
// "$argon2id$v=19$m=19456,t=2,p=1$…$…"
await argon2Verify(stored, "correct horse battery staple"); // true
await argon2Verify(stored, "hunter2"); // false
await hmacVerify(secret, body, request.headers.get("x-signature")); // boolean
uuidv7(); // "01a076ba-1e53-7411-8518-84905f353e3d"#What is not in the box
JWT, JWS, JWE and JWK are intentionally out of scope, and will stay that way. unjwt covers the whole JOSE toolset with the same runtime-agnostic, Web Crypto approach; the two libraries are meant to sit side by side.
#Where to go next
Installation
Install the package, pick between the barrel and the subpaths, or import from a CDN.
Quickstart
Five short tasks, end to end, with the output each one prints.
Migrating to 0.3
What changed since 0.2.3, and what you have to edit.
Recipes
Webhook verification, login with 2FA, password storage, safe JSON.