open-standards/openpay/crates
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science 209e39c62d
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 13s
standards workspaces build + test / macOS energy backends (pull_request) Has been skipped
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m59s
standards workspaces build + test / openpay (openpay, openpay, linux-arm64) (pull_request) Successful in 5m24s
op-screening: the audit log can outlive the process that wrote it
The crate header claims every `Screener::screen` call is recorded into a
chained, signed audit log "so that regulators can inspect the screening
history of any payment attempt after the fact". The log could not do
that. Its whole public surface was `new`, `verifying_key`, `entries`,
`record` and `verify`: no save, no load, and no way to supply an
existing key. The only constructor mints a fresh keypair from `OsRng`,
and every call site in the repo is `AuditLog::new()`.

So the trail died with the process, twice over. The rows were gone, and
the key was gone — which is the worse half: on the next run `new()`
mints a new keypair, so the verifying key a regulator ingested
out-of-band no longer matches anything, and rows an operator had
dumped to disk by hand could never be verified by any process, because
`verify` only ever checked against the key the running log happened to
own. "After the fact" was the one time the log could not be read.

The same crate already persists its other long-lived state —
`SanctionsIndex::save_snapshot` / `load_snapshot`, CBOR via ciborium —
so this follows that precedent rather than inventing one:

- `AuditLog::with_signing_key` signs with a key the operator already
  holds, instead of rotating it on every start.
- `save_entries` / `load_entries` persist the rows. The signing key is
  deliberately **not** written: a private key does not belong in a file
  beside the rows it signed, and this crate ships
  `compliance/hsm-kms-guidance.md` saying where it does belong.
- `resume(signing_key, entries)` continues the chain onto its real head,
  so the next row's `prev_hash` and `seq` follow the pre-restart rows
  rather than starting over. It verifies them first: resuming onto rows
  this key did not write would build a log that fails verification
  later, at the regulator, over a chain nobody can repair — so it fails
  here instead.
- `verify_entries(entries, verifying_key)` is a free function, which is
  the regulator's path: rows plus the published public key, with no
  signing key and no live log. `AuditLog::verify` delegates to it, so
  the two invariants have one implementation rather than two that can
  drift.

Additive throughout — `new`, `Default` and every existing call site are
unchanged.

Tests: a restart writes two rows, loads them back, resumes and appends a
third, and the whole chain verifies with `seq` continuing at 2; a
regulator verifies dumped rows from the public key alone, and both
invariants still bite on the detached rows (an edited row breaks its
chain hash at index 0, a removed row breaks the chain); resuming under
the wrong key is refused; and `a_fresh_log_cannot_verify_another_runs_rows`
pins the limit of `new()` that motivated all of it.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-17 09:35:49 -04:00
..
op-3ds2 op-3ds2: an unconverted foreign amount is not under an EUR SCA cap 2026-10-17 04:27:27 -04:00
op-batch Ingest batch 3: 9 more crates across openpay / smart-byte / eoc 2026-05-24 01:09:35 -04:00
op-bin ingest wave E: 3 crates · op-bin + smart-byte-tendermint + eoc-safety 2026-05-24 16:52:07 -04:00
op-bnpl chore(deps): update dependencies for Rust 1.98, pinning the crypto generations 2026-08-26 12:19:50 -04:00
op-cli Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00
op-connect op-connect: an unrun PEP screen is not a clearance 2026-10-17 04:13:10 -04:00
op-core Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00
op-crypto-extra ingest wave F: 3 crates · op-crypto-extra + smart-byte-acdc + eoc-memory 2026-05-24 17:28:24 -04:00
op-dispute Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-dispute-evidence ingest wave A2: 3 crates · op-dispute-evidence + smart-byte-edv + eoc-agent 2026-05-24 11:42:26 -04:00
op-driver-sdk Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00
op-emv Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-ewallets-asia chore(deps): update dependencies for Rust 1.98, pinning the crypto generations 2026-08-26 12:19:50 -04:00
op-ffi-jni Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-ffi-swift Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-fraud Repo-wide toolchain refresh + jouleclaw policy/sandbox updates 2026-06-07 17:04:45 -04:00
op-fraud-graph Repo-wide toolchain refresh + jouleclaw policy/sandbox updates 2026-06-07 17:04:45 -04:00
op-fx Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-graph Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-iso8583 Ingest batch 2: 9 new crates across openpay / smart-byte / eoc 2026-05-23 18:12:24 -04:00
op-iso20022 Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-ledger Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-mobile-wallets chore(deps): update dependencies for Rust 1.98, pinning the crypto generations 2026-08-26 12:19:50 -04:00
op-openbanking ingest wave A4: 3 crates · smart-byte-atproto + op-openbanking + eoc-distributed 2026-05-24 12:22:09 -04:00
op-orchestrator Repo-wide toolchain refresh + jouleclaw policy/sandbox updates 2026-06-07 17:04:45 -04:00
op-payouts ingest wave B: 3 crates · op-payouts + smart-byte-nostr + eoc-streaming 2026-05-24 13:39:09 -04:00
op-rails-a2a Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-rails-card Parallel ingest: 9 issues' worth of work across openpay / smart-byte / eoc 2026-05-23 13:24:05 -04:00
op-rails-crypto Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-rails-x402 op-rails-x402: x402 agentic-payment rail (HTTP 402 acceptance) 2026-06-25 15:43:20 -04:00
op-reconciliation Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-refund Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-revrec ingest batch 5 (salvage): 2 crates from 9-agent dispatch (session-limit hit mid-batch) 2026-05-24 09:28:38 -04:00
op-routing Ingest batch 2: 9 new crates across openpay / smart-byte / eoc 2026-05-23 18:12:24 -04:00
op-screening op-screening: the audit log can outlive the process that wrote it 2026-10-17 09:35:49 -04:00
op-server Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-settlement Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-statements ingest wave C: 3 crates · op-statements + smart-byte-activitypub + eoc-observability 2026-05-24 13:51:09 -04:00
op-subscriptions Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-tax chore(deps): update dependencies for Rust 1.98, pinning the crypto generations 2026-08-26 12:19:50 -04:00
op-vault op-vault: test that a token is independent of the PAN, not free of "4242" 2026-09-30 23:07:55 -04:00
op-wasm Initial import — three open standards under one shelf 2026-05-23 07:58:09 -04:00
op-webhook fix(op-webhook): import KeyInit — hmac 0.13 moved new_from_slice off the Mac trait, breaking compute_signature 2026-08-26 09:28:40 -04:00