Skip to content

feat(nexusswift): zero-trust ISO 20022 pacs.008 cross-border clearing hub - #325

Merged
thecelestialmismatch merged 2 commits into
mainfrom
claude/swift-cross-border-transfer-qwp7qj
Sep 3, 2026
Merged

feat(nexusswift): zero-trust ISO 20022 pacs.008 cross-border clearing hub#325
thecelestialmismatch merged 2 commits into
mainfrom
claude/swift-cross-border-transfer-qwp7qj

Conversation

@thecelestialmismatch

Copy link
Copy Markdown
Owner

Summary

Adds NexusSwift, a self-contained cross-border financial messaging and settlement engine that implements the technical core of a SWIFT-style clearing hub: ISO 20022 pacs.008.001.10 customer credit transfers, RSA-4096/PSS non-repudiation, UETR-keyed idempotency, and atomic hash-chained double-entry settlement.

This is a new, independent project, not a HoundShield change. It lives in its own top-level nexusswift/ directory with its own dependencies, test harness and container, shares no code or imports with the web plane or the proxy, and can be extracted at any time with git subtree split -P nexusswift. Nothing outside nexusswift/ is touched except two .gitignore additions (see below).

The one shared-file change is deliberate and necessary: the repo's .gitignore carries a blanket tests/ rule that matches at any depth and would have silently dropped the entire 80-test security harness from every commit. Git cannot re-include a file whose parent directory is excluded, so the directory is un-excluded first and the files second.

Closes #

Change type

  • Bug fix
  • New capability
  • Security hardening
  • Documentation or developer-experience improvement
  • Refactor or maintenance
  • Breaking change

Impact and operating considerations

  • Authentication, sessions, identity, or authorization
  • Sensitive-data handling, logging, telemetry, or outbound requests
  • Proxy detection, policy evaluation, block/quarantine behavior, or performance
  • Database schema, migrations, retention, or access controls
  • Deployment, environment variables, integrations, or scheduled work
  • Evidence, reporting, or audit-chain behavior
  • None of the above

Operational impact, threat-model note, and rollback plan:

Operational impact on this repository: none. No existing file is imported, executed, built or deployed differently. The web plane and the proxy do not reference nexusswift/, and it is not wired into any build, workflow or deploy path. Its SQLite schema is its own database file, entirely separate from Supabase — it introduces no migration into the existing supabase/migrations/ sequence and no new environment variable for any existing service.

Rollback: delete the nexusswift/ directory and revert the two .gitignore hunks. There is no state to unwind, no migration to reverse and no configuration to restore.

Threat model (internal to the new component). NexusSwift's own boundary assumption is that the transport is already authenticated and encrypted; it defends the message and the ledger, not the channel. Within that boundary the controls are: RSA-PSS over invariant signed bytes (tamper), UETR primary key plus per-sender MsgId unique index (replay), settlement lock plus BEGIN IMMEDIATE (double-spend), account-to-agent ownership checks (cross-account debits), pinned namespace URI (message-type confusion), pre-parse DTD rejection and a 1 MiB cap (XXE / billion laughs), and a SHA-256 hash chain over the journal (post-hoc mutation).

Pipeline order is itself a control and is documented as such: idempotency is checked before signature verification so a replay flood cannot be amplified into CPU exhaustion, and authentication precedes every balance read so an unauthenticated caller cannot probe balances through error codes or timing.

Validation

The web-plane and proxy checks below are listed unchecked because they were not run and do not apply — this PR changes no TypeScript, no React, no proxy code and no schema. Running them would validate nothing about this diff.

  • Web plane: npx tsc --noEmit — N/A, no TS changed
  • Web plane: npm run lint — N/A
  • Web plane: npm run test:coverage — N/A
  • Web plane: npm run build — N/A
  • Proxy: npm run lint — N/A
  • Proxy: npm run test:coverage — N/A
  • Proxy: npm run bench — N/A
  • Manual verification (described below)

Results and manual verification:

python -m unittest discover -s tests -t . 
  → Ran 80 tests ... OK   (three consecutive runs: 11.3s / 14.2s / 13.8s, no flakes)

python run.py --key-size 2048
  → 12/12 runtime checks passed, ledger reconciled, exit 0

python run.py --db /tmp/vol.db --key-size 2048      (x3, same persistent volume)
  → 12/12 passed on every run; ledger accumulated to 18 payments,
    18 ledger legs, 102 audit records, hash chain intact across restarts

python run.py --db /tmp/vol.db --healthcheck        → Healthy, exit 0
python run.py --migrate-only                        → exit 0
python -m compileall core tests run.py              → clean

