Two layers, and why the order is the design
The first layer is an in-process fixed window that cannot fail. The second is a durable counter in Postgres, rate_limit_hit, which performs the increment and the decision in one statement so concurrent requests serialize on the row instead of racing.
The ordering gives the only honest definition of fail-open for this system. If the database is unreachable, the limiter degrades to per-instance limiting, never to unbounded. A test asserts exactly that by making 100 attempts against a broken durable layer and observing that only the burst gets through.
The limit is checked before the request body is read, and a caller refused by the in-process floor never reaches the database, so the limiter cannot itself become an amplifier.
Identifying the caller without trusting the caller
The client is identified from platform-set headers, x-real-ip and x-vercel-forwarded-for, not from x-forwarded-for, which the caller supplies. Keying on a caller-supplied header would let a caller mint a fresh bucket per request: a limiter that looks like one and bounds nothing. IPv6 addresses are collapsed to their /64, so one allocation is one bucket.
POST /api/retell/bind also caps its agents array at 25 and returns 400 batch_too_large above that. Without the cap, the rate limit would bound requests while one request could still trigger an unbounded number of store writes.
The wiring test found six more routes
The gap analysis named four surfaces, and the first pass bounded those four. A guard that walks every route.ts under app/api and requires each POST handler to call the limiter then failed on six more routes. All ten are now bounded. The standing rule applies: a fix scoped to the instance you found is a sample, not a fix.
The guard also asserts that the limiter runs before the body is parsed, and it carries an explicit exemption list with a stated reason per entry. Removing the limiter from the registration route turns three tests red.
Counters are telemetry, not registry data
rate_limit_counters ships with row-level security enabled and no policies at all, unlike every other table in the schema. Per-caller request counts are operational telemetry, and publishing them would turn the limiter's own state into a reconnaissance surface.
A follow-up fix corrected the grants on the two SECURITY DEFINER functions. Revoking EXECUTE from anon and authenticated removed nothing, because Postgres grants EXECUTE to PUBLIC on new functions. A corrective migration closes that, a test fails on any such function whose EXECUTE is never revoked from PUBLIC, and the sweep of old windows is now actually called, on roughly one durable hit in 200.
What this does not do
Rate limiting bounds volume. It does not authenticate anyone. Nothing yet binds a live HTTP request to a registered agent, and the residual risk from distributed sources is rated medium rather than closed. It also changes nothing about levels. The reference deployment at agenid.com issues nothing above L1_REGISTERED today: L2 and higher require a VerificationAssertion signed by a root authority key, and that key has not been created yet. L5 is reserved and is not issuable at all.
Which agent is acting is not the same question as what it is allowed to do. AgenID answers the first. Protocol v1.1.1 defines no signed authorization object, so permission stays with the operator and with the party on the other side.
To see it, register an agent at /issue; the write path it calls is one of the ten bounded routes.