Inkress ADRsarchitecture decisions
Overview / Payments
ADR 0007

The CDE runs entirely on Cloudflare with envelope key custody, no HSM

AcceptedPayments

Context

Having decided cardholder data lives only in an edge CDE (ADR 0006), two questions follow: on what platform does the CDE run, and how are its keys custodied to satisfy PCI DSS v4 for the data that protects account data (req 3.6/3.7). The keys in play are the RSA/ECDH card-decryption key at the edge and the KEK that seals PanTokens at rest in the control plane.

Two temptations had to be resisted: spreading the CDE across clouds (card page on one, decrypt on another), and assuming a hardware HSM is mandatory for card-key custody.

Decision

Run the whole CDE on one assessed platform, Cloudflare, and custody keys with envelope encryption under a separately-stored KEK — the third PCI-compliant method (req 3.6), no HSM — with an env switch to AWS KMS kept ready but off by default.

  • All-Cloudflare CDE. The card page (Workers Static Assets), the decrypt/tokenize Worker, key custody (Cloudflare Secrets Store) and e-skimming monitoring (Page Shield) all sit on Cloudflare, whose PCI DSS v4 AoC names Workers and Secrets Store in scope. One platform → one AoC to inherit and one deploy model.
  • Envelope encryption, KEK in a software vault local to each service. Each sealed blob uses a per-record random data key (AES-256-GCM); only the data key is wrapped by the KEK. The two sensitive keys live where their code runs and neither store reaches across: the card-decryption key in Cloudflare Secrets Store (with the Worker), the PanToken KEK in the control plane's own secrets (with commerce-payments). Every blob carries a kek_version (and the fingerprint carries its key version) so keys rotate without re-encrypting or migrating data.
  • KMS is a config switch, not a rewrite. KEY_CUSTODY=secrets-store|kms (default secrets-store) routes the data-key wrap/unwrap to AWS KMS when set; the envelope format is unchanged, so a hardware-HSM mandate is a deployment change, not a code change. Switching custody is a key-rotation event (a blob wrapped under one mode is not readable by the other).
  • Tamper-evident audit ledger. The control-plane audit log is append-only in practice: a Postgres trigger raises on any UPDATE/DELETE/TRUNCATE of historical rows, and each row is hash-chained to its predecessor (rowHash = sha256(prevHash ‖ canonical(row))), so a tampering attempt is blocked outright, and any out-of-band edit remains detectable by a verifier walking the chain. (Revoking UPDATE/DELETE on the app role is an additional operator step, not the enforced mechanism.)

Consequences

  • Positive: one PCI AoC and one deploy model for the whole CDE; no cross-cloud secret movement; key rotation is a version bump, not a data migration.
  • Positive: the HSM decision is deferred cheaply — if an acquirer ever mandates one, flip KEY_CUSTODY=kms (a Worker calls KMS directly over its signed HTTPS API) and keep compute, the card page and Page Shield on Cloudflare, rather than moving clouds.
  • Neutral / to revisit: Cloudflare attests it does not store/process/transmit cardholder data — the moment our Worker decrypts a card, that Worker is our CDE and our scope; the AoC covers the infrastructure beneath it, not our PAN handling. Cloudflare is leaned on for compute and e-skimming, not as a payment gateway or 3-D Secure provider.
  • Neutral / to revisit: the KMS hybrid adds a cross-cloud call on the key path (amortized by caching decrypted data keys per rotation, not per charge) and an availability coupling to AWS; worth it only under a hardware-key mandate.

Alternatives considered

  • Split platform (card page on Cloudflare, decrypt on AWS Lambda): rejected — two vendors, two AoCs, two deploy models and cross-cloud secret movement. Strictly more scope.
  • All-AWS (CloudFront/S3 page, Lambda decrypt, KMS/CloudHSM key): the only all-in-one path to hardware-HSM custody, but AWS has no managed e-skimming product, so we would hand-build what Page Shield gives us on the most audit-sensitive surface. Kept as a documented fallback for a hardware-HSM mandate or portability — but the KMS-from-Cloudflare hybrid usually beats a full move.
  • Hardware HSM for the card key: rejected as a default — PCI req 3.6 explicitly accepts a separately-stored KEK, and the acquirer agreement carries no HSM clause, so an HSM is an option, not a mandate. The KEY_CUSTODY=kms switch preserves the option.
← 0006 Cardholder data is isolated to an edge CDE; the platform handles only PanTokens 0008 Payments run through a provider-agnostic adapter, chosen per merchant →