Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
558 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.

status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1//! Usage other services meter through the month, charged once it is over:
2//! security scans, search embeddings, and two billing measures itself each
3//! day: private repository storage and git operations.
4//!
5//! Each reports what it cost g1t so far this month (`note_pending`), so the
6//! workspace's limit counts it as it happens. When the month is over,
7//! billing charges it once: at cost plus the margin, on the account's
8//! terms, after the plan's included usage and the trial credit (see
9//! `credits`), dated the month's last second so it falls in that month's
10//! statement and invoice.
11//!
12//! The forge is free for every workspace up to the same amounts, on the
13//! plan or not; past them, a workspace on the plan pays at cost plus the
14//! margin and is never refused or slowed, and a free workspace is never
15//! charged but is held to them.
16//!
17//! **Git operations.** Cloudflare Artifacts charges g1t $0.15 per 1,000
18//! operations (clones, fetches, pushes) from 2026-10-14. The repos service
19//! counts those through g1t's git endpoints. Every workspace has
20//! `GIT_OPERATIONS_INCLUDED` (50,000) a month free; past it, the plan pays
21//! at cost plus the margin, and a free workspace is slowed down instead
22//! (the repos service's `GIT_OPERATIONS_FREE_CAP`, the same number).
23//!
Fast pages, required checks on the branch, self-hosted runners, honest incidents24//! **Actions cache.** The actions service reports what each workspace's
25//! `actions/cache` entries held each day (source `cache`), at what R2
26//! charges g1t to store them ($0.015 a GB-month). Only the plan is charged
27//! for it, from the first byte; a free workspace is never charged, and its
28//! repositories are held to the cache's quota like everyone's.
29//!
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas30//! **Storage.** The git store does not report a repository's size, so the
31//! repos service counts the packs pushed through g1t's git endpoints (see
32//! `g1t_contracts::repos::StorageArgs`): a lower bound. Each day billing
33//! records what each workspace's private repositories hold and what is
34//! free that day (`FREE_PRIVATE_STORAGE_BYTES`, 1 GB, for everyone). Like
35//! Cloudflare's own storage billing, a month's GB-months are the days'
36//! amounts past the free one, added up and divided by 30, and only the
37//! plan is charged for them. A free workspace is never charged for
38//! storage: pushes to its private repositories stop once they hold its
39//! free amount. Public repositories are never charged.
40
41use g1t_contracts::billing::PlanKind;
42use g1t_contracts::new_id;
43use g1t_contracts::repos::{GitOperationsArgs, StorageArgs, WorkspaceGitOperations, WorkspaceStorage};
44use g1t_contracts::time::rfc3339;
45use g1t_kit::now_ms;
46use serde::Deserialize;
47use worker::Result;
48
49use crate::credits::{self, Drawn, Eligible};
50use crate::{Billing, optional};
51
52/// Sources billing charges itself when the month is over.
Fast pages, required checks on the branch, self-hosted runners, honest incidents53pub(crate) const CHARGED_HERE: [&str; 5] = ["security", "context", "storage", "git", "cache"];
54
55/// Sources only the plan is charged for: a free workspace's are not kept.
56pub(crate) const PLAN_ONLY: [&str; 1] = ["cache"];
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas57
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily58/// Sources g1t pays for itself on a free workspace: security scans and
59/// search embeddings. No security feature is held back for the plan, and
60/// a free workspace never runs up a bill, so on a workspace without the
61/// plan they are recorded at cost, covered by g1t, and charged nothing
62/// (not even from the trial). On the plan they are charged like any usage.
63pub(crate) const COVERED_FOR_FREE: [&str; 2] = ["security", "context"];
64
65/// What pays for a month-end source's charge: the plan's included usage
66/// and the trial, and for a covered source on a free workspace, g1t.
67pub(crate) fn month_end_eligible(source: &str, plan: bool) -> Eligible {
68 let covered = !plan && COVERED_FOR_FREE.contains(&source);
69 Eligible { trial: !covered, repo: None, cover_rest: covered }
70}
71
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas72/// What Artifacts charges g1t, when the price book cannot be read: $0.50
73/// a GB-month of storage, and $0.15 per 1,000 git operations.
74pub(crate) const STORAGE_MICROS_PER_GB_MONTH: i64 = 500_000;
75pub(crate) const GIT_MICROS_PER_THOUSAND: i64 = 150_000;
76
77/// When Cloudflare starts charging for Artifacts operations: none before
78/// count.
79pub(crate) const GIT_BILLING_STARTS: &str = "2026-10-14T00";
80
81/// What git operations past the free amount cost g1t, at
82/// `micros_per_thousand`: nothing up to it.
83pub(crate) fn git_cost(operations: u64, included: u64, micros_per_thousand: f64) -> i64 {
84 let past = operations.saturating_sub(included);
85 (past as f64 * micros_per_thousand / 1000.0).ceil() as i64
86}
87
88/// The first hour of `month` to count git operations from.
89pub(crate) fn git_since(month: &str) -> String {
90 let start = format!("{month}-01T00");
91 if start.as_str() < GIT_BILLING_STARTS { GIT_BILLING_STARTS.to_owned() } else { start }
92}
93
94/// A gigabyte, as Cloudflare bills storage.
95pub(crate) const GB: f64 = 1_000_000_000.0;
96
97/// GB-months from a month's daily measures, each `(private, free)` bytes:
98/// what was past the free amount each day, over 30 days.
99pub(crate) fn storage_gb_months(days: &[(i64, i64)]) -> f64 {
100 days.iter().map(|(private, free)| (private - free).max(0) as f64).sum::<f64>() / GB / 30.0
101}
102
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member103/// GB-months of package storage past the free amounts from a month's
104/// daily measures, each `(public, private, public_free, private_free)`:
105/// public and private past their own amounts, over 30 days.
106pub(crate) fn package_gb_months(days: &[(i64, i64, i64, i64)]) -> f64 {
107 days.iter()
108 .map(|(public, private, public_free, private_free)| ((public - public_free).max(0) + (private - private_free).max(0)) as f64)
109 .sum::<f64>()
110 / GB
111 / 30.0
112}
113
114/// What R2 charges g1t a GB-month, when the price book cannot be read.
115pub(crate) const PACKAGE_MICROS_PER_GB_MONTH: i64 = 15_000;
116
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas117/// What `gb_months` cost g1t at `micros_per_gb_month`, rounded up.
118pub(crate) fn storage_cost(gb_months: f64, micros_per_gb_month: f64) -> i64 {
119 (gb_months * micros_per_gb_month).ceil() as i64
120}
121
122/// What a source is called on the statement.
123pub(crate) fn title(source: &str) -> &'static str {
124 match source {
125 "security" => "Security scans",
126 "context" => "Search embeddings",
127 "storage" => "Private repository storage past the free amount",
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member128 "package_storage" => "Package storage past the free amounts",
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas129 "git" => "Git operations past the free amount",
Fast pages, required checks on the branch, self-hosted runners, honest incidents130 "cache" => "Actions cache storage",
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas131 "domains" => "Custom domains",
132 _ => "Metered usage",
133 }
134}
135
136/// A usage entry to put on the ledger.
137pub(crate) struct UsageLine<'a> {
138 pub workspace: &'a str,
139 /// What the workspace is charged, after terms and what paid for it.
140 pub charged: i64,
141 pub description: &'a str,
142 pub repo: Option<&'a str>,
143 pub task: &'a str,
144 pub cost: i64,
145 pub reference: &'a str,
146 pub created_at: &'a str,
147 pub drawn: Drawn,
148}
149
150impl Billing {
151 /// Puts a usage entry on the ledger and takes it off the balance, as
152 /// one write.
153 pub(crate) async fn post_usage(&self, line: UsageLine<'_>) -> Result<()> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily154 // The price versions it was charged at (pricing.rs).
155 let mut versions = Vec::new();
156 for meter in crate::pricing::meters_of(line.task) {
157 versions.extend(self.version_now(meter).await?);
158 }
159 let price_version = versions.join(",");
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas160 self.db
161 .batch(vec![
162 self.db
163 .prepare(
164 "INSERT INTO ledger
165 (id, workspace, kind, amount_micros, description, repo, task, cost_micros, reference,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily166 created_at, billed_to, credit_micros, trial_micros, oss_micros, price_version)
167 VALUES (?, ?, 'usage', ?, ?, ?, ?, ?, ?, ?, 'g1t', ?, ?, ?, ?)",
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas168 )
169 .bind(&[
170 new_id("led", now_ms()).into(),
171 line.workspace.into(),
172 (-(line.charged as f64)).into(),
173 line.description.into(),
174 optional(line.repo),
175 line.task.into(),
176 (line.cost as f64).into(),
177 line.reference.into(),
178 line.created_at.into(),
179 (line.drawn.credit as f64).into(),
180 (line.drawn.trial as f64).into(),
181 (line.drawn.oss as f64).into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily182 optional(Some(price_version.as_str()).filter(|v| !v.is_empty())),
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas183 ])?,
184 self.db
185 .prepare(
186 "INSERT INTO accounts (workspace, balance_micros, created_at) VALUES (?1, ?2, ?3)
187 ON CONFLICT (workspace) DO UPDATE SET balance_micros = balance_micros + ?2",
188 )
189 .bind(&[line.workspace.into(), (-(line.charged as f64)).into(), rfc3339(now_ms()).into()])?,
190 ])
191 .await?;
192 Ok(())
193 }
194
195 /// Writes down what a source cost g1t so far in `month`, what it will
196 /// be charged, and how much of it there was (`detail`, for the Billing
197 /// page), replacing the last figure.
198 pub(crate) async fn set_pending(
199 &self,
200 workspace: &str,
201 source: &str,
202 month: &str,
203 cost_micros: i64,
204 detail: Option<&str>,
205 ) -> Result<()> {
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily206 // A covered source on a free workspace is never charged, so it does
207 // not count toward the workspace's limit either.
208 let covered = COVERED_FOR_FREE.contains(&source) && !self.has_plan(&workspace.to_lowercase()).await?;
209 let charge = if covered { 0 } else { credits::with_margin(cost_micros, self.margin_percent) };
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas210 self.db
211 .prepare(
212 "INSERT INTO pending_usage (workspace, source, month, charge_micros, cost_micros, updated_at, detail)
213 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)
214 ON CONFLICT (workspace, source, month) DO UPDATE SET
215 charge_micros = ?4, cost_micros = ?5, updated_at = ?6, detail = COALESCE(?7, detail)",
216 )
217 .bind(&[
218 workspace.to_lowercase().into(),
219 source.into(),
220 month.into(),
221 (charge as f64).into(),
222 (cost_micros.max(0) as f64).into(),
223 rfc3339(now_ms()).into(),
224 optional(detail),
225 ])?
226 .run()
227 .await?;
228 Ok(())
229 }
230
231 /// Charges every month that is over for the usage billing charges
232 /// itself, once each.
233 pub(crate) async fn charge_pending(&self) -> Result<()> {
234 if self.stripe.is_none() {
235 return Ok(());
236 }
237 let now = rfc3339(now_ms());
238 let current = credits::month_of(&now);
239 #[derive(Deserialize)]
240 struct Row {
241 workspace: String,
242 source: String,
243 month: String,
244 cost_micros: Option<i64>,
245 }
246 let marks = CHARGED_HERE.iter().map(|s| format!("'{s}'")).collect::<Vec<_>>().join(", ");
247 let due = self
248 .db
249 .prepare(format!(
250 "SELECT workspace, source, month, cost_micros FROM pending_usage
251 WHERE month < ? AND charged_at IS NULL AND source IN ({marks}) ORDER BY month LIMIT 50"
252 ))
253 .bind(&[current.as_str().into()])?
254 .all()
255 .await?
256 .results::<Row>()?;
257 for row in due {
258 // Claimed first, so two crons never charge it twice.
259 let claimed = self
260 .db
261 .prepare(
262 "UPDATE pending_usage SET charged_at = ?1
263 WHERE workspace = ?2 AND source = ?3 AND month = ?4 AND charged_at IS NULL RETURNING workspace",
264 )
265 .bind(&[now.as_str().into(), row.workspace.as_str().into(), row.source.as_str().into(), row.month.as_str().into()])?
266 .first::<serde_json::Value>(None)
267 .await?;
268 let cost = row.cost_micros.unwrap_or(0);
269 if claimed.is_none() || cost <= 0 {
270 continue;
271 }
272 let base = credits::with_margin(cost, self.margin_percent);
Merge branch 'worktree-agent-a633ac0f7f66d419d'273 let (charge, terms_note, discount) = self.charged(&row.workspace, base).await?;
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily274 let plan = self.has_plan(&row.workspace).await?;
275 let drawn = self.draw(&row.workspace, charge, &row.month, &month_end_eligible(&row.source, plan)).await?;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas276 let detail = if row.source == "storage" {
277 let gb_months = self.gb_months(&row.workspace, &row.month).await?;
278 format!(": {gb_months:.2} GB-months")
279 } else {
280 String::new()
281 };
282 let description = format!("{} in {}{detail}{terms_note}{}", title(&row.source), row.month, drawn.note());
283 let reference = format!("{}/{}/{}", row.source, row.workspace, row.month);
284 let created_at = credits::month_end(&row.month);
285 self.post_usage(UsageLine {
286 workspace: &row.workspace,
287 charged: charge - drawn.total(),
288 description: &description,
289 repo: None,
290 task: &row.source,
291 cost,
292 reference: &reference,
293 created_at: &created_at,
294 drawn,
295 })
296 .await?;
Merge branch 'worktree-agent-a633ac0f7f66d419d'297 self.record_discount(&reference, discount).await?;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas298 }
299 Ok(())
300 }
301
302 /// A workspace's private storage past the free amount in `month`.
303 async fn gb_months(&self, workspace: &str, month: &str) -> Result<f64> {
304 #[derive(Deserialize)]
305 struct Day {
306 private_bytes: i64,
307 free_bytes: i64,
308 }
309 let days = self
310 .db
311 .prepare("SELECT private_bytes, free_bytes FROM storage_days WHERE workspace = ? AND substr(day, 1, 7) = ?")
312 .bind(&[workspace.into(), month.into()])?
313 .all()
314 .await?
315 .results::<Day>()?;
316 Ok(storage_gb_months(&days.iter().map(|d| (d.private_bytes, d.free_bytes)).collect::<Vec<_>>()))
317 }
318
319 /// Once a day: what each workspace's private repositories hold, and
320 /// what this month's storage past the free amount comes to so far.
321 pub(crate) async fn measure_storage(&self) -> Result<()> {
322 let Some(repos) = &self.repos else { return Ok(()) };
323 let list: Vec<WorkspaceStorage> = g1t_kit::call(repos, "storage", &StorageArgs {}).await?;
324 let now = rfc3339(now_ms());
325 let (day, month) = (&now[..10], credits::month_of(&now));
326 let price = self.price("private_storage").await?.map_or(STORAGE_MICROS_PER_GB_MONTH as f64, |(cost, _)| cost);
327 for workspace in list {
328 let slug = workspace.namespace.to_lowercase();
329 let plan = self.has_plan(&slug).await?;
330 // The same free amount for everyone: the plan pays past it.
331 let free = self.plans.free_storage_bytes;
332 self.db
333 .prepare(
334 "INSERT INTO storage_days (workspace, day, private_bytes, free_bytes) VALUES (?1, ?2, ?3, ?4)
335 ON CONFLICT (workspace, day) DO UPDATE SET private_bytes = ?3, free_bytes = ?4",
336 )
337 .bind(&[slug.as_str().into(), day.into(), (workspace.private_bytes as f64).into(), (free as f64).into()])?
338 .run()
339 .await?;
340 // Only the plan pays for storage past its amount. A free
341 // workspace is never charged: the repos service stops its pushes
342 // to private repositories once it is full (see git_ops.rs there).
343 let gb_months = if plan { self.gb_months(&slug, &month).await? } else { 0.0 };
344 if gb_months > 0.0 {
345 let detail = format!("{gb_months:.2} GB-months past the free {}", crate::features::bytes(free));
346 self.set_pending(&slug, "storage", &month, storage_cost(gb_months, price), Some(&detail)).await?;
347 }
348 }
349 Ok(())
350 }
351
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member352 /// Once a day: what each workspace's packages hold, public and private,
353 /// from the packages service, and for the plan what this month's
354 /// storage past the free amounts comes to so far. A free workspace is
355 /// never charged: the packages service refuses its pushes past them.
356 pub(crate) async fn measure_packages(&self) -> Result<()> {
357 #[derive(Deserialize)]
358 struct Held {
359 workspace: String,
360 public_bytes: i64,
361 private_bytes: i64,
362 }
363 #[derive(Deserialize)]
364 struct Day {
365 public_bytes: i64,
366 private_bytes: i64,
367 public_free_bytes: i64,
368 private_free_bytes: i64,
369 }
370 let Some(packages) = &self.packages else { return Ok(()) };
371 let list: Vec<Held> = g1t_kit::call(packages, "storage_all", &serde_json::json!({})).await?;
372 let now = rfc3339(now_ms());
373 let (day, month) = (&now[..10], credits::month_of(&now));
374 let price = self.price("package_storage").await?.map_or(PACKAGE_MICROS_PER_GB_MONTH as f64, |(cost, _)| cost);
375 let (public_free, private_free) = (self.plans.public_package_free_bytes, self.plans.private_package_free_bytes);
376 for held in list {
377 let slug = held.workspace.to_lowercase();
378 self.db
379 .prepare(
380 "INSERT INTO package_storage_days (workspace, day, public_bytes, private_bytes, public_free_bytes, private_free_bytes)
381 VALUES (?1, ?2, ?3, ?4, ?5, ?6)
382 ON CONFLICT (workspace, day) DO UPDATE SET public_bytes = ?3, private_bytes = ?4, public_free_bytes = ?5, private_free_bytes = ?6",
383 )
384 .bind(&[
385 slug.as_str().into(),
386 day.into(),
387 (held.public_bytes as f64).into(),
388 (held.private_bytes as f64).into(),
389 (public_free as f64).into(),
390 (private_free as f64).into(),
391 ])?
392 .run()
393 .await?;
394 if !self.has_plan(&slug).await? {
395 continue;
396 }
397 let days = self
398 .db
399 .prepare(
400 "SELECT public_bytes, private_bytes, public_free_bytes, private_free_bytes FROM package_storage_days
401 WHERE workspace = ? AND substr(day, 1, 7) = ?",
402 )
403 .bind(&[slug.as_str().into(), month.as_str().into()])?
404 .all()
405 .await?
406 .results::<Day>()?;
407 let gb_months = package_gb_months(
408 &days.iter().map(|d| (d.public_bytes, d.private_bytes, d.public_free_bytes, d.private_free_bytes)).collect::<Vec<_>>(),
409 );
410 if gb_months > 0.0 {
411 let detail = format!(
412 "{gb_months:.2} GB-months past the free {} public and {} private",
413 crate::features::bytes(public_free),
414 crate::features::bytes(private_free)
415 );
416 self.set_pending(&slug, "package_storage", &month, storage_cost(gb_months, price), Some(&detail)).await?;
417 }
418 }
419 Ok(())
420 }
421
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas422 /// Git operations this month for each workspace, from the repos
423 /// service: what is past the free amount goes to the month's pending
424 /// usage for workspaces on the plan, which are never slowed or refused
425 /// for them. Free workspaces are never charged for them.
426 pub(crate) async fn measure_git(&self) -> Result<()> {
427 let Some(repos) = &self.repos else { return Ok(()) };
428 let month = credits::month_of(&rfc3339(now_ms()));
429 let list: Vec<WorkspaceGitOperations> =
430 g1t_kit::call(repos, "git_operations", &GitOperationsArgs { since: Some(git_since(&month)), month: month.clone(), namespace: None }).await?;
431 let price = self.price("git_operations").await?.map_or(GIT_MICROS_PER_THOUSAND as f64, |(cost, _)| cost);
432 for workspace in list {
433 let slug = workspace.namespace.to_lowercase();
434 if self.plan_kind(&slug).await? == PlanKind::Free {
435 continue;
436 }
437 let cost = git_cost(workspace.operations, self.plans.git_included, price);
438 if cost > 0 {
439 let detail = format!(
440 "{} operations, {} of them free",
441 crate::features::thousands(workspace.operations),
442 crate::features::thousands(self.plans.git_included)
443 );
444 self.set_pending(&slug, "git", &month, cost, Some(&detail)).await?;
445 }
446 }
447 Ok(())
448 }
449
450 /// The workspace's git operations this month, as last measured.
451 pub(crate) async fn git_operations_this_month(&self, workspace: &str) -> Result<u64> {
452 let Some(repos) = &self.repos else { return Ok(0) };
453 let month = credits::month_of(&rfc3339(now_ms()));
454 let list: Result<Vec<WorkspaceGitOperations>> =
455 g1t_kit::call(repos, "git_operations", &GitOperationsArgs { since: Some(format!("{month}-01T00")), month, namespace: Some(workspace.to_lowercase()) }).await;
456 Ok(list
457 .ok()
458 .and_then(|list| list.into_iter().find(|w| w.namespace.eq_ignore_ascii_case(workspace)))
459 .map_or(0, |w| w.operations))
460 }
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 #[test]
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily468 fn scans_and_embeddings_on_a_free_workspace_are_covered_by_g1t() {
469 // No plan: g1t pays, and the trial is left alone.
470 let free = month_end_eligible("security", false);
471 assert!(free.cover_rest && !free.trial);
472 assert!(month_end_eligible("context", false).cover_rest);
473 // On the plan: charged like any usage, from its included usage first.
474 let plan = month_end_eligible("security", true);
475 assert!(!plan.cover_rest && plan.trial);
476 // Storage, git and the cache are never kept for a free workspace;
477 // were one at the close, it would not be covered either.
478 assert!(!month_end_eligible("storage", false).cover_rest);
479 assert!(!month_end_eligible("git", false).cover_rest);
480 // A covered source is still a source billing charges at the close.
481 assert!(COVERED_FOR_FREE.iter().all(|s| CHARGED_HERE.contains(s)));
482 }
483
484 #[test]
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas485 fn storage_past_the_free_amount_is_counted_by_the_day() {
486 // 3 GB private with 1 GB free, every day of a 30-day month: 2 GB-months.
487 let month = vec![(3_000_000_000, 1_000_000_000); 30];
488 assert!((storage_gb_months(&month) - 2.0).abs() < 1e-9);
489 // Under the free amount: nothing.
490 assert_eq!(storage_gb_months(&[(500_000_000, 1_000_000_000); 30]), 0.0);
491 // The plan has the same 1 GB free: 11 GB on it all month is 10
492 // GB-months, $5.00 to g1t, $6.00 charged, from its included usage.
493 let days = vec![(11_000_000_000, 1_000_000_000); 30];
494 assert!((storage_gb_months(&days) - 10.0).abs() < 1e-9);
495 assert_eq!(credits::with_margin(storage_cost(storage_gb_months(&days), STORAGE_MICROS_PER_GB_MONTH as f64), 20), 6_000_000);
496 // Ten days of 4 GB past it: a third of 4 GB-months.
497 let days = vec![(5_000_000_000, 1_000_000_000); 10];
498 assert!((storage_gb_months(&days) - 4.0 / 3.0).abs() < 1e-9);
499 }
500
501 #[test]
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member502 fn package_storage_counts_public_and_private_past_their_own_free_amounts() {
503 let gb = GB as i64;
504 // 12 GB public with 10 free, 0.5 GB private with 0.5 free: 2 GB past, for 15 days.
505 let days = vec![(12 * gb, gb / 2, 10 * gb, gb / 2); 15];
506 assert!((package_gb_months(&days) - 1.0).abs() < 1e-9);
507 // Under both amounts: nothing.
508 assert_eq!(package_gb_months(&[(gb, gb / 4, 10 * gb, gb / 2)]), 0.0);
509 // At R2's rate, a GB-month is $0.015.
510 assert_eq!(storage_cost(1.0, PACKAGE_MICROS_PER_GB_MONTH as f64), 15_000);
511 }
512
513 #[test]
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas514 fn storage_is_priced_at_cloudflares_rate_plus_the_margin() {
515 // $0.50 a GB-month to g1t: 2 GB-months cost $1.00, charged $1.20.
516 let cost = storage_cost(2.0, 500_000.0);
517 assert_eq!(cost, 1_000_000);
518 assert_eq!(credits::with_margin(cost, 20), 1_200_000);
519 // A fraction of a millionth rounds up.
520 assert_eq!(storage_cost(0.000_000_001, 500_000.0), 1);
521 }
522
523 #[test]
524 fn embeddings_and_scans_are_charged_at_cost_plus_the_margin() {
525 // 10 million tokens at $0.067 a million: $0.67, charged $0.804.
526 assert_eq!(credits::with_margin(670_000, 20), 804_000);
527 assert_eq!(title("context"), "Search embeddings");
528 assert_eq!(title("security"), "Security scans");
529 assert!(CHARGED_HERE.contains(&"storage") && !CHARGED_HERE.contains(&"deployments"));
530 assert!(CHARGED_HERE.contains(&"git"));
531 }
532
533 #[test]
534 fn git_operations_are_charged_past_the_free_amount_at_cloudflares_price() {
535 // $0.15 per 1,000 to g1t; 50,000 a month free for everyone.
536 let per_thousand = GIT_MICROS_PER_THOUSAND as f64;
537 let free = crate::credits::Config::default().git_included;
538 assert_eq!(free, 50_000);
539 assert_eq!(git_cost(49_000, free, per_thousand), 0);
540 assert_eq!(git_cost(50_000, free, per_thousand), 0);
541 // A workspace on the plan pushes on past it, and pays: 70,000
542 // operations are 20,000 past it, $3.00 to g1t, $3.60 charged.
543 assert_eq!(git_cost(70_000, free, per_thousand), 3_000_000);
544 assert_eq!(credits::with_margin(git_cost(70_000, free, per_thousand), 20), 3_600_000);
545 // A million in a month: no cap, $142.50 to g1t.
546 assert_eq!(git_cost(1_000_000, free, per_thousand), 142_500_000);
547 // One past it: a fraction of a cent, rounded up to a millionth.
548 assert_eq!(git_cost(50_001, free, per_thousand), 150);
549 assert_eq!(title("git"), "Git operations past the free amount");
550 }
551
552 #[test]
553 fn git_operations_count_from_when_cloudflare_starts_charging() {
554 assert_eq!(git_since("2026-10"), "2026-10-14T00");
555 assert_eq!(git_since("2026-11"), "2026-11-01T00");
556 assert_eq!(git_since("2026-09"), "2026-10-14T00");
557 }
558}

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