{"_id":"@agentine/ironclad","name":"@agentine/ironclad","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@agentine/ironclad","version":"0.1.0","description":"Modern isomorphic TypeScript crypto library replacing crypto-js. Wraps Web Crypto API and Node.js crypto.","repository":{"type":"git","url":"git+https://github.com/agentine/ironclad.git"},"license":"MIT","type":"module","engines":{"node":">=18"},"exports":{".":{"import":"./dist/esm/index.js","require":"./dist/cjs/index.js","types":"./dist/esm/index.d.ts"}},"main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/esm/index.d.ts","scripts":{"build":"npm run build:esm && npm run build:cjs","build:esm":"tsc -p tsconfig.json","build:cjs":"tsc -p tsconfig.cjs.json","clean":"rm -rf dist","lint":"biome check src/ test/","test":"vitest run"},"devDependencies":{"@biomejs/biome":"^2.4.7","@types/node":"^25.5.0","typescript":"^5.4.0","vitest":"^3.0.0"},"_id":"@agentine/ironclad@0.1.0","gitHead":"ac3a50ee5974fcae006bb01af1c60823bf1894f2","bugs":{"url":"https://github.com/agentine/ironclad/issues"},"homepage":"https://github.com/agentine/ironclad#readme","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-S+Zcf7rgkxw6ZmCoXIiiv4RZLdwbxASgxkNqqOXn+VDurORQD7KjjgwY2BAyFny31KN9zRFCi85SGaaQ+aCGRw==","shasum":"d1c21adce1359df18ea903e8666f789d35335ebf","tarball":"https://registry.npmjs.org/@agentine/ironclad/-/ironclad-0.1.0.tgz","fileCount":131,"unpackedSize":165434,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentine%2fironclad@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGZZEhGtY/S2CIOk7FhNmKJa6Q6ot7d6DEV/sKiweVldAiAAue6grWGoHOuUE1Dgwq1bVyr2zeXDQcC16ho1FCwDYA=="}]},"_npmUser":{"name":"mtingers","email":"matthingersoll@gmail.com"},"directories":{},"maintainers":[{"name":"mtingers","email":"matthingersoll@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ironclad_0.1.0_1773543363958_0.5031558601997721"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-15T02:56:03.801Z","0.1.0":"2026-03-15T02:56:04.117Z","modified":"2026-03-15T02:56:04.611Z"},"maintainers":[{"name":"mtingers","email":"matthingersoll@gmail.com"}],"description":"Modern isomorphic TypeScript crypto library replacing crypto-js. Wraps Web Crypto API and Node.js crypto.","homepage":"https://github.com/agentine/ironclad#readme","repository":{"type":"git","url":"git+https://github.com/agentine/ironclad.git"},"bugs":{"url":"https://github.com/agentine/ironclad/issues"},"license":"MIT","readme":"# ironclad\n\n[![npm](https://img.shields.io/npm/v/@agentine/ironclad)](https://www.npmjs.com/package/@agentine/ironclad)\n[![CI](https://github.com/agentine/ironclad/actions/workflows/publish.yml/badge.svg)](https://github.com/agentine/ironclad/actions/workflows/publish.yml)\n[![Node](https://img.shields.io/node/v/@agentine/ironclad)](https://www.npmjs.com/package/@agentine/ironclad)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n\nModern isomorphic TypeScript crypto library replacing crypto-js. Wraps Web Crypto API and Node.js crypto for security and performance — zero custom crypto implementations.\n\n## Why ironclad?\n\n[crypto-js](https://github.com/brix/crypto-js) was the de facto browser crypto library, but it implements cryptographic algorithms in pure JavaScript — a significant security risk compared to platform-native implementations. It is also effectively unmaintained, with no releases since 2023.\n\nironclad is a clean replacement:\n\n| | crypto-js | ironclad |\n|---|---|---|\n| Implementation | Pure JavaScript | **Platform crypto (Web Crypto API)** |\n| Async API | No (sync) | **Yes (Promise-based)** |\n| TypeScript | No (types via `@types`) | **First-class TypeScript** |\n| Isomorphic | Partial | **Node.js, Browser, Deno, Bun, Workers** |\n| Security | Pure-JS (not constant-time) | **FIPS-validated, constant-time, hardware-accelerated** |\n| Maintained | No (abandoned 2023) | **Yes** |\n\n## Install\n\n```bash\nnpm install @agentine/ironclad\n```\n\n## Quick Start\n\n```typescript\nimport { sha256, aes, password } from '@agentine/ironclad';\n\n// Hashing\nconst hash = await sha256('hello world');\n\n// AES encryption (defaults to AES-256-GCM)\nconst encrypted = await aes.encrypt('secret data', 'my-password');\nconst decrypted = await aes.decrypt(encrypted, 'my-password');\n\n// Password hashing (PBKDF2, 600K iterations)\nconst hashed = await password.hash('user-password');\nconst valid = await password.verify('user-password', hashed);\n```\n\n## API Reference\n\n### Hashing\n\n```typescript\nsha256(input: string | Uint8Array): Promise<string>  // hex output\nsha384(input: string | Uint8Array): Promise<string>\nsha512(input: string | Uint8Array): Promise<string>\nsha1(input: string | Uint8Array): Promise<string>    // legacy — prefer SHA-256+\nmd5(input: string | Uint8Array): Promise<string>     // deprecated — see below\ndigest(algorithm: HashAlgorithm, input): Promise<string>\n```\n\n> **MD5 deprecation:** `md5()` is provided only for interoperability with legacy systems. MD5 is cryptographically broken — it is vulnerable to collision attacks and must not be used for security purposes (passwords, signatures, integrity checks). Calling `md5()` emits a one-time `console.warn` at runtime. Use `sha256()` or stronger for all new code.\n\n### HMAC\n\n```typescript\nhmac(algorithm: HmacAlgorithm, key, data): Promise<string>\nhmacSha256(key, data): Promise<string>\nhmacSha384(key, data): Promise<string>\nhmacSha512(key, data): Promise<string>\nhmacVerify(algorithm, key, data, expectedHex): Promise<boolean>\n```\n\n### AES Encryption\n\n```typescript\naes.encrypt(data, key, options?): Promise<EncryptedData>\naes.decrypt(encrypted, key, options?): Promise<Uint8Array>\naes.serialize(data: EncryptedData): string\naes.deserialize(str: string): EncryptedData\n```\n\nDefault: AES-256-GCM with random IV. String keys are automatically derived via PBKDF2 (600K iterations).\n\nOptions: `{ mode?: 'GCM' | 'CBC' | 'CTR', keySize?: 128 | 192 | 256 }`\n\n**Mode security notes:**\n\n| Mode | Authentication | Recommendation |\n|------|---------------|----------------|\n| **GCM** (default) | ✅ Authenticated (AEAD) | Use for all new code |\n| **CBC** | ❌ None | Legacy interop only — see warning below |\n| **CTR** | ❌ None | Legacy interop only — see warning below |\n\n> **AES-CBC warning:** CBC mode provides confidentiality but no integrity protection. Ciphertext can be tampered with without detection. It is vulnerable to padding oracle attacks if error messages leak decryption state. Only use CBC for legacy system compatibility; prefer GCM for all new code. Ironclad emits a `console.warn` the first time CBC is used.\n\n> **AES-CTR warning:** CTR mode is a stream cipher that provides no authentication. Without a MAC, an attacker can flip bits in the ciphertext to predictably modify the plaintext (bit-flip attack). Only use CTR for legacy system compatibility; prefer GCM for all new code. Ironclad emits a `console.warn` the first time CTR is used.\n\nSerialization example:\n\n```typescript\n// Serialize to a portable string for storage/transport\nconst encrypted = await aes.encrypt('secret', 'password');\nconst str = aes.serialize(encrypted);   // \"GCM:base64iv:base64ct:base64tag\"\nconst back = aes.deserialize(str);\nconst decrypted = await aes.decrypt(back, 'password');\n```\n\n### Key Derivation\n\n```typescript\npbkdf2(password, salt, iterations, keyLength, hash?): Promise<Uint8Array>\npbkdf2Default(password, salt): Promise<Uint8Array>  // SHA-256, 600K iter, 32 bytes\nhkdf(ikm, length, info?, salt?, hash?): Promise<Uint8Array>\n```\n\n### Password Hashing\n\n```typescript\npassword.hash(password: string): Promise<string>\npassword.verify(password: string, hash: string): Promise<boolean>\n```\n\nFormat: `ironclad:v1:iterations:base64(salt):base64(hash)`\n\n### Encoding\n\n```typescript\ntoHex(bytes) / fromHex(hex) / isHex(str)\ntoBase64(bytes) / fromBase64(b64)\ntoBase64Url(bytes) / fromBase64Url(b64url)\ntoUtf8Bytes(str) / fromUtf8Bytes(bytes)\nbytesToWords(bytes) / wordsToBytes(words, sigBytes)\n```\n\n### Random\n\n```typescript\nrandomBytes(length: number): Uint8Array\nrandomHex(length: number): string\nrandomBase64(length: number): string\n```\n\n### Platform Detection\n\n```typescript\nisNode(): boolean             // true in Node.js\nisBrowser(): boolean          // true in browser environments\nisDeno(): boolean             // true in Deno\nisBun(): boolean              // true in Bun\nisCloudflareWorker(): boolean // true in Cloudflare Workers\n```\n\n### WordArray (crypto-js compatibility)\n\n```typescript\nbytesToWords(bytes: Uint8Array): WordArray\nwordsToBytes(words: number[], sigBytes: number): Uint8Array\nwordArrayToString(wa: WordArray): string  // hex string\n```\n\nThese helpers allow interop with crypto-js `WordArray` objects during migration.\n\n## Migrating from crypto-js\n\n```typescript\n// Before (crypto-js)\nimport CryptoJS from 'crypto-js';\nconst hash = CryptoJS.SHA256('message').toString();\nconst hmac = CryptoJS.HmacSHA256('message', 'key').toString();\nconst encrypted = CryptoJS.AES.encrypt('data', 'secret').toString();\n\n// After (ironclad)\nimport { sha256, hmacSha256, aes } from '@agentine/ironclad';\nconst hash = await sha256('message');\nconst hmac = await hmacSha256('key', 'message');\nconst encrypted = await aes.encrypt('data', 'secret');\n```\n\nKey differences:\n- **Async API** — All operations return Promises (Web Crypto is async)\n- **Secure defaults** — AES-256-GCM, PBKDF2 with 600K iterations\n- **Platform crypto** — Delegates to Web Crypto / Node.js crypto, not pure JS\n- **HMAC argument order** — `hmacSha256(key, data)` not `HmacSHA256(data, key)`\n\n## Browser Compatibility\n\nironclad requires the [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) (`crypto.subtle` and `crypto.getRandomValues`), available in all modern environments:\n\n| Environment | Version | Notes |\n|-------------|---------|-------|\n| Chrome / Edge | 37+ | Full support |\n| Firefox | 34+ | Full support |\n| Safari | 10.1+ | Full support |\n| Node.js | 18+ | Uses `crypto.webcrypto` on 18, `globalThis.crypto` on 19+ |\n| Deno | All | Web Crypto built-in |\n| Bun | All | Web Crypto built-in |\n| Cloudflare Workers | All | Web Crypto built-in |\n| Internet Explorer | — | ❌ Not supported |\n\n**Node 18 note:** On Node.js 18, `globalThis.crypto` is not automatically set. ironclad falls back to `require('node:crypto').webcrypto` automatically — no configuration needed.\n\nIf `crypto.subtle` is unavailable in your environment, ironclad throws:\n```\nError: Web Crypto API (crypto.subtle) is not available in this environment\n```\n\nYou can check your environment at runtime with the platform detection helpers:\n\n```typescript\nimport { isNode, isBrowser, isDeno } from '@agentine/ironclad';\n\nif (!isBrowser() && !isNode()) {\n  console.warn('Unsupported environment');\n}\n```\n\n## Security\n\nironclad delegates all cryptographic operations to platform-native implementations:\n- **Node.js**: `crypto.subtle` (Web Crypto) and `crypto.createHash` (MD5 only)\n- **Browser**: Web Crypto API (`crypto.subtle`)\n- **Deno/Bun/Workers**: Web Crypto API\n\nThis is fundamentally more secure than crypto-js, which implements algorithms in pure JavaScript. Platform implementations are FIPS-validated, constant-time, and hardware-accelerated where available.\n\n## License\n\nMIT","readmeFilename":"README.md","_rev":"1-9c335138a344e94ddd83e98348a5de3c"}