Skip to content
2,274 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1//! What g1t earns on each thing it sells, measured against what
2//! Cloudflare actually charged for it.
3//!
4//! Once a day, after `costs` has read Cloudflare's bill, the reconciler
5//! puts three figures side by side for every day and each of g1t's
6//! products (a "bucket": sandboxes, deployments, git, repository storage,
7//! …):
8//!
9//! 1. **What Cloudflare charged**: the day's cost lines `cost_map` gives
10//! the bucket.
11//! 2. **What g1t's meters recorded**: the cost on the ledger's entries for
12//! it (the price book's cost at the time) and, where a mapping names
13//! one, g1t's own count of the same units (git operations).
14//! 3. **What customers were charged**: the entries' value at price, before
15//! the plan's included usage, a trial or a pool paid part of it; and of
16//! that, what workspaces paid. Month-end meters (git, storage, scans,
17//! embeddings, the actions cache) come from daily snapshots of what they
18//! had come to (`pending_days`). The plan's price is the `platform`
19//! bucket's: the plan pays for running g1t.
20//!
21//! From those: margin per product (value against cost) and for all of g1t
22//! (money in against every cost); drift (counts or costs that disagree past
23//! a mapping's threshold, and leaks: cost with no revenue, or a Cloudflare
24//! meter no one mapped); each workspace's cost, Cloudflare's figure shared
25//! out by each workspace's own meters; and price proposals when a unit's
26//! real cost has moved (`pricing`). Alerts go to staff by email and as a
27//! banner in sudo. See docs/BILLING_OPERATIONS.md.
28
29use std::collections::{BTreeMap, BTreeSet};
30
31use g1t_contracts::billing::*;
32use g1t_contracts::{FailureCode, Outcome, new_id};
33use g1t_contracts::time::rfc3339;
34use g1t_kit::now_ms;
35use serde::{Deserialize, Serialize};
36use worker::wasm_bindgen::JsValue;
37use worker::{Env, Result};
38
39use crate::Billing;
40use crate::costs::{self, ARTIFACTS_OPERATIONS, DAY_MS, Rule, SOURCE_ARTIFACTS, SOURCE_BILLABLE, UNMAPPED};
41
42/// Buckets that are the cost of running g1t, paid by the plan rather than
43/// sold by the unit: never a leak for having no revenue of their own.
44pub(crate) const OVERHEAD: [&str; 1] = ["platform"];
45/// Buckets Cloudflare does not bill: their cost is g1t's own figure.
46pub(crate) const NOT_CLOUDFLARE: [&str; 1] = ["models"];
47/// The days drift is judged over.
48const DRIFT_DAYS: u64 = 7;
49/// The days a workspace's cost is set against its revenue.
50const ANOMALY_DAYS: u64 = 30;
51/// The days a unit's cost is measured over.
52const MEASURE_DAYS: u64 = 30;
53/// Fewer of g1t's units than this say nothing about cost per unit.
54const MIN_UNITS: f64 = 1_000.0;
55/// An open alert is emailed again after this long.
56const REMIND_MS: u64 = 7 * DAY_MS;
57
58// ---------------------------------------------------------------------
59// The arithmetic, apart from the database so it can be tested.
60// ---------------------------------------------------------------------
61
62/// One of g1t's products on one day.
63#[derive(Clone, Debug, Default, PartialEq)]
64pub(crate) struct ProductDay {
65 pub day: String,
66 pub bucket: String,
67 /// What Cloudflare charged g1t, in millionths of a dollar.
68 pub cf_cost_micros: i64,
69 /// What g1t's meters recorded it cost (the price book's cost).
70 pub own_cost_micros: i64,
71 /// What customers were charged for it at price, before what paid.
72 pub value_micros: i64,
73 /// Of that, what workspaces paid themselves.
74 pub cash_micros: i64,
75 /// Units Cloudflare counted and units g1t counted, where a mapping
76 /// says they are the same units.
77 pub cf_quantity: f64,
78 pub own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it79 /// Of `cost()`, what went on usage g1t gave away (the workspaces'
80 /// `WorkspaceDay::given`, added up).
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running81 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily82}
83
84impl ProductDay {
85 /// What it cost: Cloudflare's figure where Cloudflare bills it, else
86 /// g1t's own (models are billed by their providers, through the gateway).
87 pub fn cost(&self) -> i64 {
88 if NOT_CLOUDFLARE.contains(&self.bucket.as_str()) { self.own_cost_micros } else { self.cf_cost_micros }
89 }
90}
91
92/// A line of Cloudflare's bill, as stored.
93#[derive(Clone, Debug, Deserialize)]
94pub(crate) struct LineRow {
95 pub day: String,
96 pub source: String,
97 pub product: String,
98 pub meter: String,
99 pub quantity: f64,
100 pub cost_usd: f64,
101}
102
103/// A count of g1t's own, as stored.
104#[derive(Clone, Debug, Deserialize)]
105pub(crate) struct OwnRow {
106 pub day: String,
107 pub meter: String,
108 pub workspace: String,
109 pub quantity: f64,
110}
111
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running112/// What g1t gave away, by why: its own comped workspaces, free use (a
113/// free period, free allowances, overruns g1t covered), the trial, and the
Merge branch 'worktree-agent-a633ac0f7f66d419d'114/// open-source pool, and discounts on an account's terms (what they took
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging115/// below cost plus the margin, `ledger.discount_micros`), and credits g1t
116/// staff gave, promotional and goodwill, when spent (`grants`). The Team
117/// plan's included usage is paid for by the plan's price, so it is sold,
118/// not given; so is what a refund pays for.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running119#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
120pub(crate) struct Given {
121 pub comped: i64,
122 pub free: i64,
123 pub trial: i64,
124 pub pool: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'125 pub discount: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging126 pub credit_promotional: i64,
127 pub credit_goodwill: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running128}
129
130impl Given {
131 pub fn total(&self) -> i64 {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging132 self.comped + self.free + self.trial + self.pool + self.discount + self.credit()
133 }
134
135 /// Credits from g1t, both kinds.
136 pub fn credit(&self) -> i64 {
137 self.credit_promotional + self.credit_goodwill
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running138 }
139
140 fn add(&mut self, other: &Given) {
141 self.comped += other.comped;
142 self.free += other.free;
143 self.trial += other.trial;
144 self.pool += other.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'145 self.discount += other.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging146 self.credit_promotional += other.credit_promotional;
147 self.credit_goodwill += other.credit_goodwill;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running148 }
149
150 /// The same shares of `cost` as these are of `value`, at most all of it.
151 fn of(&self, cost: i64, value: i64) -> Given {
152 let total = self.total();
153 if value <= 0 || cost <= 0 || total <= 0 {
154 return Given::default();
155 }
156 let given = cost as i128 * total.min(value) as i128 / value as i128;
157 let part = |x: i64| (given * x.max(0) as i128 / total as i128) as i64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'158 Given {
159 comped: part(self.comped),
160 free: part(self.free),
161 trial: part(self.trial),
162 pool: part(self.pool),
163 discount: part(self.discount),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging164 credit_promotional: part(self.credit_promotional),
165 credit_goodwill: part(self.credit_goodwill),
166 }
167 }
168}
169
170/// Credits from g1t in the reconciliation (`grants`): usage paid for with
171/// promotional or goodwill credit is given, not money in; a refund comes
172/// off money in on the day it refunds, shared over that day's paid usage.
173/// What credit paid for that is not among `rows` (month-end meters, or
174/// what was owed from before) is a row of its own on its day.
175pub(crate) fn apply_credits(rows: &mut Vec<UsageRow>, draws: &[(String, crate::grants::Draw)], refunds: &[crate::grants::Refunded]) {
176 let mut paid: BTreeMap<(String, String, String), Given> = BTreeMap::new();
177 for (workspace, draw) in draws {
178 let given = paid
179 .entry((draw.at[..10].to_owned(), workspace.clone(), crate::grants::usage_key(draw.task.as_deref(), &draw.reference)))
180 .or_default();
181 match draw.kind {
182 CreditKind::Promotional => given.credit_promotional += draw.micros,
183 CreditKind::Goodwill => given.credit_goodwill += draw.micros,
184 // Money already paid: what it pays for is paid for.
185 CreditKind::Refund | CreditKind::Purchased => {}
186 }
187 }
188 for ((day, workspace, key), given) in paid {
189 if given.credit() == 0 {
190 continue;
Merge branch 'worktree-agent-a633ac0f7f66d419d'191 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging192 match rows.iter_mut().find(|r| r.day == day && r.workspace == workspace && r.key == key) {
193 Some(row) => {
194 row.cash -= given.credit();
195 row.given.add(&given);
196 }
197 None => rows.push(UsageRow { day, workspace, key, value: 0, cash: -given.credit(), cost: 0, given }),
198 }
199 }
200 for refund in refunds {
201 let weights: Vec<(String, f64)> = rows
202 .iter()
203 .enumerate()
204 .filter(|(_, r)| r.day == refund.day && r.workspace == refund.workspace && r.cash > 0)
205 .map(|(i, r)| (format!("{i:08}"), r.cash as f64))
206 .collect();
207 let shares = attribute(refund.micros, &weights);
208 if shares.is_empty() {
209 rows.push(UsageRow {
210 day: refund.day.clone(),
211 workspace: refund.workspace.clone(),
212 key: "other".into(),
213 cash: -refund.micros,
214 ..UsageRow::default()
215 });
216 }
217 for (index, micros) in shares {
218 if let Ok(i) = index.parse::<usize>() {
219 rows[i].cash -= micros;
220 }
221 }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running222 }
223}
224
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily225/// What a workspace was charged for one key on one day.
226#[derive(Clone, Debug, Default, PartialEq)]
227pub(crate) struct UsageRow {
228 pub day: String,
229 pub workspace: String,
230 /// A ledger task (or `builds`), a month-end source, or `plan`.
231 pub key: String,
232 pub value: i64,
233 pub cash: i64,
234 pub cost: i64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it235 /// Of `value`, what g1t gave away: all of it for g1t's own (comped)
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running236 /// workspaces and in a free period, else what the trial and the pool
237 /// paid and the overruns g1t covered.
238 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily239}
240
241/// One workspace's share of a product's cost on one day.
242#[derive(Clone, Debug, PartialEq)]
243pub(crate) struct WorkspaceDay {
244 pub day: String,
245 pub workspace: String,
246 pub bucket: String,
247 pub cost: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead248 /// What the workspace paid in cash.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily249 pub revenue: i64,
Margin alerts measure what is sold, and say dollars when a percentage would mislead250 /// What its usage was priced at, whoever paid for it.
251 pub value: i64,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running252 /// Of `cost`, the part g1t gave away: all of it for a comped workspace
253 /// or one with nothing priced that day (free use), else the cost times
254 /// the shares of its usage that day that g1t paid for.
255 pub given: Given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily256}
257
258fn micros(dollars: f64) -> i64 {
259 (dollars * 1_000_000.0).round() as i64
260}
261
262/// Puts the day's bill, g1t's counts and what customers were charged side
263/// by side, a row per day and bucket, and shares each bucket's cost out
264/// to workspaces.
265pub(crate) fn fold(
266 rules: &[Rule],
267 revenue_map: &BTreeMap<String, String>,
268 lines: &[LineRow],
269 own: &[OwnRow],
270 usage: &[UsageRow],
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running271 internal: &BTreeSet<String>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily272) -> (Vec<ProductDay>, Vec<WorkspaceDay>) {
273 let mut days: BTreeMap<(String, String), ProductDay> = BTreeMap::new();
274 let entry = |day: &str, bucket: &str| -> ProductDay {
275 ProductDay { day: day.to_owned(), bucket: bucket.to_owned(), ..ProductDay::default() }
276 };
277 // Which of g1t's own meters count each bucket's units.
278 let mut own_meters: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new();
279 for rule in rules {
280 if let Some(meter) = &rule.own_meter {
281 own_meters.entry(rule.bucket.as_str()).or_default().insert(meter.as_str());
282 }
283 }
284 let mut events: BTreeMap<(String, String), f64> = BTreeMap::new();
285 for line in lines {
286 let rule = costs::classify(rules, &line.product, &line.meter);
287 let bucket = rule.map_or(UNMAPPED, |r| r.bucket.as_str());
288 let key = (line.day.clone(), bucket.to_owned());
289 if line.source == SOURCE_ARTIFACTS {
290 // What Artifacts counted: operations only, and only where the
291 // bill does not count them itself.
292 if ARTIFACTS_OPERATIONS.contains(&line.meter.as_str()) {
293 *events.entry(key).or_default() += line.quantity;
294 }
295 continue;
296 }
297 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
298 row.cf_cost_micros += micros(line.cost_usd);
299 if line.source == SOURCE_BILLABLE && rule.is_some_and(|r| r.own_meter.is_some()) {
300 row.cf_quantity += line.quantity;
301 }
302 }
303 for (key, quantity) in events {
304 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
305 if row.cf_quantity == 0.0 {
306 row.cf_quantity = quantity;
307 }
308 }
309 // g1t's own counts of the same units, by bucket and by workspace.
310 let mut own_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
311 // Cloudflare's own count by workspace, where it gives one
312 // (`cloudflare_<bucket>`): the best way to share its cost.
313 let mut cf_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
314 for count in own {
315 if let Some(bucket) = count.meter.strip_prefix("cloudflare_") {
316 cf_by.entry((count.day.clone(), bucket.to_owned())).or_default().push((count.workspace.clone(), count.quantity));
317 continue;
318 }
319 for (bucket, meters) in &own_meters {
320 if meters.contains(count.meter.as_str()) {
321 let key = (count.day.clone(), (*bucket).to_owned());
322 days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1)).own_quantity += count.quantity;
323 own_by.entry(key).or_default().push((count.workspace.clone(), count.quantity));
324 }
325 }
326 }
327 // What customers were charged.
328 let bucket_of = |key: &str| revenue_map.get(key).cloned().unwrap_or_else(|| "models".to_owned());
329 let mut value_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
330 let mut cost_by: BTreeMap<(String, String), Vec<(String, f64)>> = BTreeMap::new();
331 let mut revenue: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Margin alerts measure what is sold, and say dollars when a percentage would mislead332 let mut valued: BTreeMap<(String, String, String), i64> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily333 let mut active: BTreeMap<String, Vec<(String, f64)>> = BTreeMap::new();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running334 let mut gave: BTreeMap<(String, String), (Given, i64)> = BTreeMap::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily335 for u in usage {
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it336 let g = gave.entry((u.day.clone(), u.workspace.clone())).or_default();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running337 g.0.add(&u.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it338 g.1 += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily339 let bucket = bucket_of(&u.key);
340 let key = (u.day.clone(), bucket.clone());
341 let row = days.entry(key.clone()).or_insert_with(|| entry(&key.0, &key.1));
342 row.own_cost_micros += u.cost;
343 row.value_micros += u.value;
344 row.cash_micros += u.cash;
345 value_by.entry(key.clone()).or_default().push((u.workspace.clone(), u.value as f64));
346 cost_by.entry(key).or_default().push((u.workspace.clone(), u.cost as f64));
Margin alerts measure what is sold, and say dollars when a percentage would mislead347 *revenue.entry((u.day.clone(), u.workspace.clone(), bucket.clone())).or_default() += u.cash;
348 *valued.entry((u.day.clone(), u.workspace.clone(), bucket)).or_default() += u.value;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily349 active.entry(u.day.clone()).or_default().push((u.workspace.clone(), u.value.max(u.cost) as f64));
350 }
351 // Each bucket's cost shared out: by Cloudflare's own count per
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running352 // workspace, else by g1t's own count of its units, else by what its
353 // usage cost (so free use carries its own cost), else by what it was
354 // charged; running g1t, and what no one mapped, by each workspace's
355 // share of all usage that day.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily356 let mut shares: BTreeMap<(String, String, String), i64> = BTreeMap::new();
357 for ((day, bucket), row) in &days {
358 let key = (day.clone(), bucket.clone());
359 let weigh = |m: &BTreeMap<(String, String), Vec<(String, f64)>>| m.get(&key).filter(|w| w.iter().any(|(_, v)| *v > 0.0)).cloned();
360 let weights = if OVERHEAD.contains(&bucket.as_str()) || bucket == UNMAPPED {
361 active.get(day).cloned()
362 } else {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running363 weigh(&cf_by).or_else(|| weigh(&own_by)).or_else(|| weigh(&cost_by)).or_else(|| weigh(&value_by)).or_else(|| active.get(day).cloned())
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily364 };
365 for (workspace, micros) in attribute(row.cost(), &weights.unwrap_or_default()) {
366 *shares.entry((day.clone(), workspace, bucket.clone())).or_default() += micros;
367 }
368 }
369 let keys: BTreeSet<(String, String, String)> = shares.keys().chain(revenue.keys()).cloned().collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it370 let workspaces: Vec<WorkspaceDay> = keys
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily371 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it372 .map(|(day, workspace, bucket)| {
373 let cost = shares.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running374 // The day's shares given away apply to every bucket, so a
375 // comped workspace's part of running g1t is given too. A
376 // workspace with nothing priced that day used g1t for free.
377 let given = if internal.contains(&workspace) {
378 Given { comped: cost, ..Given::default() }
379 } else {
380 match gave.get(&(day.clone(), workspace.clone())) {
381 Some((given, value)) if *value > 0 => given.of(cost, *value),
382 _ => Given { free: cost.max(0), ..Given::default() },
383 }
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it384 };
385 WorkspaceDay {
386 cost,
387 revenue: revenue.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
388 value: valued.get(&(day.clone(), workspace.clone(), bucket.clone())).copied().unwrap_or(0),
389 given,
390 day,
391 workspace,
392 bucket,
393 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily394 })
395 .collect();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it396 for w in &workspaces {
397 if let Some(row) = days.get_mut(&(w.day.clone(), w.bucket.clone())) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running398 row.given.add(&w.given);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it399 }
400 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily401 (days.into_values().collect(), workspaces)
402}
403
404/// A month-end source's day, from the snapshots of what it had come to:
405/// each day's figure less the day before's in the same month (the first
406/// day of a month, or the first snapshot, is its own).
407pub(crate) fn pending_deltas(snapshots: &[(String, String, String, i64, i64)]) -> Vec<UsageRow> {
408 // (day, workspace, source, cost, charge), any order.
409 let mut sorted = snapshots.to_vec();
410 sorted.sort_by(|a, b| (&a.1, &a.2, &a.0).cmp(&(&b.1, &b.2, &b.0)));
411 let mut out = Vec::new();
412 let mut previous: Option<&(String, String, String, i64, i64)> = None;
413 for snap in &sorted {
414 let (day, workspace, source, cost, charge) = snap;
415 let (before_cost, before_charge) = match previous {
416 Some(p) if p.1 == *workspace && p.2 == *source && p.0[..7] == day[..7] => (p.3, p.4),
417 _ => (0, 0),
418 };
419 let (cost, charge) = ((cost - before_cost).max(0), (charge - before_charge).max(0));
420 if cost > 0 || charge > 0 {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running421 out.push(UsageRow { day: day.clone(), workspace: workspace.clone(), key: source.clone(), value: charge, cash: charge, cost, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily422 }
423 previous = Some(snap);
424 }
425 out
426}
427
428/// Margin as a share of what was charged, in percent; None when nothing was.
429pub(crate) fn margin_percent(revenue_micros: i64, cost_micros: i64) -> Option<f64> {
430 (revenue_micros > 0).then(|| (revenue_micros - cost_micros) as f64 * 100.0 / revenue_micros as f64)
431}
432
433/// How far `ours` is from `theirs`, in percent of theirs; None when theirs
434/// is nothing.
435pub(crate) fn delta_percent(ours: f64, theirs: f64) -> Option<f64> {
436 (theirs > 0.0).then(|| (ours - theirs) * 100.0 / theirs)
437}
438
439#[derive(Clone, Copy, Debug, PartialEq, Eq)]
440pub(crate) enum DriftKind {
441 /// g1t counted a different number of units than Cloudflare did.
442 Count,
443 /// What Cloudflare charged differs from what the price book says the
444 /// same usage cost.
445 Cost,
446 /// Cloudflare charged for something nothing charges customers for.
447 Leak,
Merge branch 'worktree-agent-a633ac0f7f66d419d'448 /// Model usage AI Gateway put no price on: its cost is not what the
449 /// provider bills, so neither the ledger nor the gateway total has it.
450 Unpriced,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily451}
452
453impl DriftKind {
454 pub fn as_str(self) -> &'static str {
455 match self {
456 DriftKind::Count => "count",
457 DriftKind::Cost => "cost",
458 DriftKind::Leak => "leak",
Merge branch 'worktree-agent-a633ac0f7f66d419d'459 DriftKind::Unpriced => "unpriced",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily460 }
461 }
462}
463
464#[derive(Clone, Debug, PartialEq)]
465pub(crate) struct Drift {
466 pub bucket: String,
467 pub kind: DriftKind,
468 pub ours: f64,
469 pub cloudflare: f64,
470 pub delta_percent: Option<f64>,
471}
472
473/// Drift over a window for one bucket: counts more than `threshold`
474/// percent apart, a bill that far from the price book's cost of the same
475/// usage, and cost with nothing charged for it. Under `min_cost_micros`
476/// in all, cost says nothing.
477pub(crate) fn drifts(bucket: &str, days: &[ProductDay], threshold: f64, counted: bool, min_cost_micros: i64) -> Vec<Drift> {
478 let overhead = OVERHEAD.contains(&bucket);
479 let sum = |f: &dyn Fn(&ProductDay) -> f64| days.iter().map(f).sum::<f64>();
480 let cf_cost = sum(&|d| d.cf_cost_micros as f64);
481 let own_cost = sum(&|d| d.own_cost_micros as f64);
482 let value = sum(&|d| d.value_micros as f64);
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift483 // Counts are compared from the first day g1t counted: before its meter
484 // was deployed there is only Cloudflare's side. A meter that never
485 // counted anything is compared over every day, so it still shows.
486 let first_counted = days.iter().filter(|d| d.own_quantity > 0.0).map(|d| d.day.as_str()).min();
487 let compared = |d: &&ProductDay| first_counted.is_none_or(|from| d.day.as_str() >= from);
488 let (cf_quantity, own_quantity) = days.iter().filter(compared).fold((0.0, 0.0), |(cf, own), d| (cf + d.cf_quantity, own + d.own_quantity));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily489 let mut out = Vec::new();
490 if counted && cf_quantity > 0.0 {
491 let delta = delta_percent(own_quantity, cf_quantity);
492 if delta.is_some_and(|d| d.abs() > threshold) {
493 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Count, ours: own_quantity, cloudflare: cf_quantity, delta_percent: delta });
494 }
495 }
496 let enough = cf_cost.max(own_cost) >= min_cost_micros as f64;
Merge branch 'worktree-agent-a633ac0f7f66d419d'497 // Models: what AI Gateway priced g1t's own provider traffic at (its
498 // lines, as "Cloudflare's" side) against the ledger's model cost. Only
499 // once the gateway has been read; then the ledger having none of it is
500 // drift too (traffic no run was charged for).
501 let models = NOT_CLOUDFLARE.contains(&bucket) && cf_cost > 0.0;
502 if enough && !overhead && cf_cost > 0.0 && (own_cost > 0.0 || models) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily503 let delta = delta_percent(own_cost, cf_cost);
504 if delta.is_some_and(|d| d.abs() > threshold) {
505 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: cf_cost, delta_percent: delta });
506 }
507 }
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said508 // The ledger has model cost and the gateway priced none of it: a token
509 // that cannot see AI Gateway reads as no rows, never an error, so this
510 // is not agreement. Said, rather than left as no row at all.
511 if NOT_CLOUDFLARE.contains(&bucket) && cf_cost <= 0.0 && own_cost >= min_cost_micros as f64 && own_cost > 0.0 {
512 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Cost, ours: own_cost, cloudflare: 0.0, delta_percent: None });
513 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily514 if !overhead && cf_cost >= min_cost_micros as f64 && value <= 0.0 {
515 out.push(Drift { bucket: bucket.into(), kind: DriftKind::Leak, ours: value, cloudflare: cf_cost, delta_percent: None });
516 }
517 out
518}
519
Merge branch 'worktree-agent-a633ac0f7f66d419d'520/// What can make AI Gateway's cost differ from what the providers bill,
521/// said for staff: cache tokens (priced by the gateway at its own rates for
522/// them, which may lag the provider's), requests Cloudflare billed itself,
523/// models it has no price for, and runs settled short.
524fn caveat_notes(c: &costs::GatewayCaveats) -> Vec<String> {
525 let mut notes = Vec::new();
526 if c.cache_read_tokens > 0.0 || c.cache_write_tokens > 0.0 {
527 notes.push(format!(
528 "{} prompt-cache read and {} cache write tokens went through it: check its cost against the provider's invoice, since cache reads are billed far below input and writes above it",
529 crate::features::thousands(c.cache_read_tokens.round() as u64),
530 crate::features::thousands(c.cache_write_tokens.round() as u64)
531 ));
532 }
533 if c.wholesale_usd > 0.0 {
534 notes.push(format!(
535 "{} of it Cloudflare billed itself (unified billing): that part is on Cloudflare's bill, not a provider's",
536 dollars(micros(c.wholesale_usd))
537 ));
538 }
539 if !c.unpriced.is_empty() {
540 notes.push(format!("it has no price for {} (tokens used, $0)", c.unpriced.join(", ")));
541 }
542 if c.short_runs > 0 {
543 notes.push(format!("{} runs were settled at no less than the sandbox reported because the gateway could not price all of them", c.short_runs));
544 }
545 notes
546}
547
548/// The models drift's detail: the gateway's total against the ledger's.
549pub(crate) fn models_detail(drift: &Drift, caveats: &costs::GatewayCaveats) -> String {
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said550 if drift.cloudflare <= 0.0 {
551 return format!(
552 "Models: the ledger's model cost is {} over the last {DRIFT_DAYS} days and AI Gateway priced nothing, so the two were not compared. Either the gateway's analytics cannot be seen (Cloudflare answers a token without AI Gateway: Read with no rows, not an error; billing reads them with CLOUDFLARE_USAGE_TOKEN, then CLOUDFLARE_BILLING_TOKEN), or model calls went around the gateway.",
553 dollars(drift.ours as i64)
554 );
555 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'556 let lower = drift.ours < drift.cloudflare;
557 let mut detail = format!(
558 "Models: AI Gateway priced g1t's own provider traffic at {} over the last {DRIFT_DAYS} days; the ledger's model cost for the same days is {} ({:+.1}%). {}",
559 dollars(drift.cloudflare as i64),
560 dollars(drift.ours as i64),
561 drift.delta_percent.unwrap_or(0.0),
562 if lower {
563 "Model calls g1t paid for were not charged: runs not yet settled, runs with no session, or calls with no run (the ledger catches up as runs settle; a gap that stays is a leak)."
564 } else {
565 "The ledger counts more than the gateway priced: runs that went to a provider without the gateway, or sandbox reports the gateway could not correct."
566 }
567 );
568 let notes = caveat_notes(caveats);
569 if !notes.is_empty() {
570 detail.push_str(" The gateway's cost may be off: ");
571 detail.push_str(&notes.join("; "));
572 detail.push('.');
573 }
574 detail
575}
576
577/// The unpriced drift's detail.
578pub(crate) fn unpriced_detail(caveats: &costs::GatewayCaveats) -> String {
579 format!(
580 "Models: AI Gateway's cost is not all of what the providers bill over the last {DRIFT_DAYS} days: {}. Runs on a model with no gateway price are charged no less than the sandbox reported; add the model's price to the gateway (or route away from it) so it is charged at cost.",
581 caveat_notes(&costs::GatewayCaveats { cache_read_tokens: 0.0, cache_write_tokens: 0.0, wholesale_usd: 0.0, ..caveats.clone() }).join("; ")
582 )
583}
584
585/// Model usage AI Gateway could not price over the window, as drift on
586/// the models bucket: models with tokens and no cost, or runs settled
587/// short. None when there is none.
588pub(crate) fn unpriced_drift(caveats: &costs::GatewayCaveats) -> Option<(Drift, String)> {
589 if caveats.unpriced.is_empty() && caveats.short_runs == 0 {
590 return None;
591 }
592 let drift = Drift {
593 bucket: NOT_CLOUDFLARE[0].into(),
594 kind: DriftKind::Unpriced,
595 ours: f64::from(caveats.short_runs),
596 cloudflare: caveats.unpriced.len() as f64,
597 delta_percent: None,
598 };
599 Some((drift, unpriced_detail(caveats)))
600}
601
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily602/// When the last `days` in a row (each with enough cost to say something)
603/// were all under the floor: the first of them and the worst margin.
604/// Each item is a day's (day, revenue, cost).
605pub(crate) fn breach(series: &[(String, i64, i64)], floor_percent: f64, days: usize, min_cost_micros: i64) -> Option<(String, f64)> {
606 if days == 0 || series.len() < days {
607 return None;
608 }
609 let tail = &series[series.len() - days..];
610 let mut worst = f64::INFINITY;
611 for (_, revenue, cost) in tail {
612 if *cost < min_cost_micros {
613 return None;
614 }
615 let margin = margin_percent(*revenue, *cost).unwrap_or(-100.0);
616 if margin >= floor_percent {
617 return None;
618 }
619 worst = worst.min(margin);
620 }
621 Some((tail[0].0.clone(), worst))
622}
623
624/// `total` shared out in proportion to `weights`, in whole millionths that
625/// add up to it exactly (largest remainder first). Nothing to share, or no
626/// weight, shares nothing.
627pub(crate) fn attribute(total: i64, weights: &[(String, f64)]) -> Vec<(String, i64)> {
628 let mut merged: BTreeMap<String, f64> = BTreeMap::new();
629 for (key, w) in weights {
630 *merged.entry(key.clone()).or_default() += w.max(0.0);
631 }
632 let sum: f64 = merged.values().sum();
633 if total <= 0 || sum <= 0.0 {
634 return Vec::new();
635 }
636 let mut shares: Vec<(String, i64, f64)> = merged
637 .into_iter()
638 .map(|(key, w)| {
639 let exact = total as f64 * w / sum;
640 (key, exact.floor() as i64, exact - exact.floor())
641 })
642 .collect();
643 let mut left = total - shares.iter().map(|s| s.1).sum::<i64>();
644 let mut order: Vec<usize> = (0..shares.len()).collect();
645 order.sort_by(|a, b| shares[*b].2.total_cmp(&shares[*a].2).then(shares[*a].0.cmp(&shares[*b].0)));
646 for index in order {
647 if left <= 0 {
648 break;
649 }
650 shares[index].1 += 1;
651 left -= 1;
652 }
653 shares.into_iter().filter(|s| s.1 > 0).map(|(key, micros, _)| (key, micros)).collect()
654}
655
656/// Workspaces that cost g1t more than `factor` times what they paid, with
657/// at least `floor_micros` of cost: each (workspace, cost, revenue), the
658/// biggest gap first.
Models' margin read -14%: usage nothing paid for is valued at price, not $0659/// What a day's usage was worth at price. g1t's own workspaces are valued
660/// at price. So is usage nothing paid for, neither charged nor drawn from
661/// the plan, a trial, a pool or a gift (a free period): it was given away at
662/// its price, not sold for nothing. Anything paid keeps what it was paid, so
663/// a discount still shows as one.
664pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
665 if internal || (paid == 0 && cost > 0) {
666 return crate::credits::with_margin(cost, margin_percent);
667 }
668 paid
669}
670
Margin alerts measure what is sold, and say dollars when a percentage would mislead671/// What the overall alert says: the money as money, and a percentage only
672/// while there is enough coming in for one to mean something (a few cents
673/// against dollars of cost reads as -8000%).
674pub(crate) fn overall_detail(took: i64, spent: i64, days: usize, floor: f64, worst: f64) -> String {
675 if took < 1_000_000 * days as i64 {
676 return format!(
677 "All of g1t, comped workspaces left out: took in {} against {} of Cloudflare's bill over {days} days.",
678 dollars(took),
679 dollars(spent)
680 );
681 }
682 format!("All of g1t, comped workspaces left out: money in against Cloudflare's bill under {floor:.0}% for {days} days running, as low as {worst:.1}%.")
683}
684
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily685pub(crate) fn anomalies(rows: &[(String, i64, i64)], factor: f64, floor_micros: i64) -> Vec<(String, i64, i64)> {
686 let mut out: Vec<(String, i64, i64)> = rows
687 .iter()
688 .filter(|(_, cost, revenue)| *cost >= floor_micros && *cost as f64 > *revenue as f64 * factor)
689 .cloned()
690 .collect();
691 out.sort_by(|a, b| (b.1 - b.2).cmp(&(a.1 - a.2)).then(a.0.cmp(&b.0)));
692 out
693}
694
695/// Cloudflare's marginal rate for one of its units: the median over the
696/// charged days of cost over quantity, in dollars. None while the included
697/// amounts still cover it. Each item is a day's (quantity, cost).
698pub(crate) fn billed_rate(days: &[(f64, f64)]) -> Option<f64> {
699 let mut rates: Vec<f64> = days.iter().filter(|(q, c)| *q > 0.0 && *c > 0.0).map(|(q, c)| c / q).collect();
700 if rates.is_empty() {
701 return None;
702 }
703 rates.sort_by(f64::total_cmp);
704 Some(rates[rates.len() / 2])
705}
706
707/// What one of g1t's units costs, from Cloudflare's rate per its own unit
708/// and how many of Cloudflare's units each of g1t's took: if Cloudflare
709/// counts three operations for every git operation g1t counts, a git
710/// operation costs three of Cloudflare's. None without enough of g1t's
711/// units to say.
712pub(crate) fn derived_unit_cost(rate_per_cf_unit: f64, cf_units: f64, own_units: f64) -> Option<f64> {
713 (own_units >= MIN_UNITS && cf_units > 0.0 && rate_per_cf_unit > 0.0).then(|| rate_per_cf_unit * cf_units / own_units)
714}
715
716/// How many units a price is per: `1,000 operations` → 1,000, `million
717/// requests` → 1,000,000, `second` → 1.
718pub(crate) fn unit_size(unit: &str) -> f64 {
719 let first = unit.split_whitespace().next().unwrap_or_default().replace(',', "");
720 match first.as_str() {
721 "million" => 1_000_000.0,
722 "thousand" => 1_000.0,
723 n => n.parse().unwrap_or(1.0),
724 }
725}
726
727fn day_before(day: &str, days: u64) -> String {
728 let ms = g1t_contracts::time::parse_rfc3339(&format!("{day}T00:00:00Z")).unwrap_or(0);
729 rfc3339(ms.saturating_sub(days * DAY_MS))[..10].to_owned()
730}
731
732/// Dollars to the cent from a dollar up, finer below: `$17.02`, `$0.063`.
733fn dollars(micros: i64) -> String {
734 if micros.abs() >= 1_000_000 {
735 let cents = (micros as f64 / 10_000.0).round() as i64;
736 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
737 } else {
738 crate::features::dollars(micros)
739 }
740}
741
742/// The days a plan payment is spread over.
743const PLAN_DAYS: u64 = 30;
744
745/// `micros` paid on `day` spread evenly over `days` days from it, in
746/// whole micros that add up to it (the first days take the remainder).
747pub(crate) fn spread(day: &str, micros: i64, days: u64) -> Vec<(String, i64)> {
748 if micros <= 0 || days == 0 {
749 return Vec::new();
750 }
751 let start = g1t_contracts::time::parse_rfc3339(&format!("{}T00:00:00Z", &day[..10.min(day.len())])).unwrap_or(0);
752 let each = micros / days as i64;
753 let rest = micros % days as i64;
754 (0..days)
755 .map(|n| (rfc3339(start + n * DAY_MS)[..10].to_owned(), each + i64::from((n as i64) < rest)))
756 .collect()
757}
758
759// ---------------------------------------------------------------------
760// The daily run, and what sudo reads.
761// ---------------------------------------------------------------------
762
763#[derive(Serialize)]
764struct Mail<'a> {
765 to: &'a str,
766 from: &'a str,
767 subject: &'a str,
768 text: String,
769 html: String,
770}
771
772fn escape(text: &str) -> String {
773 text.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;").replace('"', "&quot;")
774}
775
776/// Emails staff through Cloudflare Email Sending, the `EMAIL` binding.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays777pub(crate) async fn email_staff(env: &Env, to: &str, subject: &str, lines: &[String]) -> Result<()> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily778 let link = "https://sudo.g1t.sh/costs";
779 let text = format!("{}\n\nCosts & margin: {link}\n\nSent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).\n", lines.join("\n\n"));
780 let mut html = String::from("<div style=\"font-family:system-ui,sans-serif;max-width:560px;margin:0 auto;padding:24px 16px;color:#16150f\">");
781 for line in lines {
782 html.push_str(&format!("<p style=\"font-size:15px;line-height:1.6\">{}</p>", escape(line)));
783 }
784 html.push_str(&format!(
785 "<p><a href=\"{link}\">Open Costs &amp; margin in sudo</a></p><p style=\"font-size:13px;color:#6e6a5e\">Sent by g1t-billing's margin guard (COSTS_ALERT_EMAIL).</p></div>"
786 ));
787 let mail = Mail { to, from: "g1t <noreply@g1t.sh>", subject, text, html };
788 let binding = g1t_kit::js::binding(env, "EMAIL")?;
789 g1t_kit::js::call(&binding, "send", &[g1t_kit::js::to_js(&mail)?]).await?;
790 Ok(())
791}
792
793#[derive(Deserialize)]
794struct AlertRow {
795 id: String,
796 kind: String,
797 subject: String,
798 detail: String,
799 since: String,
800 opened_at: String,
801 emailed_at: Option<String>,
802}
803
804impl From<AlertRow> for MarginAlert {
805 fn from(r: AlertRow) -> Self {
806 MarginAlert { id: r.id, kind: r.kind, subject: r.subject, detail: r.detail, since: r.since, opened_at: r.opened_at, emailed_at: r.emailed_at }
807 }
808}
809
810#[derive(Deserialize)]
811struct MarginRow {
812 day: String,
813 bucket: String,
814 cf_cost_micros: i64,
815 own_cost_micros: i64,
816 value_micros: i64,
817 cash_micros: i64,
818 cf_quantity: f64,
819 own_quantity: f64,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it820 #[serde(default)]
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running821 given_comped_micros: Option<i64>,
822 #[serde(default)]
823 given_free_micros: Option<i64>,
824 #[serde(default)]
825 given_trial_micros: Option<i64>,
826 #[serde(default)]
827 given_pool_micros: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'828 #[serde(default)]
829 given_discount_micros: Option<i64>,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging830 #[serde(default)]
831 given_credit_promotional_micros: Option<i64>,
832 #[serde(default)]
833 given_credit_goodwill_micros: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily834}
835
836impl From<MarginRow> for ProductDay {
837 fn from(r: MarginRow) -> Self {
838 ProductDay {
839 day: r.day,
840 bucket: r.bucket,
841 cf_cost_micros: r.cf_cost_micros,
842 own_cost_micros: r.own_cost_micros,
843 value_micros: r.value_micros,
844 cash_micros: r.cash_micros,
845 cf_quantity: r.cf_quantity,
846 own_quantity: r.own_quantity,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running847 given: Given {
848 comped: r.given_comped_micros.unwrap_or(0),
849 free: r.given_free_micros.unwrap_or(0),
850 trial: r.given_trial_micros.unwrap_or(0),
851 pool: r.given_pool_micros.unwrap_or(0),
Merge branch 'worktree-agent-a633ac0f7f66d419d'852 discount: r.given_discount_micros.unwrap_or(0),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging853 credit_promotional: r.given_credit_promotional_micros.unwrap_or(0),
854 credit_goodwill: r.given_credit_goodwill_micros.unwrap_or(0),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running855 },
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily856 }
857 }
858}
859
860impl Billing {
861 /// The day's work: read Cloudflare's bill and g1t's own counts,
862 /// reconcile, look for drift, measure unit costs, apply prices whose
863 /// day has come, and raise or clear alerts.
864 pub(crate) async fn costs_daily(&self, env: &Env, keeper: &crate::keeper::Keeper) -> Result<CostsRun> {
865 let mut run = CostsRun::default();
866 let (since, until) = match self.read_cloudflare(keeper, &mut run.problems).await? {
867 Some((since, until, lines)) => {
868 run.lines = lines;
869 (since, until)
870 }
871 // Without the bill, still reconcile what g1t knows itself, over
872 // the same days the bill would be read for.
873 None => {
874 #[derive(Deserialize)]
875 struct Last {
876 day: Option<String>,
877 }
878 let last = self.db.prepare("SELECT MAX(day) AS day FROM margin_days").first::<Last>(None).await?.and_then(|l| l.day);
879 costs::window(last.as_deref(), now_ms())
880 }
881 };
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing882 // Not a problem for the run: the last read, or the estimate, stays.
883 if keeper.can_read_bill()
884 && let Err(error) = self.read_subscriptions(keeper).await
885 {
886 worker::console_error!("Cloudflare's subscriptions were not read: {error}");
887 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily888 if let Err(error) = self.count_own(&since, &until).await {
889 run.problems.push(format!("g1t's own counts could not be read: {error}"));
890 }
891 self.snapshot_pending(&until).await?;
Models' margin read -14%: usage nothing paid for is valued at price, not $0892 // Reconciled over the whole window sudo shows, not only the days the
893 // bill was read for: it reads only what is already kept, so a change
894 // in how a day is valued reaches every day shown at the next run.
895 let window = day_before(&until, costs::BACKFILL_DAYS - 1);
896 let reconcile_from = if window < since { window } else { since.clone() };
897 run.days = self.reconcile_range(&reconcile_from, &until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily898 let drift = self.find_drift(&until).await?;
899 run.proposals = self.measure_units(&until).await?;
900 self.apply_due_versions().await?;
901 run.alerts = self.raise_alerts(env, &until, &drift).await?;
902 if let Some(identity) = &self.identity
903 && let Err(error) = self.tell_owners_of_rises(identity).await
904 {
905 run.problems.push(format!("owners could not be told of a price rise: {error}"));
906 }
907 for problem in &run.problems {
908 worker::console_warn!("costs: {problem}");
909 }
910 Ok(run)
911 }
912
913 /// What each month-end source had come to by the end of `day`.
914 async fn snapshot_pending(&self, day: &str) -> Result<()> {
915 self.db
916 .prepare(
917 "INSERT INTO pending_days (day, workspace, source, cost_micros, charge_micros)
918 SELECT ?1, workspace, source, COALESCE(cost_micros, 0), COALESCE(charge_micros, 0) FROM pending_usage WHERE month = ?2
919 ON CONFLICT (day, workspace, source) DO UPDATE SET cost_micros = excluded.cost_micros, charge_micros = excluded.charge_micros",
920 )
921 .bind(&[day.into(), day[..7].into()])?
922 .run()
923 .await?;
924 Ok(())
925 }
926
927 /// What customers were charged on the days, by workspace and key.
928 async fn usage_rows(&self, since: &str, until: &str) -> Result<Vec<UsageRow>> {
929 #[derive(Deserialize)]
930 struct Row {
931 day: String,
932 workspace: String,
933 key: String,
934 internal: i64,
935 own_provider: i64,
936 cash: Option<i64>,
937 drawn: Option<i64>,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running938 trial: Option<i64>,
939 oss: Option<i64>,
940 covered: Option<i64>,
Merge branch 'worktree-agent-a633ac0f7f66d419d'941 discount: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily942 cost: Option<i64>,
943 }
944 let charged_here = crate::storage::CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
945 let end = format!("{until}T23:59:59.999Z");
946 let rows = self
947 .db
948 .prepare(format!(
949 "SELECT substr(created_at, 1, 10) AS day, workspace,
950 CASE WHEN task = 'deployments' AND reference LIKE 'deploy/%' THEN 'builds' ELSE COALESCE(task, 'other') END AS key,
951 CASE WHEN workspace IN ({internal}) THEN 1 ELSE 0 END AS internal,
952 CASE WHEN billed_to = 'workspace' THEN 1 ELSE 0 END AS own_provider,
953 -SUM(amount_micros) AS cash,
954 SUM(COALESCE(credit_micros, 0) + COALESCE(trial_micros, 0) + COALESCE(oss_micros, 0) + COALESCE(given_micros, 0)) AS drawn,
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running955 SUM(COALESCE(trial_micros, 0)) AS trial,
956 SUM(COALESCE(oss_micros, 0)) AS oss,
957 SUM(COALESCE(given_micros, 0)) AS covered,
Merge branch 'worktree-agent-a633ac0f7f66d419d'958 SUM(COALESCE(discount_micros, 0)) AS discount,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily959 SUM(COALESCE(cost_micros, 0)) AS cost
960 FROM ledger
961 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND COALESCE(task, '') NOT IN ({charged_here})
962 GROUP BY 1, 2, 3, 4, 5",
963 internal = crate::sales::INTERNAL_SQL
964 ))
965 .bind(&[since.into(), end.as_str().into()])?
966 .all()
967 .await?
968 .results::<Row>()?;
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it969 let mut internal = BTreeSet::new();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily970 let mut out: Vec<UsageRow> = rows
971 .into_iter()
972 .map(|r| {
973 // A workspace's own model provider was paid there: no cost
974 // to g1t. g1t's own workspaces are valued at price.
975 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
976 let cash = r.cash.unwrap_or(0);
Merge branch 'worktree-agent-a633ac0f7f66d419d'977 // A discount took its part below cost plus the margin: it is
978 // valued at price and that part counted as given, so a
979 // discounted sale never reads as margin lost.
980 let discount = r.discount.unwrap_or(0).max(0);
981 let paid = cash + r.drawn.unwrap_or(0) + discount;
Models' margin read -14%: usage nothing paid for is valued at price, not $0982 let value = usage_value(r.internal == 1, cost, paid, self.margin_percent);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running983 let given = if r.internal == 1 {
984 Given { comped: value, ..Given::default() }
985 } else if paid == 0 && cost > 0 {
986 Given { free: value, ..Given::default() }
987 } else {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging988 Given { free: r.covered.unwrap_or(0), trial: r.trial.unwrap_or(0), pool: r.oss.unwrap_or(0), discount, ..Given::default() }
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running989 };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it990 if r.internal == 1 {
991 internal.insert(r.workspace.clone());
992 }
993 UsageRow { day: r.day, workspace: r.workspace, key: r.key, value, cash, cost, given }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily994 })
995 .collect();
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging996 // Credits from g1t: what promotional and goodwill credit paid for
997 // is given, not money in; a refund gives money back on its day.
998 let (draws, refunds) = self.credit_effects(since, until).await?;
999 apply_credits(&mut out, &draws, &refunds);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1000 // Month-end sources, from their daily snapshots.
1001 #[derive(Deserialize)]
1002 struct Snap {
1003 day: String,
1004 workspace: String,
1005 source: String,
1006 cost_micros: i64,
1007 charge_micros: i64,
1008 }
1009 let snaps = self
1010 .db
1011 .prepare("SELECT day, workspace, source, cost_micros, charge_micros FROM pending_days WHERE day >= ?1 AND day <= ?2")
1012 .bind(&[day_before(since, 1).into(), until.into()])?
1013 .all()
1014 .await?
1015 .results::<Snap>()?
1016 .into_iter()
1017 .filter(|s| crate::storage::CHARGED_HERE.contains(&s.source.as_str()) || s.source == "domains")
1018 .map(|s| (s.day, s.workspace, s.source, s.cost_micros, s.charge_micros))
1019 .collect::<Vec<_>>();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1020 out.extend(pending_deltas(&snaps).into_iter().filter(|u| u.day.as_str() >= since).map(|mut u| {
1021 if internal.contains(&u.workspace) {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1022 u.given = Given { comped: u.value, ..Given::default() };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1023 }
1024 u
1025 }));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1026 // The plan's price, spread over the 30 days it pays for, so a month's
1027 // payment does not read as one very good day and 29 bad ones.
1028 #[derive(Deserialize)]
1029 struct Plan {
1030 day: String,
1031 workspace: String,
1032 micros: Option<i64>,
1033 }
1034 let plans = self
1035 .db
1036 .prepare(
1037 "SELECT substr(paid_at, 1, 10) AS day, workspace, SUM(amount_micros) AS micros FROM plan_payments
1038 WHERE paid_at >= ?1 AND paid_at <= ?2 GROUP BY 1, 2",
1039 )
1040 .bind(&[day_before(since, PLAN_DAYS - 1).into(), end.as_str().into()])?
1041 .all()
1042 .await?
1043 .results::<Plan>()?;
1044 for p in plans {
1045 for (day, micros) in spread(&p.day, p.micros.unwrap_or(0), PLAN_DAYS) {
1046 if day.as_str() >= since && day.as_str() <= until {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1047 out.push(UsageRow { day, workspace: p.workspace.clone(), key: "plan".into(), value: micros, cash: micros, cost: 0, given: Given::default() });
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1048 }
1049 }
1050 }
1051 Ok(out)
1052 }
1053
1054 /// Reconciles the days and writes `margin_days` and `workspace_costs`.
1055 async fn reconcile_range(&self, since: &str, until: &str) -> Result<u32> {
1056 let rules = self.rules().await?;
1057 #[derive(Deserialize)]
1058 struct Map {
1059 key: String,
1060 bucket: String,
1061 }
1062 let revenue_map: BTreeMap<String, String> = self
1063 .db
1064 .prepare("SELECT key, bucket FROM revenue_map")
1065 .all()
1066 .await?
1067 .results::<Map>()?
1068 .into_iter()
1069 .map(|m| (m.key, m.bucket))
1070 .collect();
1071 let lines = self
1072 .db
1073 .prepare("SELECT day, source, product, meter, quantity, cost_usd FROM cost_lines WHERE day >= ?1 AND day <= ?2")
1074 .bind(&[since.into(), until.into()])?
1075 .all()
1076 .await?
1077 .results::<LineRow>()?;
1078 let own = self
1079 .db
1080 .prepare("SELECT day, meter, workspace, quantity FROM own_counts WHERE day >= ?1 AND day <= ?2")
1081 .bind(&[since.into(), until.into()])?
1082 .all()
1083 .await?
1084 .results::<OwnRow>()?;
1085 let usage = self.usage_rows(since, until).await?;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1086 #[derive(Deserialize)]
1087 struct Internal {
1088 workspace: String,
1089 }
1090 let internal: BTreeSet<String> = self
1091 .db
1092 .prepare(format!("WITH i(workspace) AS ({}) SELECT DISTINCT workspace FROM i", crate::sales::INTERNAL_SQL))
1093 .all()
1094 .await?
1095 .results::<Internal>()?
1096 .into_iter()
1097 .map(|i| i.workspace)
1098 .collect();
1099 let (days, workspaces) = fold(&rules, &revenue_map, &lines, &own, &usage, &internal);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1100 let now = rfc3339(now_ms());
1101 self.db
1102 .batch(vec![
1103 self.db.prepare("DELETE FROM margin_days WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1104 self.db.prepare("DELETE FROM workspace_costs WHERE day >= ?1 AND day <= ?2").bind(&[since.into(), until.into()])?,
1105 ])
1106 .await?;
1107 for chunk in days.chunks(50) {
1108 let mut statements = Vec::with_capacity(chunk.len());
1109 for d in chunk {
1110 statements.push(
1111 self.db
1112 .prepare(
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1113 "INSERT OR REPLACE INTO margin_days (day, bucket, cf_cost_micros, own_cost_micros, value_micros, cash_micros, cf_quantity, own_quantity, given_micros, given_comped_micros, given_free_micros, given_trial_micros, given_pool_micros, given_discount_micros, given_credit_promotional_micros, given_credit_goodwill_micros, computed_at)
1114 VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1115 )
1116 .bind(&[
1117 d.day.as_str().into(),
1118 d.bucket.as_str().into(),
1119 (d.cf_cost_micros as f64).into(),
1120 (d.own_cost_micros as f64).into(),
1121 (d.value_micros as f64).into(),
1122 (d.cash_micros as f64).into(),
1123 d.cf_quantity.into(),
1124 d.own_quantity.into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1125 (d.given.total() as f64).into(),
1126 (d.given.comped as f64).into(),
1127 (d.given.free as f64).into(),
1128 (d.given.trial as f64).into(),
1129 (d.given.pool as f64).into(),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1130 (d.given.discount as f64).into(),
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1131 (d.given.credit_promotional as f64).into(),
1132 (d.given.credit_goodwill as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1133 now.as_str().into(),
1134 ])?,
1135 );
1136 }
1137 self.db.batch(statements).await?;
1138 }
1139 for chunk in workspaces.chunks(50) {
1140 let mut statements = Vec::with_capacity(chunk.len());
1141 for w in chunk {
1142 statements.push(
1143 self.db
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1144 .prepare("INSERT OR REPLACE INTO workspace_costs (day, workspace, bucket, cost_micros, revenue_micros, value_micros, given_micros) VALUES (?, ?, ?, ?, ?, ?, ?)")
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1145 .bind(&[
1146 w.day.as_str().into(),
1147 w.workspace.as_str().into(),
1148 w.bucket.as_str().into(),
1149 (w.cost as f64).into(),
1150 (w.revenue as f64).into(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1151 (w.value as f64).into(),
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1152 (w.given.total() as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1153 ])?,
1154 );
1155 }
1156 self.db.batch(statements).await?;
1157 }
1158 Ok(costs::days_between(since, until).len() as u32)
1159 }
1160
1161 async fn margin_days(&self, since: &str, until: &str) -> Result<Vec<ProductDay>> {
1162 Ok(self
1163 .db
1164 .prepare("SELECT * FROM margin_days WHERE day >= ?1 AND day <= ?2 ORDER BY day, bucket")
1165 .bind(&[since.into(), until.into()])?
1166 .all()
1167 .await?
1168 .results::<MarginRow>()?
1169 .into_iter()
1170 .map(ProductDay::from)
1171 .collect())
1172 }
1173
Merge branch 'worktree-agent-a633ac0f7f66d419d'1174 /// What AI Gateway's lines over the days, and the runs settled in them,
1175 /// say about whether its cost is what the providers bill.
1176 async fn gateway_caveats(&self, since: &str, until: &str) -> Result<costs::GatewayCaveats> {
1177 #[derive(Deserialize)]
1178 struct Line {
1179 meter: String,
1180 quantity: f64,
1181 cost_usd: f64,
1182 }
1183 let lines: Vec<(String, f64, f64)> = self
1184 .db
1185 .prepare("SELECT meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND day >= ?2 AND day <= ?3")
1186 .bind(&[costs::SOURCE_GATEWAY.into(), since.into(), until.into()])?
1187 .all()
1188 .await?
1189 .results::<Line>()?
1190 .into_iter()
1191 .map(|l| (l.meter, l.quantity, l.cost_usd))
1192 .collect();
1193 let mut caveats = costs::gateway_caveats(&lines);
1194 #[derive(Deserialize)]
1195 struct Short {
1196 n: Option<f64>,
1197 }
1198 caveats.short_runs = self
1199 .db
1200 .prepare("SELECT COUNT(*) AS n FROM runs WHERE gateway_note IS NOT NULL AND settled_at >= ?1 AND settled_at <= ?2")
1201 .bind(&[since.into(), format!("{until}T23:59:59.999Z").into()])?
1202 .first::<Short>(None)
1203 .await?
1204 .and_then(|s| s.n)
1205 .unwrap_or(0.0) as u32;
1206 Ok(caveats)
1207 }
1208
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1209 /// Drift over the last week, written to `cost_drift` (replacing the
1210 /// last run's), with unmapped Cloudflare meters as leaks.
1211 async fn find_drift(&self, until: &str) -> Result<Vec<(Drift, String)>> {
1212 let since = day_before(until, DRIFT_DAYS - 1);
1213 let settings = self.cost_settings().await?;
1214 let rules = self.rules().await?;
1215 let days = self.margin_days(&since, until).await?;
1216 let mut by: BTreeMap<String, Vec<ProductDay>> = BTreeMap::new();
1217 for d in days {
1218 by.entry(d.bucket.clone()).or_default().push(d);
1219 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1220 let caveats = self.gateway_caveats(&since, until).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1221 let mut found = Vec::new();
Merge branch 'worktree-agent-a633ac0f7f66d419d'1222 if let Some(drift) = unpriced_drift(&caveats) {
1223 found.push(drift);
1224 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1225 for (bucket, days) in &by {
1226 let bucket_rules: Vec<&Rule> = rules.iter().filter(|r| &r.bucket == bucket).collect();
1227 let threshold = bucket_rules.iter().map(|r| r.drift_percent).fold(f64::INFINITY, f64::min);
1228 let threshold = if threshold.is_finite() { threshold } else { 10.0 };
1229 let counted = bucket_rules.iter().any(|r| r.own_meter.is_some());
1230 for drift in drifts(bucket, days, threshold, counted, settings.min_daily_cost_micros) {
1231 let title = costs::bucket_title(bucket);
1232 let detail = match drift.kind {
Merge branch 'worktree-agent-a633ac0f7f66d419d'1233 DriftKind::Cost if NOT_CLOUDFLARE.contains(&bucket.as_str()) => models_detail(&drift, &caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1234 DriftKind::Count => format!(
One operation mapping, owned by repos; billing reads it instead of keeping its own1235 "{title}: g1t counted {}, Cloudflare {} over the last {DRIFT_DAYS} days ({:+.1}%). Customers are charged for what g1t counts; check what Cloudflare counts as a unit and change the repos service's operation_mapping (set_operation_mapping).",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1236 crate::features::thousands(drift.ours.max(0.0).round() as u64),
1237 crate::features::thousands(drift.cloudflare.max(0.0).round() as u64),
1238 drift.delta_percent.unwrap_or(0.0)
1239 ),
1240 DriftKind::Cost => format!(
1241 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days; the price book's cost of the same usage is {} ({:+.1}%). A price may be stale: see the proposals.",
1242 dollars(drift.cloudflare as i64),
1243 dollars(drift.ours as i64),
1244 drift.delta_percent.unwrap_or(0.0)
1245 ),
1246 DriftKind::Leak if bucket == UNMAPPED => {
1247 format!("Cloudflare charged {} for meters no mapping claims. Map them on Costs & margin.", dollars(drift.cloudflare as i64))
1248 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1249 DriftKind::Leak if NOT_CLOUDFLARE.contains(&bucket.as_str()) => format!(
1250 "{title}: AI Gateway priced g1t's own provider traffic at {} over the last {DRIFT_DAYS} days and the ledger has no model charge for it, not even a comped or free one: model calls with no billing run behind them (a run started without a ticket, or something else using g1t's gateway).",
1251 dollars(drift.cloudflare as i64)
1252 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1253 DriftKind::Leak => format!(
1254 "{title}: Cloudflare charged {} over the last {DRIFT_DAYS} days and customers were charged nothing for it.",
1255 dollars(drift.cloudflare as i64)
1256 ),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1257 // Raised from the gateway's lines, not per bucket.
1258 DriftKind::Unpriced => unpriced_detail(&caveats),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1259 };
1260 found.push((drift, detail));
1261 }
1262 }
1263 let now = rfc3339(now_ms());
1264 let mut statements = vec![self.db.prepare("DELETE FROM cost_drift")];
1265 for (drift, detail) in &found {
1266 statements.push(
1267 self.db
1268 .prepare("INSERT OR REPLACE INTO cost_drift (bucket, kind, ours, cloudflare, delta_percent, detail, found_at) VALUES (?, ?, ?, ?, ?, ?, ?)")
1269 .bind(&[
1270 drift.bucket.as_str().into(),
1271 drift.kind.as_str().into(),
1272 drift.ours.into(),
1273 drift.cloudflare.into(),
1274 drift.delta_percent.map_or(JsValue::NULL, JsValue::from),
1275 detail.as_str().into(),
1276 now.as_str().into(),
1277 ])?,
1278 );
1279 }
1280 self.db.batch(statements).await?;
1281 Ok(found)
1282 }
1283
1284 /// Unit costs from the bill for mappings that scale to g1t's own count
1285 /// (git operations), proposed to the price book.
1286 async fn measure_units(&self, until: &str) -> Result<u32> {
1287 #[derive(Deserialize)]
1288 struct Scaled {
1289 product: String,
1290 meter: String,
1291 price_meter: String,
1292 own_meter: String,
1293 unit: Option<String>,
1294 }
1295 let scaled = self
1296 .db
1297 .prepare(
1298 "SELECT m.product, m.meter, m.price_meter, m.own_meter, p.unit FROM cost_map m LEFT JOIN prices p ON p.meter = m.price_meter
1299 WHERE m.scale_to_own = 1 AND m.price_meter IS NOT NULL AND m.own_meter IS NOT NULL",
1300 )
1301 .all()
1302 .await?
1303 .results::<Scaled>()?;
1304 let since = day_before(until, MEASURE_DAYS - 1);
1305 let rules = self.rules().await?;
1306 let mut proposed = 0;
1307 for s in scaled {
1308 #[derive(Deserialize)]
1309 struct Day {
1310 product: String,
1311 meter: String,
1312 quantity: f64,
1313 cost_usd: f64,
1314 }
1315 let lines = self
1316 .db
1317 .prepare("SELECT product, meter, quantity, cost_usd FROM cost_lines WHERE source = ?1 AND product = ?2 AND day >= ?3 AND day <= ?4")
1318 .bind(&[SOURCE_BILLABLE.into(), s.product.as_str().into(), since.as_str().into(), until.into()])?
1319 .all()
1320 .await?
1321 .results::<Day>()?;
1322 // Only the lines this very mapping claims.
1323 let mine: Vec<(f64, f64)> = lines
1324 .iter()
1325 .filter(|l| costs::classify(&rules, &l.product, &l.meter).is_some_and(|r| r.product == s.product && r.meter == s.meter))
1326 .map(|l| (l.quantity, l.cost_usd))
1327 .collect();
1328 let Some(rate) = billed_rate(&mine) else { continue };
1329 let cf_units: f64 = mine.iter().map(|(q, _)| q).sum();
1330 #[derive(Deserialize)]
1331 struct Own {
1332 total: Option<f64>,
1333 }
1334 let own_units = self
1335 .db
1336 .prepare("SELECT SUM(quantity) AS total FROM own_counts WHERE meter = ?1 AND day >= ?2 AND day <= ?3")
1337 .bind(&[s.own_meter.as_str().into(), since.as_str().into(), until.into()])?
1338 .first::<Own>(None)
1339 .await?
1340 .and_then(|o| o.total)
1341 .unwrap_or(0.0);
1342 let Some(per_unit) = derived_unit_cost(rate, cf_units, own_units) else { continue };
1343 let size = unit_size(s.unit.as_deref().unwrap_or("1"));
1344 let measured = per_unit * size * 1_000_000.0;
1345 let reason = format!(
1346 "Cloudflare billed ${:.4} per 1,000 of its units and counted {:.2} of them for each one g1t counted over the last {MEASURE_DAYS} days ({} against {})",
1347 rate * 1000.0,
1348 cf_units / own_units,
1349 crate::features::thousands(cf_units.round() as u64),
1350 crate::features::thousands(own_units.round() as u64)
1351 );
1352 if self.propose(&s.price_meter, measured, &reason, "reconciler").await?.is_some() {
1353 proposed += 1;
1354 }
1355 }
1356 Ok(proposed)
1357 }
1358
1359 /// Opens, updates and closes margin alerts, and emails staff about new
1360 /// ones (and open ones each week).
1361 async fn raise_alerts(&self, env: &Env, until: &str, drift: &[(Drift, String)]) -> Result<u32> {
1362 let settings = self.cost_settings().await?;
1363 let since = day_before(until, u64::from(settings.alert_days.max(1)) - 1);
1364 let days = self.margin_days(&since, until).await?;
1365 let mut conditions: Vec<(String, String, String, String)> = Vec::new();
1366 // Each product under the floor.
1367 let mut by: BTreeMap<String, Vec<(String, i64, i64)>> = BTreeMap::new();
1368 let mut all: BTreeMap<String, (i64, i64)> = BTreeMap::new();
1369 for d in &days {
1370 let overall = all.entry(d.day.clone()).or_default();
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1371 // What g1t gave away (comped workspaces, free periods, the
1372 // trial and the pools) is a budget it chose to spend, watched on
1373 // its own (budget.rs): not part of whether what is sold pays.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1374 overall.0 += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1375 overall.1 += (d.cost() - d.given.total()).max(0);
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1376 if !OVERHEAD.contains(&d.bucket.as_str()) && d.bucket != UNMAPPED {
1377 by.entry(d.bucket.clone()).or_default().push((d.day.clone(), d.value_micros, d.cost()));
1378 }
1379 }
1380 let floor = settings.margin_floor_percent;
1381 let n = settings.alert_days as usize;
1382 for (bucket, series) in &by {
1383 if let Some((from, worst)) = breach(series, floor, n, settings.min_daily_cost_micros) {
1384 conditions.push((
1385 "margin".into(),
1386 bucket.clone(),
1387 format!("{}: margin under {floor:.0}% for {n} days running, as low as {worst:.1}%.", costs::bucket_title(bucket)),
1388 from,
1389 ));
Margin alerts measure what is sold, and say dollars when a percentage would mislead1390 }
1391 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1392 let series: Vec<(String, i64, i64)> = all.into_iter().map(|(day, (revenue, cost))| (day, revenue, cost)).collect();
1393 if let Some((from, worst)) = breach(&series, floor, n, settings.min_daily_cost_micros) {
Margin alerts measure what is sold, and say dollars when a percentage would mislead1394 let tail = &series[series.len().saturating_sub(n)..];
1395 let (took, spent) = tail.iter().fold((0i64, 0i64), |(r, c), (_, revenue, cost)| (r + revenue, c + cost));
1396 conditions.push(("overall".into(), "g1t".into(), overall_detail(took, spent, n, floor, worst), from));
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1397 }
1398 for (d, detail) in drift {
1399 let kind = if d.kind == DriftKind::Leak { "leak" } else { "drift" };
1400 conditions.push((kind.into(), format!("{}:{}", d.bucket, d.kind.as_str()), detail.clone(), until.to_owned()));
1401 }
1402 // Workspaces costing more than they pay.
1403 for (workspace, cost, revenue) in self.workspace_anomalies(until, &settings).await? {
1404 conditions.push((
1405 "workspace".into(),
1406 workspace.clone(),
Margin alerts measure what is sold, and say dollars when a percentage would mislead1407 format!(
1408 "{workspace} cost g1t {} on Cloudflare over {ANOMALY_DAYS} days, and its usage was priced at {}: its prices are below cost.",
1409 dollars(cost),
1410 dollars(revenue)
1411 ),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1412 day_before(until, ANOMALY_DAYS - 1),
1413 ));
1414 }
1415
1416 let open = self
1417 .db
1418 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL")
1419 .all()
1420 .await?
1421 .results::<AlertRow>()?;
1422 let now = now_ms();
1423 let stamp = rfc3339(now);
1424 let mut to_email: Vec<String> = Vec::new();
1425 let mut kept: BTreeSet<String> = BTreeSet::new();
1426 for (kind, subject, detail, from) in &conditions {
1427 match open.iter().find(|a| &a.kind == kind && &a.subject == subject) {
1428 Some(alert) => {
1429 kept.insert(alert.id.clone());
1430 self.db
1431 .prepare("UPDATE margin_alerts SET detail = ? WHERE id = ?")
1432 .bind(&[detail.as_str().into(), alert.id.as_str().into()])?
1433 .run()
1434 .await?;
1435 let stale = alert
1436 .emailed_at
1437 .as_deref()
1438 .and_then(g1t_contracts::time::parse_rfc3339)
1439 .is_none_or(|at| now.saturating_sub(at) >= REMIND_MS);
1440 if stale && kind != "workspace" {
1441 to_email.push(format!("Still open: {detail}"));
1442 kept.insert(format!("email:{}", alert.id));
1443 }
1444 }
1445 None => {
1446 let id = new_id("mal", now);
1447 self.db
1448 .prepare("INSERT INTO margin_alerts (id, kind, subject, detail, since, opened_at) VALUES (?, ?, ?, ?, ?, ?)")
1449 .bind(&[id.as_str().into(), kind.as_str().into(), subject.as_str().into(), detail.as_str().into(), from.as_str().into(), stamp.as_str().into()])?
1450 .run()
1451 .await?;
1452 kept.insert(id.clone());
Margin alerts measure what is sold, and say dollars when a percentage would mislead1453 // A workspace's is for Reach out, not the inbox.
1454 if kind != "workspace" {
1455 to_email.push(detail.clone());
1456 }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1457 kept.insert(format!("email:{id}"));
1458 }
1459 }
1460 }
1461 for alert in &open {
1462 if !kept.contains(&alert.id) {
1463 self.db
1464 .prepare("UPDATE margin_alerts SET resolved_at = ? WHERE id = ?")
1465 .bind(&[stamp.as_str().into(), alert.id.as_str().into()])?
1466 .run()
1467 .await?;
1468 }
1469 }
1470 let to = env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string()).unwrap_or_default();
1471 if !to_email.is_empty() && !to.trim().is_empty() {
1472 let subject = format!("[g1t costs] {} margin alert{}", to_email.len(), if to_email.len() == 1 { "" } else { "s" });
1473 match email_staff(env, to.trim(), &subject, &to_email).await {
1474 Ok(()) => {
1475 for marker in kept.iter().filter_map(|k| k.strip_prefix("email:")) {
1476 self.db
1477 .prepare("UPDATE margin_alerts SET emailed_at = ? WHERE id = ?")
1478 .bind(&[stamp.as_str().into(), marker.into()])?
1479 .run()
1480 .await?;
1481 }
1482 }
1483 Err(error) => worker::console_error!("could not email the margin alerts: {error}"),
1484 }
1485 }
1486 Ok(conditions.len() as u32)
1487 }
1488
1489 /// Workspaces costing g1t more than they pay over 30 days, not g1t's own.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1490 /// Each day's cost shared out to comped workspaces.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1491 async fn workspace_anomalies(&self, until: &str, settings: &CostSettings) -> Result<Vec<(String, i64, i64)>> {
1492 #[derive(Deserialize)]
1493 struct Row {
1494 workspace: String,
1495 cost: Option<i64>,
1496 revenue: Option<i64>,
1497 }
1498 let rows = self
1499 .db
1500 .prepare(format!(
Margin alerts measure what is sold, and say dollars when a percentage would mislead1501 // Against what its usage was priced at, not the cash it
1502 // paid: a trial or a gift paying for usage is not a price
1503 // below cost.
The workspace cost alert compares only days that carry their value, not the days before it was kept1504 // Days from before value_micros was kept have none: only days
1505 // since the first one that does are compared.
Margin alerts measure what is sold, and say dollars when a percentage would mislead1506 "SELECT workspace, SUM(cost_micros) AS cost, SUM(value_micros) AS revenue FROM workspace_costs
The workspace cost alert compares only days that carry their value, not the days before it was kept1507 WHERE day >= ?1 AND day <= ?2 AND workspace NOT IN ({})
1508 AND day >= (SELECT MIN(day) FROM workspace_costs WHERE value_micros > 0)
1509 GROUP BY workspace",
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1510 crate::sales::INTERNAL_SQL
1511 ))
1512 .bind(&[day_before(until, ANOMALY_DAYS - 1).into(), until.into()])?
1513 .all()
1514 .await?
1515 .results::<Row>()?;
1516 let rows: Vec<(String, i64, i64)> = rows.into_iter().map(|r| (r.workspace, r.cost.unwrap_or(0), r.revenue.unwrap_or(0))).collect();
1517 Ok(anomalies(&rows, settings.anomaly_factor, settings.anomaly_floor_micros))
1518 }
1519
1520 /// For Reach out: workspaces with an open cost-over-revenue alert,
1521 /// each with its detail and cost.
1522 pub(crate) async fn costing_more_than_they_pay(&self) -> Result<Vec<(String, String, i64)>> {
1523 let alerts = self
1524 .db
1525 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL AND kind = 'workspace' ORDER BY opened_at DESC LIMIT 50")
1526 .all()
1527 .await?
1528 .results::<AlertRow>()?;
1529 let mut out = Vec::new();
1530 for alert in alerts {
1531 #[derive(Deserialize)]
1532 struct Cost {
1533 cost: Option<i64>,
1534 }
1535 let cost = self
1536 .db
1537 .prepare("SELECT SUM(cost_micros) AS cost FROM workspace_costs WHERE workspace = ? AND day >= ?")
1538 .bind(&[alert.subject.as_str().into(), alert.since.as_str().into()])?
1539 .first::<Cost>(None)
1540 .await?
1541 .and_then(|c| c.cost)
1542 .unwrap_or(0);
1543 out.push((alert.subject, alert.detail, cost));
1544 }
1545 Ok(out)
1546 }
1547
1548 /// `admin_cost_alerts`: what sudo's banner says.
1549 pub(crate) async fn admin_cost_alerts(&self, _: AdminCostAlertsArgs) -> Result<Vec<MarginAlert>> {
1550 Ok(self
1551 .db
1552 .prepare("SELECT * FROM margin_alerts WHERE resolved_at IS NULL ORDER BY opened_at DESC LIMIT 50")
1553 .all()
1554 .await?
1555 .results::<AlertRow>()?
1556 .into_iter()
1557 .map(MarginAlert::from)
1558 .collect())
1559 }
1560
1561 /// `admin_run_costs`: the daily run, now.
1562 pub(crate) async fn admin_run_costs(&self, env: &Env, a: AdminRunCostsArgs) -> Result<Outcome<CostsRun>> {
1563 let keeper = crate::keeper::Keeper::from_env(env);
1564 let run = self.costs_daily(env, &keeper).await?;
1565 if !a.by.is_empty() {
1566 self.audit(
1567 "costs",
1568 "costs_run",
1569 &format!("{} lines, {} days, {} proposals, {} alerts", run.lines, run.days, run.proposals, run.alerts),
1570 &a.by,
1571 )
1572 .await?;
1573 }
1574 Ok(Outcome::Ok(run))
1575 }
1576
1577 /// `admin_set_cost_mapping`.
1578 pub(crate) async fn admin_set_cost_mapping(&self, a: AdminSetCostMappingArgs) -> Result<Outcome<CostMapping>> {
1579 let product = costs::slug(&a.product);
1580 let meter = if a.meter.trim() == "*" { "*".to_owned() } else { costs::slug(&a.meter) };
1581 if product.is_empty() || meter.is_empty() {
1582 return Ok(Outcome::fail(FailureCode::Invalid, "Name Cloudflare's product and a meter (or * for all of it)."));
1583 }
1584 let now = rfc3339(now_ms());
1585 if a.remove {
1586 self.db
1587 .prepare("DELETE FROM cost_map WHERE product = ? AND meter = ?")
1588 .bind(&[product.as_str().into(), meter.as_str().into()])?
1589 .run()
1590 .await?;
1591 self.audit("costs", "cost_mapping_removed", &format!("{product}/{meter}"), &a.by).await?;
1592 return Ok(Outcome::Ok(CostMapping {
1593 product,
1594 meter,
1595 bucket: String::new(),
1596 price_meter: None,
1597 own_meter: None,
1598 scale_to_own: false,
1599 drift_percent: 0.0,
1600 note: String::new(),
1601 updated_at: now,
1602 updated_by: a.by,
1603 }));
1604 }
1605 let bucket = costs::slug(&a.bucket);
1606 if bucket.is_empty() {
1607 return Ok(Outcome::fail(FailureCode::Invalid, "Say which of g1t's products it is a cost of."));
1608 }
1609 let clean = |v: Option<String>| v.map(|v| v.trim().to_owned()).filter(|v| !v.is_empty());
1610 let (price_meter, own_meter) = (clean(a.price_meter), clean(a.own_meter));
1611 let drift = a.drift_percent.filter(|d| d.is_finite() && *d > 0.0).unwrap_or(10.0);
1612 self.db
1613 .prepare(
1614 "INSERT INTO cost_map (product, meter, bucket, price_meter, own_meter, scale_to_own, drift_percent, note, updated_at, updated_by)
1615 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
1616 ON CONFLICT (product, meter) DO UPDATE SET bucket = ?3, price_meter = ?4, own_meter = ?5, scale_to_own = ?6,
1617 drift_percent = ?7, note = ?8, updated_at = ?9, updated_by = ?10",
1618 )
1619 .bind(&[
1620 product.as_str().into(),
1621 meter.as_str().into(),
1622 bucket.as_str().into(),
1623 crate::optional(price_meter.as_deref()),
1624 crate::optional(own_meter.as_deref()),
1625 i32::from(a.scale_to_own).into(),
1626 drift.into(),
1627 a.note.trim().into(),
1628 now.as_str().into(),
1629 a.by.as_str().into(),
1630 ])?
1631 .run()
1632 .await?;
1633 self.audit("costs", "cost_mapping", &format!("{product}/{meter} → {bucket}"), &a.by).await?;
1634 Ok(Outcome::Ok(CostMapping {
1635 product,
1636 meter,
1637 bucket,
1638 price_meter,
1639 own_meter,
1640 scale_to_own: a.scale_to_own,
1641 drift_percent: drift,
1642 note: a.note.trim().to_owned(),
1643 updated_at: now,
1644 updated_by: a.by,
1645 }))
1646 }
1647
1648 /// `admin_costs`: the Costs & margin page.
1649 pub(crate) async fn admin_costs(&self, a: AdminCostsArgs, configured: bool) -> Result<CostsReport> {
1650 let until = rfc3339(now_ms())[..10].to_owned();
1651 let span = u64::from(a.days.unwrap_or(30).clamp(7, 90));
1652 let since = day_before(&until, span - 1);
1653 let days = self.margin_days(&since, &until).await?;
1654 let rules = self.rules().await?;
1655
1656 let mut products: BTreeMap<String, ProductMargin> = BTreeMap::new();
1657 let mut overall = OverallMargin::default();
1658 for d in &days {
1659 let p = products.entry(d.bucket.clone()).or_insert_with(|| ProductMargin {
1660 bucket: d.bucket.clone(),
1661 title: costs::bucket_title(&d.bucket),
1662 cost_source: if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) { "ledger" } else { "cloudflare" }.into(),
1663 overhead: OVERHEAD.contains(&d.bucket.as_str()),
1664 ..ProductMargin::default()
1665 });
1666 p.cf_cost_micros += d.cf_cost_micros;
1667 p.own_cost_micros += d.own_cost_micros;
1668 p.value_micros += d.value_micros;
1669 p.cost_micros += d.cost();
1670 overall.cost_micros += d.cost();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1671 overall.given_micros += d.given.total();
1672 if NOT_CLOUDFLARE.contains(&d.bucket.as_str()) {
1673 overall.models_cost_micros += d.cost();
1674 } else {
1675 overall.cloudflare_cost_micros += d.cost();
1676 }
1677 overall.given_comped_micros += d.given.comped;
1678 overall.given_free_micros += d.given.free;
1679 overall.given_trial_micros += d.given.trial;
1680 overall.given_pool_micros += d.given.pool;
Merge branch 'worktree-agent-a633ac0f7f66d419d'1681 overall.given_discount_micros += d.given.discount;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1682 overall.given_credit_promotional_micros += d.given.credit_promotional;
1683 overall.given_credit_goodwill_micros += d.given.credit_goodwill;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1684 let sold = (d.cost() - d.given.total()).max(0);
1685 if OVERHEAD.contains(&d.bucket.as_str()) {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1686 overall.plans_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1687 overall.running_cost_micros += sold;
1688 } else if d.bucket == UNMAPPED {
1689 overall.usage_micros += d.cash_micros;
1690 overall.unmapped_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1691 } else {
1692 overall.usage_micros += d.cash_micros;
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1693 overall.usage_cost_micros += sold;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1694 }
1695 }
1696 for p in products.values_mut() {
1697 p.margin_micros = p.value_micros - p.cost_micros;
1698 p.margin_percent = margin_percent(p.value_micros, p.cost_micros);
1699 }
1700 let revenue = overall.usage_micros + overall.plans_micros;
1701 overall.margin_micros = revenue - overall.cost_micros;
1702 overall.margin_percent = margin_percent(revenue, overall.cost_micros);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1703 let sold = (overall.cost_micros - overall.given_micros).max(0);
1704 overall.sold_margin_micros = revenue - sold;
1705 overall.sold_margin_percent = margin_percent(revenue, sold);
Costs: the plan's included usage counts as paid for the usage it covered, out of what plans leave for running g1t; the run button shows it is running with CSS alone (sudo ships no JavaScript)1706 // The plan's included usage was paid for by the plan's price: it is
1707 // money in for the usage it covered, and out of what the plans
1708 // leave for running g1t.
1709 #[derive(Deserialize)]
1710 struct Included {
1711 micros: Option<i64>,
1712 }
1713 overall.included_micros = self
1714 .db
1715 .prepare(format!(
1716 "SELECT SUM(COALESCE(credit_micros, 0)) AS micros FROM ledger
1717 WHERE kind = 'usage' AND created_at >= ?1 AND created_at <= ?2 AND workspace NOT IN ({})",
1718 crate::sales::INTERNAL_SQL
1719 ))
1720 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1721 .first::<Included>(None)
1722 .await?
1723 .and_then(|r| r.micros)
1724 .unwrap_or(0);
1725 let usage_in = overall.usage_micros + overall.included_micros;
1726 overall.usage_margin_micros = usage_in - overall.usage_cost_micros;
1727 overall.usage_margin_percent = margin_percent(usage_in, overall.usage_cost_micros);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1728 // Credits from g1t over the range: given, spent, and refunds' money
1729 // given back.
1730 overall.credits_given_micros = self
1731 .db
1732 .prepare("SELECT SUM(amount_micros) AS micros FROM credit_grants WHERE created_at >= ?1 AND created_at <= ?2")
1733 .bind(&[since.as_str().into(), format!("{until}T23:59:59.999Z").into()])?
1734 .first::<Included>(None)
1735 .await?
1736 .and_then(|r| r.micros)
1737 .unwrap_or(0);
1738 let (draws, refunds) = self.credit_effects(&since, &until).await?;
1739 overall.credits_used_micros = draws.iter().map(|(_, d)| d.micros).sum();
1740 overall.credits_refunded_micros = refunds.iter().map(|r| r.micros).sum();
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1741 let mut products: Vec<ProductMargin> = products.into_values().collect();
1742 products.sort_by_key(|p| std::cmp::Reverse(p.cost_micros.max(p.value_micros)));
1743
1744 #[derive(Deserialize)]
1745 struct DriftRow {
1746 bucket: String,
1747 kind: String,
1748 ours: f64,
1749 cloudflare: f64,
1750 delta_percent: Option<f64>,
1751 detail: String,
1752 found_at: String,
1753 }
1754 let drift = self
1755 .db
1756 .prepare("SELECT * FROM cost_drift ORDER BY kind, bucket")
1757 .all()
1758 .await?
1759 .results::<DriftRow>()?
1760 .into_iter()
1761 .map(|r| CostDrift {
1762 title: costs::bucket_title(&r.bucket),
1763 bucket: r.bucket,
1764 kind: r.kind,
1765 ours: r.ours,
1766 cloudflare: r.cloudflare,
1767 delta_percent: r.delta_percent,
1768 detail: r.detail,
1769 found_at: r.found_at,
1770 })
1771 .collect();
1772
1773 #[derive(Deserialize)]
1774 struct Top {
1775 workspace: String,
1776 cost: Option<i64>,
1777 revenue: Option<i64>,
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1778 given: Option<i64>,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1779 internal: i64,
1780 }
1781 let top_workspaces = self
1782 .db
1783 .prepare(format!(
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1784 "SELECT workspace, SUM(cost_micros) AS cost, SUM(revenue_micros) AS revenue, SUM(given_micros) AS given,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1785 CASE WHEN workspace IN ({}) THEN 1 ELSE 0 END AS internal
1786 FROM workspace_costs WHERE day >= ?1 AND day <= ?2 GROUP BY workspace ORDER BY cost DESC LIMIT 15",
1787 crate::sales::INTERNAL_SQL
1788 ))
1789 .bind(&[since.as_str().into(), until.as_str().into()])?
1790 .all()
1791 .await?
1792 .results::<Top>()?
1793 .into_iter()
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it1794 .map(|t| WorkspaceCost { workspace: t.workspace, cost_micros: t.cost.unwrap_or(0), revenue_micros: t.revenue.unwrap_or(0), given_micros: t.given.unwrap_or(0), internal: t.internal == 1 })
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1795 .collect();
1796
1797 #[derive(Deserialize)]
1798 struct Summary {
1799 source: String,
1800 product: String,
1801 meter: String,
1802 raw_name: String,
1803 unit: String,
1804 quantity: f64,
1805 cost_usd: f64,
1806 }
1807 let lines = self
1808 .db
1809 .prepare(
1810 "SELECT source, product, meter, MAX(raw_name) AS raw_name, MAX(unit) AS unit, SUM(quantity) AS quantity, SUM(cost_usd) AS cost_usd
1811 FROM cost_lines WHERE day >= ?1 AND day <= ?2 GROUP BY source, product, meter ORDER BY cost_usd DESC, product, meter LIMIT 200",
1812 )
1813 .bind(&[since.as_str().into(), until.as_str().into()])?
1814 .all()
1815 .await?
1816 .results::<Summary>()?
1817 .into_iter()
1818 .map(|l| CostLineSummary {
1819 bucket: costs::classify(&rules, &l.product, &l.meter).map(|r| r.bucket.clone()),
1820 product: l.product,
1821 meter: l.meter,
1822 raw_name: l.raw_name,
1823 unit: l.unit,
1824 source: l.source,
1825 quantity: l.quantity,
1826 cost_micros: micros(l.cost_usd),
1827 })
1828 .collect();
1829
1830 #[derive(Deserialize)]
1831 struct MapRow {
1832 product: String,
1833 meter: String,
1834 bucket: String,
1835 price_meter: Option<String>,
1836 own_meter: Option<String>,
1837 scale_to_own: i64,
1838 drift_percent: f64,
1839 note: String,
1840 updated_at: String,
1841 updated_by: String,
1842 }
1843 let mappings = self
1844 .db
1845 .prepare("SELECT * FROM cost_map ORDER BY product, meter")
1846 .all()
1847 .await?
1848 .results::<MapRow>()?
1849 .into_iter()
1850 .map(|m| CostMapping {
1851 product: m.product,
1852 meter: m.meter,
1853 bucket: m.bucket,
1854 price_meter: m.price_meter,
1855 own_meter: m.own_meter,
1856 scale_to_own: m.scale_to_own == 1,
1857 drift_percent: m.drift_percent,
1858 note: m.note,
1859 updated_at: m.updated_at,
1860 updated_by: m.updated_by,
1861 })
1862 .collect();
1863
1864 #[derive(Deserialize)]
1865 struct Fetched {
1866 at: Option<String>,
1867 }
1868 let fetched_at = self.db.prepare("SELECT MAX(fetched_at) AS at FROM cost_lines").first::<Fetched>(None).await?.and_then(|f| f.at);
1869
1870 Ok(CostsReport {
1871 configured,
1872 fetched_at,
1873 days: days
1874 .iter()
1875 .map(|d| CostDay {
1876 day: d.day.clone(),
1877 bucket: d.bucket.clone(),
1878 cf_cost_micros: d.cf_cost_micros,
1879 own_cost_micros: d.own_cost_micros,
1880 value_micros: d.value_micros,
1881 cash_micros: d.cash_micros,
1882 })
1883 .collect(),
1884 since,
1885 until,
1886 products,
1887 overall,
1888 drift,
1889 alerts: self.admin_cost_alerts(AdminCostAlertsArgs {}).await?,
1890 proposals: self.proposals().await?,
1891 versions: self.versions().await?,
1892 top_workspaces,
1893 lines,
1894 mappings,
1895 settings: self.cost_settings().await?,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1896 caps: self.spend_caps().await?,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1897 })
1898 }
1899}
1900
1901#[cfg(test)]
1902mod tests {
1903 use super::*;
1904
Margin alerts measure what is sold, and say dollars when a percentage would mislead1905 #[test]
Models' margin read -14%: usage nothing paid for is valued at price, not $01906 fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
1907 // A free period: charged nothing, drawn from nothing.
1908 assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
1909 // Charged, or drawn from a trial: what was paid.
1910 assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
1911 assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
1912 // g1t's own: at price.
1913 assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
1914 // No cost, nothing paid: nothing.
1915 assert_eq!(usage_value(false, 0, 0, 20), 0);
1916 }
1917
1918 #[test]
Margin alerts measure what is sold, and say dollars when a percentage would mislead1919 fn the_overall_alert_says_dollars_while_little_comes_in() {
1920 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
1921 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
1922 assert!(!small.contains('%'), "{small}");
1923 let real = overall_detail(30_000_000, 40_000_000, 3, 10.0, -33.3);
1924 assert!(real.contains("as low as -33.3%"), "{real}");
1925 }
1926
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1927 fn rule(product: &str, meter: &str, bucket: &str, own: Option<&str>) -> Rule {
1928 Rule { product: product.into(), meter: meter.into(), bucket: bucket.into(), price_meter: None, own_meter: own.map(Into::into), drift_percent: 10.0 }
1929 }
1930
1931 fn rules() -> Vec<Rule> {
1932 vec![
1933 rule("containers", "*", "sandboxes", None),
1934 rule("workers", "*", "platform", None),
1935 rule("artifacts", "*", "git", Some("git_operations")),
1936 rule("artifacts", "events_", "git", Some("git_operations")),
1937 ]
1938 }
1939
1940 fn revenue_map() -> BTreeMap<String, String> {
1941 [("sandbox", "sandboxes"), ("git", "git"), ("plan", "platform")].iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
1942 }
1943
1944 fn line(day: &str, source: &str, product: &str, meter: &str, quantity: f64, cost: f64) -> LineRow {
1945 LineRow { day: day.into(), source: source.into(), product: product.into(), meter: meter.into(), quantity, cost_usd: cost }
1946 }
1947
1948 fn usage(day: &str, workspace: &str, key: &str, value: i64, cash: i64, cost: i64) -> UsageRow {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1949 UsageRow { day: day.into(), workspace: workspace.into(), key: key.into(), value, cash, cost, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1950 }
1951
1952 #[test]
1953 fn a_day_puts_the_bill_g1ts_counts_and_charges_side_by_side() {
1954 let lines = vec![
1955 line("2026-10-15", SOURCE_BILLABLE, "containers", "container_memory", 1000.0, 2.00),
1956 line("2026-10-15", SOURCE_BILLABLE, "artifacts", "artifacts_operations", 30_000.0, 3.00),
1957 // Artifacts' own events: not used while the bill has a count.
1958 line("2026-10-15", SOURCE_ARTIFACTS, "artifacts", "events_pull", 29_000.0, 0.0),
1959 line("2026-10-15", SOURCE_BILLABLE, "workers", "workers_cpu_ms", 1.0, 0.50),
1960 line("2026-10-15", SOURCE_BILLABLE, "browser_rendering", "browser_hours", 2.0, 0.25),
1961 ];
1962 let own = vec![
1963 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "acme".into(), quantity: 7_500.0 },
1964 OwnRow { day: "2026-10-15".into(), meter: "git_operations".into(), workspace: "beta".into(), quantity: 2_500.0 },
1965 ];
1966 let usage = vec![
1967 usage("2026-10-15", "acme", "sandbox", 2_400_000, 1_000_000, 2_000_000),
1968 usage("2026-10-15", "beta", "sandbox", 1_200_000, 1_200_000, 1_000_000),
1969 usage("2026-10-15", "acme", "git", 600_000, 600_000, 500_000),
1970 usage("2026-10-15", "acme", "implement", 120_000, 120_000, 100_000),
1971 usage("2026-10-15", "beta", "plan", 20_000_000, 20_000_000, 0),
1972 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running1973 let (days, workspaces) = fold(&rules(), &revenue_map(), &lines, &own, &usage, &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1974 let get = |bucket: &str| days.iter().find(|d| d.bucket == bucket).unwrap();
1975 let sandboxes = get("sandboxes");
1976 assert_eq!((sandboxes.cf_cost_micros, sandboxes.own_cost_micros, sandboxes.value_micros, sandboxes.cash_micros), (2_000_000, 3_000_000, 3_600_000, 2_200_000));
1977 let git = get("git");
1978 assert_eq!(git.cf_cost_micros, 3_000_000);
1979 assert_eq!((git.cf_quantity, git.own_quantity), (30_000.0, 10_000.0));
1980 assert_eq!(get("platform").value_micros, 20_000_000);
1981 // Not mapped: a leak until someone maps it.
1982 assert_eq!(get(UNMAPPED).cf_cost_micros, 250_000);
1983 // Models: no Cloudflare line, their cost is g1t's own.
1984 assert_eq!(get("models").cost(), 100_000);
1985 // Git's cost shared by g1t's own counts (Cloudflare gave none per
1986 // workspace here): three quarters to acme.
1987 let share = |ws: &str, bucket: &str| workspaces.iter().find(|w| w.workspace == ws && w.bucket == bucket).map(|w| (w.cost, w.revenue));
1988 assert_eq!(share("acme", "git"), Some((2_250_000, 600_000)));
1989 assert_eq!(share("beta", "git"), Some((750_000, 0)));
1990 // Every bucket's cost is shared out exactly.
1991 for d in &days {
1992 let shared: i64 = workspaces.iter().filter(|w| w.bucket == d.bucket).map(|w| w.cost).sum();
1993 assert_eq!(shared, d.cost(), "{}", d.bucket);
1994 }
1995 }
1996
1997 #[test]
1998 fn artifacts_events_count_when_the_bill_does_not() {
1999 let lines = vec![
2000 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_pull", 120.0, 0.0),
2001 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_push", 30.0, 0.0),
2002 line("2026-10-05", SOURCE_ARTIFACTS, "artifacts", "events_ratelimited", 9.0, 0.0),
2003 ];
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2004 let (days, _) = fold(&rules(), &revenue_map(), &lines, &[], &[], &BTreeSet::new());
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2005 assert_eq!(days[0].cf_quantity, 150.0);
2006 assert_eq!(days[0].cf_cost_micros, 0);
2007 }
2008
2009 #[test]
2010 fn month_end_meters_are_told_by_the_day_from_snapshots() {
2011 let snap = |day: &str, cost: i64, charge: i64| (day.to_string(), "acme".to_string(), "git".to_string(), cost, charge);
2012 let rows = pending_deltas(&[snap("2026-10-30", 100, 120), snap("2026-10-31", 250, 300), snap("2026-11-01", 40, 48), snap("2026-11-02", 40, 48)]);
2013 assert_eq!(
2014 rows.iter().map(|r| (r.day.as_str(), r.cost, r.value)).collect::<Vec<_>>(),
2015 vec![("2026-10-30", 100, 120), ("2026-10-31", 150, 180), ("2026-11-01", 40, 48)]
2016 );
2017 }
2018
2019 #[test]
2020 fn a_plan_payment_is_spread_over_the_month_it_pays_for() {
2021 let days = spread("2026-10-01T00:00:00.000Z", 20_000_000, 30);
2022 assert_eq!(days.len(), 30);
2023 assert_eq!(days[0], ("2026-10-01".to_string(), 666_667));
2024 assert_eq!(days[29], ("2026-10-30".to_string(), 666_666));
2025 assert_eq!(days.iter().map(|d| d.1).sum::<i64>(), 20_000_000);
2026 assert!(spread("2026-10-01", 0, 30).is_empty());
2027 assert_eq!(dollars(17_024_000), "$17.02");
2028 assert_eq!(dollars(-27_668_620), "-$27.67");
2029 assert_eq!(dollars(63_000), "$0.063");
2030 }
2031
2032 #[test]
2033 fn margins_and_deltas() {
2034 assert_eq!(margin_percent(1_200_000, 1_000_000).map(|m| (m * 100.0).round() / 100.0), Some(16.67));
2035 assert_eq!(margin_percent(0, 5), None);
2036 assert_eq!(delta_percent(110.0, 100.0), Some(10.0));
2037 assert_eq!(delta_percent(1.0, 0.0), None);
2038 }
2039
2040 fn day(bucket: &str, cf: i64, own: i64, value: i64, cfq: f64, ownq: f64) -> ProductDay {
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2041 ProductDay { day: "2026-10-15".into(), bucket: bucket.into(), cf_cost_micros: cf, own_cost_micros: own, value_micros: value, cash_micros: value, cf_quantity: cfq, own_quantity: ownq, given: Given::default() }
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2042 }
2043
2044 #[test]
2045 fn counts_more_than_the_threshold_apart_are_drift() {
2046 // Cloudflare counted 30,000 operations where g1t counted 10,000:
2047 // binding reads, perhaps. -66.7%.
2048 let drift = drifts("git", &[day("git", 3_000_000, 1_500_000, 1_800_000, 30_000.0, 10_000.0)], 10.0, true, 100_000);
2049 assert_eq!(drift.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Count, DriftKind::Cost]);
2050 assert!((drift[0].delta_percent.unwrap() + 66.666).abs() < 0.01);
2051 // 9% apart: within 10%.
2052 assert!(drifts("git", &[day("git", 1_000_000, 1_000_000, 1_200_000, 10_000.0, 10_900.0)], 10.0, true, 100_000).is_empty());
2053 // Uncounted products have no count drift.
2054 assert!(drifts("sandboxes", &[day("sandboxes", 1_000_000, 1_050_000, 1_200_000, 5.0, 0.0)], 10.0, false, 100_000).is_empty());
2055 }
2056
2057 #[test]
2058 fn cost_with_no_revenue_is_a_leak_but_not_for_running_g1t() {
2059 let leak = drifts("actions_cache", &[day("actions_cache", 400_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2060 assert_eq!(leak.len(), 1);
2061 assert_eq!(leak[0].kind, DriftKind::Leak);
2062 assert!(drifts("platform", &[day("platform", 5_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2063 // Pennies say nothing.
2064 assert!(drifts("actions_cache", &[day("actions_cache", 50_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2065 assert!(drifts(UNMAPPED, &[day(UNMAPPED, 250_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000)[0].kind == DriftKind::Leak);
2066 }
2067
2068 #[test]
2069 fn a_margin_alert_needs_n_days_in_a_row_under_the_floor() {
2070 let s = |d: &str, revenue: i64, cost: i64| (d.to_string(), revenue, cost);
2071 // 5%, 0%, -20%: three days under 10%.
2072 let series = vec![s("10-13", 1_200_000, 1_000_000), s("10-14", 1_050_000, 1_000_000), s("10-15", 1_000_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
2073 let (from, worst) = breach(&series, 10.0, 3, 100_000).unwrap();
2074 assert_eq!(from, "10-14");
2075 assert!((worst + 20.0).abs() < 1e-9);
2076 // A good day in the window clears it.
2077 let mended = vec![s("10-14", 1_050_000, 1_000_000), s("10-15", 1_300_000, 1_000_000), s("10-16", 1_000_000, 1_200_000)];
2078 assert!(breach(&mended, 10.0, 3, 100_000).is_none());
2079 // Cost with no revenue at all is the worst margin there is.
2080 assert_eq!(breach(&[s("10-16", 0, 500_000)], 10.0, 1, 100_000).unwrap().1, -100.0);
2081 // Too little cost to judge.
2082 assert!(breach(&[s("10-16", 0, 5_000)], 10.0, 1, 100_000).is_none());
2083 assert!(breach(&series, 10.0, 9, 100_000).is_none());
2084 }
2085
2086 #[test]
2087 fn shared_costs_add_up_to_the_bill() {
2088 let w = |k: &str, v: f64| (k.to_string(), v);
2089 assert_eq!(attribute(100, &[w("a", 1.0), w("b", 1.0), w("c", 1.0)]), vec![("a".into(), 34), ("b".into(), 33), ("c".into(), 33)]);
2090 assert_eq!(attribute(10, &[w("a", 3.0), w("b", 1.0), w("a", 0.0)]), vec![("a".into(), 8), ("b".into(), 2)]);
2091 assert!(attribute(10, &[w("a", 0.0)]).is_empty());
2092 assert!(attribute(0, &[w("a", 1.0)]).is_empty());
2093 }
2094
2095 #[test]
Costs: counts are compared with Cloudflare's from the day g1t started counting, so days before a meter existed are not drift2096 fn counts_are_compared_from_the_day_g1t_started_counting() {
2097 let on = |day: &str, cf: f64, own: f64| ProductDay { day: day.into(), bucket: "git".into(), cf_quantity: cf, own_quantity: own, ..ProductDay::default() };
2098 // Five days of Cloudflare's count before g1t's meter, then two that match.
2099 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-05", 300.0, 0.0), on("2026-10-06", 210.0, 231.0), on("2026-10-07", 450.0, 458.0)];
2100 assert!(drifts("git", &days, 10.0, true, 0).iter().all(|d| d.kind != DriftKind::Count));
2101 // A real gap on the days both counted still shows.
2102 let days = vec![on("2026-10-01", 500.0, 0.0), on("2026-10-06", 400.0, 231.0), on("2026-10-07", 600.0, 300.0)];
2103 let found = drifts("git", &days, 10.0, true, 0);
2104 let count = found.iter().find(|d| d.kind == DriftKind::Count).unwrap();
2105 assert_eq!((count.ours, count.cloudflare), (531.0, 1000.0));
2106 // A meter that never counted is compared over every day.
2107 let days = vec![on("2026-10-06", 400.0, 0.0)];
2108 assert!(drifts("git", &days, 10.0, true, 0).iter().any(|d| d.kind == DriftKind::Count));
2109 }
2110
2111 #[test]
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2112 fn what_g1t_gives_away_is_kept_apart_from_what_it_sells() {
2113 let map = BTreeMap::new();
2114 // A comped workspace (all of it given), one in its trial (half paid
2115 // by the trial) and one paying in cash, all on models.
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2116 let comped = usage("2026-10-15", "flagon", "agent", 1_200_000, 0, 1_000_000);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2117 let mut trial = usage("2026-10-15", "acme", "agent", 1_200_000, 600_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2118 trial.given = Given { trial: 600_000, ..Given::default() };
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2119 let paying = usage("2026-10-15", "beta", "agent", 1_200_000, 1_200_000, 1_000_000);
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2120 // Nothing priced that day: free use.
2121 let free = usage("2026-10-15", "gamma", "agent", 0, 0, 1_000_000);
2122 let internal = BTreeSet::from(["flagon".to_string()]);
2123 let (days, workspaces) = fold(&[], &map, &[], &[], &[comped, trial, paying, free], &internal);
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2124 let models = days.iter().find(|d| d.bucket == "models").unwrap();
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2125 assert_eq!(models.cost(), 4_000_000);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2126 assert_eq!(models.given, Given { comped: 1_000_000, free: 1_000_000, trial: 500_000, ..Given::default() });
Costs: a statement that keeps usage sold, running g1t, subscriptions and what was given away (comped, free use, trial, pool) apart, and says who was paid; free use carries its own cost; the run button says it is running2127 let given = |w: &str| workspaces.iter().find(|x| x.workspace == w).unwrap().given.total();
2128 assert_eq!((given("flagon"), given("acme"), given("beta"), given("gamma")), (1_000_000, 500_000, 0, 1_000_000));
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2129 }
2130
2131 #[test]
Merge branch 'worktree-agent-a633ac0f7f66d419d'2132 fn a_discounted_sale_keeps_its_margin_and_counts_the_discount_as_given() {
2133 // $1 of model cost at 20%, sold to an account with 30% off: charged
2134 // $0.84, and $0.36 below cost plus the margin given (as usage_rows
2135 // reads the ledger: value at price, the discount part given).
2136 let mut sale = usage("2026-10-15", "acme", "agent", 1_200_000, 840_000, 1_000_000);
2137 sale.given = Given { discount: 360_000, ..Given::default() };
2138 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &[sale], &BTreeSet::new());
2139 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2140 assert_eq!(models.value_micros, 1_200_000);
2141 assert_eq!(models.given, Given { discount: 300_000, ..Given::default() });
2142 // What was sold (cost less given) still makes the margin.
2143 let sold = models.cost() - models.given.total();
2144 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2145 }
2146
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2147 fn draw(kind: CreditKind, reference: &str, task: Option<&str>, at: &str, micros: i64) -> (String, crate::grants::Draw) {
2148 let draw = crate::grants::Draw { grant: "crd_a".into(), kind, reference: reference.into(), task: task.map(Into::into), at: at.into(), micros };
2149 ("acme".to_owned(), draw)
2150 }
2151
2152 #[test]
2153 fn usage_paid_for_with_credit_is_given_not_money_in() {
2154 // $1.20 of usage on $1 of cost, all of it paid with promotional credit.
2155 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2156 apply_credits(&mut rows, &[draw(CreditKind::Promotional, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2157 assert_eq!(rows[0].cash, 0);
2158 assert_eq!(rows[0].given, Given { credit_promotional: 1_200_000, ..Given::default() });
2159 let (days, workspaces) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2160 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2161 // Valued at its price, none of it money in, all of its cost given:
2162 // the margin on what was sold is untouched by it.
2163 assert_eq!((models.value_micros, models.cash_micros), (1_200_000, 0));
2164 assert_eq!(models.given, Given { credit_promotional: 1_000_000, ..Given::default() });
2165 assert_eq!(models.cost() - models.given.total(), 0);
2166 assert_eq!(workspaces[0].given.total(), 1_000_000);
2167 // Half paid with goodwill credit: half the cost given, half sold.
2168 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2169 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "run_1", Some("implement"), "2026-10-15T10:00:00Z", 600_000)], &[]);
2170 let (days, _) = fold(&[], &BTreeMap::new(), &[], &[], &rows, &BTreeSet::new());
2171 let models = days.iter().find(|d| d.bucket == "models").unwrap();
2172 assert_eq!(models.cash_micros, 600_000);
2173 assert_eq!(models.given, Given { credit_goodwill: 500_000, ..Given::default() });
2174 let sold = models.cost() - models.given.total();
2175 assert_eq!(margin_percent(models.cash_micros, sold).map(|m| m.round()), Some(17.0));
2176 }
2177
2178 #[test]
2179 fn what_a_refund_pays_for_is_paid_for_and_the_refund_comes_off_its_day() {
2180 // A refund's credit pays for usage: still money in, nothing given.
2181 let mut rows = vec![usage("2026-10-15", "acme", "implement", 1_200_000, 1_200_000, 1_000_000)];
2182 apply_credits(&mut rows, &[draw(CreditKind::Refund, "run_9", Some("implement"), "2026-10-15T10:00:00Z", 1_200_000)], &[]);
2183 assert_eq!((rows[0].cash, rows[0].given), (1_200_000, Given::default()));
2184 // The $3 refunded for Oct 2 comes off that day's money in, shared
2185 // over what was paid that day.
2186 let mut rows = vec![
2187 usage("2026-10-02", "acme", "implement", 4_000_000, 4_000_000, 3_000_000),
2188 usage("2026-10-02", "acme", "sandbox", 2_000_000, 2_000_000, 1_500_000),
2189 usage("2026-10-02", "beta", "implement", 9_000_000, 9_000_000, 7_000_000),
2190 ];
2191 let refund = crate::grants::Refunded { workspace: "acme".into(), day: "2026-10-02".into(), micros: 3_000_000 };
2192 apply_credits(&mut rows, &[], std::slice::from_ref(&refund));
2193 assert_eq!((rows[0].cash, rows[1].cash, rows[2].cash), (2_000_000, 1_000_000, 9_000_000));
2194 assert!(rows.iter().all(|r| r.given == Given::default()));
2195 // Nothing paid that day: a line of its own, money in less than nothing.
2196 let mut rows = vec![];
2197 apply_credits(&mut rows, &[], &[refund]);
2198 assert_eq!((rows[0].key.as_str(), rows[0].cash, rows[0].value), ("other", -3_000_000, 0));
2199 }
2200
2201 #[test]
2202 fn credit_spent_on_month_end_meters_is_a_line_of_its_own() {
2203 // Storage is reconciled from snapshots, not its ledger line: what
2204 // credit paid of it is its own row on the day it was charged.
2205 let mut rows = vec![usage("2026-10-01", "acme", "implement", 1_000, 1_000, 800)];
2206 apply_credits(&mut rows, &[draw(CreditKind::Goodwill, "storage/2026-09", Some("storage"), "2026-10-01T00:05:00Z", 2_000_000)], &[]);
2207 assert_eq!(rows.len(), 2);
2208 assert_eq!((rows[1].key.as_str(), rows[1].cash, rows[1].value), ("storage", -2_000_000, 0));
2209 assert_eq!(rows[1].given.credit_goodwill, 2_000_000);
2210 assert_eq!(rows[0].cash, 1_000);
2211 }
2212
Merge branch 'worktree-agent-a633ac0f7f66d419d'2213 #[test]
2214 fn the_gateways_total_against_the_ledgers_model_cost_is_drift() {
2215 // The gateway priced $5 of g1t's own traffic; the ledger has $3.
2216 let short = drifts("models", &[day("models", 5_000_000, 3_000_000, 3_600_000, 0.0, 0.0)], 10.0, false, 100_000);
2217 assert_eq!(short.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost]);
2218 assert!((short[0].delta_percent.unwrap() + 40.0).abs() < 1e-9);
2219 // Gateway traffic with nothing on the ledger at all: cost drift and a leak.
2220 let none = drifts("models", &[day("models", 2_000_000, 0, 0, 0.0, 0.0)], 10.0, false, 100_000);
2221 assert_eq!(none.iter().map(|d| d.kind).collect::<Vec<_>>(), vec![DriftKind::Cost, DriftKind::Leak]);
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2222 // Within the threshold: nothing.
Merge branch 'worktree-agent-a633ac0f7f66d419d'2223 assert!(drifts("models", &[day("models", 1_050_000, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Billing: AI Gateway's analytics are read with the token that can see them, and a gateway that priced nothing is said2224 // The gateway priced nothing against a ledger that has model cost:
2225 // not agreement (a token that cannot see AI Gateway reads as no
2226 // rows), so it is said. Under the minimum, or no model cost: nothing.
2227 let silent = drifts("models", &[day("models", 0, 1_000_000, 1_200_000, 0.0, 0.0)], 10.0, false, 100_000);
2228 assert_eq!(silent, vec![Drift { bucket: "models".into(), kind: DriftKind::Cost, ours: 1_000_000.0, cloudflare: 0.0, delta_percent: None }]);
2229 let said = models_detail(&silent[0], &costs::GatewayCaveats::default());
2230 assert!(said.contains("$1.00") && said.contains("priced nothing") && said.contains("AI Gateway: Read"), "{said}");
2231 assert!(drifts("models", &[day("models", 0, 50_000, 60_000, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
2232 assert!(drifts("models", &[day("models", 0, 0, 0, 0.0, 0.0)], 10.0, false, 100_000).is_empty());
Merge branch 'worktree-agent-a633ac0f7f66d419d'2233 // The detail says which way and why it may be off.
2234 let caveats = costs::GatewayCaveats { cache_read_tokens: 3_000_000.0, unpriced: vec!["anthropic_claude_new_1".into()], ..Default::default() };
2235 let detail = models_detail(&short[0], &caveats);
2236 assert!(detail.contains("$5.00") && detail.contains("$3.00") && detail.contains("were not charged"), "{detail}");
2237 assert!(detail.contains("3,000,000 prompt-cache read") && detail.contains("no price for anthropic_claude_new_1"), "{detail}");
2238 }
2239
2240 #[test]
2241 fn model_usage_the_gateway_cannot_price_is_drift_even_when_the_totals_agree() {
2242 assert!(unpriced_drift(&costs::GatewayCaveats::default()).is_none());
2243 // Cache tokens alone are a note on the cost drift, not drift.
2244 assert!(unpriced_drift(&costs::GatewayCaveats { cache_write_tokens: 10.0, ..Default::default() }).is_none());
2245 let (drift, detail) = unpriced_drift(&costs::GatewayCaveats { unpriced: vec!["anthropic_claude_new_1".into()], short_runs: 2, ..Default::default() }).unwrap();
2246 assert_eq!((drift.bucket.as_str(), drift.kind.as_str()), ("models", "unpriced"));
2247 assert!(detail.contains("no price for anthropic_claude_new_1") && detail.contains("2 runs were settled"), "{detail}");
2248 }
2249
2250 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2251 fn a_workspace_that_costs_more_than_it_pays_is_flagged() {
2252 let rows = vec![("acme".to_string(), 5_000_000, 1_000_000), ("beta".to_string(), 900_000, 0), ("gamma".to_string(), 2_000_000, 3_000_000)];
2253 let found = anomalies(&rows, 1.0, 1_000_000);
2254 assert_eq!(found, vec![("acme".to_string(), 5_000_000, 1_000_000)]);
2255 // At twice its revenue as the threshold, $5 against $3 is fine.
2256 assert!(anomalies(&[("acme".to_string(), 5_000_000, 3_000_000)], 2.0, 1_000_000).is_empty());
2257 }
2258
2259 #[test]
2260 fn a_git_operation_costs_what_cloudflare_counts_for_it() {
2261 // $0.15 per 1,000 of Cloudflare's operations, on the charged days.
2262 let rate = billed_rate(&[(10_000.0, 0.0), (20_000.0, 3.0), (30_000.0, 4.5), (5_000.0, 0.75)]).unwrap();
2263 assert!((rate - 0.000_15).abs() < 1e-12);
2264 // Cloudflare counted 3 for every 1 g1t did: binding reads count.
2265 let per_op = derived_unit_cost(rate, 300_000.0, 100_000.0).unwrap();
2266 let per_thousand_micros = per_op * unit_size("1,000 operations") * 1e6;
2267 assert!((per_thousand_micros - 450_000.0).abs() < 1e-6, "{per_thousand_micros}");
2268 // Too few of g1t's units to say.
2269 assert!(derived_unit_cost(rate, 3_000.0, 500.0).is_none());
2270 assert!(billed_rate(&[(10_000.0, 0.0)]).is_none());
2271 assert_eq!(unit_size("million requests"), 1e6);
2272 assert_eq!(unit_size("second"), 1.0);
2273 }
2274}

This file's history is long; its oldest lines are credited to the oldest commit read.