Authenticated encryption without repeating the plumbing.
SecureKit keeps the application-facing API small while covering the pieces that tend to get rebuilt around Node.js crypto: purpose separation, versioned tokens, key rotation, exact-match indexes and diagnostics.
$npm i @bybence/securekit
securekit-demo.mjs
01
import { encrypt, decrypt } from "@bybence/securekit";
02
03
const email = "hello@example.com";
04
const token = encrypt(email, {
05
purpose: "user.email",
06
});
07
08
decrypt(token, { purpose: "user.email" });
Result
"hello@example.com"
cipher
AES-256-GCM
derivation
HKDF-SHA-256
token
sk4
runtime deps
0
What SecureKit removes from application code.
Node.js already provides the cryptographic primitive. SecureKit standardizes the repeated application code around it.
Direct Node.js crypto
01
import crypto from "node:crypto";
02
const key = Buffer.from(process.env.ENCRYPTION_KEY, "base64");
03
const iv = crypto.randomBytes(12);
04
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
05
const encrypted = Buffer.concat([
06
cipher.update(email, "utf8"),
07
cipher.final()
08
]);
09
const tag = cipher.getAuthTag();
With SecureKit
01
import { encrypt } from "@bybence/securekit";
02
const encrypted = encrypt(email);
What happens inside encrypt().
The public API stays short, while the token format and authenticated steps remain explicit and versioned.
Serialize
Preserve supported JavaScript value types.
Derive
HKDF-SHA-256 derives purpose-separated key material.
Encrypt
AES-256-GCM uses a fresh 96-bit IV.
Bind
Purpose, context and metadata are authenticated.
Frame
Emit a versioned sk4 token for future decoding.
sk4.eyJ2Ijo0LCJraWQiOiJkZWZhdWx0Iiw...
Production features stay opt-in.
Use the basic encrypt/decrypt path first. Add the extra surface only when the application has a concrete need for it.
Purpose separation
Keep user.email, billing.bank-account and other data classes cryptographically separated.
encrypt(email, { purpose: "user.email" })
Context binding
Bind ciphertext to application metadata so the token is only valid in the intended context.
context: { userId: 123 }
Blind indexes
Perform exact-match lookups without turning the ciphertext itself deterministic.
blindIndex(email, { normalize: "email" })
Key rotation
Identify old key IDs and re-encrypt values under a new current key.
needsReencrypt(token) rotate(token)
Sealed values
Attach an authenticated expiration time to application data.
seal(data, { expiresIn: "15m" })
Password hashing
Scrypt hashing and timing-safe password verification live in the same focused package.
await hashPassword(password)
Benchmark: SecureKit 0.9.0 · Apple M2 · Node.js 22.18.0 · 2026-09-25
Benchmark results against raw Node.js crypto.
Binary throughput is measured against a framed raw Node.js AES-256-GCM baseline on the same machine and runtime.
Encrypt bytes
4.49 GiB/s
223.06 µs P95
SecureKit
4,489 ops/s
measured
Raw Node.js
4,528 ops/s
baseline
Decrypt bytes
5.53 GiB/s
181.18 µs P95
SecureKit
5,535 ops/s
measured
Raw Node.js
5,571 ops/s
baseline
Blind index · email
630,160 ops/s
1.6 µs p95
Batch · 1,000 values
6.39 ms
158 batches/s · 6.84 KiB input
Reproducibility
Local reproducible benchmark, not an independent certification or security audit. Results vary by machine and runtime.
Source 65e51b9d049af6376ee0ae0baa21fe491ac709a0219e8277b9d2f9e1fa3fcdd0
Benchmark c8446e7829bbe322436c1c45ced5a7758d529a38ab5bef4cdbabadd49f5abba5
$ npx securekit doctor
✓ .env.local exists
✓ SECUREKIT_KEY exists
✓ Key is canonical 256-bit Base64
✓ .env.local is ignored by Git
✓ HKDF is available
✓ AES-256-GCM is available
✓ No problems found.
init
generate-key
doctor
inspect
guide
Setup checks from the same package.
Generate keys, inspect token metadata and validate the local environment without adding a separate diagnostics tool.
Start with the two-function path. Add the rest only when the application needs it.
The docs cover setup, purpose binding, blind indexes, lazy key rotation, sealed values and the CLI without making those features part of the minimum path.
Read the SecureKit docs