# API Gateway Platform

**Solution Architecture v1.0 · Google Cloud · Integration Platform Architecture · 2026-09 · 21 views · 18 architecture decision records**

The north–south front door for a multi-tenant SaaS that other people build on. Every SaaS product with a public API has one of these and almost nobody outside the company that runs it ever sees it: when a shop's checkout calls a payments API, when a bot posts into a team chat workspace, when a logistics dashboard pulls shipment status, the call does not land on the service that answers it — it lands here. This one carries 50,000 developer organisations, 500,000 live credentials, 40 mTLS partners and roughly 10 billion requests a day across 800 published routes over 120 upstream services, on a global anycast load balancer and Cloud Armor at the edge, an Envoy fleet on GKE as the request plane, Spanner for versioned configuration, Memorystore for counters, and Pub/Sub into BigQuery for evidence.

The design rests on one rule: **the request plane never calls the configuration plane.** Route, credential, scope, limit, version and variant are all read from state already resident in the proxy process. The control plane, the configuration store and the credential database are never on the hot path. That single boundary is what makes a 15 ms p99 achievable, what licenses a lower availability target for the control plane, and what turns every dependency failure into the loss of a dimension — accuracy, freshness, fidelity, the ability to change — rather than the loss of the front door.

The decisions that carry the design:

- **The request plane never calls the configuration plane.** A control-plane outage means "no new rollouts", not "no API", and the latency budget is a property of the proxy rather than of the slowest management service (ADR-01).
- **Configuration is an immutable signed artefact, pulled.** Revert is selecting a version, and a pod restarting during a control-plane outage still comes up serving (ADR-02).
- **Revocation is a push with a measured coverage figure**, not a lookup with a consistency guarantee — which is why the operator is told "propagating" and never "revoked" (ADR-03).
- **Fail open on read routes, fail closed on write routes.** "Fail open or closed?" has no single answer; it has an answer per consequence class (ADR-04).
- **Limits admit locally and reconcile centrally**, buying a bounded ≤ 2% overshoot inside the latency budget, and degrading to tighter local ceilings rather than to unlimited (ADR-05).
- **Billing never reads a counter.** Counters are deliberately approximate and losable; promoting them to a financial record would make every availability decision a revenue decision (ADR-06).
- **Shedding is by plan tier, and it is recorded**, because every overloaded system sheds and the only question is whether the order was chosen (ADR-07).
- **The gateway signs an identity context and upstreams trust nothing else.** Raw client credentials never cross into the service zone (ADR-08).
- **Sunset is gated on measured active callers**, with dated per-tenant pins for the tail — a deprecation policy without usage data is an announcement (ADR-10).
- **Proxy filters are code** and ship through the same canary as configuration, because risk follows what runs on the request path, not what the artefact is called (ADR-11).
- **Gateway-side version translation is deferred**, and the reason is ownership: a wrong translation pages the team least able to diagnose it (ADR-12).
- **The rollout controller can return "insufficient evidence."** An automated verdict with two values will use the safe-sounding one for the case it cannot judge (ADR-13).
- **Two regions active–active, each sized for the whole load**, so the failover path is the normal path rather than one rehearsed occasionally (ADR-14).
- **Autoscale on connections and CPU, not request rate** — scale on what runs out, which is the same number as what is easy to count only when the workload is uniform (ADR-15).
- **Telemetry is sampled by outcome class; policy decisions never are.** Uniform sampling optimises for the events you already understand (ADR-16).
- **No request or response bodies are logged**, and the capability is absent rather than disabled, because a disabled feature is one incident away from being enabled (ADR-18).

The architecture one-pager (including why the design should still hold up in ten years) and the full decision record appear on the landing page of the diagram set, directly below the index of views. The same content is published as [docs/architecture-one-pager.md](docs/architecture-one-pager.md) (~12 min) and [docs/decision-record.md](docs/decision-record.md) (~63 min).

---

## What is here

| Path | Contents |
|---|---|
| `diagrams/index.html` | The landing page: 21 views in seven acts with every format linked, then the **architecture one-pager** and the **decision record** |
| `diagrams/*.html` | One self-contained page per view: the inlined diagram plus the reasoning cards, with copy / PNG / PDF export |
| `diagrams/svg/*.svg` | The same views as SVG with the diagram XML embedded; they re-open fully editable in diagrams.net |
| `diagrams/drawio/*.drawio` | draw.io source |
| `docs/architecture-one-pager.md` | The one-pager as markdown |
| `docs/decision-record.md` | The 18 decision records, the capability-to-technology table and the package glossary as markdown |
| `specs/part-a..f.json` | Diagram specifications, the source of truth for every view |
| `specs/manifest-a..c.json` | Acts, page titles, subtitles and reasoning cards |
| `specs/adr-onepager.json`, `specs/adr-records-a..b.json` | The one-pager, the decision records, the capability-to-technology table and the glossary |
| `scripts/build.sh` | Rebuilds every deliverable from the specs (Node only) |
| `ask.md` | The requirement |