Coverage is by behaviour rather than by line: 80 tests across valid clearing, 12 tamper vectors, replay (including a 20-way concurrent burst), liquidity boundaries (including a 50-way concurrent burst), participant control and key rotation, schema/XXE/precision, ledger chain integrity, and the crypto primitives.

Four defects were found by running the system, not by reading it. Each is fixed with a regression test:

  1. UETR denial-of-service. Rejections before authentication were journalled, which consumed the UETR. Any party who observed a UETR in flight could submit it with a garbage signature and permanently block the genuine payment behind it. Fixed by splitting attributable from unattributable rejection: a message that fails authentication is unattributable, is audit-logged only, and cannot consume a UETR; only a cryptographically proven sender spends its own. Guarded by test_a_forged_attempt_cannot_burn_a_genuine_uetr.
  2. Audit hash-chain race. append_audit did SELECT-head → hash → INSERT without a transaction. Concurrent appends sharing actor, event, UETR and head computed identical hashes and collided on the UNIQUE constraint — nondeterministic, and it surfaced only under a 16-way replay burst on the second persistent run. Now serialised under BEGIN IMMEDIATE. Guarded by test_concurrent_audit_appends_do_not_collide.
  3. Balance overwrite on re-seed. open_account overwrote an existing account's balance, creating value from nothing and invalidating journal replay — reconciliation correctly reported the drift; the bug was in the reset, not the detector. It now updates terms (status, credit line) only. Guarded by test_reopening_an_account_never_overwrites_its_balance.
  4. Process hang at exit. aiosqlite 0.22 worker threads are non-daemon, so an unclosed connection blocks interpreter exit indefinitely — in a container, until the orchestrator SIGKILLs it past the grace period. Threads are now marked daemon as a backstop, with close() still the correct path.

Not verified: the container image was not built. This sandbox has the Docker CLI but no daemon, so docker build could not run. The Dockerfile is reviewed but unbuilt, and that should be confirmed before any use.

Review checklist

  • The change is focused and does not include unrelated refactoring.
  • Tests cover changed behavior, or the omission is explained above.
  • No credentials, customer data, sensitive prompts, or production exports were added. All key material is generated at runtime; no key, PEM or secret is committed.
  • Public claims are scoped, evidence-based, and consistent with the selected deployment boundary. The README states plainly what this cannot provide — SWIFT membership, correspondent or RTGS access, money-transmitter licensing, and a BSA/AML programme are not software problems and are not claimed.
  • Documentation and configuration guidance were updated where needed. nexusswift/README.md documents the architecture, every design decision with its rationale, and the full control-to-test mapping; every test name it cites was verified to exist.
  • Database migrations, configuration changes, and rollout dependencies are documented where applicable. NexusSwift's schema is internal to its own SQLite file and adds nothing to the Supabase migration sequence.
  • I performed a self-review and addressed obvious failure paths.

🤖 Generated with Claude Code

https://claude.ai/code/session_019gDSUkVHchizjCoAKmprhg


Generated by Claude Code

… hub

Adds NexusSwift, a self-contained cross-border financial messaging and
settlement engine. It is an independent project living in its own top-level
directory with its own dependencies and test harness; it shares no code with
the rest of this repository and can be extracted with `git subtree split`.

What it implements — the actual technical core of a SWIFT-style hub:

  * ISO 20022 pacs.008.001.10 assembly and namespace-aware strict parsing
  * RSA-4096 / RSASSA-PSS non-repudiation over invariant signed bytes
  * UETR-keyed idempotency enforced by storage constraints, not app checks
  * atomic double-entry settlement with a SHA-256 hash-chained journal
  * BIC/ISO 9362, ISO 4217 minor units, and per-currency amount precision

Design points that are load-bearing rather than stylistic:

  * Money is integer minor units with a looked-up exponent (JPY 0, BHD 3,
    CLF 4). An unknown currency is rejected, never defaulted to 2. An amount
    with more precision than its currency admits is rejected, not rounded —
    rounding would settle a different number than the one that was signed.
  * The signed payload is stored verbatim and never re-serialised, so a
    settled payment still verifies out of the database years later.
  * PSS salt is pinned to 32 bytes rather than MAX_LENGTH, because Java and
    most HSMs default to 32 and would reject a 446-byte-salted signature.
  * Pipeline order is a security property: idempotency precedes signature
    verification so a replay flood cannot be amplified into CPU exhaustion;
    authentication precedes every balance read.

