Skip to main content

Browser encryption & UI Components

Encrypt sensitive values in the page, against your tenant’s public key, so plaintext (a card number, an SSN) never touches your servers. You store and transmit only a portable kc: ciphertext — and only KnoxCall, holding the private key, can decrypt it. No API key is ever shipped to the browser. This is how you collapse a payment/PII frontend toward PCI SAQ A.

1. Client-side encryption (@knoxcall/browser)

Your backend fetches a sealing bundle (the tenant public key + a key reference — no private material) and hands it to the page. Encryption is then a pure client-side operation with no network round-trip.
// --- your backend (has the API key) ---
// GET /v1/encrypt/sealing-bundle?key=payments  ->  { public_key_raw, key_ref }
app.get('/checkout/sealing-bundle', async (req, res) => {
  const r = await fetch('https://api.knoxcall.com/v1/encrypt/sealing-bundle?key=payments', {
    headers: { Authorization: `Bearer ${process.env.KNOXCALL_API_KEY}` },
  });
  res.json(await r.json());
});
// --- the browser ---
import { KnoxEncryptor } from '@knoxcall/browser';

const bundle = await fetch('/checkout/sealing-bundle').then((r) => r.json());
const enc = new KnoxEncryptor(bundle, { purpose: 'pci' });

const ciphertext = await enc.encrypt(cardNumber); // "kc:1:s:…:$"
// send `ciphertext` to your backend / KnoxCall — the PAN never left the page.
The scheme (ephemeral ECDH P-256 → HKDF-SHA256 → AES-256-GCM) is byte-identical to the server, so any kc: value you produce here decrypts through /v1/decrypt, a Relay route-action, or a data-plane enclave.

2. Reveal to the browser without an API key

To show a user one decrypted value, your backend mints a single-use, payload-pinned capability token (see client-side reveal) and the page exchanges it once:
import { KnoxClient } from '@knoxcall/browser';

const knox = new KnoxClient();
const pan = await knox.reveal(capabilityToken, ciphertext);   // one-time
// or, for a vault token:
const value = await knox.detokenize(capabilityToken, 'tok_…');
The token is consumed server-side on first use, expires in minutes, and only matches the exact value it was minted for.

3. Iframe UI Components (@knoxcall/react)

For the strongest isolation, capture and display data inside a cross-origin iframe served from a KnoxCall origin. The host page — and any XSS on it — never sees the raw PAN; your callback receives only the kc: ciphertext plus non-sensitive display bits (last4/brand).
import { CardCollect } from '@knoxcall/react';

<CardCollect
  bundle={bundle}
  purpose="pci"
  onChange={(complete) => setReady(complete)}
  onToken={(ciphertext, { last4, brand }) => {
    // store the ciphertext; show `•••• last4` to the user.
  }}
/>
Every message from the iframe is origin-checked (exact match), source-checked (must be the KnoxCall iframe), and shape-validated before your app trusts it — that logic lives in the shared, unit-tested protocol. <Reveal> is the mirror: it decrypts (or detokenizes) with a capability token and displays the value inside the iframe, so plaintext is shown to the user but never crosses into your JavaScript.

Which to use

You needUse
Seal a value your own form already holdsKnoxEncryptor.encrypt()
Never let your JS touch the PAN at all<CardCollect> (iframe)
Show one decrypted value to a usercapability token + KnoxClient.reveal() or <Reveal>
Decrypt server-side / in transit/v1/decrypt or Relay route-actions