# Cost Allocation & Showback Platform

**Solution Architecture v1.0 · Microsoft Azure · Cost & Efficiency Architecture · 2026-09 · 22 views · 16 architecture decision records**

The platform that answers the question every cloud invoice refuses to: not *what did we spend*, but *whose was it*. The provider bills an account, a subscription or a project; the organisation wants to know what a team, a product and a customer cost. Everyone who has sat in a quarterly review has seen the failure it exists to prevent — a number nobody can reproduce, a spike that lands in a bucket called "untagged", a shared Kubernetes cluster whose bill arrives as one line for forty teams, and a finance figure that disagrees with the engineering figure in the same meeting. This platform ingests billing data from three clouds plus licence and SaaS spend, attributes every cost record to an accountable owner, apportions what nobody incurred alone, and publishes a frozen monthly statement that engineering and finance both believe. It starts as **showback**, where the number is informational, with a deliberate later path to **chargeback**, where it moves real budget. The operating context is an assumed estate of ~1,200 billing scopes, ~40,000 active resources, 450 engineering teams across 28 product lines, and roughly $180M a year of cloud spend plus $40M of licence and SaaS spend, built on ADLS Gen2 for the immutable raw zone, Azure Data Explorer for the cost fact store, Databricks for conformance and allocation, Azure SQL for effective-dated ownership and frozen statements, Event Hubs for container usage telemetry, and Entra ID for accountability-scoped access.

The product is not the ability to add up a bill. It is **the ability to explain a number that was produced while its source data was still moving** — and to produce the same number again, months later, from data the platform still holds.

The design rests on one rule: **every published number is a frozen artefact produced by a pure, versioned function of three immutable inputs — billing snapshot, ownership snapshot, policy version — never a live query over mutable data.**

The decisions that carry the design:

- **Allocation is a pure function of three pinned inputs.** They are resolved and hashed before any arithmetic happens, and recorded on every allocated row and every statement. "Why did my number change?" has a one-sentence answer: which of the three moved (ADR-01).
- **The bill is landed exactly as given, forever.** Immutable raw partitions per provider, period and export version; normalisation happens beside the raw form, never over it. A revised conformer re-derives history instead of losing it (ADR-02).
- **Restatement is the normal path run a second time.** A re-issued export lands as a new version, diffs at record grain, re-runs the same pure function and produces statement v2. v1 survives for seven years, and so does the reason for the delta (ADR-04).
- **Everything about the organisation is effective-dated.** Ownership resolves by a declared precedence chain — tag, parent scope, mapping, exception — as of a point in time, recording which rule fired. A reorganisation changes the future, not the meaning of a closed period (ADR-05, ADR-06).
- **The remainder is a named line, never a spread.** Unallocated spend is published in currency at every level before any absorption strategy, and charged to a named accountable owner. Balancing to 100% is exactly why tag coverage never improves (ADR-07).
- **The platform will infer an owner, and will never bill a guess.** Inferred attribution is marked at a stated confidence, counts towards showback coverage, and is excluded from chargeback postings (ADR-08).
- **Cluster idle lands on whoever sized the cluster.** Shared compute is apportioned on max(request, usage); the capacity neither requested nor used goes to the platform owner — the only party who can act on it (ADR-09).
- **Teams see the price the organisation actually pays.** Commitment savings are attributed to the consuming workload at amortised rates, with the cash view published alongside from the same ledger (ADR-10).
- **Reconciliation is a gate, not a metric.** A period whose ingested total misses the provider invoice by more than 0.1% or $500 does not publish. Showback may be late; it may not be quietly short (ADR-11).
- **One pipeline, two labels.** The current period is produced by the same allocation function and labelled an estimate. A separate fast path would be a second truth, and two truths destroy both (ADR-12).
- **A dispute never blocks the close.** It closes by a correction that supersedes, or by a reasoned rejection that is recorded — which is what stops the same argument recurring every period (ADR-13).
- **The largest store is the disposable one.** Allocated facts are kept 13 months and rebuilt from the raw zone in 24 hours; the immutable raw zone is the archive of record, which is what keeps the platform inside the 0.5% of measured spend it is allowed to cost (ADR-14, ADR-15).
- **Cost data discloses architecture, headcount and roadmap.** Scope is resolved server-side from the effective-dated org tree and cannot be widened by a parameter; out-of-scope reads are answered at aggregate grain rather than refused, because a flat denial drives people to spreadsheets (ADR-16).