Four defects were found by exercising the system rather than by reading it,
and each is fixed with a regression test:

  * Unauthenticated rejections were journalled, consuming the UETR. Any
    party who observed a UETR in flight could submit it with a garbage
    signature and permanently block the genuine payment. Rejections before
    authentication are now audit-only; only a cryptographically proven
    sender spends its UETR.
  * The audit hash chain had a read-modify-write race. Concurrent appends
    sharing an actor, event, UETR and head computed identical hashes and
    collided on the UNIQUE constraint under a replay burst. Appends now run
    under BEGIN IMMEDIATE.
  * open_account overwrote an existing account's balance, creating value
    from nothing and invalidating journal replay. It now updates terms only.
  * aiosqlite worker threads are non-daemon, so an unclosed connection hung
    process exit indefinitely — in a container, until the orchestrator
    SIGKILLed it past the grace period.

Verification: 80 tests, standard-library unittest only, three consecutive
clean runs. The demo pipeline passes 12/12 runtime checks in-memory and
across three successive runs against a persistent volume, with the ledger
accumulating and reconciling across restarts.

Not verified here: the container image was not built, as this sandbox has
the Docker CLI but no daemon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gDSUkVHchizjCoAKmprhg
@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
compliance-firewall-agent Ready Ready Preview Sep 2, 2026 8:05am UTC

@supabase

supabase Bot commented Sep 2, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project qifynzuyrdxmxlumpsrq because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

