Skip to content
717 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.

Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1//! What g1t pays for itself, and two caps on it.
2//!
3//! Every charge that settles (an agent run, sandbox time, a build) is
4//! split by what paid for it, at cost: a customer's real money, or g1t's.
5//! g1t's part goes to `g1t_spend` by day, bucket and billing account:
6//!
7//! - `comped`: work on a comped account (g1t's own, Flagon's), all of it.
8//! - `trial`, `oss`: the trial credit and the open-source pool.
9//! - `given`: a free workspace's overrun past its last bit of trial.
10//! - `unpaid`: charged, but with no real money behind it: Stripe's test
11//! key, or `FREE_WHILE_BUILDING`.
12//!
13//! The plan's included usage and on-demand charges with live payments are
14//! revenue, not g1t's. A workspace's own model provider costs g1t nothing.
15//!
16//! Two caps read it:
17//!
18//! 1. **A comped account's monthly budget**: `COMPED_MONTHLY_CEILING_MICROS`
19//! ($150), or the account's own limit in its terms (sudo, Accounts →
20//! Terms → Limit). Staff are emailed at 50, 75, 90 and 100%, once each a
21//! month; at 100% new work on it is refused until staff raise it or the
22//! month turns. Work already running finishes.
23//! 2. **The daily breaker**: when g1t's part across every workspace today
24//! (UTC) reaches `PLATFORM_DAILY_SPEND_CAP_MICROS` ($75), new agent runs
25//! on g1t's hosted models that g1t would pay for are paused for the rest
26//! of the day: everyone's except workspaces paying with real money on
27//! the plan or an enterprise contract. Staff are emailed at once and sudo
28//! shows a red bar; staff can lift it for the day.
29//!
The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were.30//! Zero for either variable turns that cap off.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays31
32use g1t_contracts::billing::{
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging33 AdminLiftBreakerArgs, BillingAccount, CompedBudget, ComputeKind, PlanKind, SpendBucket, SpendCaps,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays34};
35use g1t_contracts::time::rfc3339;
36use g1t_contracts::{FailureCode, Outcome};
37use g1t_kit::now_ms;
38use serde::Deserialize;
39use worker::{Env, Result};
40
41use crate::Billing;
42use crate::credits::Drawn;
43use crate::limits::alert_level;
44
45/// The caps, from the billing service's variables.
46#[derive(Clone, Debug)]
47pub(crate) struct Caps {
48 /// `COMPED_MONTHLY_CEILING_MICROS`: a comped account's monthly budget
49 /// at cost, unless its terms set one. Zero: none.
50 pub comped_monthly: i64,
51 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`: g1t's own spend a day before the
52 /// breaker trips. Zero: no breaker.
53 pub daily: i64,
54 /// `CLOUDFLARE_FIXED_MONTHLY_MICROS`: Cloudflare's subscriptions, an
55 /// estimate for sudo.
56 pub fixed_monthly: i64,
57 /// `COSTS_ALERT_EMAIL`. Empty: nothing is emailed.
58 pub alert_to: String,
59}
60
61impl Caps {
62 pub(crate) fn from_env(env: &Env) -> Self {
63 let number = |name: &str, default: i64| {
64 env.var(name).ok().and_then(|v| v.to_string().trim().parse::<i64>().ok()).unwrap_or(default).max(0)
65 };
66 Caps {
67 comped_monthly: number("COMPED_MONTHLY_CEILING_MICROS", 150_000_000),
68 daily: number("PLATFORM_DAILY_SPEND_CAP_MICROS", 75_000_000),
69 fixed_monthly: number("CLOUDFLARE_FIXED_MONTHLY_MICROS", 30_000_000),
70 alert_to: env.var("COSTS_ALERT_EMAIL").map(|v| v.to_string().trim().to_owned()).unwrap_or_default(),
71 }
72 }
73}
74
75/// g1t's part of one charge, at cost, by what paid for it.
76#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
77pub(crate) struct Share {
78 pub comped: i64,
79 pub trial: i64,
80 pub oss: i64,
81 pub given: i64,
82 pub unpaid: i64,
83}
84
85impl Share {
86 pub fn total(&self) -> i64 {
87 self.comped + self.trial + self.oss + self.given + self.unpaid
88 }
89
90 pub fn parts(&self) -> [(&'static str, i64); 5] {
91 [("comped", self.comped), ("trial", self.trial), ("oss", self.oss), ("given", self.given), ("unpaid", self.unpaid)]
92 }
93}
94
95/// What of a charge costing g1t `cost` g1t paid itself. `charged` is what
96/// the workspace was charged after `drawn` paid its part (both at price);
97/// `real_money` is whether payments are live. The plan's included usage
98/// and what the workspace is charged are revenue only with real money.
99pub(crate) fn share(cost: i64, charged: i64, drawn: &Drawn, comped: bool, real_money: bool) -> Share {
100 if cost <= 0 {
101 return Share::default();
102 }
103 if comped {
104 return Share { comped: cost, ..Share::default() };
105 }
106 let gross = charged.max(0) + drawn.total();
107 if gross <= 0 {
108 // Charged nothing at all (free while g1t is being built out).
109 return Share { unpaid: cost, ..Share::default() };
110 }
111 let part = |paid: i64| (i128::from(cost) * i128::from(paid.max(0)) / i128::from(gross)) as i64;
112 let (trial, oss, given) = (part(drawn.trial), part(drawn.oss), part(drawn.given));
113 let unpaid = if real_money { 0 } else { (cost - trial - oss - given).max(0) };
114 Share { comped: 0, trial, oss, given, unpaid }
115}
116
117/// A comped account's monthly budget: its own (terms' limit) or the
118/// default; and whether it is the default. Zero: none.
119pub(crate) fn comped_ceiling(own: Option<i64>, default: i64) -> (i64, bool) {
120 match own {
121 Some(own) => (own.max(0), false),
122 None => (default.max(0), true),
123 }
124}
125
126/// Whether a budget is used up.
127pub(crate) fn used_up(used: i64, ceiling: i64) -> bool {
128 ceiling > 0 && used >= ceiling
129}
130
131/// Whether the breaker stops new runs: on, reached, and not lifted today.
132pub(crate) fn breaker_open(today: i64, cap: i64, lifted: bool) -> bool {
133 cap > 0 && today >= cap && !lifted
134}
135
136/// Whether the breaker is about this start: an agent run on g1t's hosted
137/// models (an agent run that does not say is taken to be one).
138pub(crate) fn breaker_applies(kind: ComputeKind, hosted_model: Option<bool>) -> bool {
139 kind == ComputeKind::Agent && hosted_model.unwrap_or(true)
140}
141
142/// Whether a workspace's spend is covered by revenue, so the breaker
143/// leaves it alone: live payments, not comped, and on the plan it pays for
144/// (not given it by staff) or an enterprise contract.
145pub(crate) fn covered_by_revenue(plan: PlanKind, comped: bool, plan_given: bool, live: bool) -> bool {
146 live && !comped && match plan {
147 PlanKind::Enterprise => true,
148 PlanKind::Paid => !plan_given,
149 _ => false,
150 }
151}
152
153/// The alert to send now: the level reached, if higher than any sent this
154/// month.
155pub(crate) fn alert_to_send(level: u32, sent: u32) -> Option<u32> {
156 (level > 0 && level > sent).then_some(level)
157}
158
159/// `$150.00`: whole cents.
160pub(crate) fn cents(micros: i64) -> String {
161 let cents = (micros as f64 / 10_000.0).round() as i64;
162 format!("{}${}.{:02}", if cents < 0 { "-" } else { "" }, cents.abs() / 100, cents.abs() % 100)
163}
164
165/// What a start on a comped account past its budget is told. Staff-only:
166/// only comped (g1t's own) accounts see it.
167pub(crate) fn comped_refusal(name: &str, used: i64, ceiling: i64) -> String {
168 format!(
169 "{name}'s monthly budget for g1t's own agents is used up ({} of {} this month at cost), so new runs wait. Staff can raise it in sudo: Accounts, {name}, Terms, Limit.",
170 cents(used),
171 cents(ceiling)
172 )
173}
174
175/// What a hosted-model start is told while the breaker is open.
176pub(crate) fn breaker_refusal(today: i64, cap: i64) -> String {
177 format!(
178 "g1t's daily spend breaker is open: g1t has paid {} of its {} a day for work today, so new agent runs on g1t's hosted models wait until 00:00 UTC. Agents on the workspace's own model provider still run, and so does work on the paid plan.",
179 cents(today),
180 cents(cap)
181 )
182}
183
184#[derive(Deserialize)]
185struct Sum {
186 micros: Option<i64>,
187}
188
189#[derive(Deserialize)]
190struct BreakerRow {
191 tripped_at: Option<String>,
192 told_at: Option<String>,
193 lifted_by: Option<String>,
194 lifted_at: Option<String>,
195 lift_note: Option<String>,
196}
197
198fn today() -> String {
199 rfc3339(now_ms())[..10].to_owned()
200}
201
202impl Billing {
203 fn live(&self) -> bool {
204 self.stripe.as_ref().is_some_and(crate::stripe::Stripe::live)
205 }
206
207 /// Counts g1t's part of a charge that just settled, and trips the
208 /// breaker if today reached its cap. Never fails the charge: a problem
209 /// here is logged.
210 pub(crate) async fn count_spend(&self, workspace: &str, cost: i64, charged: i64, drawn: &Drawn) {
211 if let Err(error) = self.try_count_spend(workspace, cost, charged, drawn).await {
212 worker::console_error!("could not count g1t's spend for {workspace}: {error}");
213 }
214 }
215
216 async fn try_count_spend(&self, workspace: &str, cost: i64, charged: i64, drawn: &Drawn) -> Result<()> {
217 if cost <= 0 {
218 return Ok(());
219 }
220 let account = self.account_of(workspace).await?;
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging221 let paid = share(cost, charged, drawn, account.terms.full_discount(), self.live());
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays222 if paid.total() == 0 {
223 return Ok(());
224 }
225 let day = today();
226 let mut writes = vec![];
227 for (bucket, micros) in paid.parts() {
228 if micros > 0 {
229 writes.push(
230 self.db
231 .prepare(
232 "INSERT INTO g1t_spend (day, bucket, account, micros) VALUES (?1, ?2, ?3, ?4)
233 ON CONFLICT (day, bucket, account) DO UPDATE SET micros = micros + ?4",
234 )
235 .bind(&[day.as_str().into(), bucket.into(), account.id.as_str().into(), (micros as f64).into()])?,
236 );
237 }
238 }
239 self.db.batch(writes).await?;
240 if self.caps.daily <= 0 {
241 return Ok(());
242 }
243 let total = self.spent_on(&day).await?;
244 if total < self.caps.daily {
245 return Ok(());
246 }
247 // Tripped: recorded once a day, and staff told at once.
248 let now = rfc3339(now_ms());
249 let tripped = self
250 .db
251 .prepare(
252 "INSERT INTO spend_breaker (day, tripped_at, tripped_micros) VALUES (?1, ?2, ?3)
253 ON CONFLICT (day) DO UPDATE SET tripped_at = ?2, tripped_micros = ?3 WHERE spend_breaker.tripped_at IS NULL
254 RETURNING day",
255 )
256 .bind(&[day.as_str().into(), now.as_str().into(), (total as f64).into()])?
257 .first::<serde_json::Value>(None)
258 .await?;
259 if tripped.is_some() {
260 self.tell_breaker(&day, total).await?;
261 }
262 Ok(())
263 }
264
265 /// g1t's own spend on `day`, across every workspace.
266 async fn spent_on(&self, day: &str) -> Result<i64> {
267 Ok(self
268 .db
269 .prepare("SELECT SUM(micros) AS micros FROM g1t_spend WHERE day = ?")
270 .bind(&[day.into()])?
271 .first::<Sum>(None)
272 .await?
273 .and_then(|s| s.micros)
274 .unwrap_or(0))
275 }
276
277 async fn breaker_row(&self, day: &str) -> Result<Option<BreakerRow>> {
278 self.db
279 .prepare("SELECT tripped_at, told_at, lifted_by, lifted_at, lift_note FROM spend_breaker WHERE day = ?")
280 .bind(&[day.into()])?
281 .first::<BreakerRow>(None)
282 .await
283 }
284
285 /// Emails staff that the breaker tripped, and notes it was told.
286 async fn tell_breaker(&self, day: &str, total: i64) -> Result<()> {
287 if self.caps.alert_to.is_empty() {
288 return Ok(());
289 }
290 let lifted = self.breaker_row(day).await?.and_then(|row| row.lifted_by);
291 let mut lines = vec![
292 format!(
293 "g1t paid {} for work today ({day}, UTC), its daily cap of {} (PLATFORM_DAILY_SPEND_CAP_MICROS). New agent runs on g1t's hosted models that g1t pays for are paused until 00:00 UTC; workspaces paying with real money, and agents on their own model provider, are not affected. Runs already going finish.",
294 cents(total),
295 cents(self.caps.daily)
296 ),
297 "To let them start again today: sudo, Costs & margin, Lift for today. To change the cap: PLATFORM_DAILY_SPEND_CAP_MICROS in services/billing/wrangler.jsonc.".to_owned(),
298 ];
299 if let Some(by) = lifted {
300 lines.insert(1, format!("{by} had already lifted it for today, so nothing is paused."));
301 }
302 match crate::margin::email_staff(&self.env, &self.caps.alert_to, &format!("g1t: the daily spend breaker tripped at {}", cents(total)), &lines).await {
303 Ok(()) => {
304 self.db
305 .prepare("UPDATE spend_breaker SET told_at = ? WHERE day = ?")
306 .bind(&[rfc3339(now_ms()).into(), day.into()])?
307 .run()
308 .await?;
309 }
310 Err(error) => worker::console_error!("could not email the breaker: {error}"),
311 }
312 Ok(())
313 }
314
315 /// Why a start is refused by the breaker, if it is.
316 pub(crate) async fn breaker_refuses(
317 &self,
318 plan: PlanKind,
319 account: &BillingAccount,
320 kind: ComputeKind,
321 hosted_model: Option<bool>,
322 ) -> Result<Option<String>> {
323 if self.caps.daily <= 0 || !breaker_applies(kind, hosted_model) {
324 return Ok(None);
325 }
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging326 let comped = account.terms.full_discount();
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays327 if covered_by_revenue(plan, comped, account.allowances.plan, self.live()) {
328 return Ok(None);
329 }
330 let day = today();
331 let spent = self.spent_on(&day).await?;
332 if spent < self.caps.daily {
333 return Ok(None);
334 }
335 let lifted = self.breaker_row(&day).await?.is_some_and(|row| row.lifted_at.is_some());
336 Ok(breaker_open(spent, self.caps.daily, lifted).then(|| breaker_refusal(spent, self.caps.daily)))
337 }
338
339 /// A comped account's budget this month.
340 pub(crate) async fn comped_budget(&self, account: &BillingAccount) -> Result<CompedBudget> {
341 let month = &rfc3339(now_ms())[..7];
342 let used = self
343 .db
344 .prepare("SELECT SUM(micros) AS micros FROM g1t_spend WHERE account = ? AND bucket = 'comped' AND day >= ?")
345 .bind(&[account.id.as_str().into(), format!("{month}-01").into()])?
346 .first::<Sum>(None)
347 .await?
348 .and_then(|s| s.micros)
349 .unwrap_or(0);
350 let (ceiling, default_ceiling) = comped_ceiling(account.terms.ceiling_micros, self.caps.comped_monthly);
351 Ok(CompedBudget {
352 account: account.id.clone(),
353 name: account.name.clone(),
354 used_micros: used,
355 ceiling_micros: ceiling,
356 default_ceiling,
357 level: alert_level(used, ceiling),
358 })
359 }
360
361 /// Why new work on a comped account is refused, if its budget is used
362 /// up. None for every other account.
363 pub(crate) async fn comped_stop(&self, account: &BillingAccount) -> Result<Option<String>> {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging364 if !account.terms.full_discount() {
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays365 return Ok(None);
366 }
367 let budget = self.comped_budget(account).await?;
368 Ok(used_up(budget.used_micros, budget.ceiling_micros).then(|| comped_refusal(&account.name, budget.used_micros, budget.ceiling_micros)))
369 }
370
371 /// Every 15 minutes: comped budgets' alerts, once each level a month,
372 /// and a tripped breaker staff were not yet told about.
373 pub(crate) async fn watch_spend(&self) -> Result<()> {
374 if self.caps.alert_to.is_empty() {
375 return Ok(());
376 }
377 let month = rfc3339(now_ms())[..7].to_owned();
378 #[derive(Deserialize)]
379 struct Id {
380 id: String,
381 }
382 let comped = self
383 .db
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging384 .prepare(format!("SELECT id FROM billing_accounts WHERE {}", crate::sales::FULL_DISCOUNT_SQL))
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays385 .all()
386 .await?
387 .results::<Id>()?;
388 #[derive(Deserialize)]
389 struct Sent {
390 level: Option<i64>,
391 }
392 for Id { id } in comped {
393 let Some(account) = self.find_account(&id).await? else { continue };
394 let budget = self.comped_budget(&account).await?;
395 let sent = self
396 .db
397 .prepare("SELECT MAX(level) AS level FROM budget_alerts WHERE account = ? AND month = ?")
398 .bind(&[id.as_str().into(), month.as_str().into()])?
399 .first::<Sent>(None)
400 .await?
401 .and_then(|s| s.level)
402 .unwrap_or(0);
403 let Some(level) = alert_to_send(budget.level, u32::try_from(sent).unwrap_or(0)) else { continue };
404 let name = &account.name;
405 let mut lines = vec![format!(
406 "{name}'s work has cost g1t {} this month, {level}% of its {} monthly budget ({}).",
407 cents(budget.used_micros),
408 cents(budget.ceiling_micros),
409 if budget.default_ceiling { "COMPED_MONTHLY_CEILING_MICROS" } else { "its own limit, in its terms" }
410 )];
411 lines.push(if level >= 100 {
412 format!("New agent runs, checks and builds on {name} are refused until the budget is raised or the month turns. Runs already going finish. To raise it: sudo, Accounts, {name}, Terms, Limit.")
413 } else {
414 format!("At 100%, new work on {name} is refused until staff raise the budget. To raise it now: sudo, Accounts, {name}, Terms, Limit.")
415 });
416 let subject = format!("g1t: {name} has used {level}% of its monthly budget");
417 match crate::margin::email_staff(&self.env, &self.caps.alert_to, &subject, &lines).await {
418 Ok(()) => {
419 self.db
420 .prepare("INSERT OR IGNORE INTO budget_alerts (account, month, level, sent_at) VALUES (?, ?, ?, ?)")
421 .bind(&[id.as_str().into(), month.as_str().into(), level.into(), rfc3339(now_ms()).into()])?
422 .run()
423 .await?;
424 }
425 Err(error) => worker::console_error!("could not email {name}'s budget alert: {error}"),
426 }
427 }
428 // A trip whose email did not go out when it happened.
429 let day = today();
430 if self.breaker_row(&day).await?.is_some_and(|row| row.tripped_at.is_some() && row.told_at.is_none()) {
431 let total = self.spent_on(&day).await?;
432 self.tell_breaker(&day, total).await?;
433 }
434 Ok(())
435 }
436
437 /// `admin_spend_caps`: g1t's own spend against its caps.
438 pub(crate) async fn spend_caps(&self) -> Result<SpendCaps> {
439 let day = today();
440 let month = day[..7].to_owned();
441 let month_start = format!("{month}-01");
442 let today_micros = self.spent_on(&day).await?;
443 let row = self.breaker_row(&day).await?;
444 let lifted = row.as_ref().is_some_and(|r| r.lifted_at.is_some());
445 #[derive(Deserialize)]
446 struct Bucket {
447 bucket: String,
448 micros: Option<i64>,
449 }
450 let rows = self
451 .db
452 .prepare("SELECT bucket, SUM(micros) AS micros FROM g1t_spend WHERE day >= ? GROUP BY bucket")
453 .bind(&[month_start.as_str().into()])?
454 .all()
455 .await?
456 .results::<Bucket>()?;
457 let month_buckets = ["comped", "trial", "oss", "given", "unpaid"]
458 .iter()
459 .map(|bucket| SpendBucket {
460 bucket: (*bucket).to_owned(),
461 title: bucket_title(bucket).to_owned(),
462 micros: rows.iter().find(|r| r.bucket == *bucket).and_then(|r| r.micros).unwrap_or(0),
463 })
464 .collect();
465 #[derive(Deserialize)]
466 struct Id {
467 id: String,
468 }
469 let ids = self
470 .db
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging471 .prepare(format!("SELECT id FROM billing_accounts WHERE {} ORDER BY id", crate::sales::FULL_DISCOUNT_SQL))
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays472 .all()
473 .await?
474 .results::<Id>()?;
475 let mut comped = vec![];
476 for Id { id } in ids {
477 if let Some(account) = self.find_account(&id).await? {
478 comped.push(self.comped_budget(&account).await?);
479 }
480 }
481 // Free workspaces' share of the reconciled costs that are not on
482 // the ledger (models, sandboxes and builds are, above).
483 let free_tier_micros = self
484 .db
485 .prepare(format!(
486 "SELECT SUM(cost_micros) AS micros FROM workspace_costs
487 WHERE day >= ?1 AND bucket NOT IN ('models', 'sandboxes', 'deployments')
488 AND workspace NOT IN ({internal})
489 AND workspace NOT IN (SELECT workspace FROM workspace_costs WHERE day >= ?1 GROUP BY workspace HAVING SUM(revenue_micros) > 0)",
490 internal = crate::sales::INTERNAL_SQL
491 ))
492 .bind(&[month_start.as_str().into()])?
493 .first::<Sum>(None)
494 .await?
495 .and_then(|s| s.micros)
496 .unwrap_or(0);
497 let revenue_micros = self
498 .db
499 .prepare("SELECT SUM(cash_micros) AS micros FROM margin_days WHERE day >= ?")
500 .bind(&[month_start.as_str().into()])?
501 .first::<Sum>(None)
502 .await?
503 .and_then(|s| s.micros)
504 .unwrap_or(0);
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises505 // What a testing reset wiped from the ledger is still here.
506 #[derive(Deserialize)]
507 struct Reset {
508 account: String,
509 micros: Option<i64>,
510 }
511 let reset = self
512 .db
513 .prepare(RESET_SPEND_SQL)
514 .bind(&[month_start.as_str().into()])?
515 .all()
516 .await?
517 .results::<Reset>()?;
518 let reset_micros = reset.iter().filter_map(|r| r.micros).sum();
519 let reset_workspaces = reset.into_iter().map(|r| r.account.strip_prefix("ws_").unwrap_or(&r.account).to_owned()).collect();
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing520 let fixed = self.fixed_monthly(self.caps.fixed_monthly).await?;
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)521 // This month's days so far, each its billing cycle's share: the
522 // same accrual as the statement's range (`cycle::accrued`).
523 let fixed_month_micros = crate::cycle::accrued(fixed.monthly_micros, &month_start, &day, self.cycle_anchor().await?);
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays524 Ok(SpendCaps {
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises525 reset_micros,
526 reset_workspaces,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays527 day,
528 month,
529 today_micros,
530 daily_cap_micros: self.caps.daily,
531 tripped: breaker_open(today_micros, self.caps.daily, lifted),
532 tripped_at: row.as_ref().and_then(|r| r.tripped_at.clone()),
533 lifted_by: row.as_ref().and_then(|r| r.lifted_by.clone()),
534 lifted_at: row.as_ref().and_then(|r| r.lifted_at.clone()),
535 lift_note: row.as_ref().and_then(|r| r.lift_note.clone()),
536 month_buckets,
537 comped,
538 free_tier_micros,
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing539 fixed_monthly_micros: fixed.monthly_micros,
540 fixed_source: fixed.source.into(),
541 fixed_read_at: fixed.read_at,
542 fixed_items: fixed.items,
Merge Cloudflare's usage over its billing cycle: every page read, included amounts once a cycle, a projection, test-mode charges never money in (billing 0052)543 fixed_month_micros,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays544 revenue_micros,
545 })
546 }
547
548 /// `admin_lift_breaker`: hosted-model runs start again for the rest of
549 /// today (UTC).
550 pub(crate) async fn admin_lift_breaker(&self, a: AdminLiftBreakerArgs) -> Result<Outcome<SpendCaps>> {
551 let (by, note) = (a.by.trim(), a.note.trim());
552 if by.is_empty() || note.len() < 5 {
553 return Ok(Outcome::fail(FailureCode::Invalid, "Say who is lifting it, and why, in the note."));
554 }
555 let day = today();
556 let now = rfc3339(now_ms());
557 let note: String = note.chars().take(500).collect();
558 self.db
559 .prepare(
560 "INSERT INTO spend_breaker (day, lifted_by, lifted_at, lift_note) VALUES (?1, ?2, ?3, ?4)
561 ON CONFLICT (day) DO UPDATE SET lifted_by = ?2, lifted_at = ?3, lift_note = ?4",
562 )
563 .bind(&[day.as_str().into(), by.into(), now.as_str().into(), note.as_str().into()])?
564 .run()
565 .await?;
566 let spent = self.spent_on(&day).await?;
567 self.audit("costs", "breaker_lifted", &format!("{day}: lifted at {} of {}: {note}", cents(spent), cents(self.caps.daily)), by)
568 .await?;
569 Ok(Outcome::Ok(self.spend_caps().await?))
570 }
571}
572
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises573/// g1t's own spend since `?1` on accounts a testing reset wiped later than
574/// the day it was spent (`admin_actions`, action `reset`), by account.
575pub(crate) const RESET_SPEND_SQL: &str = "SELECT s.account, SUM(s.micros) AS micros FROM g1t_spend s
576 WHERE s.day >= ?1 AND EXISTS (SELECT 1 FROM admin_actions a WHERE a.action = 'reset' AND a.account = s.account AND substr(a.created_at, 1, 10) >= s.day)
577 GROUP BY s.account HAVING SUM(s.micros) > 0 ORDER BY s.account";
578
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays579/// How sudo names a bucket of g1t's own spend.
580pub(crate) fn bucket_title(bucket: &str) -> &'static str {
581 match bucket {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging582 "comped" => "100% discount (g1t's own)",
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays583 "trial" => "Trial pool",
584 "oss" => "Open-source pool",
585 "given" => "Free overruns g1t covered",
586 "unpaid" => "Charged without real money",
587 _ => "Other",
588 }
589}
590
591#[cfg(test)]
592mod tests {
593 use super::*;
594
595 fn drawn(credit: i64, trial: i64, oss: i64, given: i64) -> Drawn {
596 Drawn { credit, trial, oss, given }
597 }
598
599 #[test]
600 fn comped_work_is_all_g1ts_at_cost() {
601 let s = share(1_000_000, 0, &Drawn::default(), true, true);
602 assert_eq!(s, Share { comped: 1_000_000, ..Share::default() });
603 // Nothing that cost nothing is counted.
604 assert_eq!(share(0, 0, &Drawn::default(), true, true).total(), 0);
605 assert_eq!(share(-5, 0, &Drawn::default(), false, false).total(), 0);
606 }
607
608 #[test]
609 fn pools_pay_their_share_of_the_cost_not_the_price() {
610 // $1 of cost charged at $1.20, all from the trial: $1 is g1t's.
611 assert_eq!(share(1_000_000, 0, &drawn(0, 1_200_000, 0, 0), false, true), Share { trial: 1_000_000, ..Share::default() });
612 // Half the open-source pool, half charged on a live card: half is g1t's.
613 assert_eq!(share(1_000_000, 600_000, &drawn(0, 0, 600_000, 0), false, true), Share { oss: 500_000, ..Share::default() });
614 // A free workspace's overrun past its trial.
615 let s = share(1_000_000, 0, &drawn(0, 300_000, 0, 900_000), false, true);
616 assert_eq!((s.trial, s.given), (250_000, 750_000));
617 }
618
619 #[test]
620 fn revenue_is_only_revenue_with_real_money() {
621 // Plan credit and an on-demand charge, live: none of it is g1t's.
622 assert_eq!(share(1_000_000, 600_000, &drawn(600_000, 0, 0, 0), false, true).total(), 0);
623 // The same in test mode: all of it.
624 assert_eq!(share(1_000_000, 600_000, &drawn(600_000, 0, 0, 0), false, false), Share { unpaid: 1_000_000, ..Share::default() });
625 // Free while building: charged nothing, all g1t's.
626 assert_eq!(share(1_000_000, 0, &Drawn::default(), false, true), Share { unpaid: 1_000_000, ..Share::default() });
627 }
628
629 #[test]
630 fn a_comped_account_gets_the_default_budget_unless_its_terms_set_one() {
631 assert_eq!(comped_ceiling(None, 150_000_000), (150_000_000, true));
632 assert_eq!(comped_ceiling(Some(400_000_000), 150_000_000), (400_000_000, false));
633 // Zero: no budget.
634 assert_eq!(comped_ceiling(None, 0), (0, true));
635 assert!(!used_up(1_000_000_000, 0));
636 }
637
638 #[test]
639 fn a_comped_budget_refuses_new_work_at_one_hundred_percent() {
640 let ceiling = 150_000_000;
641 assert!(!used_up(149_999_999, ceiling));
642 assert!(used_up(150_000_000, ceiling));
643 assert!(used_up(151_000_000, ceiling));
644 let message = comped_refusal("flagon-io", 150_000_000, ceiling);
645 assert!(message.contains("used up") && message.contains("$150.00 of $150.00") && message.contains("sudo"), "{message}");
646 }
647
648 #[test]
649 fn budget_alerts_go_once_per_level_each_month() {
650 let ceiling = 150_000_000;
651 let mut sent = 0;
652 let mut emailed = vec![];
653 // Spend climbs through the month, checked every 15 minutes.
654 for used in [10_000_000, 74_000_000, 75_000_000, 80_000_000, 112_500_000, 120_000_000, 135_000_000, 140_000_000, 150_000_000, 170_000_000] {
655 if let Some(level) = alert_to_send(alert_level(used, ceiling), sent) {
656 emailed.push(level);
657 sent = level;
658 }
659 }
660 assert_eq!(emailed, vec![50, 75, 90, 100]);
661 // A jump straight past several levels sends only the highest.
662 assert_eq!(alert_to_send(alert_level(140_000_000, ceiling), 0), Some(90));
663 // A new month starts from nothing sent.
664 assert_eq!(alert_to_send(alert_level(80_000_000, ceiling), 0), Some(50));
665 }
666
667 #[test]
668 fn the_breaker_trips_at_the_cap_and_staff_can_lift_it_for_the_day() {
669 let cap = 75_000_000;
670 assert!(!breaker_open(74_999_999, cap, false));
671 assert!(breaker_open(75_000_000, cap, false));
672 // Lifted: open no more today.
673 assert!(!breaker_open(90_000_000, cap, true));
674 // Off.
675 assert!(!breaker_open(1_000_000_000, 0, false));
676 // Tomorrow's total starts at zero: the breaker resets by itself.
677 assert!(!breaker_open(0, cap, false));
678 let message = breaker_refusal(80_000_000, cap);
679 assert!(message.contains("$80.00 of its $75.00") && message.contains("00:00 UTC"), "{message}");
680 }
681
682 #[test]
683 fn the_breaker_is_about_hosted_model_agent_runs() {
684 assert!(breaker_applies(ComputeKind::Agent, Some(true)));
685 assert!(breaker_applies(ComputeKind::Agent, None));
686 assert!(!breaker_applies(ComputeKind::Agent, Some(false)));
687 assert!(!breaker_applies(ComputeKind::Check, None));
688 assert!(!breaker_applies(ComputeKind::Workflow, Some(true)));
689 }
690
691 #[test]
692 fn workspaces_paying_with_real_money_are_never_paused_by_the_breaker() {
693 assert!(covered_by_revenue(PlanKind::Paid, false, false, true));
694 assert!(covered_by_revenue(PlanKind::Enterprise, false, false, true));
695 // Test-mode payments are not money.
696 assert!(!covered_by_revenue(PlanKind::Paid, false, false, false));
697 // The plan given by staff, comped, free: g1t pays.
698 assert!(!covered_by_revenue(PlanKind::Paid, false, true, true));
699 assert!(!covered_by_revenue(PlanKind::Internal, true, false, true));
700 assert!(!covered_by_revenue(PlanKind::Free, false, false, true));
701 }
702
703 #[test]
Merge costs and margin review: gateway query, own spend, discount meters, superseded rises704 fn spend_a_testing_reset_wiped_is_found_by_the_reset_after_it() {
705 // syntaqx's $7.41 of 2026-10-02 to 10-05, reset on 10-07: still
706 // g1t's spend, gone from its ledger.
707 assert_eq!(crate::rename::parameters(RESET_SPEND_SQL), 1);
708 assert!(RESET_SPEND_SQL.contains("a.action = 'reset'") && RESET_SPEND_SQL.contains("substr(a.created_at, 1, 10) >= s.day"));
709 }
710
711 #[test]
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays712 fn amounts_read_in_cents() {
713 assert_eq!(cents(150_000_000), "$150.00");
714 assert_eq!(cents(1_234_567), "$1.23");
715 assert_eq!(cents(5_000), "$0.01");
716 }
717}

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