The architecture one-pager (including why the design should still hold up in ten years, and the five risks that would change it) 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) (~11 min) and [docs/decision-record.md](docs/decision-record.md) (~49 min).

---

## What is here

| Path | Contents |
|---|---|
| `diagrams/index.html` | The landing page: 22 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 16 decision records, the capability-to-technology table and the package glossary as markdown |
| `specs/part-a..d.json` | Diagram specifications, the source of truth for every view |
| `specs/manifest-a..b.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, no network) |
| `ask.md` | The requirement |

## The twenty-two views

| # | View | What it answers |
|---|---|---|
| 01 | System Context | Who reads the number, where the spend is billed, and the nine systems the platform reads but never writes to |
| 02 | High-Level Architecture | Six stages from a provider invoice to a number a team will defend, with allocation as a pure function in the middle |
| 03 | Actors and Journeys | Seven actors, including the shared platform owner and two machines that misbehave in specific, designed-for ways |
| 04 | Journey — Explain a Cost Spike | The load-bearing journey; the trough is "Explain", and most of the architecture exists to make it survivable |
| 05 | Journey — Close and Publish the Month | Two unrecoverable moments: a wrong reconciliation and a wrong posting |
| 06 | Layered Architecture | Eight layers, with ownership below allocation because allocation reads a snapshot of it, never the live table |
| 07 | Platform Components | Four stores because there are four mutabilities; serving and batch never share capacity |
| 08 | Integration Surface | Five read surfaces, three write surfaces, and nothing written back to the estate it measures |
| 09 | Cost Data Flow | From a provider file to a frozen line, with reconciliation as a gate rather than a dashboard metric |
| 10 | Data Ownership Zones | Five zones graded by who wrote the data; the largest and hottest has the weakest durability requirement |
| 11 | Data Model | Sixteen entities; the spine is period → cost record → run → allocated cost → statement, and the run carries all three input versions |
| 12 | Allocation Run | Pin three inputs, hash them, refuse a duplicate, reconcile, then freeze |
| 13 | Ingestion and Restatement | The bill arrives more than once; restatement is the normal path executed again |
| 14 | Shared-Cost Apportionment | Four pools, four different honest bases, and an explicit answer for idle in each |
| 15 | Statement, Dispute and Restatement | Supersede, never overwrite — and a reasoned rejection is part of the record |
| 16 | Anomaly to Owner | Attribution happens before routing, because an alert that cannot name a resource is a page to nobody |
| 17 | Deployment Architecture | One region serves, one holds what cannot be recomputed and rebuilds what can |
| 18 | Policy Promotion Pipeline | A rule change moves money, so it is simulated against a closed period before it is adopted |
| 19 | Observability | Five signal classes across five stages; only five cells page, and four of them are correctness |
| 20 | The Monthly Close Loop | Six steps; the last two are what make next month's number better than this month's |
| 21 | Security Trust Zones | Four zones, no standing human access to the data zone, and billing credentials that cannot reach a workload |
| 22 | Identity and Access Flow | Server-injected scope from the effective-dated org tree, and why an out-of-scope read is answered rather than refused |

## Rebuilding

```bash
bash scripts/build.sh          # from this directory; Node 20+, no network, no packages
```

The build runs generate → geometry gate → routing gate → editable SVG → plain SVG → draw.io →
HTML pages and index → one-pager and decision record → link check, and stops at the first
failure. The geometry gate passes with zero errors and zero warnings; the routing gate passes
with zero errors and two accepted label-proximity warnings on view 08, where the alternative
would be an unlabelled boundary crossing.
