From a0492a41ac450b0673e47dd6b12d8025263dd92a Mon Sep 17 00:00:00 2001 From: Satoshi Qazi Muhammed Date: Sat, 25 Jul 2026 02:46:40 -0700 Subject: [PATCH] docs(p2p): track the security deploy runbook (ordered migrations, edge functions, secret names) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidated gated-deploy runbook for the P2P security remediation. Contains no secret values — only the names of the env vars/secrets the owner sets on staging/prod. --- web/supabase/DEPLOY_RUNBOOK.md | 69 ++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 web/supabase/DEPLOY_RUNBOOK.md diff --git a/web/supabase/DEPLOY_RUNBOOK.md b/web/supabase/DEPLOY_RUNBOOK.md new file mode 100644 index 00000000..52919194 --- /dev/null +++ b/web/supabase/DEPLOY_RUNBOOK.md @@ -0,0 +1,69 @@ +# Security Fixes — Deploy Runbook (2026-07-25) + +Deploys are gated and applied by the owner. This runbook lists exactly what must +ship for the P2P custodial-fund security fixes. **Apply migrations first, then +edge functions, then set secrets, then ship the frontend.** All escrow/withdraw +paths fail closed, so partial deploys can block legitimate withdrawals/trades. + +## 1. Migrations (apply in this order) + +Round 1 (BOLA / RLS / dispute escrow): +1. `20260725000000_withdraw_authz_hardening.sql` — `p2p_challenge_nonces` table; locks `request_withdraw` to service role. +2. `20260725010000_harden_financial_rls.sql` — removes world-open reads on balances/ledger/deposit-withdraw/payment-PII; scoped read RPCs; locks `p2p_fiat_disputes` UPDATE to service role. +3. `20260725020000_admin_dispute_resolution.sql` — `admin_resolve_dispute` (atomic release/refund/split, service role only). + +Round 2 (escrow-RPC drain): +4. `20260725030000_lock_escrow_internal_rpcs.sql` — REVOKE PUBLIC/anon/authenticated EXECUTE on `lock_/release_/refund_escrow_internal`; GRANT to `service_role`. + +> Migration 4 MUST be accompanied by the round-2 edge functions (below) and the +> updated frontend, or offer-creation and payment-release will break (the client +> can no longer call the escrow RPCs directly). + +## 2. Edge functions to deploy + +Round 1: +- `process-withdraw` (rewritten — signed-challenge authz, derives user_id server-side) +- `process-withdrawal` (batch — now requires the service-role key) +- `verify-deposit` (adds identity-ownership binding) +- `resolve-dispute` (new — admin-signature-gated dispute claim/resolve) + +Round 2: +- `confirm-payment` (new — seller-signed escrow release) +- `lock-escrow` (new — owner-signed escrow lock) + +Shared code: `functions/_shared/identity-auth.ts` (imported by the above; not a +deployable function itself — ensure it deploys alongside). + +## 3. Secrets (supabase functions secrets) + +- `PEOPLE_RPC_ENDPOINT` — People Chain WS (default `wss://people-rpc.pezkuwichain.io`). + Required by `process-withdraw`, `verify-deposit`, `confirm-payment`, `lock-escrow` + for citizen-NFT ownership verification. +- `ADMIN_WALLETS` — optional, comma-separated SS58 admin set for `resolve-dispute` + (defaults to the 3 historical admin wallets baked into the function). +- Existing: `PLATFORM_PRIVATE_KEY`, `SUPABASE_SERVICE_ROLE_KEY`, `SUPABASE_URL`, RPC endpoints. + +## 4. Frontend + +Ship the updated client (new signing flows in withdraw/offer-create/payment-release +and the admin dispute panel). Users now sign an authorization challenge in their +wallet for: withdrawals, offer creation (lock), payment release, and admin dispute +actions. + +## 5. Post-deploy smoke checks + +- Withdraw a small amount (signs, succeeds; a replayed nonce is rejected). +- Create a sell offer (signs the lock; balance moves available→locked). +- Complete a trade release (seller signs; buyer credited). +- Resolve a test dispute as admin (escrow actually moves). +- Confirm anon `rpc('release_escrow_internal', …)` now returns permission denied. + +## Known residual (NOT closed — tracked) + +- Scoped read RPCs (`get_user_balance_transactions`, `get_user_deposit_withdraw_requests`) + are keyed by a non-secret `user_id` (UUID from citizen/visa number). They stop + full-table dumps but are not yet bound to a signed session. Deferred to avoid a + sign-prompt on every passive read; reads leak history only (no fund movement). +- `p2p_fiat_offers` / `p2p_fiat_trades` / `p2p_messages` retain `USING(true)` for + direct client read/write (no server session to bind to). Marking a trade status + moves no funds; all fund movement now flows through service-role edge functions.