@orkestrapay/cse
v1.0.7
Published
Orkestra client-side encryption (CSE) browser SDK — encrypts card data into a JWE Compact (RSA-OAEP-256 + A256GCM) using only the Web Crypto API.
Maintainers
Readme
@orkestrapay/cse
Client-side encryption (CSE) browser SDK for card data. Encrypts PAN / expiry / CVV in the browser
into a JWE Compact (alg=RSA-OAEP-256, enc=A256GCM) using only the Web Crypto API, so the
merchant only ever handles ciphertext. The private key stays in Vault; plaintext exists only transiently
inside the platform's core service.
This is the browser SDK end; the backend counterpart lives in
core. The exact contract between them is frozen in
docs/wire-contract.md.
Installation
npm / yarn / pnpm
npm install @orkestrapay/cse
# or
yarn add @orkestrapay/cse
# or
pnpm add @orkestrapay/cseimport { initialize, encryptCard, createCseClient } from "@orkestrapay/cse"SemVer: Pin the exact version (no
^or~) in production. This is a security component — updates should be intentional and audited.
CDN (script tag, no bundler)
<script src="https://<cdn-host>/cse-runtime/1.0.0/runtime.min.js"
integrity="sha384-…"
crossorigin="anonymous"></script>- Use the immutable, versioned URL. Never
/latestwith SRI (seedocs/sri-pci.md). - The
integrityvalue comes fromdist/integrity.json(npm run gen:sri). - Exposes a single global:
window.OrkestraCSE.
Quick start
<script src="https://<cdn-host>/cse-runtime/1.0.0/runtime.min.js"
integrity="sha384-…" crossorigin="anonymous"></script>
<script>
await OrkestraCSE.initialize({
keyEndpoint: "https://<cdn-host>/cse-keys/current",
audience: "core",
})
// at submit, read fields directly and encrypt:
const jwe = await OrkestraCSE.encryptCard({ pan, expMonth, expYear, cvv, holderName })
// POST only { crypto: { blob: jwe } } to your backend; clear the inputs.
</script>See docs/integration.md and examples/.
Design
Single script (Option A), single package, zero runtime dependencies, Web Crypto only, ES2020, no crypto polyfill, in-memory key cache, immutable versioned artifacts + SRI.
Security model and honest limits (JS memory, pre-encryption malware, TLS, PCI/SAQ): see
docs/security.md, docs/sri-pci.md,
security/threat-model.md.
Develop
npm ci
npm run typecheck # tsc --noEmit
npm test # vitest (crypto runs on Node's native Web Crypto)
npm run interop # zero-dep harness: proves JWE ↔ core interop
npm run build # tsup -> dist/ (ESM + CJS + IIFE runtime.min.js)
npm run gen:sri # SRI sha384 + SHA-256 checksum + integrity.json (after build)
npm run gen:sbom # SBOM; fails if any runtime dependency exists
npm run size # size-budget gateMaintaining
Prerequisites
- Node.js 22+ (see
.nvmrc) - npm 10+
- Repository access (private repo — contact Orkestra support)
Getting the code
git clone https://github.com/orkestra-pay/cse.git
cd cse
npm ciRunning tests
All quality gates must pass before a release:
npm run typecheck # TypeScript type checking (no emit)
npm test # Unit tests (vitest, 77 tests)
npm run interop # JWE round-trip interop with backend contract
npm audit --omit=dev --audit-level=high # Zero high/critical vulnerabilities
npm run build:prod # Production build (minified, no source maps)
npm run gen:sbom # SBOM — fails if any runtime dependency exists
npm run size # Size budget gate (30KB raw / 12KB gzip)
node test/dist-consumption.mjs # ESM/CJS/types consumption from dist/Versioning
This package follows Semantic Versioning. See
docs/versioning.md for the full policy.
Before releasing:
- Update
versioninpackage.jsonto the new SemVer version. - Update
VERSIONinsrc/version.tsto match. - Update
CHANGELOG.mdunder## [Unreleased]→ rename to the new version with date. - Commit all changes.
- Create a git tag matching the version exactly:
git tag v1.0.1. - Push the tag:
git push origin v1.0.1.
The CI pipeline verifies that
package.jsonversion matches the git tag. A mismatch fails the release.
Release process
Releases are triggered by pushing a v*.*.* tag to the repository. The
release.yml workflow runs automatically:
- Quality gates — typecheck, tests, interop harness,
npm audit. - Production build —
tsup --minify(ESM + CJS + IIFE, no source maps). - Supply-chain gates — SBOM (zero runtime deps), size budget, SRI generation.
- Version match —
package.jsonversion must equal the git tag. - Content gate —
npm pack --dry-runlogs exactly what would be published. - Distribution test —
test/dist-consumption.mjsvalidates ESM/CJS/types imports. - Build provenance — GitHub native attestations for the runtime artifact.
- Human approval —
environment: productionrequires manual approval in GitHub. - S3/CloudFront publish — immutable, versioned path; refuses to overwrite.
- npm publish —
npm publish --provenance --access public; refuses existing version.
Both channels (S3 + npm) publish the same version from the same tag in a single run. A version, once published, is permanent — never reuse a version number.
What does NOT change in a release
- Wire contract (JWE format, payload, header) — frozen. See
docs/wire-contract.md. - Cryptography — RSA-OAEP-256 + A256GCM via Web Crypto API only.
- Zero runtime dependencies — enforced by
gen-sbom.mjs. - API surface —
initialize,encryptCard,refreshKey,getVersion,createCseClient.
