docs/superpowers/specs/2026-06-19-db-display-only-trust-anchor.md.
The claim
The Postgres database is display-only for the customer fund path. A compromise of the database — including the owner or service_role — cannot redirect a customer’s payment, because the checkout verifies the settlement destination on-chain, client-side, before the customer signs.
The platform is therefore not a custodian of routing, not merely of funds.
The mechanism
The merchant pins their payout wallet in the embed code on their own site —CryptoCheckout.init({ settlementAnchor }) or ?anchor=0x…. That pin is the root of trust, and it lives somewhere the platform’s database cannot reach.
Before the customer signs, the widget:
1
Reads the on-chain attestation
MerchantRegistry.effectiveRecipient(anchor) — an ownerless, msg.sender-scoped attestation the database cannot author.2
Re-derives the pool address
From that chain-read recipient, using a byte-identical browser port of
predictPoolAddress, pinned to the Solidity fixture vectors. Asserts it equals the served address.3
Cross-checks
Connect rail: reads the deployed
MerchantPool.recipient() and compares. Deposit rail: re-derives the forwarder from the verified pool.4
Refuses on any mismatch
Terminal
pool_mismatch state. Signing is blocked and the pay-to address is hidden, with a plain “configuration mismatch — do not pay” message.merchant_pools.recipient.
The edge functions return a binding bundle — an account identifier plus committed configuration — which the client re-proves. The server response, like the database, is an untrusted hint.
The pool itself is immutable and ownerless. Once funds land, the split to committed recipients is frozen in bytecode.
Conditions and limits
State these whenever the claim is made.Server-side defence in depth
Shipped 2026-06-25. These do not replace the client check — they reduce the blast radius of a database compromise that the client would catch anyway.- Keeper re-resolves
recipientvia on-chaineffectiveRecipientand gates deploy and sweep on the re-derived address. - Indexer re-derives the expected pool and forwarder and flags
tamper_suspected, never reconciling an unanchored pool. - Database has an authorize-and-audit layer on fund routing: a
commit_merchant_pool()security-definer function, a before-write guard, and an audit trigger.
What this is not
The pool-deployment gate is not a security requirement. Verified against the client verification code:
predictPoolAddress is pure CREATE2 arithmetic and re-derives counterfactually with no code needed; the only load-bearing chain read is against the per-chain singleton registry, not the merchant’s pool; the deployed-recipient() cross-check is optional and skips silently on failure; and effectiveRecipient is identity-default, so even an unattested merchant verifies correctly.Gate on pool deployment for commercial and connect-rail reasons. Do not justify it as required for this guarantee — that claim would be wrong.