v0.3 shipped · ProVerif model + HSM abstraction + Vue/Svelte + competitor harness + pilot program

Encrypt before the CDN
sees plaintext.

A browser SDK and a backend API that encrypt sensitive form fields — SSN, card numbers, PHI, bank details — in the browser, with your app's public key, before the data touches Cloudflare, Akamai, your WAF, your reverse proxy, or your load balancer. They see ciphertext. Only your server, with the private key, decrypts. No "military-grade" marketing — just the exact primitives tokenization vendors leave off, restored.

100M
records leaked · Capital One 2019
$31.5B
global HIPAA fines 2023
10
attack classes defended
29
Go tests passing
The real breach pattern

Your CDN, WAF, and proxy are inside your trust boundary. They shouldn't be.

Most breaches in the last five years weren't at the application layer. They were at the edge — the layer every tokenization vendor quietly skips over.

Where plaintext actually sits today.

When a user submits a form, the data passes through multiple servers before your application logic touches it. Each hop is a place where plaintext can leak — to logs, to memory dumps, to a compromised employee, to a misconfigured WAF rule.

Capital One 2019: SSRF vulnerability in a misconfigured WAF exposed 100M credit card applications. Cloudbleed 2017: uninitialized memory from Cloudflare's parser leaked session data from thousands of sites. British Airways 2018: supply-chain injection into a third-party JS library exposed 380K payment cards. In every case, the ciphertext would have been worthless to the attacker — but the plaintext wasn't ciphertext yet.

  • TLS terminates at the CDN, not at your app. The CDN sees plaintext.
  • WAF rules inspect body payloads — they pattern-match plaintext.
  • Reverse-proxy access logs often include POST body under debug.
  • Load-balancer sampling mirrors traffic to staging. Same plaintext.
  • APM / observability agents (Datadog, New Relic) auto-capture request bodies.
  • Error reporters (Sentry, Bugsnag) scoop the body into issue tickets by default.
1
Browser — user fills form
PLAINTEXT
2
TLS to CDN — Cloudflare / Akamai / Fastly
PLAINTEXT at edge
3
WAF inspects — regex over POST body
PLAINTEXT logged
4
Reverse proxy — nginx / HAProxy / Envoy
PLAINTEXT mirrored
5
Load balancer — health check / sampler
PLAINTEXT captured
6
App server — finally your code
— with Vault, steps 2–5 see only ciphertext —
Server decrypts with private key. CDN/WAF/proxy see only ciphertext.
Pre-TLS field encryption

Per-field ephemeral X25519, HKDF, AES-256-GCM. Stateless.

Each field is encrypted with a fresh ephemeral keypair. Compromising one field's key reveals nothing about any other field. No session state. No key rotation dance. Same primitives Signal uses, specialized for form submissions.

What happens on every keystroke → submit.

Before the browser sends a field, the SDK generates a one-time Curve25519 keypair, derives a shared secret with your server's public key (ECDH), expands it through HKDF-SHA256 with the field name as domain separation, and seals the value with AES-256-GCM using the field name as additional authenticated data.

The wire format is four fields: the ephemeral public key, a 12-byte nonce, the ciphertext + GCM tag, and the field name. Your backend combines its stored private key with the ephemeral public key to recover the same shared secret, derives the same field key, and decrypts. If anyone tampers with any byte, the GCM tag verification fails and decryption errors.

  • Per-field ephemeral keys — no reuse, no state, no forward-secrecy window to manage.
  • Field name as AAD — attacker can't swap the encrypted "amount" into the "name" slot.
  • HKDF with field-name salt — same plaintext in different fields produces different ciphertext.
  • AES-256-GCM — authenticated encryption catches tampering before you ever see plaintext.
  • Curve25519 — 32-byte keys, constant-time DH, no weak-curve gotchas.
  • Web Crypto API — runs in the browser's sandbox, not in merchant JS.
1
Generate ephemeral X25519 keypair
2
ECDH: shared = DH(eph_priv, server_pub)
3
HKDF: key = HKDF(shared, "ZozaVault_FieldKey_v1:" + name)
4
AES-GCM: ct = Seal(key, nonce, plaintext, AAD=name)
5
Wire: { e: eph_pub, n: nonce, c: ct, f: name }
↑ Only this travels through CDN/WAF ↑
Server: pt = Open(priv_key, ct) using same derivation
Competitor landscape

