Reselling data you pay for changes every design decision.
Most API projects can afford to be generous when something breaks — serve stale data, retry, degrade gracefully. When each request draws down a metered upstream supply, generosity is indistinguishable from losing money. That single constraint shaped the entire gateway.
Public-safe by design
This page describes architecture only. It names no upstream provider and contains no pricing, plan economics, credentials, endpoints, hostnames, or customer information.
- Go source LOC
- 33k
- test LOC
- 23k
- internal packages
- 22
- SQL migrations
- 36
The premise
A gateway is a trust boundary in two directions
It has to protect buyers from each other — one customer's traffic cannot consume another's quota or leak their data — and protect a finite upstream supply from all of them at once. Those pull in opposite directions, and most of the interesting engineering lives in the tension.
The surface is deliberately small: a fixed set of read-only endpoints behind an exact-match route table that rejects prefixes and normalised variants, so a creative URL cannot reach an unpriced path. Everything else — metering, quotas, failover, provisioning — exists to make that small surface safe to sell.
Constraints
Four rules the money imposes
- 01
Every request has a cost
The upstream feed is metered, so serving an unmetered request is not a bug that degrades a page — it spends money. Quota is charged atomically before any upstream work begins, never after.
- 02
Correctness and cache cannot share fate
Two physically separate Redis tiers: a durable, no-eviction tier holding limits, reservations and quota charges, and a disposable bounded cache. Losing the cache degrades to misses; losing the correctness tier fails closed with a 503 rather than serving free traffic.
- 03
One credential is not a supply
Upstream keys are pooled and tried in sequence — cooled down on rate limits, quarantined on rejection, health-cooled on network errors. Production refuses to start unless two distinct credentials can each independently reserve the largest configured route.
- 04
Revocation cannot wait for a TTL
Buyer keys are looked up only by hash and carry a least-privilege scope subset. Access-reducing changes propagate through short-lived deny markers, so a revoked key stops working across every replica immediately rather than when a cache happens to expire.
Failure design
Every dependency has a declared answer
Outage behaviour was written as a table before it was written as code: for each dependency, what the caller sees and what happens to data integrity. Anything not on the table is a bug, not a judgement call made at 3am.
| Dependency | Public response | Data integrity |
|---|---|---|
| Response cache down | 200 — served, cache miss | No impact |
| Correctness tier down | 503 — fail closed | No unmetered spend |
| Database down | 503 — fail closed | Usage replayed from stream |
| All upstream keys exhausted | 503 — declared | Reservation released |
The pattern throughout is that the system would rather return nothing than return something it cannot account for.
Metering
Bill exactly once, even when the flush fails
Usage capture sits off the hot path: every admitted request appends a billable event to a durable stream, consumed by a worker that folds it into idempotent rollups. Event ids make a replay after a crash a no-op rather than a double charge.
authenticate key → sha-256 lookup, scope checkreserve charge quota atomically ← before upstreamserve cache hit? return, consume no supplycache miss → upstream, failover on 429/401/5xxmeter append billable event to durable streamrollup idempotent fold into postgres, replay-safeon any failure after reserve → reservation released, not stranded
Release governance
No workflow can deploy on its own
Shipping is four steps and two human signatures: CI tests a change, an offline signature authorises one exact revision, CI builds and attests an image digest, and a second offline signature authorises that digest for production.
The signing key never enters the repository, the CI provider, the registry, or the server. It is more ceremony than a personal project needs — which is precisely the point of building it on a personal project. The habits that make a service safe to sell are cheaper to learn before anyone is paying for it.
Stated honestly
This is a production candidate in an invite-only private beta, not a generally available product. The repository's own release documentation gates a release behind a signing ceremony and acceptance checks; the public surface today is a prelaunch page. No revenue, traffic, uptime, or customer claims are made on this page because none of them are measurable yet.
Outcome and learning
Building the boring parts is what made it a product.
The endpoints were a weekend. What turned it into something sellable was everything around them: metering that survives a crash, quotas charged before spend, credential failover proved at startup, and a deploy path a tired author cannot shortcut. That work is invisible when it succeeds, which is exactly why it is worth showing.
Access is invite-only during the beta. A technical walkthrough of the gateway architecture is available for interviews and relevant discussions.