@hyperfrontend/ cryptography§
Production-grade cryptographic primitives with isomorphic APIs for browser and Node.js environments.
What is @hyperfrontend/cryptography?
@hyperfrontend/cryptography provides a comprehensive suite of cryptographic utilities designed for secure data handling in full-stack JavaScript applications. The library implements industry-standard encryption (AES-GCM), key derivation (PBKDF2), and hashing (SHA-256) with identical APIs across browser and Node.js environments, eliminating platform-specific code branching.
The library features three modular entry points: platform-specific implementations (/browser, /node) for optimized runtime performance, and a shared entry point (/common) for platform-agnostic utilities. Core capabilities include secure password-based encryption/decryption, cryptographic vault storage with single-use modes, time-based password generation for rotating credentials, and cryptographically-secure random value generation.
Key Features
Isomorphic API Design
Write once, run everywhere with identical function signatures for browser Web Crypto API and Node.js crypto module
Industry-standard authenticated encryption with password-derived keys using PBKDF2 (100,000 iterations)
Ephemeral P-256 ECDH, HKDF key expansion with usage-restricted keys, and raw AEAD sealing for many messages under one agreed key
Password-protected in-memory storage with optional single-use mode for sensitive data
Generate rotating credentials synchronized to UTC time windows for short-lived authentication
SHA-256 hash generation with hexadecimal output and validation utilities
Zero External Dependencies
Self-contained implementation using only platform crypto APIs
Functional Architecture
Pure functions with dependency injection for testability and composability
Secondary Entry Points
Tree-shakeable imports optimize bundle size (
/browser,/node,/common)
Why Use @hyperfrontend/cryptography?
Eliminate Platform Branching in Isomorphic Applications
Full-stack applications typically require separate cryptography implementations for browser and server environments, leading to code duplication and testing complexity. This library provides identical APIs powered by platform-optimized implementations, allowing shared business logic for encryption workflows across your entire stack. Import from /browser or /node based on your runtime - the function signatures remain identical.
Production-Hardened Encryption Without Configuration Complexity
Implementing secure encryption requires careful selection of algorithms, key derivation parameters, and IV/salt generation strategies. This library encapsulates cryptographic best practices (PBKDF2 with 100,000 iterations, unique salt/IV per operation, AES-GCM authenticated encryption) in simple encrypt(message, password) and decrypt(encrypted, password) functions. No configuration decisions, no security footguns - just production-grade encryption out of the box.
Secure Credential Rotation with Time-Based Passwords
APIs requiring short-lived credentials (temporary links, time-boxed access tokens) need synchronized password generation across systems. The getTimeBasedPasswords() function generates cryptographically-secure passwords based on UTC time windows, enabling coordinated credential rotation without database round-trips. Generate passwords for current/previous/next windows to handle clock drift gracefully.
In-Memory Vaults for Sensitive Data Handling
Processing sensitive data (credit cards, API keys, personal information) in memory creates exposure risks during debugging and logging. The createVault() API provides encrypted storage with password protection - data is encrypted at rest in memory and requires the vault password for retrieval. Single-use mode automatically closes the vault after first read, preventing accidental data leakage in long-lived processes.
Dependency-Free Security for Minimal Attack Surface
Third-party cryptography libraries introduce supply chain risks and dependency bloat. This package uses only platform-native crypto APIs (Web Crypto API, Node.js crypto module) plus lightweight internal utilities, eliminating external dependencies. Smaller dependency trees mean fewer audit requirements and reduced attack surface for security-critical applications.
Installation
npm install @hyperfrontend/cryptography
Requirements
- Node.js: 18.0.0 or higher (19+ recommended for stable Web Crypto API support)
- npm: 8.0.0 or higher
- Browser: Modern browsers with Web Crypto API support
Note: The
/nodeentry point useswebcrypto.subtlewhich was experimental in Node.js 18.x. For production use with the Node.js entry point, Node.js 19+ is recommended for stable crypto APIs. The/browserentry point readsglobalThis.cryptoat first use rather than at import, so it loads on Node.js 18 and only needs that global (Node.js 19+, or 18 with--experimental-global-webcrypto) when a primitive runs.
Quick Start
Browser Environment
import { encrypt, decrypt, createVault, createHash } from '@hyperfrontend/cryptography/browser'
// Encrypt/decrypt data with password
const message = 'Sensitive data'
const password = 'secure-password'
const encrypted = await encrypt(message, password)
const decrypted = await decrypt(encrypted, password)
// Create encrypted in-memory storage
const vault = createVault()
await vault.write('api-key', 'sk-1234567890')
const password = vault.getPassword() // Share password securely
const value = await vault.read('api-key', password) // Returns 'sk-1234567890'
// Generate SHA-256 hash
const hash = await createHash('data-to-hash') // Returns hex string
Node.js Environment
import { encrypt, decrypt, createVault, getTimeBasedPasswords } from '@hyperfrontend/cryptography/node'
// Same encryption API as browser
const encrypted = await encrypt('Sensitive data', 'secure-password')
const decrypted = await decrypt(encrypted, 'secure-password')
// Time-based rotating passwords; the window is counted in minutes, so this is a 5-minute window
const generators = getTimeBasedPasswords(new Date(), 5)
const currentPassword = await generators.current()
const previousPassword = await generators.previous() // Handle clock drift
Platform-Agnostic Utilities
import { isSHA256Hash } from '@hyperfrontend/cryptography/common'
// Validate hash format (works in browser and Node.js)
isSHA256Hash('abc123') // false - too short
isSHA256Hash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') // true
API Overview
Two layers, not nineteen functions, and choosing between them is most of the work. The password layer is one call: encrypt takes a message and a password and returns a Uint8Array already carrying a fresh salt and initialization vector alongside the ciphertext and its tag, and decrypt takes that buffer and the same password back to the original string. Key derivation and nonce choice are never yours to get right, and because the salt and IV are new every call, encrypting one secret twice yields two unrelated buffers. createVault is that same layer with a lifetime attached, holding labelled values encrypted in memory behind a password it generates, optionally closing after the first read.
The session layer inverts the trade: it hands you the key and the nonce rather than managing them, which is what a stream of messages needs and a lone secret does not. createKeyAgreement gives each side an ephemeral P-256 keypair whose private half is non-extractable, and its deriveSecret turns the peer's public point into the 32 bytes both sides share; stretchPassword is the alternative start when the shared thing is a password rather than a handshake. expandKey splits that secret into per-purpose AES-GCM-256 keys, each restricted to encrypting or to decrypting but not both.
seal and open then carry individual messages under those keys, taking a 12-byte nonce and an additional-data buffer that is authenticated but not encrypted, so a header can be bound to a payload without being hidden. That control costs you one rule: a nonce must never repeat under a given key, which a per-message counter scoped to a per-session key satisfies by construction. This is the layer @hyperfrontend/network-protocol builds its envelope on.
Beside both sit the small utilities: SHA-256 hashing with its format guard, raw random bytes, and getTimeBasedPasswords, which derives the same rotating credential on two machines from the clock alone and returns current, previous and next so a receiver tolerates a peer one window out of step; that window is counted in minutes, not milliseconds. As for which entry point to import, /browser is backed by the Web Crypto API and /node by the Node.js crypto module, exporting the same names with the same signatures, so the choice is only about the runtime; /common is the narrow shared slice, currently just isSHA256Hash.
Every signature, option type and thrown error is in the API reference.
Compatibility
Runs on
- Node.js>=18.0.0supported
- Browserssupported
- Web Workerssupported
/browser reads globalThis.crypto at first use; on Node.js that needs 19+ or 18 with --experimental-global-webcrypto.
Ships as
- ESMTree-shakeable
- CJSNode and older bundlers
- IIFEHyperfrontendCryptography
- UMDHyperfrontendCryptography
CDN Usage
<!-- unpkg -->
<script src="https://unpkg.com/@hyperfrontend/cryptography"></script>
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@hyperfrontend/cryptography"></script>
<script>
const { createHash, encrypt, decrypt } = HyperfrontendCryptography
</script>
Global variable: HyperfrontendCryptography
Architecture Highlights§
encrypt generates a unique salt and initialization vector per operation, so a secret at rest never reuses a key, and the same plaintext encrypted twice produces two different ciphertexts. The session primitives (createKeyAgreement, expandKey, seal, open) hand the key and nonce lifecycle to the caller instead, which is what a message stream needs.
API Reference§
Module Structure
3 modules · 49 total exports
Browser cryptography utilities for encryption, hashing, and secure key management using Web Crypto APIs.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§data | string | The string data to hash |
§algorithm | HashAlgorithm | The hash algorithm to use (defaults to SHA-256)(default: 'SHA-256') |
Returns
Promise< string>Example
Creating a hash
const hash = await createHash('secret-message')
// => '64-character hexadecimal string'Parameters
| Name | Type | Description |
|---|---|---|
§byteLength | number | The number of random bytes to generate |
Returns
Uint8ArrayExample
Generating random bytes
const randomBytes = getRandomValues(16)
// => Uint8Array(16) with cryptographically secure random valuesParameters
| Name | Type | Description |
|---|---|---|
§hash | unknown | The value to validate as a SHA-256 hash |
Returns
booleanExample
Validating SHA-256 hashes
isSHA256Hash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
// => true
isSHA256Hash('invalid')
// => false◈ Interfaces
Properties
The private key never leaves the object: it is a non-extractable
CryptoKey held in a closure and is garbage-collected with the agreement. Create one agreement per session.Properties
readonly publicKey:Uint8ArrayThis side's public key as the 65-byte uncompressed P-256 point, to be sent to the peerProperties
stretchPassword.Properties
Properties
◆ Types
type HashAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"● Variables
generateKey and stretchPassword. 100,000 SHA-256 iterations cost roughly 17 ms on commodity hardware, which is the intended price for one password guess and the reason password stretching belongs at session or storage boundaries rather than on a per-message path.
SubtleCrypto interface, resolved from globalThis.crypto on every access, so importing this entry never needs the global to exist and a runtime without it fails at the first primitive call with a descriptive error.Environment-agnostic cryptography utilities shared between browser and Node.js.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§hash | unknown | The value to validate as a SHA-256 hash |
Returns
booleanExample
Validating SHA-256 hashes
isSHA256Hash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
// => true
isSHA256Hash('invalid')
// => falseNode.js cryptography utilities for encryption, hashing, and secure key management using native crypto module.
ƒ Functions
Parameters
| Name | Type | Description |
|---|---|---|
§data | string | The string data to hash |
§algorithm | HashAlgorithm | The hash algorithm to use (defaults to SHA-256)(default: 'SHA-256') |
Returns
Promise< string>Example
Creating a hash
const hash = await createHash('secret-message')
// => '64-character hexadecimal string'Parameters
| Name | Type | Description |
|---|---|---|
§byteLength | number | The number of random bytes to generate |
Returns
Uint8ArrayExample
Generating random bytes
const randomBytes = getRandomValues(16)
// => Uint8Array(16) with cryptographically secure random valuesParameters
| Name | Type | Description |
|---|---|---|
§hash | unknown | The value to validate as a SHA-256 hash |
Returns
booleanExample
Validating SHA-256 hashes
isSHA256Hash('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
// => true
isSHA256Hash('invalid')
// => false◈ Interfaces
Properties
The private key never leaves the object: it is a non-extractable
CryptoKey held in a closure and is garbage-collected with the agreement. Create one agreement per session.Properties
readonly publicKey:Uint8ArrayThis side's public key as the 65-byte uncompressed P-256 point, to be sent to the peerProperties
stretchPassword.Properties
Properties
◆ Types
type HashAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"● Variables
generateKey and stretchPassword. 100,000 SHA-256 iterations cost roughly 17 ms on commodity hardware, which is the intended price for one password guess and the reason password stretching belongs at session or storage boundaries rather than on a per-message path.
Related reading§
- How-to
How to encrypt and decrypt a string with a password
I need to store an API key, a token, or a note encrypted under a password, and I do not want to hand-roll key derivation, salts, and initialisation vectors around the Web Crypto API.
- How-to
How to fingerprint content with a hash
I need a stable id for a piece of content so I can skip work I already did or ask the other side whether it already has this exact thing, and the browser and the server keep computing that id two different ways.
- Package
@hyperfrontend/network-protocol
Used by @hyperfrontend/network-protocol for secure message encryption
- Package
@hyperfrontend/nexus
Looking for cross-window messaging with built-in encryption? See @hyperfrontend/nexus
- Getting started
Getting Started
Set HyperFrontend up and embed a first feature.
- Architecture
Architecture Guide
How the packages fit together.