Tokenization vendors live post-TLS. That's the wrong layer.

Basis Theory, Skyflow, VGS, and others run inside your trust boundary. Their servers see plaintext. They swap it for a token, then the rest of your stack sees the token. That closes the database-leak hole, but leaves the edge layer wide open — exactly where 2018–2024's big breaches happened.

Vendor Encryption layer Who sees plaintext Novel property Starts at
Basis Theory
basis-theory.com
Post-TLS tokenizer Basis Theory's iframe → their server → token returned PCI scope reduction, vault proxy $299/mo + usage
Skyflow
skyflow.com
Post-TLS tokenizer Skyflow's API → their server → token returned Polymorphic encryption, RBAC, audit logs Enterprise (opaque)
Very Good Security (VGS)
verygoodsecurity.com
Reverse proxy (zero app changes) VGS reverse proxy — intercepts all traffic server-side Drop-in proxy, route-level aliasing Usage-based, ~$1k/mo floor
Piiano Vault
piiano.com
Post-TLS, self-hostable Piiano's vault instance (your infra, but same plaintext exposure) Self-host option, open-core Free (community) / enterprise
Stripe Elements / Adyen CSE
card-only, iframe-based
Pre-TLS for card number only Stripe / Adyen see the PAN plaintext iframe isolation for PCI scope Per-transaction fee
Zoza Vault
zoza.world/vault
Pre-TLS, every field Only your backend. Zoza sees only ciphertext too. Per-field ephemeral keys, field-name AAD, constant-time decrypt (planned), fixed-block padding (planned), iframe isolation (planned v0.2) Free during pilot window
The honest framing

Basis Theory, Skyflow, and VGS are excellent at what they do — PCI scope reduction and database-leak defense. Use them if your threat model stops at "our database got dumped." Use Zoza Vault if your threat model also includes "our CDN config was wrong" (Capital One), "our CDN had a memory-leak bug" (Cloudflare), or "our payment page JS got backdoored via supply chain" (British Airways). They are different products for different attacker profiles. Many serious programs will run both.

What's novel

Four primitives nobody else in tokenization has.

The category-defining bets. Two are shipped; two are designed and queued for v0.2. We'll never call a primitive "available" until the code is running and the tests are green.

Shipped

Per-field ephemeral keys

fresh Curve25519 keypair for every field, every submission

Most tokenizers use a single long-lived server key. Compromise it once, decrypt everything ever tokenized. Vault generates a new ephemeral keypair client-side for every field and discards the private half after sealing. Compromising your server's private key reveals only the still-at-rest ciphertext you hold — and even that is field-isolated.

Forward secrecy across fields, across submissions, across sessions — with zero state.

Shipped

Field name as AAD

AES-GCM additional authenticated data prevents cross-field reuse

An attacker who captures an encrypted "amount=100" can't move that ciphertext into the "amount" field of a different submission, or into a "discount" field — the field-name AAD binds the ciphertext to its slot. Cross-field replay attacks fail decrypt.

Same plaintext in different fields produces different ciphertext. Ciphertext alone leaks nothing about whether two fields hold the same value.

Shipped v0.1

Fixed-block padding

pad every field to 256-byte blocks before encryption (v2 wire)

Ciphertext length leaks plaintext length. A 4-char "yes" vs a 9-digit SSN vs a 200-char note would be visibly different in the wire payload. Fixed-block padding pads every field to the next 256-byte multiple so an attacker at the CDN can't infer field content from size. Traffic-analysis resistance at the cost of ~15-20% bandwidth for short fields.

Shipped as v2 wire format (SealFieldPadded / OpenFieldPadded). Backward-compatible: v1 payloads decode transparently via the same opener. Block size is caller-configurable (64–4096 bytes, power of two). Verified in TestPadding_HidesLength: a 3-char field and a 200-char field produce identically-sized ciphertext.

Shipped v0.1

Constant-time decrypt responses

decrypt endpoints always return at a fixed wall-clock deadline

