# One door in

> Why every send on Email Fast passes through a single admission gate, the suppression bug class that design eliminates, and what the discipline costs.

Canonical: https://emailfast.dev/blog/one-admission-path

A suppression list is a promise. Someone said stop, and the platform said understood. Keeping that promise is trivial in a demo — one code path, one check — and surprisingly hard in a real email platform, because a real platform does not have one way to send. It has a REST API. Then an SMTP endpoint for the legacy app. Then a browser SDK. Then compatibility endpoints for people migrating off other providers. Then broadcasts, automations, a resend button in the admin panel, a batch importer. Every one of those is an ingress: a door through which a message can enter the system.

## How the bug happens

Here is the failure pattern. If you have operated a sending system, it will be familiar as a shape even where the details differ. The first ingress checks everything: suppression, quota, deduplication, content policy. The second ingress is built by a different person a year later and reimplements those checks — mostly. The third copies the second. Each door now carries its own copy of the rules, and copies drift. Eventually a new door ships — a compat endpoint, a migration script, an internal tool — where one check simply isn't there.

Nothing fails. No error appears, no alarm sounds, no test goes red, because a check that does not exist cannot fail. The system does exactly what its code says. And an unsubscribed person gets an email.

That is the bug class worth being afraid of: not the broken check but the missing one. Broken code announces itself; absent code is silent. And the blast radius is not technical. It is a promise broken to the exact people who most explicitly asked you to stop, with legal consequences attached in most jurisdictions.

## One gate

The architectural answer is old and unglamorous: stop copying the rules and build one door. On Email Fast, every send — REST, SMTP, browser SDK, compatibility endpoints, broadcasts, automations — passes through one admission gate: idempotency, suppression, quota, and content policy in a single checkpoint no ingress can skip.

The ingresses still exist — senders need them. But they are thin translators now. Each one parses its own protocol, normalizes the message, and hands it to the gate. The checks live in exactly one place. A new ingress cannot forget the suppression check, because the ingress does not contain one; the gate does. What a door cannot skip, it cannot get wrong. The invariant stops being a discipline ("every path remembers") and becomes a structure ("there is only one path") — the same preference for structure over memory that shows up in [how this website checks its own copy](/blog/overclaiming-build-error).

The gate is also where durability lives: a 202 from the API means the send is committed to a durable, partitioned outbox before we answer — a crash can't lose it, and a retry with the same idempotency key can't double-send. One door in; past the door, the message is a fact.

## What it costs

Single points of admission are single points of coupling, and we should be honest about the bill. Every new ingress must integrate with the gate. There is no quick hack where a new endpoint writes to the queue directly because a deadline is close — and that "no" has to hold under pressure, which makes some features slower to ship. The gate itself becomes the most carefully reviewed code in the platform: general enough for six kinds of caller, fast enough to sit in front of all of them, changed with more scrutiny than any other module gets.

We think the trade is plainly right, for a reason that has nothing to do with architectural taste: a suppression promise kept on most paths is not a kept promise. The recipient who unsubscribed does not care which ingress mailed them. Either the property holds everywhere or it does not hold — and "everywhere" is only checkable when everywhere is one place.

You can audit any stack, including your own, with a single question: how many places in the codebase can cause an email to be sent? If the answer is more than one, each of them is a place where the promise has to be re-remembered. One day, somewhere, it won't be.
