FinOps Runway Contract
Status: draft implementation note for the ops cockpit.
The FinOps tab should not make Svelte components rediscover operational meaning from raw vendor rows. The API owns the projection. UI, MCP, and future sysadmin agents should all ask the same questions against the same shapes:
- Which resource is closest to exhaustion?
- Is current burn above the safe monthly pace?
- When does the projection cross the limit?
- Which source reading produced the answer?
API shape
GET /finops/summary remains the source endpoint. It keeps the existing
budgets[] rows for detail, and adds:
resourcePressure: FinOpsResourcePressure[];
runway: {
primaryResourceId: string | null;
weakestLinks: FinOpsWeakestLink[];
resources: FinOpsRunwayResource[];
};
resourcePressure[] is the normalized, vendor-neutral resource model. A row
represents something that can become the bottleneck: Workers requests, AI
tokens, provider spend, gateway calls, Axiom ingest, or a future Railway/Koyeb
metric. Rows carry usage, limit, projection, daily burn rate, safe daily pace,
days-to-limit, state, risk score, reason, and source endpoint.
runway.resources[] is chart-ready. Each resource has points for the current
monthly period:
safe: the ideal linear budget pace.actual: the aggregate usage line up to today.projected: the current burn-rate projection through month end.limit: the monthly ceiling.
When the API lacks a usage reading or limit, it must return null values
instead of invented data. Missing vendor credentials are an operator fact, not
a hidden success.
Visual intent
The FinOps page should lead with two visual answers:
- Monthly runway chart: actual vs safe pace vs projected burn vs limit.
- Weakest-link radar/ranking: which constrained resource reaches trouble first.
The existing budget and service tables remain useful drilldown surfaces, but they should not be the first visual answer. They explain the reading after the operator or AI has already seen where the pressure is.
Extension rule
Adding a provider should mean adding an adapter that emits the same resource pressure contract. The dashboard should not know whether the source is Cloudflare, Axiom, OpenAI, Anthropic, Koyeb, Render, Railway, or something else.