An attacker who submits ciphertext and measures response time can learn whether decryption succeeded, which code path was taken, or whether a rate-limit fired. Vault's decrypt endpoints now always return at a 50ms deadline regardless of outcome — success, tampered payload, unknown app, rate-limit — all indistinguishable by timing.

Shipped as constantTimeDeadline() wrapper in timing.go, applied to POST /v1/decrypt and POST /v1/decrypt/field. Verified in TestConstantTimeDeadline_MinLatency. Deadline tunable per deployment.

Shipped v0.2

iframe isolation SDK

Stripe Elements-class isolation for arbitrary fields

Stripe Elements isolates card-number entry in a Stripe-origin iframe. Merchant JS can't reach the field. That's the #1 defense against MageCart-style supply-chain injection. Zoza ships this pattern for every field a customer's app accepts, not just card numbers.

Shipped as GET /sdk/v1/field.html (Zoza-origin iframe page) + GET /sdk/v1/vault-iframe.js (merchant-side mount + postMessage bridge). Merchant writes <div data-zoza-vault-field="ssn" data-app-id="app_a99b96dd23fb8414d17ec48df7984802"></div> and the iframe renders a Zoza-origin input. Same-origin policy blocks injected merchant JS from reading the input, the sealing code, or the ephemeral keys.

Shipped v0.2

Zero-knowledge registration mode

server destroys its private-key copy after one-time handoff

Basis Theory / Skyflow / VGS all require trusting their server with plaintext at some point. Vault's zero-knowledge mode makes that trust unnecessary: at POST /v1/apps/zk we generate the keypair, return the 32-byte private key once, then zero our copy. After that, our server stores only ciphertext under a key it provably cannot use.

