01Guardrails live in the runtime, not the template
Enforcement is a property of admission into the cluster, authored and signed separately from any golden path. A fork removes the template and never the control.
ADR-0521 views, in reading order. Every view ships three ways: an HTML page, an SVG that re-opens in diagrams.net fully editable, and draw.io source.
The platform that takes a product engineer from "we need a new service" to running, observable, compliant production code without a ticket. Read it in seven acts: what sits inside the boundary, who it serves and what they get to do, how it is put together, what it stores, what happens when someone actually uses it, how it is operated, and why it is safe. One rule runs through every view — the paved road is a product a team may decline, and the guardrails are controls it cannot.
The paved road is a product teams may decline; the guardrails are controls they cannot. Generation and enforcement are separate planes, and they meet only at the runtime.
Creating a new service at this organisation takes nineteen working days and touches seven teams: a repository request, a pipeline request, a namespace request, a database request, a DNS and certificate request, a secrets onboarding request, and a security review that arrives after the architecture is already built. Every step is a form filled in by an engineer who does not want to be an expert in the thing the form is about, and read by a specialist who is the bottleneck for four hundred other engineers. The cost is not only the nineteen days. It is the services that never get split because splitting is expensive, the security review that lands too late to change anything, and the gradual accumulation of workloads nobody can attribute to a team. The platform's job is to make the distance between an engineer's intent and a production-grade running system as short as it can honestly be — without making the result less safe, and without becoming a ticket queue with a nicer interface.
An engineer picks a golden path in the portal or the CLI. The scaffolder creates the repository with its catalog descriptor, deployment intent, infrastructure intent, a reference to the shared pipeline, and the observability and on-call wiring already in place. Everything the engineer declares lives in their own repository; the platform's surfaces write intent and then stop. Two reconcilers converge the world on that declaration — one for workloads into a shared AKS fleet, one for infrastructure through Terraform against Azure Resource Manager — and both are idempotent, resumable and drift-detecting. Alongside them, and sharing no component with them, runs the enforcement plane: policy authored as code, signed into a bundle, cached locally at every evaluation point, advisory in the pull request and binding at admission into the cluster. A component that never touched a golden path meets exactly the same binding checks. Everything that results — what is running, who owns it, what it cost, what it is failing — is projected into a catalog that is rebuildable rather than authoritative, and into the adoption and DORA metrics by which the platform judges itself.
Enforcement is a property of admission into the cluster, authored and signed separately from any golden path. A fork removes the template and never the control.
ADR-05Advisory in the pull request to teach, binding at admission to hold. A CI-pass-then-admission-fail is defined as a platform defect and measured as one.
ADR-06The portal writes intent and owns nothing. The platform's catalog is a projection, which is what lets it be rebuilt rather than protected at RPO 0.
ADR-01Declared state converged on continuously, so a partial failure resumes and a console edit becomes a detected event rather than invisible drift.
ADR-02The fork ratio is the number that predicts migration cost, and the pipeline is consumed by reference so the most change-prone artefact is not part of the fork surface.
ADR-03Owner, cost centre, tier and data classification are enforced properties applied when a thing is made, because nothing acquires an owner afterwards.
ADR-11Catalog ownership resolves to a live identity group, so a reorganisation moves access with the components and there is no second list to decay.
ADR-10A portal outage costs the ability to change, never the ability to serve. Running workloads are never evicted because the platform cannot evaluate them.
ADR-12Relief always exists; making it visible is what stops it becoming a quiet policy edit for the loudest team.
ADR-08Every concept the golden path hides is met for the first time at 03:00, so the substrate stays addressable and access to it is an entitlement rather than an incident favour.
ADR-07An internal platform outlives the tools it was built from — the portal, the reconcilers, probably the cloud. These are the properties that should survive all of that, and the ones a future team would most easily discard without noticing what they were for.
ADR-05 says that evidence of a control must come from where the workload runs, not from how it was created. That is true of Kubernetes admission, of whatever replaces it, and of a substrate that has no admission point at all — in which case the architecture must say honestly that its coverage claim has weakened, rather than quietly keep making it.
Keeping intent in the team's repository is not a Git preference. It is the reason the platform can be rebuilt without asking 140 teams what they had, and the reason the platform team is never asked to act as the owner of somebody else's configuration on the worst day.
A mandate would raise coverage and destroy the only honest measurement of whether the paved road is good. The shadow estate is uncomfortable to report and is the most valuable number the platform produces.
Every golden path will want one more concept, and each addition is individually reasonable. Stating the budget as a design constraint is what makes the tenth addition arguable rather than inevitable.
The rule that a platform outage may cost the ability to change but never the ability to serve is the one that decides whether the platform survives its first bad day. It is also the first rule broken under deadline, which is why the SLOs are published to tenants.
The storage classification in ADR-16 is a method rather than a configuration: ask of every store what it could be reconstructed from, and protect only the ones with no answer. That survives any change of database, cloud or retention regime.
Every number here is a stated assumption from the requirement, chosen to be argued with rather than believed. The right-hand column names the view where the mechanism that meets it is drawn.
| Quality | Target | How it is met | View |
|---|---|---|---|
| Time to first production deploy | ≤ 30 min p50, ≤ 2 h p95 for a new component | One scaffold action produces repository, pipeline reference, manifests, observability wiring and catalog entry; provisioning runs in parallel with the engineer writing code. | 04 |
| Self-service provisioning latency | ≤ 12 min p95 for the golden-path resource set | Declared intent admitted against quota, then reconciled by a runner pool with idempotent, resumable applies. | 14 |
| Commit to production | ≤ 25 min p95 on the paved road | Shared pipeline consumed by reference, one artefact promoted unchanged, reconciler applies without a second build. | 17 |
| Binding guardrail evaluation | ≤ 250 ms p99, ≥ 99.95% monthly | Three admission webhook replicas per cluster evaluating a locally cached signed bundle — no network call to a central service on the admission path. | 15 |
| Advisory guardrail feedback | ≤ 90 s p95 in the pull request | Same bundle, evaluated in the pipeline; a divergence from the admission verdict is instrumented as a defect. | 15 |
| Rollback | ≤ 5 min p99 to serving the previous version | Reconciler-level revert to the previously converged state, independent of the pipeline and of a fresh build. | 17 |
| Reconciliation lag | ≤ 5 min p95, ≤ 20 min p99 | Sharded reconcilers with bounded worst-case lag, plus drift detection on resource-graph change events. | 14 |
| Catalog freshness | ≤ 10 min p95 after a deploy or ownership change | Webhook-driven descriptor ingestion with a periodic full reconcile as the backstop. | 10 |
| Platform availability | Portal ≥ 99.5%, build and deploy control plane ≥ 99.9%, admission ≥ 99.95% | Availability is graded by what each component is in the path of; the portal holds no truth and is in no production path. | 16 |
| Guardrail coverage | ≥ 99% of production workloads, off-road included | Enforcement at admission rather than in templates, so coverage describes what is running rather than how it was made. | 15 |
| Adoption | ≥ 70% on a current golden path by month 12; shadow estate ≤ 10% | Measured from the catalog's projection of declared and observed state, and reported as the platform's own scoreboard. | 18 |
| Recovery | Provisioning state RPO 0 / RTO 1 h; catalog RPO 15 min / RTO 4 h; portal RPO 24 h | Storage classified by what a loss would cost, with a full control-plane rebuild exercised twice a year. | 11 |
Four of this architecture's claims are cheap to test and expensive to be wrong about. A prototype that proves these can be built on; one that only demonstrates scaffolding a repository has proved the easy half.
| Risk | If it lands | Response |
|---|---|---|
| Adoption stalls below the threshold where the platform pays for itself | Twelve engineers maintaining a paved road most teams decline is a worse outcome than no platform, because the guardrails still have to be run and the shadow estate still has to be supported. | Adoption and satisfaction reported per path from day one, with a declining path treated as a signal about the path. The requirement leaves the mandate question explicitly open, and the answer changes the business case rather than the architecture. |
| Advisory and binding verdicts diverge in practice | Engineers stop believing the pull-request verdict, and every control is met for the first time at admission — the experience that makes platforms hated and routed around. | One signed bundle for both points, the divergence rate instrumented as a defect class in view 18, and a narrower advisory set if identical evaluation proves impossible at some stage. |
| The forked tail consumes the platform team | If the fork ratio is materially above the assumed 16%, migrations stop being automatable and the 1:75 staffing ratio fails quietly — as unshipped fixes rather than as an incident. | Measure the ratio in the prototype. The lever is moving more of the template into artefacts consumed by reference, so that forking the repository does not fork the pipeline. |
| The abstraction is wrong for the on-call population | Too thin and every engineer pays learning costs the platform was meant to remove; too thick and the person paged at 03:00 meets the substrate for the first time under pressure. | Escape-hatch usage is measured, and a spike names the abstraction that is failing. This is the requirement's most openly unresolved question, and the substrate is kept addressable so the line can move in either direction without a rewrite. |
| Shared-cluster upgrades become the platform's dominant risk | One upgrade can affect every tenant in a cluster, and a bad policy bundle can stop deploys fleet-wide. | Staged fleet rollout with automated tenant smoke checks and abort-and-hold, a canary component per golden path walking the whole path continuously, and staged reversible policy publication. |
| Provisioning state is lost or corrupted | The platform stops knowing what it created; recovery becomes reconciliation by hand across an estate of 1,400 components. | RPO 0 with versioning and locking, restore exercised twice a year rather than assumed, and a rebuild path from declared intent as the backstop of last resort. |
Sixteen decisions make up this architecture. Everything else across the twenty-one views is convention, sizing or consequence. Each record states the forcing question, the context that makes it hard, what was decided, how it is realised on Azure, the options weighed, what the choice buys and costs, the conditions that would flip it, why it should still be right in ten years, and the lesson that transfers beyond this platform.
Status of this document. This is a design, not a report on a running system. The rates, latencies, volumes, retentions and targets are the requirement's stated assumptions for a mid-size engineering organisation — 900 engineers, 140 teams, 1,400 components, 5,200 deploys a week, a platform team of twelve — invented to be defensible and arguable rather than absent. They are to be replaced by measured telemetry before build, and four in particular are the first numbers real data should overturn: the 19-working-day baseline the whole business case rests on, the 16% fork ratio that predicts migration cost, the ≥ 70% adoption target at month 12, and the assumption that twelve platform engineers can serve nine hundred.
Why generation and enforcement are separate machinery, and where each one binds.
ADR-05Guardrails are enforced at the runtime, never inside the templates that generate components ADR-06One policy source, two evaluation points; fail closed on new admissions and open on running workloadsWhere truth about what should exist lives, and how the world is moved towards it.
ADR-01Intent lives in the team's repository; the portal writes it and owns nothing ADR-02Provision by reconciling declared intent, not by calling the cloud from the portal ADR-04Quota, budget and data classification are checked at admission, and a rejection is written for the requesterWhat the platform knows about the estate, and how every running thing keeps an owner.
ADR-09The catalog is a projection, reconciled against the runtime in both directions ADR-10Ownership resolves to a live identity group, and entitlement derives from it ADR-11Owner, cost centre, tier and classification are applied at creation as enforced properties ADR-16Storage is classified by what a loss would cost, and only one band carries RPO 0Who may do what, and the fact that the platform is the most privileged thing in the estate.
ADR-08Exceptions are named, approved by someone else, expiring and published ADR-15Workload identity everywhere, and the control plane is held to the controls it enforcesThe tenancy boundary, and recovery that does not depend on the machinery that broke.
ADR-13Rollback does not depend on the pipeline that produced the deploy ADR-14Namespace per component in shared clusters, with staged fleet upgrades and a canary component per pathThe decisions that only make sense if teams are allowed to say no.
ADR-03Components track their golden-path template by default, and forking is a recorded, supported choice ADR-07A thin abstraction with strong defaults, and an escape hatch that is a designed path ADR-12The API is primary, and no platform component sits in the critical path of running productionThe platform is built on Microsoft Azure with open-source platform components running on it. Azure was chosen for rotation — the four use cases before this one ran on Google Cloud and AWS — and because an internal developer platform's two hardest questions, who is entitled to provision what and where the isolation boundary sits, map onto Entra entitlements and the management-group hierarchy rather than having to be invented on top of a flat account model. Everything in the requirement document outside its first section is vendor-neutral, and the table below is where the cloud is actually chosen.
| Capability | Choice | Origin | Credible alternative | Why this one | Record |
|---|---|---|---|---|---|
| Developer portal | Backstage, self-hosted on the control-plane AKS cluster | Open source | A bespoke portal, or a commercial IDP product | The portal is a client of the platform API (ADR-12), so its replaceability matters more than its features. Backstage supplies the catalog model and plugin surface without owning any truth. | ADR-12 |
| Intent and source of truth | GitHub Enterprise repositories owned by product teams | Existing | A platform-owned configuration database | Review, history, blame and revert for infrastructure then use the tooling every engineer already has, and the platform's own stores become rebuildable projections. | ADR-01 |
| Infrastructure reconciliation | Terraform modules from an internal registry, run by a pool on AKS with workload identity, state in Azure Storage with locking | Open source on Azure | Crossplane compositions reconciled in-cluster; Bicep with deployment stacks | A mature module ecosystem and an explicit plan step the platform can gate on, against a state object that can be backed up and locked. Crossplane remains the natural successor if resource kinds become more platform-specific than cloud-specific. | ADR-02 |
| Workload reconciliation | Argo CD, sharded by team, one instance per cluster | Open source | Flux; a push-based deploy step in the pipeline | Pull-based reconciliation keeps the deploy path alive when the pipeline is not, and makes drift in the cluster a detected event rather than an assumption. | ADR-13 |
| Policy authoring and evaluation | Policy-as-code compiled to a signed OCI bundle in ACR, evaluated by admitting webhooks in every cluster and by the pipeline evaluator | Open source on Azure | Azure Policy for Kubernetes alone; a hosted policy service called at admission | One artefact drives both evaluation points, and a locally cached bundle removes a central service from the critical path of every admission. Azure Policy remains the landing zone's floor beneath it. | ADR-06 |
| Build plane | GitHub Actions with ephemeral self-hosted runners on AKS, reusable workflows consumed by reference | Existing + Azure | Azure Pipelines; per-team runners | Keeping the pipeline a referenced artefact rather than a copied file is what makes a fix shippable once; ephemeral runners keep build identity scoped to one component. | ADR-03 |
| Artefact registry and supply chain | Azure Container Registry, geo-replicated, with signing and provenance attestations | Azure | A self-hosted registry; a third-party artefact platform | The registry is also where the policy bundle lives, so one trust story and one replication story cover both the images and the controls that judge them. | ADR-16 |
| Runtime | Shared AKS clusters, namespace per component, split by environment and subscription | Azure | Cluster per team; Azure Container Apps for the stateless golden path | A twelve-person platform team cannot operate 140 clusters. The namespace boundary is cheap and the cluster upgrade is not, which is the trade stated openly in ADR-14. | ADR-14 |
| Identity and entitlement | Microsoft Entra ID groups, Conditional Access, and workload identity federation | Azure | A platform-local role model synchronised from the directory | Entitlement is derived from catalog ownership resolved against live groups, so there is no second access list to fall out of date when a team reorganises. | ADR-10 |
| Secrets | Azure Key Vault, paths created by the platform and bound to workload identities | Azure | A self-hosted secret manager; sealed secrets in the repository | The platform grants access to a path and never reads or holds secret material, which keeps plaintext out of the catalog, the portal and every pipeline log. | ADR-15 |
| Catalog store | Azure Database for PostgreSQL Flexible Server, zone redundant | Azure | A graph database; the portal's own storage | The catalog is a projection with a four-hour RTO (ADR-09); a well-understood relational store with recursive queries covers 9,000 relations without adding an engine to operate. | ADR-09 |
| Metrics and insight | Azure Data Explorer for DORA, adoption, drift and cost facts | Azure | Log Analytics alone; a warehouse | The questions are time-series and high-cardinality — per team, per path, per component, over three years — and they are asked interactively in reviews. | ADR-12 |
| Telemetry | OpenTelemetry collectors into Azure Monitor, Managed Prometheus and Managed Grafana | Azure + open source | A self-hosted observability stack | The golden path wires a component's observability at scaffold time; the value is in the wiring being automatic, not in owning the backend. | ADR-11 |
| Audit evidence | Append-only blob storage with immutability policy and legal hold, separate from operational stores | Azure | Audit rows in the catalog database | The platform's own operators are in scope as a threat; the audit store is the one place they cannot write, which is only true if it is not a table they administer. | ADR-16 |
| Cost attribution | Azure Cost Management daily exports, joined to catalog ownership | Azure | A third-party FinOps platform | Attribution is applied at creation as tags (ADR-11), so the export needs no reconciliation step — the join is already correct when the data arrives. | ADR-11 |
Is a control something a golden path produces, or something the runtime insists on?
If the unbypassable check happens at admission, what is the point of checking in the pull request — and what happens when the evaluator is unavailable?
When an engineer asks the platform for something, where does the record of what they asked for live?
Does a provisioning request execute an imperative sequence of cloud API calls, or declare a desired state that a controller converges on?
When a request cannot be satisfied, when does the requester find out, and in whose language?
Is the platform's picture of the estate built from what teams declared, or from what is actually running?
How does the platform know who may act on a component, and what happens when that answer goes stale?
When does a resource acquire the metadata that makes it attributable?
Which of the platform's stores must never lose data, and which are allowed to be rebuilt?
What happens when a binding guardrail blocks something the organisation has decided to do anyway?
What credentials exist in this system, and who guards the thing that guards everything?
What is the recovery path when the thing that deployed the change is itself the problem?
Where does the tenancy boundary sit, and who absorbs the cost of the answer?
After scaffolding, does the generated code belong to the team, to the platform, or to both?
How much of the underlying runtime should the golden path hide from the engineer who uses it?
What may fail when the platform fails?
Eleven terms that carry specific meaning in this package. Several are used loosely in the wider platform-engineering literature; here they are the names of decisions.
| Package | What it is | What it does here | Considered instead |
|---|---|---|---|
| Golden path | An opinionated, end-to-end route from empty repository to production for one named kind of workload. | A product a team may decline. Its adoption number is meaningful precisely because declining is allowed. | A mandated standard, which guarantees coverage and destroys the feedback signal that says whether the path is any good. |
| Paved road | The generation plane — templates, the scaffolder, the shared pipeline, the update bot. | Everything the platform offers that a fork can remove. | Conflating it with the guardrails, which is the failure this architecture exists to avoid. |
| Guardrail | A control evaluated at admission into the runtime, from a signed policy bundle. | Applies to every workload regardless of how it was created. Cannot be declined, only excepted with attribution and an expiry. | A check inside a template, which is deleted by the fork that removes the template. |
| Intent | The declaration in a team's repository of what should exist: catalog descriptor, deployment manifests, infrastructure declaration. | The system of record. The platform reconciles towards it and never holds an authoritative copy. | A row in the platform's database, which makes the platform the owner of every team's configuration. |
| Projection | Anything the platform derives from intent plus observed runtime state — principally the catalog and the insight stores. | Rebuildable, which is what lets it carry an RTO instead of a zero-loss obligation. | Treating the catalog as truth, after which a catalog outage becomes an estate outage. |
| Template drift | The distance between a component and the current version of the template that generated it. | A measured, published per-component figure, and the input that makes a migration plannable. | Not measuring it, and discovering the divergence when a security fix needs to reach the estate. |
| Shadow estate | Production components deployed outside the platform's paths. | The platform's own competitive metric. Guardrails still cover it; the paved road does not. | Treating it as non-compliance, which hides the product failure it actually reports. |
| Advisory evaluation | A policy verdict produced in the pull request and pipeline, from the same bundle as the binding one. | Exists to explain and to be fast (≤ 90 s). Carries no authority of its own. | A separate ruleset for CI, which diverges from the binding one within a quarter. |
| Binding evaluation | The admission-time verdict, evaluated locally from a cached signed bundle within 250 ms p99. | The only unbypassable point, and therefore the only place coverage can honestly be claimed. | Trusting the pipeline, which covers only the population that already uses the pipeline. |
| Exception | A named, expiring, separately approved departure from a binding guardrail for one component. | The legitimate relief valve, published on a standing register so accumulation is visible. | A quiet policy edit for the loudest team, which is the same relief with none of the evidence. |
| Cognitive load | The number of systems, concepts and credentials an engineer must hold to ship on a golden path. | A stated, defended budget. Each concept a path adds is a withdrawal the platform must justify. | An aspiration in a vision document, which loses every argument against one more useful feature. |
svg/<view>.svg or drawio/<view>.drawio in draw.io Desktop or at app.diagrams.net to edit. The SVG carries the diagram inside it, so it is both the picture and the source. This folder is self-contained — copy it whole and every link still resolves.