Skip to main content
Two client-side packages shrink your PCI/PII compliance scope the way Stripe Elements does: The idea: a sensitive value (a PAN, an SSN, an API credential) is sealed into a portable kc: ciphertext in the page, using your tenant’s public key — plaintext never reaches your servers. The kc: string is byte-compatible with KnoxCall’s server-side format, so only KnoxCall (holding the private key) can decrypt it. These are client-side packages, not management-API SDKs — they never hold an API key.

Install

Not yet published to npm — install from a monorepo checkout by path. @knoxcall/browser requires WebCrypto (any modern browser, or Node ≥ 18 for SSR/tests); @knoxcall/react needs react >= 18 and @knoxcall/browser as a peer.

Seal in the browser

1. Server side — fetch the sealing bundle with the management SDK (the bundle contains only public material, so it is safe to hand to the page):
2. Browser side — seal the value before it ever leaves the page:
encrypt() accepts strings, finite numbers, booleans, null, and JSON-serializable objects/arrays; the original type is preserved through decryption. See Browser-Side Encryption for the underlying format.

Capture cards with <CardCollect>

<CardCollect> embeds a KnoxCall-hosted cross-origin iframe that captures card details and seals the PAN inside the iframe. Your page — and any XSS on it — only ever receives the ciphertext (plus display-safe last4 / brand). That collapses your frontend PCI scope toward SAQ A.

Reveal with capability tokens

Reading a value back in the browser never uses an API key. Your backend mints a single-use, payload-pinned capability token (kct_…) bound to the exact ciphertext or vault token, and hands only that token to the page:
Consume it browser-side — either headless with KnoxClient:
…or with the <Reveal> component, which renders the plaintext to the user inside the iframe — your JavaScript never sees it:
The token is consumed server-side on first use; replaying it fails. KnoxClient refuses anything that is not a kct_… token before making a network call, so an API key pasted into the browser by mistake never leaves the page.

Security model

  • No API key in the browser. Sealing needs only the public bundle; revealing needs only a short-lived kct_ capability token minted by your backend. Nothing shipped to the page can decrypt at will.
  • Single-use, payload-pinned reveal tokens. A kct_ token is bound to one specific ciphertext/vault token and is consumed on first use.
  • Context-bound ciphertexts. The HKDF info pins tenant, app key, key version, datatype, and optional purpose (data-role, e.g. "pci") into the derived key; the kc: header rides as AES-GCM AAD. Decrypting under the wrong purpose, key, or a tampered header simply fails.
  • Exact-origin iframe trust. Element messages are only accepted from an allowlisted origin (exact string match — no prefix or substring semantics, so https://api.knoxcall.com.evil.com never passes), from the expected iframe window, and only after strict shape validation. Outbound messages are posted with an explicit targetOrigin, never *.
If you override elementBase (the origin the iframe is served from), you must also pass origins — the default allowlist covers production and sandbox only, so messages from any other origin are silently dropped.

Full reference

Component props, the raw postMessage protocol (parseKnoxMessage, isTrustedElementOrigin, KNOX_MESSAGE), and development setup: sdk/knoxcall-browser/README.md and sdk/knoxcall-react/README.md in the monorepo.