The Security Audit workflow's `npm audit (prod deps)
(compliance-firewall-agent)` leg fails on `browserslist <=4.28.6`:

  GHSA-c83g-rgw3-j3cx  unbounded memory growth (no cache eviction) -> OOM
  GHSA-73wf-gq98-2v4g  uncaught crash / prototype write via untrusted
                       browserslist-stats.json custom stats

This is not caused by the NexusSwift change in this branch, which adds a
Python project and touches no npm dependency. The same check failed on two
unrelated Dependabot PRs on 2026-09-01 at 19:12 and 19:26 UTC, roughly ten
hours before this branch existed, and the last scheduled main run (08-31)
predates the advisory. The workflow only runs on pull_request and a Monday
schedule, so main will not re-run on its own until then; every PR opened
in the meantime inherits the red.

Ported the remediation rather than waiting: `npm audit fix
--package-lock-only --omit=dev`. This is lockfile-only and no-ops once the
base branch carries an equivalent bump. browserslist is a transitive
dependency, absent from package.json, and all four dependents already
accept the new version (@babel/helper-compilation-targets ^4.24.0,
autoprefixer ^4.28.6, update-browserslist-db >=4.21.0, webpack ^4.28.1),
so no direct dependency version changes -- the same shape as the
2026-08-08 baseline clearance the workflow header documents.

  browserslist              4.28.6      -> 4.28.8
  baseline-browser-mapping  2.10.42     -> 2.11.20
  caniuse-lite              1.0.30001806 -> 1.0.30001810
  electron-to-chromium      1.5.389     -> 1.5.420
  node-releases             2.0.51      -> 2.0.54
  update-browserslist-db    1.2.3       -> 1.3.2

Nothing added or removed; package.json untouched.

Verified: failure reproduced locally first, then npm audit --omit=dev
--audit-level=high reports 0 vulnerabilities; npm ci, npx tsc --noEmit and
npm run build all pass; the proxy matrix leg was already clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gDSUkVHchizjCoAKmprhg

Copy link
Copy Markdown
Owner Author

CI: npm audit (prod deps) (compliance-firewall-agent) — not this PR's failure, fix ported

Failing check: npm audit (prod deps) (compliance-firewall-agent) on 939c166.

browserslist  <=4.28.6
Severity: high
  GHSA-c83g-rgw3-j3cx  unbounded memory growth (no cache eviction) → eventual OOM
  GHSA-73wf-gq98-2v4g  uncaught crash / prototype write via untrusted
                       browserslist-stats.json custom stats (normalizeStats)
1 high severity vulnerability

Why it isn't this PR's. This branch adds a standalone Python project under nexusswift/ plus two .gitignore lines. It touches no package.json, no lockfile, and no npm dependency. The same check failed on two unrelated Dependabot PRs on 2026-09-01 at 19:12 and 19:26 UTC — roughly ten hours before this branch existed — and the last scheduled main run (08-31 12:12) was green, predating the advisory. security-audit.yml runs only on pull_request and a Monday cron, so main won't re-run on its own until then and every PR opened meanwhile inherits the red.

Ported rather than waited. No Dependabot PR covers browserslist yet, so I applied the remediation the workflow header itself documents — npm audit fix --package-lock-only --omit=dev, lockfile-only. It no-ops once the base branch carries an equivalent bump.

package from to
browserslist 4.28.6 4.28.8
baseline-browser-mapping 2.10.42 2.11.20
caniuse-lite 1.0.30001806 1.0.30001810
electron-to-chromium 1.5.389 1.5.420
node-releases 2.0.51 2.0.54
update-browserslist-db 1.2.3 1.3.2

Nothing added or removed; package.json untouched. browserslist is transitive — absent from package.json — and all four dependents already accept the new version (@babel/helper-compilation-targets ^4.24.0, autoprefixer ^4.28.6, update-browserslist-db >=4.21.0, webpack ^4.28.1), so no direct dependency version changed. Same shape as the 2026-08-08 baseline clearance the workflow comment describes.

Verified before pushing — failure reproduced locally first, then:

check result
npm audit --omit=dev --audit-level=high 0 vulnerabilities (was 1 high)
npm ci clean, 861 packages
npx tsc --noEmit exit 0
npm run build exit 0, all routes emitted
proxy matrix leg already 0 vulnerabilities

No test was skipped, disabled or quarantined, and no re-run was spent — the failure was real and is now fixed at source.


Generated by Claude Code

thecelestialmismatch pushed a commit that referenced this pull request Sep 2, 2026
The Security Audit workflow's `npm audit (prod deps)
(compliance-firewall-agent)` leg fails on `browserslist <=4.28.6`:

  GHSA-c83g-rgw3-j3cx  unbounded memory growth (no cache eviction) -> OOM
  GHSA-73wf-gq98-2v4g  uncaught crash / prototype write via untrusted
                       browserslist-stats.json custom stats

Not caused by this branch. `git diff --stat HEAD~2 -- package.json
package-lock.json` is empty: the two commits here touch three source files and
documentation, no dependency. The same check failed on unrelated Dependabot PRs
on 2026-09-01 and is red on the base branch, so every PR opened since inherits
it. #325 hit the identical failure and reached the identical remediation.

Ported rather than waited on, because waiting on another PR to merge is still
waiting: `npm audit fix --package-lock-only --omit=dev`. Lockfile only,
4.28.6 -> 4.28.8, a patch bump inside the range the tree already resolves. It
no-ops once the base branch carries the same change.

Reproduced and verified:

  before   npm audit --omit=dev  ->  1 high severity vulnerability
  after    npm audit --omit=dev  ->  found 0 vulnerabilities

Re-validated against the new lockfile rather than the old node_modules:
`npm ci` (861 packages, browserslist resolves to 4.28.8), `npx tsc --noEmit`
clean, `npm run lint` 0 errors, `npm run test:coverage` 223 files and 3099/3099
passed with the coverage gate met, `npm run build` succeeded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UNmNvaCuGcZz418MoTyB5
@thecelestialmismatch
thecelestialmismatch marked this pull request as ready for review September 3, 2026 21:38
@thecelestialmismatch
thecelestialmismatch merged commit 08a062c into main Sep 3, 2026
11 checks passed
thecelestialmismatch pushed a commit that referenced this pull request Sep 3, 2026
…conflicts

main advanced 3bc6df7 -> 23856c7 while this branch sat ready for review,
bringing the NexusSwift addition, the Mode B audit-log hash chain, a readiness
gate, and a refactor deleting 29 unreferenced files and 9 runtime dependencies.
Three conflicts, resolved on their merits rather than by picking a side:

CLAUDE.md — Session Start Protocol, step 3. Both sides rewrote the same line.
main replaced the health check with the token-gated `/api/health/ready`, which
is strictly better: `/api/health` is a bare liveness probe that returns
`{"status":"ok"}` under every failure condition. Taking main's version alone
would have dropped a real distinction this branch adds, because
`lib/health/service-status.ts` reports `payments` / `payments_webhook` from the
same diagnostics the new weekly alert uses — so readiness answers "is the money
path CONFIGURED", and only the reconciler answers "was a sale actually missed".
Kept main's route, appended that distinction.

tasks/todo.md and tasks/lessons.md — append-style logs where each side is a
pure addition. Both sides kept; main's dated section opens the lessons block
and this branch's entries follow inside it.

Verified the deletion refactor does not undercut this branch: none of the nine
removed dependencies (@react-three/fiber, @remotion/*, date-fns, next-themes,
react-hook-form, react-markdown, remotion, three) is imported by
lib/stripe/report-fulfillment.ts, lib/stripe/money-path.ts,
app/api/cron/reconcile-orders/route.ts or
lib/email/templates/money-path-alert.ts.

Gates re-run on the merged tree against the new lockfile, not carried over:
npm ci exit 0 · tsc --noEmit clean · 3150 app tests pass (227 files, exit 0) ·
npm run build exit 0, registering both /api/cron/reconcile-orders and
/api/health/ready · eslint 0 errors, none in the new files ·
npm audit --omit=dev --audit-level=high (the exact CI command) exit 0 ·
verify-structure PASS · 120 proxy tests pass · proxy bench p99 0.883 ms
against a 10 ms budget.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GRc3QBQMgVNecjE7TLY1sG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants