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
| Measure | Detail |
|---|---|
| SHA-256 hashing | Plaintext tokens are never stored. Only the SHA-256 hash is saved in the database. |
| One-time display | Full tokens are shown once at creation and cannot be retrieved again. |
| Scoped permissions | Tokens carry independent read, pay, and x402 permissions. Agents only get the access you explicitly grant. |
| Optional expiry | Tokens can be configured with an expiration date for time-limited access. |
| Instant revocation | Revoked tokens are rejected within the Redis cache TTL (up to 5 minutes). |
| Rate limiting | 100 requests per minute per token via Redis sliding window. Stricter limits apply to expensive operations like payments. |
USDC Custody
| Measure | Detail |
|---|---|
| Bridge.xyz custodial wallets | All USDC funds are held in Bridge.xyz custodial wallets on Base chain with institutional-grade custody. |
| On-chain verification | USDC deposits are verified on-chain before crediting project balances. Bridge.xyz webhooks confirm deposit finality. |
| Chain validation | Deposit addresses are chain-specific. The platform validates that USDC is sent on the correct network. |
| EIP-3009 signing | x402 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-Afterheader indicating when the client can retry.
Webhook Verification
All incoming webhooks are verified before processing:| Provider | Verification Method |
|---|---|
| Bridge.xyz | Webhook signatures verified using the BRIDGE_WEBHOOK_PUBLIC_KEY. Invalid signatures are rejected with 400. |
| Stripe | Webhook signatures verified using the Stripe SDK with STRIPE_WEBHOOK_SECRET. Invalid signatures are rejected with 400. |
Idempotency
Duplicate payment protection is enforced at multiple levels:- Idempotency keys: Clients include an
idempotency_keyfield 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 vialedger_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
| Measure | Detail |
|---|---|
| HTTPS everywhere | All API traffic is encrypted in transit. |
| Environment isolation | Secrets are stored in environment variables, never in code. |
| Redis caching | Token validation cached to reduce database load. Balance is never cached for payment authorization. |
| Error monitoring | Sentry integration for real-time error tracking and alerting on critical failures. |
| Row Level Security | Supabase RLS policies ensure users can only access their own data. Only the backend uses the service key. |