| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
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
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]> |
||
| .. | ||
| op-3ds2 | ||
| op-batch | ||
| op-bin | ||
| op-bnpl | ||
| op-cli | ||
| op-connect | ||
| op-core | ||
| op-crypto-extra | ||
| op-dispute | ||
| op-dispute-evidence | ||
| op-driver-sdk | ||
| op-emv | ||
| op-ewallets-asia | ||
| op-ffi-jni | ||
| op-ffi-swift | ||
| op-fraud | ||
| op-fraud-graph | ||
| op-fx | ||
| op-graph | ||
| op-iso8583 | ||
| op-iso20022 | ||
| op-ledger | ||
| op-mobile-wallets | ||
| op-openbanking | ||
| op-orchestrator | ||
| op-payouts | ||
| op-rails-a2a | ||
| op-rails-card | ||
| op-rails-crypto | ||
| op-rails-x402 | ||
| op-reconciliation | ||
| op-refund | ||
| op-revrec | ||
| op-routing | ||
| op-screening | ||
| op-server | ||
| op-settlement | ||
| op-statements | ||
| op-subscriptions | ||
| op-tax | ||
| op-vault | ||
| op-wasm | ||
| op-webhook | ||