How it’s built

Why a refusal is the cheapest thing the bakery does

Every sandbox costs the bakery requests, rows and queue operations, and a script could ask for thousands. This is the line of checks a request for one passes, why they sit in that order, what each costs the one who is turned away, and what the bakery gives up so that a refusal stays cheap.

A script at the door

Picture a loop that posts to /v1/sandboxes as fast as the network allows. Each sandbox it is given means a seed of about 2,500 rows written and as many deleted at the wipe (docs/adr/0005, estimate as of 2026-11-04). A design that says yes to the first thousand has spent the money before it learns that the thousand were one script. A design that says no to everything has no demo. The bakery says yes to a bounded number a day, and makes the no cost almost nothing.

What one check does, and where it breaks

The simple design is one gate: a counter, and a refusal when it is full. It is correct and it is expensive, because the counter lives in one place and every request, from a person or a loop, has to reach it and be counted. When the loop is the traffic, the gate’s own cost is the attack. It also fails the person: a full counter shuts the door on the visitor who arrived second after the script arrived first. No lab page shows this one yet, so the bakery explains it itself; the lab’s index is where the lab covers the family of techniques, and as read on 2026-11-06 it has five labs, none on admission.

Four checks, each ahead of what the next would cost

Nygard’s Release It! asks a system to shed load early, and to keep one failure from using up what another needs. The bakery’s checks run in the order of what they cost to run.

The rate limit. The router asks a Rate Limiting binding before any handler runs: one sandbox a minute per hashed address, 60 commands a minute per session, 6 socket upgrades a minute (workers/api/wrangler.jsonc). Cloudflare’s documentation, read on 2026-11-06, says those counts are kept per location and are eventually consistent, so the limit is approximate. The bakery treats it as a courtesy and never as a budget, and when the binding is unavailable the code lets the request through and logs rate_limit_unavailable once.

The human check. A guest’s request, and a demo account’s, then needs a Turnstile token, which workers/api/src/http/outbound.rs sends to Siteverify. Without a token it is refused turnstile-missing; if Siteverify cannot be reached, that is a refusal too, never a pass. Cloudflare’s documentation, read on 2026-11-06, makes a token valid for 300 seconds and good for one check. Until launch the page uses Cloudflare’s always-pass test key, so this layer is built and does not yet turn anyone away.

The ledger. Then the router asks Control, the one object that counts the day. Its admit runs in one transaction: a retry with the same Idempotency-Key gets the first answer, without taking a second place, even when the day has filled in between. Otherwise the pure decision in crates/app/src/admission.rs checks the flag, the day’s cap of 300, and for a guest the share of 100. A yes bumps the counts and writes the sandbox’s row. A no writes nothing, so a refused request leaves the ledger as it found it. Demo accounts have a cap of 100, and an account may make 3 sandboxes a day.

The token’s own limit. A script that has a sandbox can still lean on it, so every request an API token makes spends TOKEN_LIMIT, 60 a minute, before the Sandbox is asked. Simulated traffic has its own ledger in Control: 30 minutes a sandbox a UTC day, taken before the run starts and returned if it stops early.

A refusal that says when to come back

The layers are cheap in different currencies. A Turnstile pass costs the visitor a moment at the sign-in screen. A rate-limit refusal costs the Worker one binding call. A cap refusal costs one request to Control and a few reads. The order follows from that: the free refusals come before the one that touches an object.

Each answer says what to do. A rate limit is a 429 with Retry-After: 60. A cap is a 503 whose Retry-After counts the seconds to the next 00:00 UTC, and the page turns it into words such as “about 3 hours”. A flag set to off or read-only has no known end and carries none. A refusal that gave no time would send the visitor back at once, and a loop would not wait at all.

What it costs

That is the trade docs/security.md names: the cap turns a denial of wallet into a denial of service. The money stays bounded, and the visitors who lose are the ones who arrive after the day is full.

Try it

Open sign-in and continue as a guest. Then ask GET /v1/status on the API, or read the counters on the status page, and compare today’s sandboxes with the caps. In the console, start traffic and stop it early: the minutes you did not use are returned to the day’s ledger.

Sources

Back to the admission principle on How it’s built, to the decisions that chose it, ADR 0005 and ADR 0010, or to the lab.