From Node.js to Go — Why We're Rewriting minia2a's Gateway
The Problem
minia2a started as a Node.js monolith. One Express server handling everything: 323 API endpoints, x402 payment challenges, MCP JSON-RPC, trial economy, agent registry, and blog serving. It worked. Until it didn't.
In the last 72 hours, the V4 Node process restarted 49 times. Not from traffic spikes — from accumulated technical debt:
- Memory creep: 143MB baseline, growing 5-10MB/day under idle load
- Cold start race conditions: service registry and x402 handler competing for DB initialization
- Uncaught exceptions from malformed proxy responses crashing the event loop
- Single-threaded: one CPU-bound request blocks all others
These aren't Node.js's fault. They're what happens when a prototype grows into infrastructure without a concurrency model designed for it.
What Changed
V5 splits the monolith into two processes:
Client → Go Gateway (:80) → Node Sidecar (:3000) → 199 Service Handlers
Go Gateway (Gin + modernc/sqlite): Handles HTTP, x402 payment challenges, rate limiting, JWT auth, and trial economy. Uses pure-Go SQLite (no CGO) so deployment is a single binary.
Node Sidecar (Express): Runs the 199 individual service handler files. The Go gateway proxies to them over localhost.
Why Go
| Requirement | Node.js (V4) | Go (V5) |
|---|---|---|
| Concurrent connections | ~1K (single thread) | 10K+ (goroutines) |
| Memory after 24h idle | 143MB | ~15MB (estimated) |
| Startup time | 2-4s | <100ms |
| Deployment | node_modules + 200 files | Single binary |
| Type safety | Runtime crashes | Compile-time |
1. Payment Idempotency Needs Atomicity
When an agent pays for an API call, the gateway must atomically verify payment, store receipt, forward request, return response. Go's explicit error handling makes this control flow visible and compiler-checked.
2. Rate Limiting Per-Endpoint
V5's rate limiter is a 60-line token-bucket in pure Go. POST /api/v1/register-simple gets 5 req/min, GET /x402/gas gets 30. No external dependencies.
3. Single Binary Deployment
The Go gateway compiles to one static binary. No npm install, no runtime mismatches. Deploy with scp gateway user@host: and restart.
What Stays in Node.js
The 199 service handlers stay in JavaScript. They're thin wrappers — most 20-40 lines calling external APIs. Rewriting them in Go would be busy work. The sidecar pattern: migrate what matters (gateway), leave what works (handlers).
Numbers (from production /api/stats)
- 388,365 total requests (~900/hour)
- 323 services, 199 active x402 endpoints
- 10,142 trial calls, 322 unique agents
- $12.75 in real USDC agent-to-agent payments
- 14 successful payment transactions
These aren't VC-pitch numbers. They're real infrastructure numbers from a live marketplace where 99% of traffic is agent-originated.
Key insight: The 49 V4 restarts weren't from load — they were from complexity. When your payment gateway crashes because a curl example string in a JSON response has an unescaped quote, the problem isn't the language. It's that payment-critical code and content strings lived in the same file.
What's Next
V5 is open source (MIT). The gateway handles: health checks, stats API, service registry, x402 payment challenges, receipt verification, MCP JSON-RPC, rate limiting, agent registration, credit purchases, trial economy. Each feature is ~100-200 lines of Go.
If you're running a Node.js payment service and considering Go: don't rewrite everything. Move the gateway to Go, leave business logic where it is. The sidecar pattern works.
Top comments (0)