open-standards/openpay/examples/pci-zero
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science d14f583d92 Cargo: no minimum-supported-version floor, because the policy is latest stable
The repository tracks the latest stable Rust, pinned at the root. A
`rust-version` key states a *floor* -- the oldest toolchain a crate supports --
which is a different promise, and one this repository does not make. It also
said 1.97 while the pin was 1.98.0 and nothing verified it, so it was a claim in
the wrong direction and unchecked in both.

So it goes, everywhere:

    59  rust-version = "1.97"            (workspace and package declarations)
   417  rust-version.workspace = true    (members inheriting them)
   476  files, 476 deletions, 0 insertions

The 417 are why this is not the sixty-file change it looks like from a grep for
the value. A member inheriting a key that no longer exists does not fall back to
a default -- its manifest stops resolving. Removing the declarations without the
inherits would have broken every workspace that has members.

Deliberately untouched: `joule-code-rs/vendor/wtransport/Cargo.toml`. That is a
vendored third-party crate declaring a floor about itself, not about us, and our
pin is above it. Editing vendored source to satisfy our own policy would make
the copy diverge from upstream for no benefit.

Verified rather than assumed:

- all 493 `Cargo.toml` files in the tree parse (`tomllib`)
- `cargo metadata --no-deps --locked` resolves on `joule-code-rs`, `wai-rs`,
  `openpay`, `mesh-rs`, `sandbox-rs` and `jcp-rs` -- the check that actually
  exercises workspace inheritance, which a TOML parse does not
- no `Cargo.lock` changed; `rust-version` is not lockfile state

`wai/wai-verify/Cargo.toml` already carried a comment saying it omits
`rust-version` on purpose, so this makes the rest of the tree agree with the one
crate that had already decided.

What this gives up, stated plainly: cargo can no longer greet an older
toolchain with "requires rustc 1.97" and will instead fail wherever the code
first needs something newer. That is the cost of not maintaining a floor, and
it is the right cost when the floor was fiction.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-03 17:42:20 -05:00
..
src Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00
Cargo.toml Cargo: no minimum-supported-version floor, because the policy is latest stable 2026-11-03 17:42:20 -05:00
README.md Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00

pci-zero — Code-form demonstration of the PCI-zero topology

A runnable counterpart to compliance/pci-zero-architecture.md. Wires the same components — vault, orchestrator, card rail — in a single Rust process so the data-flow is auditable as one program.

What this demonstrates

The binary exercises four boundary crossings:

  1. Browser → vault. collection_iframe::submit constructs a CardData from a test PAN and calls Vault::tokenize. The PAN exists in the Rust process for the duration of this call and no longer; ZeroizeOnDrop wipes the bytes when submit returns.
  2. Merchant → orchestrator. merchant::Merchant::checkout accepts a VaultRef and a Money amount. The merchant module deliberately does not import op-vault; it cannot construct a CardData or a RawPan. The orchestrator routes the intent to the registered card rail.
  3. Driver → vault. mock_card::PciZeroCardAcquirer::authorize is the only place in the entire program (other than collection_iframe) that holds plaintext PAN. It calls Vault::detokenize, posts to the (mock) acquirer, and immediately drops the CardData.
  4. Driver → acquirer. A mock acquirer response — Settled, with the masked card number for the receipt.

The merchant code path (the merchant module) holds only VaultRef, Money, and Payment<S> typestate values. There is no RawPan import in the merchant module, no CardData value, no PAN-bearing string.

What this does NOT demonstrate

  • A real network boundary. "Boundary" here is Rust module visibility. In deployment the boundary is mTLS between two processes on different hosts.
  • A production cryptographic backend. The example uses op_vault::InMemoryVault, the AES-256-GCM-SIV reference implementation. Production deployments use a KMS- or HSM-backed vault per compliance/hsm-kms-guidance.md.
  • Card-on-file flow. The example tokenizes with TokenizationPolicy::default() (random, reusable, no TTL). Card-on-file uses TokenizationPolicy::card_on_file(); 3DS challenge flow uses TokenizationPolicy::single_use(120).

How to run

cd /path/to/open-standards/openpay
cargo run -p pci-zero

Expected output:

=== OpenPay pci-zero topology demo ===
Boundary crossings in this run:
  [B1] browser  -> vault       (tokenize, raw PAN)
  [B2] merchant -> orchestrator (VaultRef only)
  [B3] driver   -> vault       (detokenize, scoped)
  [B4] driver   -> acquirer     (raw PAN, mock)

[B1] Customer types card into vault-served iframe.
     vault returned VaultRef = tok_v7_...

[B2] Merchant runs orchestrator with VaultRef.

[B3/B4] driver detokenized + posted to mock acquirer.
       terminal status: APPROVED
       attempts: 1
       psp_payment_id: pci-zero-card_acq_ORDER-PCI-ZERO-001
         [0] rail=Card driver=pci-zero-card outcome=Settled

=== Done. Merchant code path held VaultRef only. ===

Source layout

examples/pci-zero/
├── Cargo.toml          workspace member; depends on op-core,
│                       op-vault (in-memory), op-orchestrator,
│                       op-rails-card, op-fraud
├── README.md           this file
└── src/main.rs
    ├── mod merchant          out-of-scope code path
    ├── mod collection_iframe vault-served browser stand-in
    ├── mod mock_card         CardAcquirer that detokenizes
    └── fn main               wires the three modules together

Mapping back to the deployment topology

In-process construct Deployment counterpart
Arc<dyn Vault> mTLS HTTP API exposed by the vault service in the CDE segment
InMemoryVault::ephemeral KMS- or HSM-backed vault implementation (see compliance/hsm-kms-guidance.md)
mod merchant merchant application server in the connected-to-scope segment
mod collection_iframe TLS-isolated iframe served from the vault's own domain
mod mock_card real PSP driver (e.g. op-rails-card Hyperswitch impl) talking to the acquirer over TLS 1.2+