Shipping on the edge

Engineering

Kool runs on Workers at the edge, close to whoever opened the app. No regions to pick, no cold starts to explain away. That is the part that goes in the pitch.

Here is the part that does not: several things you know how to write correctly on one server are quietly wrong when your code runs in hundreds of places at once, and they fail by working.

The clearest example we have is rate limiting.

The obvious implementation keeps a bucket per caller in a map, in memory, and refills it on a timer. It is fast, it is free, and it does bound one client hammering one instance. What it does not do is enforce a quota, because Cloudflare runs many isolates in many datacentres and creates and discards them freely. A caller spread across them gets a fresh bucket each time. The code reads like a limit of five per five minutes. What it actually provides is the appearance of one.

For some routes the appearance is enough. Abusing our hashtag suggestions costs an attacker a wasted read and costs us nothing worth counting. But sign-in is different, because the thing being spent there is somebody's account, an SMS bill, or their mailbox. On those routes the appearance of a limit is not a smaller version of a limit. It is nothing.

So the auth routes count somewhere that can actually count. A Durable Object is one instance globally, so five means five no matter which datacentre answered. Ours is keyed per caller, by address or by user id, and holds every auth bucket for that caller in one object.

Per caller rather than per route, and that choice is the whole design. One object per route would put every sign-in on the planet through a single instance, which makes it both a bottleneck and the most attractive thing in the system to overload. Per caller, load spreads exactly as traffic does, and an attacker gets an object to themselves, which is the property that makes them cheap to isolate and impossible to use against anybody else.

It fails open. If that object cannot be reached, the caller is let through, with the in-memory limiter still in front of them. This is a decision and not an oversight, and it is the kind of decision worth writing down before an incident rather than after. A limiter that fails closed converts its own outage into a total sign-in outage: nobody can log in, including the people trying to fix it. The exposure while it is unreachable is exactly the exposure we had before the object existed, which is bounded and known.

The general shape of edge work is in that story. The runtime gives you enormous reach for free, and in exchange it takes away a few assumptions you did not notice you were making, mostly about there being one of anything. The bugs it produces do not throw. They pass review, they pass staging, and they are only wrong at scale, which is the one place you were not watching.

All posts