- Title
- Description
1–2 sentences:
what it is: Conflict-free Verifiable History
what it’s for: Making sensitive or critical claims by an entity immutably verifiable, it can prove that an entity said after this timestamp, do not trust anything else singed by me and it is conflict free so all replicas of the history become eventually consistent on merge etc
(optional) what it’s not for
- Compatibility
A small bullet list, always in this order:
Runtimes: Node >= X; Browsers: ; Workers/Edge:
Module format: ESM/CJS
Required globals / APIs: crypto.subtle, CompressionStream, indexedDB, etc.
TypeScript: bundled types / source TS / etc.
- Goals
3–6 bullets, phrased as outcomes:
“Developer-friendly API…”
“No deps…”
“Returns copies for safety…”
- Installation
Always the same triple:
npm install
pnpm add
yarn add
- Usage
Smallest runnable example (the “copy/paste test”)
Common patterns: 2–5 short subsections max (don’t turn this into an API book)
- Runtime behavior
This is where you put the “how it behaves in different environments” and “what errors look like”.
Use a consistent set of subheadings when relevant:
Node
Browsers / Edge runtimes
Validation & errors
Safety / copying semantics
Caching semantics (if applicable)
- Tests
This section should answer:
What test types exist? (unit / integration / e2e / type tests)
Where do they run? (Node versions; browser matrix)
Coverage: tool + percentage (and what it measures)
Status claim: “passes on …” (keep it factual)
Example format:
Suite: unit (Node), integration, E2E (Playwright)
Matrix: Chromium / Firefox / WebKit (+ mobile emulation if you do it)
Coverage: c8 — 100% statements/branches/functions/lines (Node)
Notes: any known skips
- Benchmarks
This should show actual numbers, plus reproduction context.
Minimum content:
How it was run: command
Environment: runtime version + platform
Results: table or block of key ops/s or timings
Disclaimer: results vary by machine
- License
One line. Always last.
EXAMPLES:
Typed JavaScript byte utilities for base64url, UTF-8 strings, JSON, and gzip that behave the same in browsers and Node. Built to make JavaScript/TypeScript projects with lots of byte-format data a breeze to build, without having to write your own utilities or boilerplate.
- Runtimes: Node >= 18; Browsers: modern browsers with TextEncoder/TextDecoder + btoa/atob; Workers/Edge: runtimes with TextEncoder/TextDecoder + btoa/atob (gzip needs CompressionStream/DecompressionStream).
- Module format: ESM-only (no CJS build).
- Required globals / APIs: Node
Buffer(base64/UTF-8 fallback); browser/edgeTextEncoder,TextDecoder,btoa,atob; gzip in browser/edge needsCompressionStream/DecompressionStream. - TypeScript: bundled types.
- Developer-friendly API for base64url, UTF-8, JSON, gzip, concat, and equality.
- No dependencies or bundler shims.
- ESM-only and side-effect free for tree-shaking.
- Returns copies for safety when normalizing inputs.
- Consistent behavior across Node, browsers, and edge runtimes.
npm install @z-base/bytecodec
# or
pnpm add @z-base/bytecodec
# or
yarn add @z-base/bytecodecimport { Bytes } from '@z-base/bytecodec'
// The `Bytes` convenience class wraps the same functions as static methods.
const encoded = Bytes.toBase64UrlString(new Uint8Array([1, 2, 3]))import { toBase64UrlString, fromBase64UrlString } from '@z-base/bytecodec'
const bytes = new Uint8Array([104, 101, 108, 108, 111])
const encoded = toBase64UrlString(bytes) // string of base64url chars
const decoded = fromBase64UrlString(encoded) // Uint8Arrayimport { fromString, toString } from '@z-base/bytecodec'
const textBytes = fromString('caffe and rockets') // Uint8Array
const text = toString(textBytes) // "caffe and rockets"import { fromJSON, toJSON } from '@z-base/bytecodec'
const jsonBytes = fromJSON({ ok: true, count: 3 }) // Uint8Array
const obj = toJSON(jsonBytes) // { ok: true, count: 3 }import { toCompressed, fromCompressed } from '@z-base/bytecodec'
const compressed = await toCompressed(new Uint8Array([1, 2, 3])) // Uint8Array
const restored = await fromCompressed(compressed) // Uint8Arrayimport { toUint8Array, toArrayBuffer, toBufferSource } from '@z-base/bytecodec'
const normalized = toUint8Array([1, 2, 3]) // Uint8Array
const copied = toArrayBuffer(normalized) // ArrayBuffer
const bufferSource = toBufferSource(normalized) // Uint8Array as BufferSourceimport { equals } from '@z-base/bytecodec'
const isSame = equals(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3])) // true | falseimport { concat } from '@z-base/bytecodec'
const joined = concat([new Uint8Array([1, 2]), new Uint8Array([3, 4]), [5, 6]]) // Uint8ArrayUses Buffer.from for base64 and TextEncoder/TextDecoder when available, with Buffer fallback; gzip uses node:zlib.
Uses TextEncoder/TextDecoder and btoa/atob. Gzip uses CompressionStream/DecompressionStream when available.
Validation failures throw BytecodecError with a code string (for example BASE64URL_INVALID_LENGTH, UTF8_DECODER_UNAVAILABLE, GZIP_COMPRESSION_UNAVAILABLE), while underlying runtime errors may bubble through.
Normalization helpers return copies (Uint8Array/ArrayBuffer) to avoid mutating caller-owned buffers.
Suite: unit + integration (Node), E2E (Playwright) Matrix: Chromium / Firefox / WebKit + mobile emulation (Pixel 5, iPhone 12) Coverage: c8 — 100% statements/branches/functions/lines (Node) Notes: no known skips
How it was run: node benchmark/bench.js
Environment: Node v22.14.0 (win32 x64)
Results:
| Benchmark | Result |
|---|---|
| base64 encode | 514,743 ops/s (97.1 ms) |
| base64 decode | 648,276 ops/s (77.1 ms) |
| utf8 encode | 1,036,895 ops/s (48.2 ms) |
| utf8 decode | 2,893,954 ops/s (17.3 ms) |
| json encode | 698,985 ops/s (28.6 ms) |
| json decode | 791,690 ops/s (25.3 ms) |
| concat 3 buffers | 617,497 ops/s (81.0 ms) |
| toUint8Array | 10,149,502 ops/s (19.7 ms) |
| toArrayBuffer | 620,992 ops/s (322.1 ms) |
| toBufferSource | 8,297,585 ops/s (24.1 ms) |
| equals same | 4,035,195 ops/s (49.6 ms) |
| equals diff | 2,760,784 ops/s (72.4 ms) |
| gzip compress | 10,275 ops/s (38.9 ms) |
| gzip decompress | 18,615 ops/s (21.5 ms) |
Results vary by machine.
Apache
Developer-experience-first cryptography toolkit that lets you powerfully express cryptographic intentions through a semantic and declarative API surface.
- Runtimes: Modern JavaScript hosts with WebCrypto.
- Module format: ESM-only (no CJS build).
- Required globals / APIs:
crypto,crypto.subtle,crypto.getRandomValues. - TypeScript: bundled types.
- Consistent JWK validation for AES-GCM, HMAC, Ed25519, and RSA-OAEP.
- Byte-oriented APIs (
Uint8ArrayandArrayBuffer) to avoid ambiguous inputs. - No side effects on import; all work happens per call.
- Clean separation between agents (stateful) and clusters (cached).
- Minimal, but strict WebCrypto wrappers with explicit
CryptosuiteErrorcodes.
npm install @z-base/cryptosuite
# or
pnpm add @z-base/cryptosuite
# or
yarn add @z-base/cryptosuiteimport { Cryptosuite } from '@z-base/cryptosuite'
// The `Cryptosuite` convenience class wraps classes and functions into an intuitive structure.
const cipherJwk = await Cryptosuite.cipher.generateKey()
const payload = new Uint8Array([1, 2, 3])
const artifact = await Cryptosuite.cipher.encrypt(cipherJwk, payload)
const roundtrip = await Cryptosuite.cipher.decrypt(cipherJwk, artifact)import {
deriveOID,
generateOID,
validateOID,
type OpaqueIdentifier,
} from '@z-base/cryptosuite'
const oid = await generateOID() // 43 random base64url chars
const derived = await deriveOID(idBytesFromSomewhere) // 43 deterministic base64url chars
const valid = validateOID(uncontrolledOID) // 43 base64url chars | false
if (!valid) returnimport { fromJSON, toJSON } from '@z-base/bytecodec'
import {
deriveCipherKey,
CipherCluster,
CipherAgent,
type CipherJWK,
} from '@z-base/cryptosuite'
const cipherJwk = await deriveCipherKey(deterministicBytes)
const state = { name: 'Bob', email: 'bob@email.com' }
const enc = await CipherCluster.encrypt(cipherJwk, fromJSON(state)) // {iv, ciphertext}
const dec = await CipherCluster.decrypt(cipherJwk, enc)
const restored = toJSON(dec)
console.log(restored.name) // "Bob"import { fromString, toString } from '@z-base/bytecodec'
import {
generateCipherKey,
generateExchangePair,
ExchangeCluster,
WrapAgent,
type WrapJWK,
UnwrapAgent,
type UnwrapJWK,
CipherAgent,
type CipherJWK,
} from '@z-base/cryptosuite'
const { wrapJwk, unwrapJwk } = await generateExchangePair()
const encryptJwk = await generateCipherKey()
const encryptAgent = new CipherAgent(encryptJwk)
const body = await encryptAgent.encrypt(fromString('Hello world!')) // {iv, ciphertext}
const header = await ExchangeCluster.wrap(wrapJwk, encryptJwk) // ArrayBuffer
const message = { header, body }
const decryptJwk = (await ExchangeCluster.unwrap(
unwrapJwk,
message.header
)) as CipherJWK
const decryptAgent = new CipherAgent(decryptJwk)
const decryptedBody = await decryptAgent.decrypt(message.body)
const messageText = toString(decryptedBody) // "Hello world!"import { fromString } from '@z-base/bytecodec'
import {
generateHMACKey,
HMACCluster,
HMACAgent,
type HMACJWK,
} from '@z-base/cryptosuite'
const hmacJwk = await generateHMACKey()
const challenge = crypto.getRandomValues(new Uint8Array(32))
const sig = await HMACCluster.sign(hmacJwk, challenge) // ArrayBuffer
const ok = await HMACCluster.verify(hmacJwk, challenge, sig) // true | falseimport {
generateVerificationPair,
VerificationCluster,
SignAgent,
type SignJWK,
VerifyAgent,
type VerifyJWK,
} from '@z-base/cryptosuite'
const { signJwk, verifyJwk } = await generateVerificationPair()
const payload = new Uint8Array([9, 8, 7])
const sig = await VerificationCluster.sign(signJwk, payload) // ArrayBuffer
const ok = await VerificationCluster.verify(verifyJwk, payload, sig) // true | falseUses Node's global WebCrypto (globalThis.crypto) when available. Node is not the primary target, but tests and benchmarks run on Node 18+.
Uses crypto.subtle and crypto.getRandomValues. Ed25519 and RSA-OAEP support vary by engine; unsupported operations throw CryptosuiteError codes.
Validation failures throw CryptosuiteError with a code string (for example AES_GCM_KEY_EXPECTED, RSA_OAEP_UNSUPPORTED, ED25519_ALG_INVALID). Cryptographic failures (e.g., decrypt with the wrong key) bubble the underlying WebCrypto error.
- Keep
{iv, ciphertext}together and never mix IVs across messages or keys. - Treat all JWKs and raw key bytes as secrets; never log them and rotate on exposure.
- Always sign a canonical byte serialization so verifiers see identical bytes.
- Ciphertext length leaks; add padding at your protocol layer if size is sensitive.
- Handle decrypt/verify failures uniformly; don't leak which check failed.
Suite: unit + integration (Node), E2E (Playwright) Matrix: Chromium / Firefox / WebKit + mobile emulation (Pixel 5, iPhone 12) Coverage: c8 — 100% statements/branches/functions/lines (Node)
How it was run: node benchmark/bench.js
Environment: Node v22.14.0 (win32 x64)
Results:
| Benchmark | Result |
|---|---|
| AES-GCM encrypt | 30.41ms (6575.9 ops/sec) |
| HMAC sign+verify | 29.95ms (6678.1 ops/sec) |
| Ed25519 sign+verify | 76.45ms (2616.0 ops/sec) |
| RSA-OAEP wrap+unwrap | 1224.07ms (163.4 ops/sec) |
Results vary by machine.
Apache
Client-side WebAuthn credential discovery for strict zero-knowledge apps. Deterministically derive a routing identifier and cryptographic root keys from a user-verifying authenticator, without accounts, identifiers, or server-side state.
- Runtimes: modern browsers with WebAuthn + PRF extension + user verification.
- Module format: ESM-only (no CJS build).
- Required globals / APIs:
window,navigator.credentials,PublicKeyCredential, PRF extension,crypto.subtle,crypto.getRandomValues. - TypeScript: bundled types.
- Enable strict local-first zero-knowledge for browsers.
- Deterministic, runtime-only derivation of an opaque ID and root keys.
- No storage, no networking, no server-side requirements.
- Explicit failure modes with stable error codes.
npm install @z-base/zero-knowledge-credentials
# or
pnpm add @z-base/zero-knowledge-credentials
# or
yarn add @z-base/zero-knowledge-credentialsThese give a general idea and MUST NOT be interpreted as a full solution.
import {
ZKCredentials,
type ZKCredential,
type ZKCredentialErrorCode,
} from '@z-base/zero-knowledge-credentials'
await ZKCredentials.registerCredential(
'User display name',
'platform' // or 'cross-platform'
)import { Bytes } from '@z-base/bytecodec'
import { Cryptosuite } from '@z-base/cryptosuite'
import { ZKCredentials } from '@z-base/zero-knowledge-credentials'
const root = await ZKCredentials.discoverCredential()
const id = root.id // routing identifier / OpaqueIdentifier
const hmacJwk = root.hmacJwk // HMAC root key / HMACJWK
const cipherJwk = root.cipherJwk // AES-GCM root key / CipherJWK
const cache = await caches.open('opaque-blobs')
let artifact = await cache.match(id) // {iv, ciphertext}
if (!artifact) {
const challengeRaw = await fetch(`/api/v1/artifact/${id}/challenge`)
const challengeText = await challengeRaw.text()
const challengeBytes = Bytes.fromBase64UrlString(challengeText)
const signature = await Cryptosuite.hmac.sign(hmacJwk, challengeBytes)
const raw = await fetch(`/api/v1/artifact/${id}`, {
headers: {
Authorization: Bytes.toBase64UrlString(signature),
},
})
artifact = await raw.json() // {iv, ciphertext}
}
const accountCredentials = await Cryptosuite.cipher.decrypt(cipherJwk, artifact)
// const {id, hmacJwk, cipherJwk} = accountCredentials
// repeat...
// const {profileCredentials, workspaceCredentials} = resourceCredentialsimport { Bytes } from '@z-base/bytecodec'
import { Cryptosuite } from '@z-base/cryptosuite'
import { ZKCredentials } from '@z-base/zero-knowledge-credentials'
const profile = {
name: 'Bob',
preferences: {
theme: 'dark',
},
}
const credentials = await ZKCredentials.generateCredential()
const id = credentials.id // resource routing identifier / OpaqueIdentifier
const hmacJwk = credentials.hmacJwk // HMAC resource key / HMACJWK
const cipherJwk = credentials.cipherJwk // AES-GCM resource key / CipherJWK
const profileBytes = Bytes.fromJSON(profile)
const artifact = await Cryptosuite.cipher.encrypt(cipherJwk, profileBytes)
fetch(
`/api/v1/artifact/${id}`,
JSON.stringify({
verifier: hmacJwk,
state: {
iv: Bytes.toBase64UrlString(artifact.iv),
ciphertext: Bytes.toBase64UrlString(artifact.ciphertext),
},
}),
{
method: 'POST',
}
)Uses WebAuthn PRF outputs to derive:
id(SHA-256 -> base64url ofrawId)cipherJwk(AES-GCM)hmacJwk(HMAC-SHA256)
All failures are explicit and semantic. Errors are instances of ZKCredentialError with a stable code:
unsupportedaborteduser-deniedno-credentialprf-unavailablekey-derivation-failed
Suite: unit + integration (Node), E2E (Playwright) Matrix: Chromium / Firefox / WebKit + mobile emulation (Pixel 5, iPhone 12) Coverage: c8 — 100% statements/branches/functions/lines (dist via source maps)
How it was run: npm run bench
Environment: Node v22.14.0 (win32 x64)
Results:
| Benchmark | Result |
|---|---|
| fromPRF | 5,224 ops/s (0.191 ms/op, 200 ops) |
| generateCredential | 5,825 ops/s (0.172 ms/op, 50 ops) |
Results vary by machine.
Apache