@nexussdk/crypto
v0.1.0
Published
AES-GCM 256-bit payload encryption for Nexus SDK telemetry — renders F12 network payloads unreadable using native Web Crypto API
Maintainers
Readme
@nexussdk/crypto
AES-GCM 256-bit payload encryption for the Nexus SDK ecosystem.
Renders telemetry payloads completely unreadable in browser DevTools (F12 Network tab).
Zero bundle cost — uses only the native Web Crypto API.
What It Does
Anyone who opens browser DevTools on your app can see exactly what your analytics SDK sends — user IDs, session data, error details, PII. @nexussdk/crypto encrypts payloads before transport so the Network tab shows only a base64 blob.
Before:
{ "userId": "usr_12345", "error": "TypeError", "breadcrumbs": [...] }After:
{ "alg": "AES-GCM-256", "iv": "dGhpcyBpcyBhIG5v", "ciphertext": "eyJhbGciOi...", "keyId": "key_v1" }Installation
npm install @nexussdk/crypto
# or
pnpm add @nexussdk/cryptoQuick Start
1. Generate a key (once, on your server)
# Using the SDK:
node -e "
const { generateAESKey } = require('@nexussdk/crypto');
generateAESKey().then(({ base64Key }) => console.log('NEXUS_AES_KEY=' + base64Key));
"
# Or with OpenSSL:
openssl rand -base64 32Store in your environment: NEXUS_AES_KEY=<44-char base64 string>
2. Expose the key via your backend
// Express / NestJS / Hono / any Node.js framework
app.get('/api/nexus/key', requireAuth, (req, res) => {
res.json({ key: process.env.NEXUS_AES_KEY, keyId: 'key_v1' });
});3. Encrypt on the client
import { importAESKey, encryptPayload, isCryptoSupported } from '@nexussdk/crypto';
// On SDK init
const { key: base64Key, keyId } = await fetch('/api/nexus/key').then(r => r.json());
const cryptoKey = await importAESKey(base64Key);
// Before every telemetry send
const envelope = await encryptPayload(errorEvent, cryptoKey, keyId);
await fetch('/v1/ingest', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(envelope),
});4. Decrypt on the server
import { importAESKey, decryptEnvelope } from '@nexussdk/crypto';
const serverKey = await importAESKey(process.env.NEXUS_AES_KEY!);
app.post('/v1/ingest', async (req, res) => {
const payload = await decryptEnvelope(req.body, serverKey);
// payload is now the original decrypted object
await processEvent(payload);
res.status(204).end();
});Framework Compatibility
Works in any environment with Web Crypto support:
| Environment | Supported |
|-------------|-----------|
| Chrome 37+ / Edge 79+ | ✅ |
| Firefox 34+ | ✅ |
| Safari 11+ | ✅ |
| Node.js 18+ | ✅ (via globalThis.crypto) |
| React / Next.js / Vue / Nuxt / Angular / Svelte | ✅ |
| Vanilla JS / Web Workers | ✅ |
Check support at runtime:
import { isCryptoSupported } from '@nexussdk/crypto';
if (!isCryptoSupported()) {
// Fall back to unencrypted transport for very old browsers
}Security Model
- Algorithm: AES-GCM 256-bit (NIST SP 800-38D)
- IV: 96-bit random, freshly generated per request via
crypto.getRandomValues - Key storage: Non-extractable
CryptoKey— raw key bytes cannot be read by JavaScript - Authentication: 128-bit GCM auth tag — any tampered ciphertext throws
OperationErroron decryption - Threat model: Protects against DevTools inspection by end users. This is symmetric encryption — your server holds the decryption key.
API Reference
// Import a base64 key as non-extractable CryptoKey
importAESKey(base64Key: string): Promise<CryptoKey>
// Encrypt any JSON-serializable payload
encryptPayload(payload: unknown, key: CryptoKey, keyId: string): Promise<EncryptedEnvelope>
// Decrypt an envelope (server-side / Node.js)
decryptEnvelope<T>(envelope: EncryptedEnvelope, key: CryptoKey): Promise<T>
// Generate a fresh 256-bit key (development / key rotation)
generateAESKey(): Promise<{ base64Key: string; cryptoKey: CryptoKey }>
// Check environment support
isCryptoSupported(): booleanEncryptedEnvelope Shape
interface EncryptedEnvelope {
alg: 'AES-GCM-256'; // Always this value
iv: string; // Base64 96-bit random IV (unique per request)
ciphertext: string; // Base64 AES-GCM ciphertext
keyId: string; // Key version identifier for rotation
}License
MIT © Hồ Huỳnh Dũng
