Skip to main content

Security

Unwall is built for handling real money and on-chain USDC payments. Security is enforced at every layer — from token authentication to double-entry accounting.

Token Security

MeasureDetail
SHA-256 hashingPlaintext tokens are never stored. Only the SHA-256 hash is saved in the database.
One-time displayFull tokens are shown once at creation and cannot be retrieved again.
Scoped permissionsTokens carry independent read, pay, and x402 permissions. Agents only get the access you explicitly grant.
Optional expiryTokens can be configured with an expiration date for time-limited access.
Instant revocationRevoked tokens are rejected within the Redis cache TTL (up to 5 minutes).
Rate limiting100 requests per minute per token via Redis sliding window. Stricter limits apply to expensive operations like payments.

USDC Custody

MeasureDetail
Bridge.xyz custodial walletsAll USDC funds are held in Bridge.xyz custodial wallets on Base chain with institutional-grade custody.
On-chain verificationUSDC deposits are verified on-chain before crediting project balances. Bridge.xyz webhooks confirm deposit finality.
Chain validationDeposit addresses are chain-specific. The platform validates that USDC is sent on the correct network.
EIP-3009 signingx402 payments use EIP-3009 transferWithAuthorization signatures. The platform signing key is stored securely in environment variables.

Rate Limiting

Rate limiting uses a per-token sliding window backed by Redis sorted sets:
  • Standard limit: 100 requests per minute per token.
  • Payment operations: Stricter limits to prevent rapid-fire spending.
  • Fail-open: If Redis is unavailable, requests are allowed through to avoid blocking legitimate traffic.
  • 429 responses include a Retry-After header indicating when the client can retry.

Webhook Verification

All incoming webhooks are verified before processing:
ProviderVerification Method
Bridge.xyzWebhook signatures verified using the BRIDGE_WEBHOOK_PUBLIC_KEY. Invalid signatures are rejected with 400.
StripeWebhook signatures verified using the Stripe SDK with STRIPE_WEBHOOK_SECRET. Invalid signatures are rejected with 400.
Events with invalid signatures are logged but never processed. Unknown event types are acknowledged with 200 to prevent unnecessary retries.

Idempotency

Duplicate payment protection is enforced at multiple levels:
  • Idempotency keys: Clients include an idempotency_key field to prevent duplicate payments from retries or agent loops.
  • Database constraints: Unique constraints on idempotency keys per project ensure that even concurrent duplicate requests result in only one payment.
  • Atomic balance operations: Ledger postings are atomic (via pg_advisory_xact_lock), preventing double-spend from race conditions.

Double-Entry Accounting

The Supabase ledger is the authoritative source of truth for all balances. Every financial operation — deposit, payment, fee, refund — is recorded as an immutable ledger posting with balanced debits and credits. Balance is computed from completed transaction rows via ledger_get_balance. This design ensures:
  • Auditability: Every balance change has a corresponding ledger entry that can be traced.
  • Consistency: Double-entry accounting guarantees that funds cannot appear or disappear without a matching counterpart.
  • Atomicity: Ledger postings are atomic. A debit and its corresponding credit either both succeed or both fail.

Infrastructure

MeasureDetail
HTTPS everywhereAll API traffic is encrypted in transit.
Environment isolationSecrets are stored in environment variables, never in code.
Redis cachingToken validation cached to reduce database load. Balance is never cached for payment authorization.
Error monitoringSentry integration for real-time error tracking and alerting on critical failures.
Row Level SecuritySupabase RLS policies ensure users can only access their own data. Only the backend uses the service key.

Responsible Disclosure

If you discover a security vulnerability, please contact security@unwall.xyz.