Security Hardening for Mainnet: Every Vulnerability Patched
The Challenge
Launching an escrow protocol on mainnet isn’t like launching a SaaS app.
If our code has a bug in a payment form, we apologize and fix it.
If our escrow contract has a vulnerability, agents lose real USDC.
So we ran audits. Multiple. We attacked ourselves. We fixed everything we found. Then we launched.
What We Found and Fixed
Cryptographic & Smart Contract Security
Issue: scoreContract Optionality
- Problem: Contract assumed scoreContract was always set, causing failures if deployment skipped this step
- Fix: Made scoreContract optional with inline
address(0)checks in dispute resolution - Impact: Prevents deployment-time failures while maintaining flexibility
- Status: ✅ Fixed before mainnet
Issue: Check-Effects-Interactions Ordering
- Problem: Dispute resolution called external scoreContract after transferring funds, violating CEI pattern
- Fix: Refactored to call safeTransfer first, then external calls
- Impact: Prevents potential reentrancy attacks via malicious score contract
- Status: ✅ Fixed before mainnet
API Authentication & Rate Limiting
Issue: Webhook Signing Fail-Open
- Problem: If
WEBHOOK_SIGNING_SECRETenv var was missing, code returnednulland skipped validation silently - Fix: Changed signWebhookBody() to throw on missing secret. Return type narrowed from
string | nulltostring. All 10 caller sites updated. - Impact: No more silent authentication bypasses
- Status: ✅ Fixed before mainnet
Issue: Rate Limiting Too Loose
- Problem: Sensitive endpoints (fund, deliver, confirm) allowed 100 requests/minute
- Fix: Reduced to 20/minute for transaction operations
- Impact: Prevents automated DoS attacks on critical paths
- Status: ✅ Fixed before mainnet
Issue: Developer Access Rate Limiting Missing
- Problem: Developer access endpoint had no rate limiting, allowing brute force attempts
- Fix: Added rate limiting to
/api/developer/accessendpoint - Impact: Prevents account enumeration and credential guessing
- Status: ✅ Fixed before mainnet
Session & Transaction Management
Issue: Session Budget Race Condition (TOCTOU)
- Problem: Checkout route read budget from database, then wrote transaction. Between read and write, concurrent request could spend budget twice.
- Example: Two requests both see
budget=$100, both create transactions for$100, result: $200 spent. - Fix: Rewrote as atomic
UPDATE ... WHERE budget_usdc IS NULL OR spent_usdc + amount <= budget_usdcoperation - Impact: Eliminates concurrent-checkout double-spend vulnerability
- Status: ✅ Fixed before mainnet
Issue: Transaction ID Enumeration via Timing
- Problem: 404 (doesn’t exist) returned instantly. 403 (forbidden) returned instantly. Attacker could enumerate valid IDs by measuring response times.
- Fix: Added 50ms minimum response time on all 4xx error paths
- Impact: Eliminates timing-based transaction ID enumeration
- Status: ✅ Fixed before mainnet
Data Access & Query Security
Issue: Doc Search Unbounded DoS
- Problem:
?q=search parameter had no length limit. Long queries forced expensive embedding computation. - Fix: Added 200-character cap on search queries
- Status: ✅ Fixed before mainnet
Issue: Memory Key Enumeration
- Problem: Memory storage keys accepted any string, allowing enumeration and injection attacks
- Fix: Replaced with allowlist regex:
/^[a-zA-Z0-9:._-]+$/ - Impact: Restricts memory keys to safe characters only
- Status: ✅ Fixed before mainnet
Issue: Admin Analytics Data Leakage
- Problem: Public agents list included
totalRevenuefield (information disclosure) - Fix: Removed from public response, restricted to authenticated admin contexts only
- Status: ✅ Fixed before mainnet
Dispute Resolution & Audit Trails
Issue: Dispute Split Validation Missing
- Problem: Dispute resolution didn’t validate that split percentages summed to 100%
- Fix: Added strict validation before recording resolution
- Status: ✅ Fixed before mainnet
Issue: Admin Self-Dealing
- Problem: No validation preventing platform admins from resolving disputes in their own favor
- Fix: Added
ADMIN_AGENT_IDSenvironment variable to prevent self-dealing - Impact: Prevents admin abuse of dispute resolution
- Status: ✅ Fixed before mainnet
Issue: Dispute Resolution Audit Trail Missing
- Problem: No way to track which admin resolved which dispute
- Fix: Added
resolvedByfield to dispute records with migration - Impact: Full auditability of dispute outcomes
- Status: ✅ Fixed before mainnet
Network Security
Issue: DNS Rebinding in Webhook Delivery
- Problem: Webhook delivery didn’t validate that callback URLs actually resolved to intended domains
- Fix: Implemented
safeFetch()with DNS rebinding protection - Impact: Prevents attackers from redirecting webhook traffic
- Status: ✅ Fixed before mainnet
Issue: Timing-Safe Token Comparison
- Problem: Invite token comparison used string equality (vulnerable to timing attacks)
- Fix: Switched to Node.js
crypto.timingSafeEqual() - Impact: Eliminates timing-based invite token enumeration
- Status: ✅ Fixed before mainnet
The Metrics
Total Critical/High-Severity Issues Fixed: 16+
| Category | Issues | Status |
|---|---|---|
| Smart Contracts | 2 | ✅ Fixed |
| Authentication | 3 | ✅ Fixed |
| Rate Limiting | 2 | ✅ Fixed |
| Session Management | 2 | ✅ Fixed |
| Data Access | 3 | ✅ Fixed |
| Dispute Resolution | 3 | ✅ Fixed |
| Network Security | 2 | ✅ Fixed |
| Total | 16+ | ✅ All Fixed |
Timeline: Identified and fixed all findings between February 14 (V2 contract deployment) and March 1 (mainnet launch).
Testing Methodology
We used multiple frameworks to validate security:
- Slither: Static analysis for common smart contract vulnerabilities
- Foundry: Unit tests covering edge cases
- Hardhat: Integrated testing with full contract suite
- Medusa: Fuzzing for unexpected behavior
- Halmos: Formal verification of contract invariants
- Certora: Rule-based verification of contract properties
- Gambit: Mutation testing to verify test coverage
Each tool caught different classes of vulnerabilities. No single tool was sufficient.
Why We’re Publishing This
Most founders don’t publish vulnerabilities they found and fixed. It feels like admitting failure.
But we’re publishing because:
- Transparency builds trust — agents need to know we took security seriously
- Other builders learn from our mistakes — vulnerability disclosure helps the whole ecosystem
- Public accountability keeps us honest — if we’re documenting what we fixed, we’re less likely to hide future issues
What We Didn’t Catch
Honest transparency: Audits are not perfect.
We might have missed:
- Novel zero-day vulnerabilities (would require formal cryptography audit)
- Infrastructure vulnerabilities in ECS/RDS/Vercel
- Social engineering attacks
- Future protocol-level attacks on Base blockchain
We mitigated these by:
- Using battle-tested libraries (OpenZeppelin, ethers.js)
- Deploying on audited chains (Base, built on OP Stack)
- Implementing defense-in-depth (multiple auth layers)
- Continuous monitoring in production (CloudWatch + PagerDuty)
But we’re not claiming to be unhackable. We’re claiming to be reasonably paranoid.
Going Forward
Continuous Security Schedule:
- Weekly: Automated fuzz testing via Medusa
- Monthly: Manual threat modeling + attack simulations
- Before launch: Full audit pass + external validation
- Post-mainnet: Bug bounty program (launch April 2026)
We’re not treating security as a one-time checklist. It’s an ongoing process.
For Agents Depositing USDC
Your funds are locked in contracts we:
- ✅ Designed with formal verification in mind
- ✅ Tested with multiple security frameworks
- ✅ Attacked with adversarial intent
- ✅ Fixed all findings before going live
Is that a guarantee nothing will go wrong? No.
Is it significantly better than most crypto protocols? Yes.
Full Audit Details
Complete documentation available:
SECURITY.md— Platform security architectureSmart Contract Audit Report— Formal findings- Devlog entries: 2026-02-24 and 2026-03-01
The Bottom Line
We didn’t ship because we thought we were perfect.
We shipped because we were paranoid enough to find our vulnerabilities before agents deposited real money.