docs: update portal for WRNexusJS 0.8.0
This commit is contained in:
@@ -10,57 +10,68 @@ page wrnexusencryption {
|
||||
<header class="topbar">
|
||||
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
|
||||
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
|
||||
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.7.0</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
|
||||
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.8.0</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
|
||||
</header>
|
||||
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
|
||||
<main class="portal-main docs-layout">
|
||||
<article id="main" class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/encryption</span></nav><section class="doc-intro"><span class="eyebrow">Security · Package reference</span><h1>@wrnexus/encryption</h1><p>Hashing, HMAC, authenticated encryption, and key derivation.</p><div class="doc-meta"><span>v0.7.0</span><span>Private registry</span><span>Security</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/encryption@0.7.0</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><blockquote>Dependency-free crypto helpers for WRNexusJS: authenticated symmetric encryption (AES-256-GCM), hashing, and HMAC signing.</blockquote>
|
||||
<p>Part of the <strong>WRNexusJS</strong> framework — an SSR-first, Bun-native full-stack web framework.</p>
|
||||
<h3 id="overview">Overview</h3>
|
||||
<p>This package provides small, focused cryptographic primitives for server-side use: encrypting secrets/tokens/database fields at rest with AES-256-GCM, deriving keys from passwords via PBKDF2, computing SHA-256 digests, and signing/verifying payloads with HMAC-SHA256. It is built entirely on the standard <strong>Web Crypto API</strong> (<code>crypto.subtle</code>) plus <code>btoa</code>/<code>atob</code> and <code>TextEncoder</code>/<code>TextDecoder</code> — no third-party dependencies. Reach for it whenever you need to protect sensitive values or verify webhook signatures. All functions are <code>async</code> (Web Crypto is promise-based).</p>
|
||||
<pre data-language="bash"><code>bun add @wrnexus/encryption</code></pre>
|
||||
<blockquote>Private package — the machine must be authenticated to the <code>wrnexus</code> npm org</blockquote>
|
||||
<blockquote>(a read token in <code>~/.npmrc</code>). Requires <strong>Bun</strong> (Node is not supported).</blockquote>
|
||||
<h3 id="api">API</h3>
|
||||
<p>All keys are exchanged as <strong>base64 strings</strong> and all digests/signatures as <strong>hex strings</strong>.</p>
|
||||
<div class="table-wrap"><table>
|
||||
<thead><tr><th>Export</th><th>Signature</th><th>Description</th></tr></thead>
|
||||
<tbody><tr><td><code>generateKey</code></td><td><code>() => Promise<string></code></td><td>Generate a random 256-bit AES key, base64-encoded. Store it as a secret.</td></tr><tr><td><code>deriveKey</code></td><td><code>(password: string, salt: string) => Promise<string></code></td><td>Derive a base64 AES-256 key from a password + salt using PBKDF2 (100,000 iterations, SHA-256).</td></tr><tr><td><code>encrypt</code></td><td><code>(plaintext: string, key: string) => Promise<string></code></td><td>AES-256-GCM encrypt a string. Returns base64 of <code>iv(12 bytes) ‖ ciphertext+tag</code>. A fresh random IV is used each call.</td></tr><tr><td><code>decrypt</code></td><td><code>(payload: string, key: string) => Promise<string></code></td><td>Decrypt a value produced by <code>encrypt</code>. Throws if the key is wrong or the data was tampered with.</td></tr><tr><td><code>sha256</code></td><td><code>(data: string) => Promise<string></code></td><td>SHA-256 hex digest of a string (e.g. content hashing, dedup keys).</td></tr><tr><td><code>hmacSign</code></td><td><code>(data: string, secret: string) => Promise<string></code></td><td>HMAC-SHA256 hex signature of <code>data</code> with <code>secret</code> (e.g. signing webhooks).</td></tr><tr><td><code>hmacVerify</code></td><td><code>(data: string, secret: string, signature: string) => Promise<boolean></code></td><td>Constant-time verify of an HMAC-SHA256 hex signature.</td></tr></tbody></table></div>
|
||||
<p>Notes:</p>
|
||||
<article id="main" class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/encryption</span></nav><section class="doc-intro"><span class="eyebrow">Security · Package reference</span><h1>@wrnexus/encryption</h1><p>Hashing, HMAC, authenticated encryption, and key derivation.</p><div class="doc-meta"><span>v0.8.0</span><span>Private registry</span><span>Security</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the release-aligned package:</p><pre><code>bun add @wrnexus/encryption@0.8.0</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><p>Authenticated encryption, hashing, HMAC, key rotation, and optional encrypted HTTP exchanges for WRNexusJS.</p>
|
||||
<h3 id="core-helpers">Core helpers</h3>
|
||||
<ul>
|
||||
<li><code>generateKey</code> produces a 32-byte (256-bit) key via <code>crypto.getRandomValues</code>.</li>
|
||||
<li><code>encrypt</code>/<code>decrypt</code> require a base64-encoded 256-bit key; anything else throws <code>"Encryption key must be a base64 256-bit key"</code>.</li>
|
||||
<li><code>decrypt</code> throws <code>"Invalid ciphertext"</code> if the payload is shorter than the 12-byte IV, and the underlying Web Crypto call throws on any authentication (tag) mismatch.</li>
|
||||
<li><code>hmacVerify</code> compares in constant time (length check plus XOR accumulation) to avoid timing leaks.</li>
|
||||
<li><code>generateKey()</code> — random 256-bit AES key encoded as base64.</li>
|
||||
<li><code>deriveKey(password, salt)</code> — PBKDF2-derived AES key.</li>
|
||||
<li><code>encrypt(plaintext, key)</code> / <code>decrypt(payload, key)</code> — AES-256-GCM.</li>
|
||||
<li><code>sha256(data)</code> — SHA-256 digest.</li>
|
||||
<li><code>hmacSign(data, secret)</code> / <code>hmacVerify(...)</code> — HMAC-SHA256.</li>
|
||||
<li><code>createKeyring(keys)</code> — active/previous key management.</li>
|
||||
<li><code>seal()</code> / <code>open()</code> — versioned ciphertext with key ID.</li>
|
||||
</ul>
|
||||
<h3 id="usage">Usage</h3>
|
||||
<p>Symmetric encryption of a secret at rest:</p>
|
||||
<pre data-language="ts"><code>import { generateKey, encrypt, decrypt } from "@wrnexus/encryption";
|
||||
<h3 id="encrypted-http-envelope">Encrypted HTTP envelope</h3>
|
||||
<pre data-language="ts"><code>import {
|
||||
createEncryptedRequest,
|
||||
createKeyring,
|
||||
createMemoryReplayStore,
|
||||
decryptEncryptedResponse,
|
||||
encryptedExchange,
|
||||
} from "@wrnexus/encryption";
|
||||
|
||||
const key = await generateKey(); // store this safely (env/secret manager)
|
||||
const keyring = createKeyring([{ id: "2026-08", secret: process.env.API_BODY_KEY!, active: true }]);
|
||||
|
||||
const box = await encrypt("card #1234", key); // opaque base64 string, safe to persist
|
||||
const plain = await decrypt(box, key); // "card #1234"</code></pre>
|
||||
<p>Deriving a key from a user password instead of a random key:</p>
|
||||
<pre data-language="ts"><code>import { deriveKey, encrypt } from "@wrnexus/encryption";
|
||||
const replayStore = createMemoryReplayStore();
|
||||
|
||||
const key = await deriveKey("correct horse battery staple", "per-user-salt");
|
||||
const box = await encrypt("secret note", key);</code></pre>
|
||||
<p>Hashing and webhook signature verification:</p>
|
||||
<pre data-language="ts"><code>import { sha256, hmacSign, hmacVerify } from "@wrnexus/encryption";
|
||||
// Server middleware.
|
||||
app.use(
|
||||
encryptedExchange({
|
||||
keyring,
|
||||
replayStore,
|
||||
maxAgeMs: 60_000,
|
||||
maxBodyBytes: 1_048_576,
|
||||
}),
|
||||
);
|
||||
|
||||
const digest = await sha256("some content"); // 64-char hex string
|
||||
|
||||
const signature = await hmacSign(rawBody, webhookSecret);
|
||||
const ok = await hmacVerify(rawBody, webhookSecret, incomingSignatureHeader);
|
||||
if (!ok) throw new Error("Invalid webhook signature");</code></pre>
|
||||
<h3 id="requirements-notes">Requirements / Notes</h3>
|
||||
// Controlled service/native client.
|
||||
const request = await createEncryptedRequest(
|
||||
"https://api.example.com/private/report",
|
||||
{ reportId: "report-1" },
|
||||
{ method: "POST", keyring },
|
||||
);
|
||||
const response = await fetch(request);
|
||||
const result = await decryptEncryptedResponse(response, request, { keyring });</code></pre>
|
||||
<p>The envelope binds authenticated ciphertext to:</p>
|
||||
<ul>
|
||||
<li><strong>Bun-only.</strong> Relies on the Web Crypto API (<code>crypto.subtle</code>, <code>crypto.getRandomValues</code>) and the global <code>btoa</code>/<code>atob</code>, <code>TextEncoder</code>/<code>TextDecoder</code> — all available in Bun's runtime.</li>
|
||||
<li><strong>No dependencies.</strong> The package has an empty dependency set; nothing is bundled beyond standard runtime APIs.</li>
|
||||
<li>Algorithms: AES-256-GCM (encryption), PBKDF2 with 100k SHA-256 iterations (key derivation), SHA-256 (digest), HMAC-SHA256 (signing).</li>
|
||||
<li>Keep generated/derived keys and HMAC secrets out of source control; treat them as first-class secrets.</li>
|
||||
</ul></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>interface EncryptionKey {
|
||||
<li>HTTP method</li>
|
||||
<li>URL path and query</li>
|
||||
<li>request ID</li>
|
||||
<li>timestamp and expiry window</li>
|
||||
<li>encryption key ID</li>
|
||||
<li>optional replay-store consumption</li>
|
||||
</ul>
|
||||
<p><code>encryptedBody()</code> decrypts request bodies only. <code>encryptedExchange()</code> also encrypts successful downstream responses while allowing application exceptions to propagate normally. <code>encryptedFetch()</code> provides a convenient controlled-client call.</p>
|
||||
<h3 id="security-boundary">Security boundary</h3>
|
||||
<p>Encrypted HTTP bodies <strong>do not replace TLS/HTTPS</strong>. Always use HTTPS.</p>
|
||||
<p>This layer is appropriate for service-to-service traffic, native/mobile applications, controlled agents, and selected fields protected with server-managed keys. It cannot conceal data from an end user when browser JavaScript receives the decryption key. Never ship a long-lived server encryption key to a browser.</p>
|
||||
<p>Use a shared replay store such as Redis in multi-instance deployments. The memory replay store is process-local.</p></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>import { Middleware } from '@wrnexus/core';
|
||||
|
||||
interface EncryptionKey {
|
||||
id: string;
|
||||
secret: string;
|
||||
active?: boolean;
|
||||
@@ -80,6 +91,66 @@ declare function open(sealed: string, keyring: EncryptionKeyring): Promise<st
|
||||
declare function sealedKeyId(sealed: string): string | null;
|
||||
declare function needsRotation(sealed: string, keyring: EncryptionKeyring): boolean;
|
||||
|
||||
declare const ENCRYPTED_HTTP_CONTENT_TYPE = "application/wrn+json";
|
||||
declare const ENCRYPTED_HTTP_VERSION = "wrn-http-1";
|
||||
interface EncryptedHttpEnvelope {
|
||||
version: typeof ENCRYPTED_HTTP_VERSION;
|
||||
keyId: string;
|
||||
requestId: string;
|
||||
timestamp: number;
|
||||
ciphertext: string;
|
||||
}
|
||||
interface ReplayStore {
|
||||
consume(id: string, expiresAt: number): boolean | Promise<boolean>;
|
||||
}
|
||||
interface EncryptedHttpOptions {
|
||||
keyring: EncryptionKeyring;
|
||||
maxAgeMs?: number;
|
||||
maxBodyBytes?: number;
|
||||
replayStore?: ReplayStore;
|
||||
now?: () => number;
|
||||
/** Require the clear request-id header used to bind encrypted responses. Default true. */
|
||||
requireRequestIdHeader?: boolean;
|
||||
}
|
||||
interface DecryptedHttpBody<T> {
|
||||
body: T;
|
||||
requestId: string;
|
||||
timestamp: number;
|
||||
keyId: string;
|
||||
}
|
||||
declare function createMemoryReplayStore(now?: () => number): ReplayStore;
|
||||
declare function encryptHttpBody<T>(body: T, input: {
|
||||
keyring: EncryptionKeyring;
|
||||
method?: string;
|
||||
url: string | URL;
|
||||
requestId?: string;
|
||||
timestamp?: number;
|
||||
}): Promise<EncryptedHttpEnvelope>;
|
||||
declare function decryptHttpBody<T>(value: unknown, input: {
|
||||
keyring: EncryptionKeyring;
|
||||
method?: string;
|
||||
url: string | URL;
|
||||
maxAgeMs?: number;
|
||||
replayStore?: ReplayStore;
|
||||
now?: () => number;
|
||||
expectedRequestId?: string;
|
||||
}): Promise<DecryptedHttpBody<T>>;
|
||||
declare function createEncryptedRequest<T>(url: string | URL, body: T, input: Omit<RequestInit, "body"> & {
|
||||
keyring: EncryptionKeyring;
|
||||
requestId?: string;
|
||||
}): Promise<Request>;
|
||||
declare function decryptRequest<T>(request: Request, options: EncryptedHttpOptions): Promise<DecryptedHttpBody<T>>;
|
||||
declare function encryptResponse<T>(body: T, request: Request, options: EncryptedHttpOptions & {
|
||||
status?: number;
|
||||
headers?: HeadersInit;
|
||||
}): Promise<Response>;
|
||||
declare function decryptEncryptedResponse<T>(response: Response, request: Request, options: EncryptedHttpOptions): Promise<DecryptedHttpBody<T>>;
|
||||
declare function encryptedFetch<TRequest, TResponse>(url: string | URL, body: TRequest, input: Omit<RequestInit, "body"> & EncryptedHttpOptions): Promise<TResponse>;
|
||||
declare function encryptedBody(options: EncryptedHttpOptions): Middleware;
|
||||
declare function encryptedExchange(options: EncryptedHttpOptions & {
|
||||
encryptResponses?: boolean;
|
||||
}): Middleware;
|
||||
|
||||
/**
|
||||
* @wrnexus/encryption — authenticated symmetric encryption (AES-256-GCM) via
|
||||
* WebCrypto, dependency-free. Use it to encrypt secrets, tokens, or database
|
||||
@@ -109,25 +180,40 @@ declare function decrypt(payload: string, key: string): Promise<string>;
|
||||
/** Derive a base64 AES key from a password + salt (PBKDF2, 100k iterations). */
|
||||
declare function deriveKey(password: string, salt: string): Promise<string>;
|
||||
|
||||
export { type EncryptionKey, type EncryptionKeyring, createKeyring, decrypt, deriveKey, encrypt, generateKey, hmacSign, hmacVerify, needsRotation, open, seal, sealedKeyId, sha256 };
|
||||
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>Symmetric encryption of a secret at rest</h3><pre data-language="ts"><code>import { generateKey, encrypt, decrypt } from "@wrnexus/encryption";
|
||||
export { type DecryptedHttpBody, ENCRYPTED_HTTP_CONTENT_TYPE, ENCRYPTED_HTTP_VERSION, type EncryptedHttpEnvelope, type EncryptedHttpOptions, type EncryptionKey, type EncryptionKeyring, type ReplayStore, createEncryptedRequest, createKeyring, createMemoryReplayStore, decrypt, decryptEncryptedResponse, decryptHttpBody, decryptRequest, deriveKey, encrypt, encryptHttpBody, encryptResponse, encryptedBody, encryptedExchange, encryptedFetch, generateKey, hmacSign, hmacVerify, needsRotation, open, seal, sealedKeyId, sha256 };
|
||||
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>## Encrypted HTTP envelope</h3><pre data-language="ts"><code>import {
|
||||
createEncryptedRequest,
|
||||
createKeyring,
|
||||
createMemoryReplayStore,
|
||||
decryptEncryptedResponse,
|
||||
encryptedExchange,
|
||||
} from "@wrnexus/encryption";
|
||||
|
||||
const key = await generateKey(); // store this safely (env/secret manager)
|
||||
const keyring = createKeyring([{ id: "2026-08", secret: process.env.API_BODY_KEY!, active: true }]);
|
||||
|
||||
const box = await encrypt("card #1234", key); // opaque base64 string, safe to persist
|
||||
const plain = await decrypt(box, key); // "card #1234"</code></pre></article><article class="example-card"><h3>Deriving a key from a user password instead of a random key</h3><pre data-language="ts"><code>import { deriveKey, encrypt } from "@wrnexus/encryption";
|
||||
const replayStore = createMemoryReplayStore();
|
||||
|
||||
const key = await deriveKey("correct horse battery staple", "per-user-salt");
|
||||
const box = await encrypt("secret note", key);</code></pre></article><article class="example-card"><h3>Hashing and webhook signature verification</h3><pre data-language="ts"><code>import { sha256, hmacSign, hmacVerify } from "@wrnexus/encryption";
|
||||
// Server middleware.
|
||||
app.use(
|
||||
encryptedExchange({
|
||||
keyring,
|
||||
replayStore,
|
||||
maxAgeMs: 60_000,
|
||||
maxBodyBytes: 1_048_576,
|
||||
}),
|
||||
);
|
||||
|
||||
const digest = await sha256("some content"); // 64-char hex string
|
||||
|
||||
const signature = await hmacSign(rawBody, webhookSecret);
|
||||
const ok = await hmacVerify(rawBody, webhookSecret, incomingSignatureHeader);
|
||||
if (!ok) throw new Error("Invalid webhook signature");</code></pre></article></div></section></article>
|
||||
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#overview">Overview</a><a class="toc-level-3" href="#api">API</a><a class="toc-level-3" href="#usage">Usage</a><a class="toc-level-3" href="#requirements-notes">Requirements / Notes</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
|
||||
// Controlled service/native client.
|
||||
const request = await createEncryptedRequest(
|
||||
"https://api.example.com/private/report",
|
||||
{ reportId: "report-1" },
|
||||
{ method: "POST", keyring },
|
||||
);
|
||||
const response = await fetch(request);
|
||||
const result = await decryptEncryptedResponse(response, request, { keyring });</code></pre></article><article class="example-card"><h3>Install @wrnexus/encryption</h3><pre data-language="sh"><code>bun add @wrnexus/encryption</code></pre></article></div></section></article>
|
||||
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#core-helpers">Core helpers</a><a class="toc-level-3" href="#encrypted-http-envelope">Encrypted HTTP envelope</a><a class="toc-level-3" href="#security-boundary">Security boundary</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
|
||||
</main>
|
||||
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.7.0</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
|
||||
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.8.0</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
|
||||
<BackToTop />
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user