## Rebuilding

```bash
bash scripts/build.sh
```

Node 20+ and nothing else — no draw.io Desktop, no browser, no network. The build assembles
`specs/views.json` and `specs/manifest.json` from the authoring parts, then runs generate →
validate → route check → SVG → draw.io → HTML → link check, and finally renders the one-pager
and decision record into `diagrams/index.html` and `docs/`. Edit the parts, never the
assembled files.

Current state: **0 validator errors, 0 validator warnings, 169 edges routed with 0 routing
errors and 4 clutter warnings, 396 icons embedded across 21 views, all 166 relative links
resolve.** The four remaining warnings are edge labels sitting near an adjacent parallel line
on the two fan-in views (01 system context and 08 integration surface), where several edges
converge on one centre. Clearing them entirely would mean shortening the labels past the point
where they say anything, so they are left and named here.

## The seven acts

| # | View | What it answers |
|---|---|---|
| 01 | System Context | Who calls, who changes it, and why there is no way around it |
| 02 | High-Level Architecture | The seven things that happen to a request, in the order they happen |
| 03 | Actors and Their Core Journeys | Who this is for — including the three actors that are not people |
| 04 | Journey — The First Successful Call | Where a new integrator is won or lost, and why it is an error message |
| 05 | Journey — Shipping v2 Without Breaking Anyone | Two troughs: one statistical, one social |
| 06 | Layered Architecture | Eight layers, and the one arrow that defines the platform points downward |
| 07 | Platform Components | Three planes that are deliberately not allowed to become each other |
| 08 | Integration Surface | Every surface in and out, and the one genuinely synchronous obligation |
| 09 | Data Flow | Configuration down, evidence up, and nothing else on a request |
| 10 | Data Architecture and Ownership | Four classes with four write rules, and the data deliberately absent |
| 11 | Core Data Model | Fourteen entities and the foreign key that makes a response attributable |
| 12 | A Request From TLS to Upstream | Fifteen messages, of which exactly two leave the pod |
| 13 | Degradation Paths | Seven dependencies, seven things given up, and the front door is not one |
| 14 | Quota Enforcement | Four scopes, one verdict, and a rejection the caller can act on |
| 15 | Revoking a Leaked Credential | What a 30-second promise looks like when it has to be measured |
| 16 | Deployment Architecture | Two regions, each sized to carry all of it |
| 17 | Configuration Rollout and Canary | Why a routing change and an upstream canary use the same pipeline |
| 18 | Observability | Eight signal families, and why one of them is never sampled |
| 19 | The API Version Lifecycle | How a published version ends, and what permits it |
| 20 | Security Trust Zones | Five zones by exposure, and where trust is established exactly once |
| 21 | Identity and Authorisation | The refusal the platform exists to make, and why it explains itself |

## Evidence and limits

This is a design, not a report on a running system. Every rate, latency, volume, retention and
threshold is a **stated assumption** from `ask.md`, invented to be defensible and arguable
rather than absent, and to be replaced by measured traffic before build. Four numbers in
particular are the first that real data should overturn: the 15 ms p99 gateway overhead, which
is what makes the resident-state design necessary rather than merely tidy; the 30 s revocation
propagation SLO, which sets token lifetime and therefore the whole credential design; the ≤ 2%
counter overshoot, which decides whether local admission is acceptable at all; and the
credential cache miss rate, which is what actually sets the tail latency.

Three questions are left genuinely open rather than quietly resolved. Whether a 1% canary on a
low-traffic route can ever produce a meaningful signal decides whether progressive delivery is
a safety mechanism or theatre on exactly the routes least exercised — the design's answer is to
make "insufficient evidence" a first-class verdict rather than to average the problem away
(ADR-13). Whether a single shared fleet is acceptable is deferred with two named triggers
rather than settled, because the blast radius is the largest accepted risk in the package and
the cost of cells should be known before the decision is made under pressure (ADR-17). And
whether gateway-side version translation is worth the ownership it moves is left to a specific
migration with a named owner and a differential test corpus, not decided in the abstract
(ADR-12).