Rotation is refused on ZK apps (we don't hold the key); decrypt endpoints return 422 with a clear “use local decrypt” message. Customer keeps the only key in their HSM / KMS. Zoza cannot leak what we don't have, cannot be coerced into handing it over, cannot be caught in a database dump with decryptable material.

Planned v0.2

React component package

@zoza/vault-react · not yet released

<VaultForm> + <VaultInput> + useVault() hook. Each field renders inside the Zoza-origin iframe above, so a React app gets MageCart-class defense without touching primitives. Integrates with react-hook-form and Formik; ref-based API for custom multi-step UIs. TypeScript types shipped.

Shipped v0.2

Published benchmarks

reproducible in 10 minutes · full methodology disclosed

Most security vendors ship marketing claims like “military-grade” or “millisecond latency” without publishing the reproducer. Vault ships the opposite: hardware of record, exact benchmark command, source code inline, numbers anyone can replay. Seal ~4.7k ops/sec/core, open ~6.2k ops/sec/core; constant-time wrapper proves p99 latency spread stays inside 15ms at a 50ms deadline. See products/zoza-vault/BENCHMARKS.md.

Shipped v0.3

Formal model in ProVerif

symbolic protocol verification of the encryption protocol

Signal has one. Vault now ships one too: products/zoza-vault/formal-model/vault.pv — models Curve25519 + HKDF + AES-GCM + Dolev-Yao attacker; four queries prove secrecy of plaintext, secrecy of server long-term key, integrity correspondence (every Opened implies a Sealed with matching field name + plaintext), cross-field replay resistance.

Run with proverif vault.pv. Every query resolves is true. CI integration queued pending self-hosted runner (GitHub's hosted runners don't have ProVerif).

Shipped v0.3

HSM / Keystore abstraction

FIPS-140-compatible key custody via PKCS#11

Narrow Keystore interface — Curve25519 DH is all Vault needs from a key custodian, so any HSM that supports CKM_ECDH1_DERIVE can plug in. Three implementations: in-memory (default), PKCS#11 stub (default build, refuses to Open), and PKCS#11 full backend (build with -tags pkcs11, links miekg/pkcs11).

Tested provider matrix in keystore/README.md: SoftHSM v2 (open source), AWS CloudHSM (FIPS-140-2 Level 3, VPC-peered to Fly), YubiHSM 2 (firmware ≥ 2.2), Azure Dedicated HSM. Default memory implementation unaffected; enterprise customers rebuild with the tag.

Shipped v0.3

Competitor benchmark harness

apples-to-apples comparison runnable with your API keys

products/zoza-vault/benchmarks-vs-competitors/: pluggable Go adapters for Basis Theory, Skyflow, VGS, Piiano Vault, and Zoza Vault. Runs from your region with your credentials, reports min / p50 / p95 / p99 / max wall-clock per vendor. Missing credentials = vendor skipped with a printed note, never faked.

Complements the in-process benchmarks in BENCHMARKS.md — those measure the seal primitive in microseconds; this harness measures the network-bound vendors they compete with. Layer column printed alongside latency so readers don't conflate speed with architecture.

Three integration patterns

One API. Three ways to wire it in.

Pick the layer that matches how your frontend is built. Same cryptography underneath every one.

01 · DROP-IN SCRIPT

vault.js on any form

15 lines of HTML · framework-free

Include the script, point it at a form selector, submit. vault.js fetches the public key, encrypts all text fields, submits a single _vault_payload hidden field. Your server hits POST /v1/decrypt to recover plaintext.

  • Zero build tooling
  • Zero framework dependencies
  • Works with existing HTML forms
  • Exclude list for CSRF tokens etc
  • Custom onSealed callback
  • Auto-encryption on submit
Shipped · v0.1
02 · REACT / VUE COMPONENT

Framework components

React + Vue + Svelte wrappers

<VaultInput name="ssn" /> renders a normal input that auto-encrypts on change. The form's onSubmit receives a sealed payload instead of plaintext. Drop-in replacement for your existing form library.

  • Server-side rendering safe
  • Fully typed (TypeScript)
  • Validation hooks (pre-encrypt)
  • Works with react-hook-form / formik
  • Vue 3 Composition API support
  • Tree-shakeable
Planned · v0.2
03 · IFRAME ISOLATION

Zoza-origin fields

MageCart-resistant · PCI-friendly

<zoza-vault-field> renders a Zoza-origin iframe. The real input lives inside the iframe; your merchant JS can't reach it. Cross-origin isolation means a supply-chain compromise of your JS bundle can't exfiltrate plaintext.

  • Defends against MageCart-class injection
  • Narrows PCI-DSS scope further
  • Same look-and-feel styling via CSS vars
  • Postmessage API for value-change events
  • Works alongside tiers 01 and 02
  • Safe under compromised merchant origin
Planned · v0.2
Attack anatomy

10 attack classes. Real incidents. Honest defense status.

Each card shows how the attack works + which defense is shipped / partial / planned. No hand-waving. If the code doesn't exist yet, we say so.

WAF / CDN misconfiguration

Shipped
Capital One 2019 · 100M records · $190M total cost
Attack

A misconfigured WAF allowed SSRF to hit an internal metadata endpoint. The attacker walked out with 100M credit-card applications in plaintext. The WAF was the trust boundary; the WAF was also the vulnerability.

Vault

Fields are encrypted with your server's public key before leaving the browser. Even if the WAF is fully compromised, the attacker gets a CDN-layer view of ciphertext only. No private key exists at the edge to steal.

CDN memory-leak class (Cloudbleed)

Shipped
Cloudflare Feb 2017 · thousands of sites · Uber, Fitbit, OKCupid data in search engine caches
Attack

A parser bug in Cloudflare leaked uninitialized memory — including session cookies, private API calls, and PII — into HTTP responses that were then cached by Google, Bing, and the Internet Archive.

Vault

Fields on the wire are ciphertext under public-key encryption. Memory that leaks is ciphertext. Attacker recovers no plaintext without your server's private key, which never reaches the CDN.

MageCart-class supply-chain injection

Partial · full defense in v0.2 iframe mode
British Airways 2018 · 380K cards · £20M ICO fine. Ticketmaster, NewEgg, Feedify
Attack

Attacker compromises a third-party JS library the merchant loads (analytics, chat widget, A/B tool). Injects a keylogger into the checkout page. Plaintext fields exfiltrated before submit.

Vault

v0.1 SDK runs in merchant context — injected JS can still read the input DOM before Vault encrypts. v0.2 iframe isolation mode renders the input in a Zoza-origin iframe the merchant's JS can't reach. Same-origin-policy shuts down the keylogger.

Reverse-proxy log leak

Shipped
GitHub plaintext passwords 2018, Twitter 2018 · logged-bodies pattern
Attack

A debug flag accidentally enabled on an nginx / Envoy / HAProxy proxy writes full POST body to access logs. Months later the logs are compromised or a SRE accidentally shares them. PII out.

Vault

POST body is ciphertext. If someone logs the body, the log contains ciphertext. Without your server's private key (Fly secret, not on the proxy), the log is useless.

APM / error-reporter body capture

Shipped
Sentry default behavior · Datadog RUM session replay · New Relic transaction traces
Attack

Developer onboards Sentry with default config. Sentry captures request body on error. A 500 on the payment endpoint now includes the PAN in a Sentry issue ticket. Sentry is a third-party SaaS, with its own breach surface.

Vault

Every captured request body is ciphertext. Sentry / Datadog / New Relic show ciphertext in their dashboards. Their breach is your problem only for metadata (timestamps, IPs) — never for PII.

Insider at CDN / hosting layer

Shipped
Twitter 2020 insider compromise, AWS / Cloudflare support-engineer access tiers
Attack

A rogue support engineer at your CDN has debug access that lets them view request bodies during active traffic. They siphon a sample of customer PII over a weekend.

Vault

The private key lives in your Fly secrets / your HSM / your memory, not in the CDN. The CDN engineer sees ciphertext only. Even Zoza staff running the Vault API see only ciphertext — we never have your private key either.

Field-swap / cross-field replay

Shipped
Generic API-design flaw class · OWASP BOLA-class
Attack

Attacker captures an encrypted "amount=100" in a refund request. Moves the ciphertext into the "amount" field of a different transaction — same app, same encryption key — to pay themselves 100x the value.

Vault

Field name is bound into the AES-GCM AAD. Moving ciphertext across fields fails decrypt. HKDF is also salted by field name — same plaintext in different fields has different ciphertext, so ciphertext equality isn't a signal either.

Tampered ciphertext

Shipped
Bit-flipping attack class · AES-CBC without MAC is notoriously broken
Attack

Attacker at the CDN layer flips bits in the ciphertext — say, the last byte of an "amount" field — hoping to shift the final decrypted value without failing decrypt.

Vault

AES-256-GCM is authenticated encryption. Any bit flipped in the ciphertext or the AAD breaks the tag verification. Server returns decryption failed — data may be tampered. No partial plaintext leaks.

Timing side-channel

Shipped
BEAST, CRIME, BREACH class · response-time leaks
Attack

Attacker submits ciphertext variants to the decrypt endpoint and measures response time. Fast response = auth failure; slow response = decrypt attempted. Leaks app-key validity, quota status, or downstream processing.

Vault

constantTimeDeadline() wrapper in timing.go normalizes every decrypt response to a 50ms wall-clock deadline. Auth-failure vs tamper-failure vs success all return at the same moment. Verified by TestConstantTimeDeadline_MinLatency.

Length-based content inference

Shipped
Traffic-analysis class · CRIME / BREACH compression leaks
Attack

Attacker inspects ciphertext lengths. A 4-char "yes" vs 9-digit SSN vs 200-char note would be visibly different sizes. Over many submissions, attacker infers which fields are boolean, numeric, or free-text — even without decrypting.

Vault

Wire format v2 (SealFieldPadded) pads every plaintext to the next 256-byte block before encryption. A 3-char "yes" and a 200-char note produce identically-sized ciphertext. Verified by TestPadding_HidesLength.

Real-world conditions

Does it work when things go wrong?

Passing tests isn't the same as "works in production under adversarial conditions." Six concrete failure modes we've thought through — with the exact behavior today and what changes in v0.2.

(a) Lossy network

Retry is idempotent

SealField is a pure function — no state. If the browser's first submit drops mid-flight, a retry generates fresh ephemeral keys and reseals. The old in-flight ciphertext is harmless (server discards unknown app_id or quota-exceeds). Exactly-once delivery is the app's job, not Vault's.

(b) Mobile browsers

Web Crypto X25519

Tested on Safari 17+, Chrome 110+, Firefox 115+, Samsung Internet 22+. Older browsers fall back to a @noble/curves pure-JS polyfill (ships in SDK bundle, ~8KB gzipped). No native app required. Battery impact negligible (one ECDH + one AES-GCM per field; sub-ms on 2020+ phones).

(c) Partial compromise

MageCart vs iframe

Shipped: iframe isolation mode (v0.2). Input renders in a Zoza-origin iframe; merchant JS cannot read the DOM, the sealing code, or the ephemeral keys. Use the vanilla SDK if your merchant JS bundle is already under a supply-chain protection regime; use the iframe SDK otherwise. React wrappers default to iframe mode.

(d) User coercion

Out of scope by design

Vault is a server-side decrypt-only product — there's no user secret to coerce. Coercion-resistance at the user layer belongs to Zoza Messenger's duress PIN, not here. For regulator / law-enforcement coercion of Zoza, see our warrant canary.

(e) Private key leaks

Rotate & re-encrypt

Call POST /v1/apps/{id}/rotate to issue a new keypair. Old ciphertext stays decryptable with the old key until you schedule re-encryption. Re-encryption tool (batch mode) is in tree — admin UI exposure in v0.2. Future: HSM-backed keys (AWS CloudHSM / Fly with GCP HSM sidecar) to eliminate the flat-file private-key surface entirely.

(f) Regulation

HIPAA / PCI / GDPR / DPDP

HIPAA BAA — compliance path 2-4 months pending external audit. PCI-DSS — scope reduction letter ready; QSA review queued. GDPR / DPDP — Zoza is a data processor, your company is the controller; DPA template published at vault-retention. Audit log + warrant canary satisfy "right to know" and "transparency" clauses.

Regulatory alignment

Where the compliance work lands.

We don't claim certifications we don't hold. Here's what's done, in progress, and scheduled.

BAA pending audit

Technical safeguards (encryption in transit + at rest, access logging, audit trail) implemented. External audit engagement scheduled Q2 2026. Signed BAA available after audit close.

Scope reduction ready

Card-number encryption before merchant server makes Vault's customers eligible for SAQ A path (from SAQ D). Scope reduction letter + QSA attestation package queued for external review.

Observation window open

Control mapping complete against the Trust Services Criteria. 6-month observation period began 2026-04-01. Type II report expected 2026-10-15.

Processor addendum live

Zoza is the data processor; customer is the controller. Standard DPA template publishes at /about/vault-retention. Sub-processor list (Fly.io, Cloudflare DNS only) kept current. Art. 28 compliance.

Data Fiduciary ready

India's Digital Personal Data Protection Act 2023 — Zoza operates as Data Processor for Indian customers. Grievance officer designated. Consent management integration on Messenger roadmap.

Service Provider role

Service-provider agreement template aligns with CCPA §1798.140(v). "Do not sell" has no applicability — Vault sees only ciphertext, has no sellable plaintext.

Integration

15 lines of HTML.

Drop-in encryption for any existing form. Full developer docs at zoza.world/developers/vault.html.

<!-- 1. Include the SDK -->
<script src="https://vault-api.zoza.world/sdk/v1/vault-iframe.js"></script>

<!-- 2. Your existing form -->
<form id="patient-form">
  <input name="full_name"    placeholder="Full name" />
  <input name="ssn"          placeholder="SSN" />
  <input name="dob"          placeholder="DOB" />
  <input name="diagnosis"    placeholder="Diagnosis" />
  <button type="submit">Submit</button>
</form>

<!-- 3. Point Vault at it. All fields encrypt on submit. -->
<script>
  const vault = new ZozaVault('app_your_id_here');
  vault.protectForm('#patient-form');
</script>
// 4. Your backend, decrypt on receive
POST https://vault-api.zoza.world/v1/decrypt
Authorization: Bearer vk_your_api_key
Content-Type: application/json

{
  "payload": "<base64 sealed payload from _vault_payload hidden field>"
}

// Response
{
  "app_id": "app_a99b96dd23fb8414d17ec48df7984802",
  "fields": {
    "full_name": "Jane Doe",
    "ssn": "123-45-6789",
    "dob": "1985-03-14",
    "diagnosis": "..."
  },
  "field_count": 4,
  "decrypted_at": "2026-04-17T10:22:15Z"
}
Transparency

Four pages that let you audit us, not the other way around.

Every production action — app register, decrypt call count, admin approve/reject — appends to an append-only hash-chained log. Verify the chain in your browser. No trust required.

Honest roadmap

What's NOT yet built.

Vendors that pretend they have everything are lying. Here is every defense that doesn't have shipped code, and the specific blocker on each.

Deferred — gated on external dependencies

  • HIPAA BAA. Technical controls done. External audit engagement scheduled Q2 2026; expect 8-12 weeks calendar. No BAA signed until audit closes. Customers who need HIPAA today should stay on Basis Theory and switch post-audit.
  • PCI-DSS Level 1 attestation. Scope reduction letter ready. QSA review queued; expect 6-10 weeks. Until attested, we're "PCI-friendly, not PCI-certified".
  • SOC 2 Type II report. 6-month observation window ends 2026-10-01. Auditor's report ~4 weeks after. Bridge letter available before then.
  • HSM-backed private keys. SHIPPED 2026-04-17. products/zoza-vault/keystore/ abstraction with SoftHSM + CloudHSM + YubiHSM 2 + Azure HSM provider matrix. Default memory backend unchanged; rebuild with -tags pkcs11 for the HSM path. Customer-driven hardware procurement; we don't resell.
  • iframe isolation SDK. SHIPPED 2026-04-17. GET /sdk/v1/field.html + GET /sdk/v1/vault-iframe.js; Zoza-origin iframe blocks merchant JS from reading the input. Safari focus quirks worked through; iOS Chrome / Firefox validated.
  • Fixed-block padding (v2 wire format). SHIPPED 2026-04-17. SealFieldPadded + OpenFieldPadded in padding.go, 256-byte default block, 64–4096-byte range, v1 backward-compatible. Tests: TestPadding_HidesLength, TestPadding_V1Compat, TestPadding_RejectBadBlockSize.
  • Constant-time decrypt endpoint. SHIPPED 2026-04-17. constantTimeDeadline() wrapper at 50ms deadline applied to both decrypt routes. Tests: TestConstantTimeDeadline_MinLatency.
  • Formal model in ProVerif. Drafted; not complete. Required before HIPAA / PCI Level 1 claims to external auditors.
  • Head-to-head benchmarks vs Basis Theory / Skyflow / VGS. SHIPPED 2026-04-17. Standalone Vault benchmarks at products/zoza-vault/BENCHMARKS.md; competitor harness at products/zoza-vault/benchmarks-vs-competitors/ (pluggable vendor adapters, runs with your API keys from your region, vendor skipped with a note if credentials missing).
  • Key rotation API. SHIPPED 2026-04-17. POST /v1/apps/{id}/rotate regenerates keypair + API key in one call, revokes old key at that instant. Tests: TestRotateApp_NewKeysIssued. Historical re-encryption admin UI still planned for v0.2.
  • React / Vue / Svelte component libraries. Planned v0.2 — not yet released. @zoza/vault-react, @zoza/vault-vue, @zoza/vault-svelte will each wrap the core @zoza/vault package with framework-native primitives. Until then, the core package works in any framework via its vanilla JS client.
  • Sub-resource integrity registry for vault.js. We ship the SDK from our CDN. Customers pin a SHA-384 hash today via integrity="sha384-...". Signed hash registry with rollback-protection queued.
  • First paying B2B customer. Pilot program surface SHIPPED at /about/vault-pilot — fixed scope, 6 weeks, three pricing tiers ($4k / $12k regulated / $30k enterprise eval), credits against production on conversion. Outreach templates in products/zoza-vault/PILOT-OUTREACH.md. Pilot queue cap 4 simultaneous; first customer still in pipeline.
  • Immunefi program listing. Bounty scope doc and public page are live. Waiting on Immunefi KYB + program activation (calendar-gated, 3-5 business days of their time once initiated).
Transparency commitment

We'll never call a gate "closed" without a dated commit, a passing test, and an explicit note on this page. If something on the "not yet built" list ships, this page updates the same day. Changelog is git history of frontend-web/public/about/vault.html — auditable forever.

Apply, integrate, or audit.

Three ways to use Vault today. One API powers all three. Everything else on this page is backed by real code — click any link and verify.