docs/SECURITY_THREAT_MODEL.md and docs/audits/2026-07-28-production-readiness-audit.md.
Trust boundaries
Attacks defended
Database compromise redirecting payments
Database compromise redirecting payments
Blocked by client-side on-chain re-derivation. A self-consistent tamper fails because verification anchors to the chain. See DB-display-only trust.
Recipient changed between quote and signature
Recipient changed between quote and signature
The widget re-reads
effectiveRecipient immediately before signing. Mismatch produces a recipient_changed state with no signature sent.Merchant not attested
Merchant not attested
create-payment-intent performs an eth_call against MerchantRegistry.effectiveRecipient on the payment chain. No attestation returns HTTP 422 with a stable error code, so the customer never signs a transaction that would revert.SSRF via webhook URL
SSRF via webhook URL
_shared/url-allowlist.ts is the single source of truth. merchants.webhook_url carries a CHECK constraint via is_safe_webhook_url(text), and the delivery function re-validates at fetch time. Blocks loopback, RFC 1918, 169.254 cloud metadata, CGNAT, IPv6 ULA and link-local, non-HTTP schemes, and non-canonical ports.Loopback SIWE origin minting production sessions
Loopback SIWE origin minting production sessions
siwe-verify and siwe-whoami once shipped localhost:5173,localhost:8080 in their default allowlists and production never overrode them — a page on those ports could mint a real 24-hour production session, reproduced live. Loopback is now stripped unless an explicit flag is set and the Supabase URL is genuinely localhost; setting the flag on a hosted URL throws at boot. CORS refuses to credential-reflect loopback as a second line of defence.Nonce table exposure
Nonce table exposure
siwe_nonces has explicit service_role ALL ALLOW plus anon and authenticated ALL DENY policies. The prior state was deny-by-default with no policies, which worked but left the intent undocumented — a future GRANT could have opened a hole silently.Sanctioned recipient locking a pool forever
Sanctioned recipient locking a pool forever
Recipient and partner payout addresses are screened before being baked into a pool’s immutable init code. A sanctioned address in an immutable pool would permanently lock funds. The partner is screened here regardless of sign-in history, because a referrer auto-created from a code may never have signed in.
Known-unfixed and accepted
Open items from the 2026-07-28 audit, deliberately left:RouterFeeV3_SWAP_EXACT_OUT = 0x09mis-encoding (that is V2’s opcode). Needs a redeploy, Phase 2 only.- Partner earnings always display $0.00, and partner-visible merchants RLS is incomplete.
- Webhook
'none'secret option, and a test-versus-live signature mismatch. - TRON keeper drops expired intents.
- TRON has no on-chain recipient anchor, so the trust guarantee does not extend there.
Economic attacks
Self-dealing at the floor. A merchant mints invoices at the minimum and pays them to themselves, recovering 99% while the platform eats the sponsored gas. Leverage was measured at 6.4:1 on Ethereum — roughly 13,700 of keeper float across 100,000 invoices. Largely defused once the merchant bears their own invoices’ gas: the attack costs the attacker more than it costs the platform. Cheap account creation. 10,000 fake merchant accounts cost nothing to create and could trigger keeper-funded pool deploys — 288,200 on TRON**. The cost is not the point on EVM; the unauthenticated ability to make the platform spend is. Closed by making pool deployment merchant-paid and a prerequisite for SDK access. Griefing the reimbursement cap.distribute() is permissionless, so anyone can call it with a cost parameter at the ceiling. They gain nothing but can shave a merchant’s payout up to the cap. Bounded nuisance rather than theft — provided the cap is min(bps, absolute) and tight. A bps-only cap on a large order authorises a very large claim.
Address pre-derivation. For per-customer forwarders, the customer identifier must not be guessable, or a third party could pre-derive addresses and grief a merchant.