@playstack/crypto
Workers-compatible encryption, digests, signatures, verification, and secure token generation.
Free. MIT licensed. Check preview availability before installing. See package access.
@playstack/crypto provides Workers-compatible authenticated encryption, digests, signatures, verification, and secure token generation while keeping key ownership explicit.
Install
npm install @playstack/core @playstack/cryptoCompose it once
Resolve secrets at the deployment boundary and inject a clock and Web Crypto implementation. Capability packages receive the resulting suite or the smaller function they require.
import { systemClock } from '@playstack/core'
import { createCrypto } from '@playstack/crypto'
export const applicationCrypto = createCrypto({
crypto: globalThis.crypto,
clock: systemClock(),
encryptionKeys: {
keys: [
{ id: 'enc-2026-08', key: process.env.ENCRYPTION_KEY_CURRENT!, active: true },
{ id: 'enc-2026-05', key: process.env.ENCRYPTION_KEY_PREVIOUS! },
],
},
signingKeys: {
keys: [
{ id: 'sig-2026-08', key: process.env.SIGNING_KEY_CURRENT!, active: true },
{ id: 'sig-2026-05', key: process.env.SIGNING_KEY_PREVIOUS! },
],
},
maximumTokenTtl: '15m',
replayStore: singleUseTokenStore,
})
const token = await applicationCrypto.sign({ userId }, { purpose: 'email-verification', ttl: '10m' })The key strings are canonical padded base64. Encryption keys must decode to exactly 32 bytes; signing keys must decode to at least 32 bytes. Keep old keys available for verification and decryption until their data has expired or been re-encrypted, but mark exactly one key in each keyset active.
Node-only and browser declarations
The public digest, HMAC, and encryption capabilities use minimal DOM-independent interfaces rather than requiring ambient SubtleCrypto. Node-only consumers can use lib: ["es2022"], Node types, and skipLibCheck: false; browser consumers can retain their DOM libraries. Inject the runtime's actual Web Crypto implementation. Enabling browser globals or disabling declaration checking is not required for server projects, and this type boundary does not supply a runtime crypto polyfill.
Configuration reference
CryptoSuiteOptions property | Type | Required | Purpose |
|---|---|---|---|
crypto | WebCrypto | Yes | Supplies subtle and getRandomValues; usually globalThis.crypto. |
clock | Clock | Yes | Controls issue, expiry, and verification time. |
encryptionKeys | KeysetConfig | Yes | AES-GCM keys with one active key. |
signingKeys | KeysetConfig | Yes | HMAC-SHA-256 keys with one active key. |
replayStore | ReplayStore | No | Atomically consumes token IDs for single-use verification. |
maximumTokenTtl | string | No | Maximum signed-token lifetime; defaults to 5m. Supports ms, s, m, or h. |
Each KeyConfig has an id, a base64 key, and optional active. IDs are embedded in output envelopes so key rotation does not require trial decryption.
Operations
| Method | Use |
|---|---|
encrypt / decrypt / decryptText | Authenticated AES-GCM envelopes for strings or bytes. |
needsReencryption / reencrypt | Detect and migrate values written by an older encryption key. |
randomToken | Generate an opaque base64url token; defaults to 32 bytes and requires at least 16. |
digestSha256 | Produce a stable digest for storing opaque-token lookups. |
verifyHmacSha256 | Compare an expected HMAC without timing-sensitive string comparison. |
sign / verify | Create and verify purpose-bound, expiring structured tokens. |
Drain legacy encrypted envelopes
createSecretMigrationCodec() composes the current suite with application-owned legacy decoders. A decoder must positively identify its format; ambiguous and unknown values fail closed, and a malformed current Playstack envelope never falls back to a legacy reader.
Successful legacy decryption returns a current replacement envelope but performs no write. The application persists that replacement with compare-and-swap after the surrounding domain operation succeeds, allowing retired formats and keys to be drained without making them permanent Playstack behavior.
Replay-store bridge
Single-use verification calls one application-owned bridge:
interface ReplayStore {
consume(jti: string, expiresAtSeconds: number): Promise<boolean>
}consume must be atomic across all instances. Return true only for the first consumption; retain the marker until expiresAtSeconds. Redis SET NX or a database uniqueness constraint are suitable implementations. If singleUse: true is requested without this bridge, verification fails closed.
Testing
@playstack/crypto/testing exports DeterministicCrypto and MemoryReplayStore. Use them with FakeClock from @playstack/core/testing to test rotation, expiry, and replay behavior without nondeterminism.
Boundary
The package does not load environment variables, manage or derive keys, hash passwords, or provide persistent replay storage. The application owns its secret manager, rotation procedure, and bridge lifecycle.
API entry points and requirements
Reference snapshot: @playstack/crypto@0.1.0-beta.1. Import only the entry point your runtime needs. Paths below are relative to the installed package; use Go to Definition in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.
| Public entry point | Declaration file |
|---|---|
@playstack/crypto | ./dist/index.d.ts |
@playstack/crypto/primitives | ./dist/primitives.d.ts |
@playstack/crypto/errors | ./dist/errors.d.ts |
@playstack/crypto/testing | ./dist/testing.d.ts |
@playstack/crypto/playstack.integration.json | No TypeScript declaration (asset or metadata export). |
@playstack/crypto/package.json | No TypeScript declaration (asset or metadata export). |
Node.js engine requirement: >=20. This is not a claim that every entry point works in browsers or Workers.
Peer dependencies
This package declares no peer dependencies. Its ordinary dependencies are resolved by the package manager.
For a complete first program, start with Getting started. For API lookup and partial-example conventions, see Reading the reference. Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.