Skip to content

g1t/crates/contracts/src/billing.rs

3,636 lines131,492 bytesCodeBlame

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.

Agents as a team: lifecycle, merge queue, billing and a new shell1//! The billing service: what agents cost, charged to the workspace they
2//! worked for.
3//!
4//! A workspace buys credit and each agent run deducts what it cost, plus
5//! g1t's margin. With no credit, no agent starts. Money is held in
6//! millionths of a US dollar, so that a run costing a fraction of a cent is
7//! recorded exactly.
8//!
9//! Each `*Args` struct is the argument of the method of the same name,
10//! served at `POST /rpc/<method>`.
11
12use serde::{Deserialize, Serialize};
13
14use crate::repos::RepoPath;
15use crate::{User, Viewer};
16
17/// Millionths of a US dollar in one dollar.
18pub const MICROS_PER_DOLLAR: i64 = 1_000_000;
19
20/// Whether workspaces are charged for agents at all, and with real money.
21/// `status` takes nothing and returns this.
22#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
23pub struct Status {
24 /// False when no payment provider is configured: nothing is charged,
25 /// and who may run agents is decided some other way.
26 pub enabled: bool,
27 /// False while the payment provider is in its test mode, where cards
28 /// are not real.
29 pub live: bool,
Free while g1t is being built out; agents can check out their own forks30 /// True while g1t is being built out: runs are recorded, with what
31 /// they cost, but nothing is charged and no credit is needed. Not a
32 /// promise that it stays free.
33 #[serde(default)]
34 pub free: bool,
Agents as a team: lifecycle, merge queue, billing and a new shell35}
36
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put37/// `trial`: a workspace's trial credit, so people can try g1t (its agents on
38/// g1t's hosted models among it) without a key or a card of their own. Each
39/// new workspace gets one grant of usage credit (`TRIAL_WORKSPACE_MICROS`),
40/// made when it first uses something, out of a pool for everyone that
41/// resets each calendar month (`TRIAL_MONTHLY_POOL_MICROS`). When this
42/// month's pool is given out, new grants wait for the next month. Returns
43/// `Trial`.
A free allowance on g1t's models, so anyone can try its agents44#[derive(Debug, Serialize, Deserialize)]
45#[serde(rename_all = "camelCase")]
46pub struct TrialArgs {
47 pub workspace: String,
48 /// Workspaces open to hosted models anyway, whose use is not counted
49 /// against the pool.
50 #[serde(default)]
51 pub exempt: Vec<String>,
52}
53
54#[derive(Clone, Debug, Serialize, Deserialize)]
55#[serde(rename_all = "camelCase")]
56pub struct Trial {
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put57 /// Whether its agents may use g1t's hosted models on the trial now: it
58 /// has credit left, or this month's pool can still grant it some.
A free allowance on g1t's models, so anyone can try its agents59 pub open: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put60 /// What the trial has paid for so far, in millionths of a dollar.
A free allowance on g1t's models, so anyone can try its agents61 pub used_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put62 /// Its grant, or what it would be granted.
A free allowance on g1t's models, so anyone can try its agents63 pub limit_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put64 /// No longer used: the trial does not end on a date. Kept for older
65 /// readers; always null.
A free allowance on g1t's models, so anyone can try its agents66 pub ends_at: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put67 /// Why it is closed: `off` (no trials), `used` (this workspace's grant
68 /// is spent) or `pool` (this month's grants are all given out; see
69 /// `waits_until`). `ended` is no longer sent.
A free allowance on g1t's models, so anyone can try its agents70 pub reason: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put71 /// Whether the workspace has its grant already.
72 #[serde(default)]
73 pub granted: bool,
74 /// RFC 3339: when a workspace waiting for a grant can get one, the
75 /// first of next month. Only with reason `pool`.
76 #[serde(default)]
77 pub waits_until: Option<String>,
A free allowance on g1t's models, so anyone can try its agents78}
79
Agents as a team: lifecycle, merge queue, billing and a new shell80/// A workspace's standing.
81#[derive(Clone, Debug, Serialize, Deserialize)]
82#[serde(rename_all = "camelCase")]
83pub struct Account {
84 pub workspace: String,
85 /// Credit left, in millionths of a dollar. Can dip below zero by the
86 /// cost of the runs that were under way when it ran out.
87 pub balance_micros: i64,
88 pub status: Status,
89 /// What is added to a run's cost, in percent.
90 pub margin_percent: u32,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace91 /// The card g1t charges as the workspace nears its limit and when a
92 /// month closes, if one is on file.
93 #[serde(default)]
94 pub card: Option<Card>,
Agents as a team: lifecycle, merge queue, billing and a new shell95}
96
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace97/// A saved card, as far as it is safe to show.
98#[derive(Clone, Debug, Serialize, Deserialize)]
99#[serde(rename_all = "camelCase")]
100pub struct Card {
101 /// `visa`, `mastercard`, ...
102 pub brand: String,
103 pub last4: String,
104 pub exp_month: u32,
105 pub exp_year: u32,
106}
107
108/// `billing_portal`: Stripe's hosted billing page for the workspace, where
109/// an owner adds or replaces the card, sees invoices and receipts, and sets
110/// the billing email and address. g1t never handles card numbers. Owners
111/// only. Returns `Outcome<Checkout>` (its `url`); Stripe sends them back
112/// to `return_url`.
113#[derive(Debug, Serialize, Deserialize)]
114pub struct BillingPortalArgs {
115 pub actor: User,
116 pub workspace: String,
117 pub return_url: String,
118}
119
120/// `admin_billing_link`: for staff to send a customer: their Stripe billing
121/// page. Returns `Outcome<BillingLink>`.
122#[derive(Debug, Serialize, Deserialize)]
123pub struct AdminBillingLinkArgs {
124 pub workspace: String,
125 pub by: String,
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
129#[serde(rename_all = "camelCase")]
130pub struct BillingLink {
131 /// A one-time session on Stripe's billing page, signed in already.
132 pub portal_url: String,
133 /// The billing page's sign-in page, which does not expire: the
134 /// customer signs in with the email Stripe has for them.
135 pub login_url: Option<String>,
136 pub customer_email: Option<String>,
137 pub expires_note: String,
138}
139
Agents as a team: lifecycle, merge queue, billing and a new shell140#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
141#[serde(rename_all = "snake_case")]
142pub enum EntryKind {
143 /// Credit bought with a card.
144 TopUp,
Paid features: a workspace turns on Deployments with a monthly plan145 /// An agent's run, or a paid feature's usage past its allowance.
Agents as a team: lifecycle, merge queue, billing and a new shell146 Usage,
147}
148
149/// One line of a workspace's statement.
150#[derive(Clone, Debug, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct LedgerEntry {
153 pub id: String,
154 pub kind: EntryKind,
155 /// Positive for credit added, negative for usage.
156 pub amount_micros: i64,
157 pub description: String,
158 /// For usage: the repository and pull request the agent worked on.
159 pub repo: Option<String>,
160 pub number: Option<u32>,
161 /// For usage: `implement`, `review` or `update`.
162 pub task: Option<String>,
163 /// For usage: the model, by its public name.
164 pub model: Option<String>,
Integrations: your own model provider, alerts that open issues, tickets agents read165 /// For usage: `g1t` when g1t paid the model provider, `workspace` when
Prices are what g1t pays plus 20%, from the first second166 /// the workspace's own account did. Runs on the workspace's own
167 /// provider pay only their sandbox time now, so only older entries
168 /// are `workspace`.
Integrations: your own model provider, alerts that open issues, tickets agents read169 #[serde(default = "g1t")]
170 pub billed_to: String,
Agents as a team: lifecycle, merge queue, billing and a new shell171 /// For a top-up: the username of whoever paid.
172 pub created_by: Option<String>,
173 /// RFC 3339.
174 pub created_at: String,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace175 /// The workspace the line belongs to, which tells an enterprise's
176 /// lines apart.
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub workspace: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look179 /// For usage: what the g1t plan's monthly included usage paid of it.
180 /// The entry's `amount_micros` is what is left to pay.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put181 #[serde(default)]
182 pub credit_micros: i64,
183 /// For usage: what the workspace's trial credit paid of it.
184 #[serde(default)]
185 pub trial_micros: i64,
186 /// For usage: what g1t's open-source pool paid of it.
187 #[serde(default)]
188 pub oss_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look189 /// For usage: what g1t covered itself, such as the part of a free
190 /// workspace's last trial run that went past its trial credit.
191 #[serde(default)]
192 pub given_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging193 /// For usage: what the account's discount took off its price. The
194 /// price is `-amount_micros` plus this and what paid for it.
195 #[serde(default)]
196 pub discount_micros: i64,
197 /// For a credit from g1t, and for what of one expired or was revoked:
198 /// its kind.
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub credit_kind: Option<CreditKind>,
Agents as a team: lifecycle, merge queue, billing and a new shell201}
202
Integrations: your own model provider, alerts that open issues, tickets agents read203fn g1t() -> String {
204 "g1t".to_owned()
205}
206
Agents as a team: lifecycle, merge queue, billing and a new shell207/// `account` (`Outcome<Account>`) and `ledger` (`Outcome<Vec<LedgerEntry>>`,
208/// newest first). Members of the workspace only.
209#[derive(Debug, Serialize, Deserialize)]
210pub struct AccountArgs {
211 pub workspace: String,
212 pub viewer: Viewer,
213}
214
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look215/// `checkout`: prepays usage: money paid in advance, drawn down by usage
216/// after the plan's included usage, which raises what can be used before
217/// work stops by the same amount at once. $25 at the least. By card, with
218/// 3-D Secure; from $1,000 also by bank transfer. Owners of the workspace
Agents as a team: lifecycle, merge queue, billing and a new shell219/// only. Returns `Outcome<Checkout>`.
220#[derive(Debug, Serialize, Deserialize)]
221#[serde(rename_all = "camelCase")]
222pub struct CheckoutArgs {
223 pub actor: User,
224 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look225 /// How much to prepay, in cents.
Agents as a team: lifecycle, merge queue, billing and a new shell226 pub amount_cents: u32,
227 /// Where the payment page sends the person afterwards. The payment's
228 /// id is appended as `session`.
229 pub return_url: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look230 /// `card` (the default) or `bank_transfer` (from $1,000): Stripe gives
231 /// the account details, and the money counts once it arrives.
232 #[serde(default)]
233 pub method: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell234}
235
236#[derive(Debug, Serialize, Deserialize)]
237pub struct Checkout {
238 /// The payment page to send the person to.
239 pub url: String,
240}
241
242/// `confirm`: credits a payment once the provider says it was made. Safe
243/// to call any number of times. Returns `Outcome<Account>`.
244#[derive(Debug, Serialize, Deserialize)]
245pub struct ConfirmArgs {
246 pub workspace: String,
247 pub viewer: Viewer,
248 /// The payment's id, as returned to `return_url`.
249 pub session: String,
250}
251
252/// `can_start`: whether a workspace may start an agent now, asked before
253/// anything is opened for it. Returns `Outcome<bool>`: a failure, with the
254/// reason to show, when it has no credit.
255#[derive(Debug, Serialize, Deserialize)]
256pub struct CanStartArgs {
257 pub workspace: String,
258}
259
260/// `start_run`: asks whether a workspace may start an agent, and opens the
261/// run it will be charged for. Called by the runner service. Returns
262/// `Outcome<Option<RunTicket>>`: no ticket when billing is off, a failure
263/// when the workspace has no credit.
264#[derive(Debug, Serialize, Deserialize)]
265pub struct StartRunArgs {
266 pub workspace: String,
267 pub repo: RepoPath,
268 pub number: u32,
269 /// `implement`, `review` or `update`.
270 pub task: String,
271 /// The model, by its public name.
272 pub model: String,
Integrations: your own model provider, alerts that open issues, tickets agents read273 /// `workspace` when the run uses the workspace's own model provider.
Models per workspace: several providers, routed by kind of work274 /// The runner, which is TypeScript, sends it as `billedTo`.
275 #[serde(default = "g1t", alias = "billedTo")]
Integrations: your own model provider, alerts that open issues, tickets agents read276 pub billed_to: String,
Prices keep themselves current with what g1t pays277 /// The model session's id, when its requests go through g1t's AI
278 /// Gateway: settling charges the run what the gateway priced them at.
279 #[serde(default)]
280 pub session: Option<String>,
Auto model routing: the cheapest tier that can do each piece of work, a retry goes up a tier, and each run records its tier281 /// `small` or `large`: the tier g1t routed the run to, when g1t pays
282 /// for its model. None on the workspace's own provider.
283 #[serde(default)]
284 pub tier: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell285}
286
287#[derive(Clone, Debug, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase")]
289pub struct RunTicket {
290 pub run_id: String,
291 /// Lets the sandbox, and nothing else, report what this run cost.
292 pub token: String,
293}
294
295/// `finish_run`: what a run cost, as its sandbox reports it. Charged once.
296/// Returns `Outcome<bool>`.
297#[derive(Debug, Serialize, Deserialize)]
298#[serde(rename_all = "camelCase")]
299pub struct FinishRunArgs {
300 pub run_id: String,
301 pub token: String,
302 /// What the model provider charged, in US dollars.
303 pub cost_usd: f64,
304 #[serde(default)]
305 pub turns: u32,
306}
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request307
308
309/// `usage`: what a workspace's agents cost over a period, broken down.
310/// Members only. Returns `Outcome<Usage>`.
311#[derive(Debug, Serialize, Deserialize)]
312pub struct UsageArgs {
313 pub workspace: String,
314 pub viewer: Viewer,
315 /// RFC 3339: the start of the period. The period runs to now.
316 pub since: String,
317}
318
319/// One slice of usage: what it was for, what it cost, how many runs.
320#[derive(Clone, Debug, Serialize, Deserialize)]
321#[serde(rename_all = "camelCase")]
322pub struct UsageSlice {
323 pub key: String,
324 pub micros: i64,
325 pub runs: u32,
326}
327
328/// What a workspace's agents cost over a period.
329#[derive(Clone, Debug, Serialize, Deserialize)]
330#[serde(rename_all = "camelCase")]
331pub struct Usage {
332 pub since: String,
333 /// Charged, including g1t's margin.
334 pub spent_micros: i64,
Billing and Usage reconcile: own-provider runs leave Billing's at-price total, and Usage's not-charged part is at price less charged335 /// What g1t's usage came to at price, less what was charged: the plan's
336 /// included usage, the trial, a pool or a free period paid it. Usage at
337 /// price is `spent_micros` plus this.
Billing's usage total is labeled at price, and Usage says what part of it was paid for338 #[serde(default)]
339 pub covered_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging340 /// What the account's discount took off the price. Usage at price is
341 /// `spent_micros` plus `covered_micros` plus this.
342 #[serde(default)]
343 pub discount_micros: i64,
344 /// The account's discount now, in percent; absent without one. With
345 /// one, the slices measure usage at price.
346 #[serde(default)]
347 pub discount_percent: Option<u32>,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit348 /// Usage at price: `spent_micros` plus `covered_micros` plus
349 /// `discount_micros`, from the same ledger lines. The one figure every
350 /// page shows as usage (mission control, the agent fleet, Usage and
351 /// Billing), labelled "usage at price".
352 #[serde(default)]
353 pub price_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read354 /// What g1t's model provider charged, before the margin.
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request355 pub cost_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read356 /// What runs on the workspace's own provider cost there, as the harness
357 /// estimated it. Not charged by g1t.
358 pub provider_micros: i64,
Usage while free is shown at cost; agents get rustfmt and clippy359 /// What the runs used, at cost: g1t's models and the workspace's own
360 /// provider together, whatever was charged for them.
361 pub used_micros: i64,
362 /// g1t charges nothing for now. The slices then measure usage at cost,
363 /// since every charge is zero.
364 pub free: bool,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request365 pub runs: u32,
366 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
367 pub by_day: Vec<UsageSlice>,
368 /// Per task: implement, review, revise, update, plan.
369 pub by_task: Vec<UsageSlice>,
370 /// Per repository, `namespace/name`.
371 pub by_repo: Vec<UsageSlice>,
372 /// The pull requests that cost most, as `namespace/name#number`.
373 pub by_pull: Vec<UsageSlice>,
374 /// Per model, by its public name.
375 pub by_model: Vec<UsageSlice>,
376 /// Credit bought in the period.
377 pub added_micros: i64,
378}
Models per workspace: several providers, routed by kind of work379
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix380/// `record_tokens`: what one model answer used, added to the day's count
381/// for its run. The model proxy sends it after each answer. For usage
382/// views only: runs are still priced from AI Gateway. Returns
383/// `Outcome<bool>`: false when there was nothing to count.
384#[derive(Debug, Serialize, Deserialize)]
385#[serde(rename_all = "camelCase")]
386pub struct RecordTokensArgs {
387 pub workspace: String,
388 /// The model session's id (`ModelSession::id`), one per run.
389 pub session: String,
390 /// The person the run is for, by username. Absent when nobody asked.
391 #[serde(default)]
392 pub person: Option<String>,
393 pub model: String,
394 /// On g1t's hosted models: `small` or `large`.
395 #[serde(default)]
396 pub tier: Option<String>,
397 #[serde(default)]
398 pub input: u64,
399 #[serde(default)]
400 pub output: u64,
401 #[serde(default)]
402 pub cache_read: u64,
403 #[serde(default)]
404 pub cache_write: u64,
405}
406
407/// `token_usage`: the model tokens a workspace's runs used, day by day,
408/// for the whole workspace or for one person. Members only; a member may
409/// ask only for themselves, an owner for anyone. Returns
410/// `Outcome<TokenUsage>`.
411#[derive(Debug, Serialize, Deserialize)]
412pub struct TokenUsageArgs {
413 pub workspace: String,
414 pub viewer: Viewer,
415 /// A username: only the runs for them.
416 #[serde(default)]
417 pub person: Option<String>,
418 /// How many days, to today: 42 when absent, 366 at most.
419 #[serde(default)]
420 pub days: Option<u32>,
421}
422
423/// One day's tokens.
424#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
425pub struct DayTokens {
426 /// `YYYY-MM-DD`, UTC.
427 pub day: String,
428 pub tokens: u64,
429}
430
431/// The model tokens runs used over a window of days.
432#[derive(Clone, Debug, Serialize, Deserialize)]
433#[serde(rename_all = "camelCase")]
434pub struct TokenUsage {
435 /// `YYYY-MM-DD`: the first day counted.
436 pub since: String,
437 pub days: u32,
438 /// Null for the whole workspace.
439 pub person: Option<String>,
440 pub total_tokens: u64,
441 pub input_tokens: u64,
442 pub output_tokens: u64,
443 pub cache_read_tokens: u64,
444 pub cache_write_tokens: u64,
445 /// What those runs were charged, as `usage` measures it.
446 pub cost_micros: i64,
447 /// Days in the window with any tokens.
448 pub active_days: u32,
449 /// Every day in the window, oldest first, zeros included.
450 pub by_day: Vec<DayTokens>,
451}
452
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look453/// What a workspace pays a monthly price for. There is one plan, `plan`
454/// ("g1t"): a flat price per workspace, never per person, with included
455/// usage each month, more private storage, and deployments. Never free:
456/// `FREE_WHILE_BUILDING` does not cover it.
457///
458/// `deployments` is not sold on its own any more: it comes with the plan.
459/// A service that asks `has_feature` for it is told whether the workspace
460/// has the plan, and a Deployments subscription bought before the change
461/// keeps working until its period ends.
Paid features: a workspace turns on Deployments with a monthly plan462#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
463#[serde(rename_all = "snake_case")]
464pub enum Feature {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look465 /// The g1t plan. Older readers called it `team`.
466 #[serde(alias = "team")]
467 Plan,
468 /// Previews per pull request and production on g1t.page: part of the
469 /// plan.
Paid features: a workspace turns on Deployments with a monthly plan470 Deployments,
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar471 /// The Security and quality activation: the security suite's paid
472 /// features on private repositories, for a monthly price per workspace
473 /// from the price book (`security_activation`). Sold on its own; it
474 /// does not need the plan, and the plan does not include it.
475 Security,
Paid features: a workspace turns on Deployments with a monthly plan476}
477
478impl Feature {
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar479 /// What is sold: the plan, and the Security and quality activation.
480 pub const ALL: [Feature; 2] = [Feature::Plan, Feature::Security];
Paid features: a workspace turns on Deployments with a monthly plan481
482 pub fn as_str(self) -> &'static str {
483 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look484 Feature::Plan => "plan",
Paid features: a workspace turns on Deployments with a monthly plan485 Feature::Deployments => "deployments",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar486 Feature::Security => "security",
Paid features: a workspace turns on Deployments with a monthly plan487 }
488 }
489
490 pub fn parse(name: &str) -> Option<Feature> {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look491 match name {
492 "plan" | "team" => Some(Feature::Plan),
493 "deployments" => Some(Feature::Deployments),
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar494 "security" => Some(Feature::Security),
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look495 _ => None,
496 }
Paid features: a workspace turns on Deployments with a monthly plan497 }
498
499 pub fn title(self) -> &'static str {
500 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look501 Feature::Plan => "g1t",
Paid features: a workspace turns on Deployments with a monthly plan502 Feature::Deployments => "Deployments",
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar503 Feature::Security => "Security and quality",
Paid features: a workspace turns on Deployments with a monthly plan504 }
505 }
506}
507
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas508/// What deployments cost g1t, in millionths of a dollar: fallbacks for
509/// when billing's price book cannot be read. Nothing here is an allowance:
510/// on the plan every unit is metered from the first, at cost plus the
511/// margin, and drawn from the plan's included usage before anything is
512/// charged. Projects, previews and the apps behind them are not metered at
513/// all: Cloudflare's Workers for Platforms includes far more scripts than
514/// g1t runs, so an app costs g1t only the requests and CPU it answers with.
515pub mod deployment_costs {
516 /// Workers for Platforms: $0.30 per million requests.
Paid features: a workspace turns on Deployments with a monthly plan517 pub const MICROS_PER_MILLION_REQUESTS: i64 = 300_000;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas518 /// $0.02 per million CPU milliseconds.
Paid features: a workspace turns on Deployments with a monthly plan519 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
Deployments: a preview for every pull request, production on g1t.page520 /// What one second of a build's sandbox costs g1t (Cloudflare
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look521 /// Containers, standard-1: half a vCPU, 4 GiB, 8 GB disk), rounded up,
522 /// as the price keeper measured it on 2026-10-05 (14.5). Only a
523 /// fallback: billing charges builds at the price book's `build_second`,
524 /// which the keeper keeps current.
525 pub const MICROS_PER_BUILD_SECOND: i64 = 15;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas526 /// What one custom hostname costs g1t a month (Cloudflare for SaaS):
527 /// $0.10.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains528 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
Paid features: a workspace turns on Deployments with a monthly plan529}
530
Every sandbox is metered by the second531/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
Prices are what g1t pays plus 20%, from the first second532/// the runner when it stops. Every sandbox g1t starts for a workspace
533/// (agents, reviews, checks, the merge queue, workflow jobs) is metered by
534/// the second, from the first: recorded once per `reference`, with what it
535/// cost g1t, and charged at the price book's `sandbox_second` price unless
536/// `FREE_WHILE_BUILDING`. Deploy builds are charged by the Deployments plan
537/// instead.
Every sandbox is metered by the second538/// Returns `Outcome<bool>`: false if that reference was recorded before.
539#[derive(Debug, Serialize, Deserialize)]
540#[serde(rename_all = "camelCase")]
541pub struct RecordSandboxArgs {
542 pub workspace: String,
543 pub seconds: u32,
544 /// What ran, e.g. `Checks on acme/api#12`.
545 pub description: String,
546 /// `namespace/name`.
547 pub repo: Option<String>,
548 /// Unique to the run.
549 pub reference: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look550 /// What ran: `agent`, `check`, `workflow` or `queue`. Decides whether
551 /// g1t's open-source pool may pay for it (checks, workflows and the
552 /// merge queue on public repositories). Absent: not the pool.
553 #[serde(default)]
554 pub kind: Option<ComputeKind>,
555 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
556 /// run is priced on its own CPU (`sandbox_base_second` per second plus
557 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
558 /// (`sandbox_second`).
559 #[serde(default, alias = "cpu_seconds")]
560 pub cpu_seconds: Option<f64>,
561 /// The reservation the work started under, settled with this cost.
562 #[serde(default, alias = "reservation_id")]
563 pub reservation_id: Option<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents564 /// It ran on one of the workspace's self-hosted runners: recorded as
565 /// self-hosted time, for the minutes, at $0.
566 #[serde(default, alias = "self_hosted")]
567 pub self_hosted: bool,
568 /// The machine it ran on, by label (`g1t-4core`); absent, the standard
569 /// one. A larger machine's memory and disk cost more each second.
570 #[serde(default)]
571 pub instance: Option<String>,
Every sandbox is metered by the second572}
573
Usage limits: unpaid usage can only go so far574/// How much a workspace has earned g1t's trust with money, which sets how
575/// far its unpaid usage can go before its work stops.
576#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
577#[serde(rename_all = "snake_case")]
578pub enum Trust {
579 /// No live payment yet: only a little past the free allowances.
580 New,
581 /// Has paid g1t real money: the ceiling grows with what it has paid.
582 Paid,
Two limits, real invoices, trust that grows by itself, sales signals583 /// Has paid steadily for months, with nothing disputed or declined:
584 /// the ceiling follows its monthly spend, up to $10,000, by itself.
585 Established,
Usage limits: unpaid usage can only go so far586 /// A ceiling g1t set by hand, after talking to the workspace.
587 Reviewed,
588 /// g1t's own workspaces: no ceiling.
589 Internal,
590}
591
592#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
593#[serde(rename_all = "snake_case")]
594pub enum LimitState {
595 Ok,
596 /// Past 80% of the ceiling.
597 Warning,
598 /// At or past it: no new sandboxes, builds or app requests.
599 Stopped,
600}
601
602/// How far a workspace's unpaid usage has gone this month, and where its
603/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
604/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
605/// or what it is charged, whichever is more, so it counts while g1t is
606/// free too.
607#[derive(Clone, Debug, Serialize, Deserialize)]
608#[serde(rename_all = "camelCase")]
609pub struct Limit {
610 pub workspace: String,
Billing accounts, terms and enterprises; g1t is no longer free611 /// The account that pays, whose usage and payments the limit counts:
612 /// the workspace's own, or its enterprise's.
613 #[serde(default)]
614 pub account: String,
615 #[serde(default)]
616 pub account_name: String,
Usage limits: unpaid usage can only go so far617 pub trust: Trust,
618 /// Usage this month (UTC) less what was paid this month.
619 pub exposure_micros: i64,
620 /// Where work stops: the lower of g1t's ceiling and the owner's own
621 /// spend limit. None for g1t's own workspaces.
622 pub ceiling_micros: Option<i64>,
623 /// The ceiling g1t sets from `trust`.
624 pub trust_ceiling_micros: Option<i64>,
625 /// The owner's own monthly limit, if they set one.
626 pub spend_limit_micros: Option<i64>,
627 pub state: LimitState,
628 /// What to tell people when work is stopped or close to it.
629 pub message: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals630 /// Charged this month, which the spend limit is measured against.
631 #[serde(default)]
632 pub spent_micros: i64,
633 /// True while the owners have not chosen a spend limit of their own, so
634 /// the automatic one applies: $200, or twice last month's spend.
635 #[serde(default)]
636 pub default_spend_limit: bool,
637 /// The most the owners may set their own limit to: g1t's ceiling. To
638 /// go past it, they contact g1t.
639 #[serde(default)]
640 pub available_micros: Option<i64>,
641 /// How the ceiling grows from here, in a sentence.
642 #[serde(default)]
643 pub growth: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look644 /// Money paid in advance and not used yet. It raises what can be used
645 /// before work stops by the same amount, at once.
646 #[serde(default)]
647 pub prepaid_micros: i64,
648 /// The highest ceiling the workspace has ever had. Owners may set their
649 /// spend limit anywhere up to it (plus what is prepaid) without asking.
650 #[serde(default)]
651 pub max_ceiling_micros: Option<i64>,
652 /// The most the owners may raise the limit to themselves, once, with
653 /// `raise_once`: twice the highest ceiling. None once it is used.
654 #[serde(default)]
655 pub raise_once_micros: Option<i64>,
656 /// When the one-time raise was used, RFC 3339.
657 #[serde(default)]
658 pub raised_at: Option<String>,
659 /// True in a paid workspace's first billing cycle, when the ceiling is
660 /// the starting one (`LIMIT_PAID_START_MICROS`).
661 #[serde(default)]
662 pub first_month: bool,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit663 /// The budget's alerts, in percent of the spend limit: some of 50, 75,
664 /// 90 and 100. Each is emailed to the owners once a month.
665 #[serde(default)]
666 pub alert_levels: Vec<u32>,
667 /// Whether usage pauses at the spend limit (the default). Off, the
668 /// limit only alerts; g1t's own ceiling still applies.
669 #[serde(default = "yes")]
670 pub pause_at_limit: bool,
671 /// An HTTPS address told of each budget alert with a JSON POST.
672 #[serde(default)]
673 pub budget_webhook: Option<String>,
Usage limits: unpaid usage can only go so far674}
675
Usage, Billing settings and prepaid AI credit; fixes from the UX audit676fn yes() -> bool {
677 true
678}
679
Usage limits: unpaid usage can only go so far680/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
681#[derive(Debug, Serialize, Deserialize)]
682pub struct LimitArgs {
683 pub workspace: String,
684 pub viewer: Viewer,
685}
686
687/// `check_limit`: the same, for the services that enforce it. Returns
688/// `Outcome<Limit>`.
689#[derive(Debug, Serialize, Deserialize)]
690pub struct CheckLimitArgs {
691 pub workspace: String,
692}
693
Prices keep themselves current with what g1t pays694/// `note_pending`: usage this month that will be charged later, such as
695/// app traffic past a plan, so the workspace's limit counts it now. Each
696/// report replaces the last for that workspace, source and month. Called
697/// by the service that meters it. Returns `bool`.
698#[derive(Debug, Serialize, Deserialize)]
699#[serde(rename_all = "camelCase")]
700pub struct NotePendingArgs {
701 pub workspace: String,
Fast pages, required checks on the branch, self-hosted runners, honest incidents702 /// `deployments`, `security` (scans), `context` (search embeddings),
703 /// `storage` or `cache` (actions/cache, plan only). Billing charges
704 /// `security`, `context`, `storage` and `cache` itself once the month
705 /// is over; `deployments` charges its own.
Prices keep themselves current with what g1t pays706 pub source: String,
707 /// What it cost g1t so far this month, before the margin.
708 pub cost_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas709 /// How much of it, for the Billing page: `1.2 million requests and
710 /// 3.4 million CPU ms`, `2 custom domains`.
711 #[serde(default)]
712 pub detail: Option<String>,
Prices keep themselves current with what g1t pays713}
714
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas715/// `usage_meters`: this month's usage for a workspace, one line per kind
716/// of meter, at what it is charged (cost plus the margin, on the account's
717/// terms) before the plan's included usage, the trial or g1t's pools paid
718/// for any of it. Members only. Returns `Outcome<Vec<MeterUsage>>`.
719#[derive(Debug, Serialize, Deserialize)]
720pub struct UsageMetersArgs {
721 pub workspace: String,
722 pub viewer: Viewer,
723}
724
725/// One kind of meter's usage this month.
726#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
727#[serde(rename_all = "camelCase")]
728pub struct MeterUsage {
729 /// `agents` (agent runs, models and sandboxes for checks, workflows
730 /// and the merge queue), `builds`, `requests` (app requests and CPU),
731 /// `domains`, `git_storage` (git operations and private storage) or
732 /// `search_scans` (search embeddings and security scans).
733 pub key: String,
734 pub label: String,
735 /// At price, before what paid for it.
736 pub micros: i64,
737 /// How much, when it is known: `12 runs`, `41 build minutes`.
738 #[serde(default)]
739 pub quantity: Option<String>,
740}
741
Usage limits: unpaid usage can only go so far742/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
743/// removes it. Owners only. Returns `Outcome<Limit>`.
744#[derive(Debug, Serialize, Deserialize)]
745#[serde(rename_all = "camelCase")]
746pub struct SetSpendLimitArgs {
747 pub actor: User,
748 pub workspace: String,
Two limits, real invoices, trust that grows by itself, sales signals749 /// A monthly limit, at most what is available; None goes back to the
750 /// default.
Usage limits: unpaid usage can only go so far751 pub spend_limit_micros: Option<i64>,
Two limits, real invoices, trust that grows by itself, sales signals752 /// Use everything available, with no limit of their own.
753 #[serde(default)]
754 pub use_full_limit: bool,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look755 /// Use the one-time raise: up to twice the highest ceiling the
756 /// workspace has had, without asking. Once per workspace.
757 #[serde(default, alias = "raiseOnce")]
758 pub raise_once: bool,
Usage limits: unpaid usage can only go so far759}
760
Prices keep themselves current with what g1t pays761/// One metered unit: what it costs g1t, and what it is sold at. The price
762/// is always `cost × (100 + markup) / 100`, so it follows the cost.
763#[derive(Clone, Debug, Serialize, Deserialize)]
764#[serde(rename_all = "camelCase")]
765pub struct Price {
766 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
767 pub meter: String,
768 pub title: String,
769 pub unit: String,
770 /// Millionths of a dollar per unit; may have a fraction.
771 pub cost_micros: f64,
772 pub markup_percent: u32,
773 pub price_micros: f64,
774 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
775 /// actually billed g1t, measured.
776 pub source: String,
777 /// When it was last checked against Cloudflare's bill.
778 pub checked_at: Option<String>,
779 pub updated_at: String,
780}
781
782impl Price {
783 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
784 cost_micros * f64::from(100 + markup_percent) / 100.0
785 }
786}
787
788/// A cost that moved.
789#[derive(Clone, Debug, Serialize, Deserialize)]
790#[serde(rename_all = "camelCase")]
791pub struct PriceChange {
792 pub meter: String,
793 pub old_cost_micros: f64,
794 pub new_cost_micros: f64,
795 pub markup_percent: u32,
Prices are what g1t pays plus 20%, from the first second796 /// The markup before, when the change was to the markup rather than
797 /// to the cost. Absent when the markup stayed `markup_percent`.
798 #[serde(default, skip_serializing_if = "Option::is_none")]
799 pub old_markup_percent: Option<u32>,
Prices keep themselves current with what g1t pays800 pub reason: String,
801 pub created_at: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily802 /// When a change still to come takes effect: a rise is announced
803 /// before it is charged. Absent for changes already made.
804 #[serde(default, skip_serializing_if = "Option::is_none")]
805 pub effective_at: Option<String>,
Prices keep themselves current with what g1t pays806}
807
808/// `prices`: every metered price and the recent changes. Public. Returns
809/// `PriceBook`.
810#[derive(Clone, Debug, Serialize, Deserialize)]
811#[serde(rename_all = "camelCase")]
812pub struct PriceBook {
813 pub prices: Vec<Price>,
814 pub changes: Vec<PriceChange>,
815 /// The margin on model usage, which is charged at what AI Gateway
816 /// priced each request at.
817 pub model_margin_percent: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put818 /// Every plan, as it is sold now.
819 #[serde(default)]
820 pub plans: Vec<Plan>,
821 /// What is free, and what pays for it.
822 #[serde(default)]
823 pub free: Option<FreeTier>,
Prices keep themselves current with what g1t pays824}
825
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put826/// What g1t gives without a plan, each with what pays for it: a capped
827/// budget, never an open-ended allowance.
828#[derive(Clone, Debug, Default, Serialize, Deserialize)]
829#[serde(rename_all = "camelCase")]
830pub struct FreeTier {
831 /// Each new workspace's trial credit, once.
832 pub trial_workspace_micros: i64,
833 /// Trial grants each month, in all; new trials wait when it is spent.
834 pub trial_monthly_pool_micros: i64,
835 /// g1t's open-source pool each month, and any one repository's share.
836 pub oss_pool_micros: i64,
837 pub oss_repo_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas838 /// Private repository storage that is free for every workspace. Past
839 /// it, the plan pays at cost plus the margin; a free workspace's pushes
840 /// to private repositories stop instead.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put841 pub free_private_storage_bytes: i64,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo842 /// Days of audit log a free workspace keeps.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put843 pub audit_retention_days: u32,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo844 /// Days of audit log the g1t plan keeps, and g1t's own and enterprise
845 /// workspaces. Longer is by arrangement, set per account in sudo.
846 #[serde(default)]
847 pub plan_audit_retention_days: u32,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look848 /// The smallest amount a card is charged when a month closes; less
849 /// carries over. Charges at a limit always go through.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put850 pub min_charge_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas851 /// Git operations (clones, fetches and pushes through g1t) that are
852 /// free for every workspace each month. Past it, the plan pays at cost
853 /// plus the margin and is never slowed; a free workspace is slowed
854 /// down, never charged.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look855 #[serde(default)]
856 pub git_operations_included: u64,
857 /// A new paid workspace's ceiling in its first month.
858 #[serde(default)]
859 pub paid_start_ceiling_micros: i64,
860 /// The most a one-click goodwill credit can cost g1t.
861 #[serde(default)]
862 pub overage_forgive_cost_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put863}
864
Billing accounts, terms and enterprises; g1t is no longer free865/// Who pays: a billing account. Every workspace has one; by default its
866/// own. An enterprise account pays for several workspaces at once, as
867/// GitHub Enterprise does: one bill, one limit, one set of terms.
868#[derive(Clone, Debug, Serialize, Deserialize)]
869#[serde(rename_all = "camelCase")]
870pub struct BillingAccount {
871 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
872 pub id: String,
873 pub kind: AccountKind,
874 pub name: String,
875 pub terms: Terms,
876 /// The workspaces it pays for.
877 pub workspaces: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both878 /// Where an enterprise's invoices go.
879 #[serde(default)]
880 pub billing_email: Option<String>,
881 /// An enterprise's invoices, newest first. Empty for a workspace's own.
882 #[serde(default)]
883 pub invoices: Vec<EnterpriseInvoice>,
Billing accounts, terms and enterprises; g1t is no longer free884 pub created_at: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put885 /// What g1t staff set for the account beyond its terms.
886 #[serde(default)]
887 pub allowances: Allowances,
888}
889
890/// Set per account by g1t staff in sudo, on top of its terms.
891#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
892#[serde(rename_all = "camelCase")]
893pub struct Allowances {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look894 /// The g1t plan without paying for its monthly price, such as for a
895 /// partner. Usage is charged as usual. Comped accounts have it anyway.
896 #[serde(default, alias = "team")]
897 pub plan: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put898 /// Each of the account's public repositories' monthly cap on g1t's
899 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
900 #[serde(default)]
901 pub oss_repo_micros: Option<i64>,
902 /// The trial credit each of its workspaces gets, in place of
903 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
904 #[serde(default)]
905 pub trial_micros: Option<i64>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look906 /// Agents at once, in place of the plan's (2 in the first month or on
907 /// the trial, then 10). None: the default.
908 #[serde(default)]
909 pub max_concurrent_agents: Option<u32>,
910 /// One run's spend cap, in place of `RUN_CAP_MICROS` and the owners'
911 /// own. None: theirs, or the default.
912 #[serde(default)]
913 pub run_cap_micros: Option<i64>,
914 /// What the agents on one issue may spend in all, in place of
915 /// `ISSUE_CAP_MICROS` and the owners' own. None: theirs, or the default.
916 #[serde(default)]
917 pub issue_cap_micros: Option<i64>,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo918 /// Days of audit log its workspaces keep, in place of the plan's (7
919 /// free, 90 on the plan), longer or shorter. None: the plan's.
920 #[serde(default)]
921 pub audit_retention_days: Option<u32>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look922 /// A hold g1t staff put on new compute, with why. None: no hold.
923 #[serde(default)]
924 pub hold: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put925}
926
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look927/// `admin_set_allowances`: the plan on or off without charge, overrides of
928/// the plan's caps, a hold, and the account's share of g1t's pools.
929/// Recorded with who and why. Returns `Outcome<BillingAccount>`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put930#[derive(Debug, Serialize, Deserialize)]
931pub struct AdminSetAllowancesArgs {
932 pub id: String,
933 pub allowances: Allowances,
934 pub note: String,
935 pub by: String,
936}
937
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look938// --- Entitlements, and compute started under a reservation -----------------
939//
940// Every service that starts compute (sandboxes for agents, checks,
941// workflows and the merge queue; builds; models; semantic search) asks
942// billing first:
943//
944// 1. `entitlements { workspace }` says what the workspace may do at all:
945// its plan, whether it may start compute, its caps, and whether compute
946// is paused.
947// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
948// work's estimated cost against what may pay for it, so that starts at
949// the same moment cannot overshoot the ceiling together. It answers who
950// pays first, or refuses with a stable code and a message for the owner.
951// 3. `settle { reservation_id, actual_micros }` releases the hold once the
952// work is done. The charge itself goes on the ledger the usual way
953// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
954//
955// A reservation never settled expires after `RESERVATION_HOURS`.
956
957/// A reservation that is never settled stops holding after this long.
958pub const RESERVATION_HOURS: u64 = 3;
959/// What a ceiling reads as when there is none (g1t's own workspaces): a
960/// billion dollars, which JavaScript holds exactly.
961pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
962
963/// What a workspace pays g1t on, as far as compute is concerned.
964#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
965#[serde(rename_all = "snake_case")]
966pub enum PlanKind {
967 /// No plan: the forge is free; compute only from a trial or g1t's
968 /// open-source pool, after a card check.
969 Free,
970 /// The g1t plan, paid for (or given by g1t staff without its price).
971 Paid,
972 /// g1t's own workspaces and Flagon's (comped terms): the plan without
973 /// being charged. Usage is still recorded at what it cost.
974 Internal,
975 /// Paid for by an enterprise account, invoiced.
976 Enterprise,
977}
978
979impl PlanKind {
980 pub fn as_str(self) -> &'static str {
981 match self {
982 PlanKind::Free => "free",
983 PlanKind::Paid => "paid",
984 PlanKind::Internal => "internal",
985 PlanKind::Enterprise => "enterprise",
986 }
987 }
988
989 /// Whether usage past what is included may be charged (on demand).
990 pub fn on_demand(self) -> bool {
991 !matches!(self, PlanKind::Free)
992 }
993}
994
995/// What compute is for.
996#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
997#[serde(rename_all = "snake_case")]
998pub enum ComputeKind {
999 /// An agent's run: its sandbox and its model.
1000 Agent,
1001 /// Checks on a pull request.
1002 Check,
1003 /// A workflow job.
1004 Workflow,
1005 /// The merge queue's checks.
1006 Queue,
1007 /// A deployment's build.
1008 Deploy,
1009 /// Semantic search: embeddings in the context hub.
1010 Embedding,
1011}
1012
1013impl ComputeKind {
1014 pub fn as_str(self) -> &'static str {
1015 match self {
1016 ComputeKind::Agent => "agent",
1017 ComputeKind::Check => "check",
1018 ComputeKind::Workflow => "workflow",
1019 ComputeKind::Queue => "queue",
1020 ComputeKind::Deploy => "deploy",
1021 ComputeKind::Embedding => "embedding",
1022 }
1023 }
1024
1025 /// Whether g1t's open-source pool may pay for it on a public
1026 /// repository: checks, workflows and the merge queue only.
1027 pub fn open_source_pool(self) -> bool {
1028 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
1029 }
1030}
1031
1032/// Who pays first for reserved work. What the first source cannot cover
1033/// falls to the next, in this order.
1034#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1035#[serde(rename_all = "snake_case")]
1036pub enum PaidBy {
1037 /// The plan's included usage this month.
1038 Credit,
1039 /// The workspace's one-time trial credit.
1040 Trial,
1041 /// g1t's open-source pool.
1042 Oss,
1043 /// Charged to the workspace, at cost plus the margin.
1044 OnDemand,
1045}
1046
1047/// `entitlements`: what a workspace may do now, for the services that
1048/// start compute and the pages that show it. Takes `EntitlementsArgs`;
1049/// returns `Entitlements`. No viewer: callers decide who sees it.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1050#[derive(Debug, Serialize, Deserialize)]
1051pub struct EntitlementsArgs {
1052 pub workspace: String,
1053}
1054
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1055/// `audit_retention`: how many days of audit log each workspace keeps, for
1056/// the events service's daily purge. Takes `AuditRetentionArgs`; returns
1057/// `Vec<AuditRetention>`, one for each workspace asked about.
1058#[derive(Debug, Serialize, Deserialize)]
1059pub struct AuditRetentionArgs {
1060 pub workspaces: Vec<String>,
1061}
1062
1063#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1064pub struct AuditRetention {
1065 pub workspace: String,
1066 pub days: u32,
1067}
1068
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1069#[derive(Clone, Debug, Serialize, Deserialize)]
1070#[serde(rename_all = "camelCase")]
1071pub struct Entitlements {
1072 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1073 pub plan: PlanKind,
1074 /// May start sandboxes, models, deployments and semantic search at all:
1075 /// paid, internal and enterprise workspaces, or a free one with trial
1076 /// credit left. A free workspace may still use the open-source pool
1077 /// for checks, workflows and the merge queue on public repositories
1078 /// after a card check; `reserve` decides that per start.
1079 pub compute: bool,
1080 /// The one-time trial credit left; 0 if none was granted or it is used.
1081 pub trial_micros_left: i64,
1082 /// A card check has been done. The trial and the open-source pool need
1083 /// it.
1084 pub trial_verified: bool,
1085 /// A paid workspace still in its first billing cycle.
1086 pub first_month: bool,
1087 /// Agents at once: 2 in the first month or on the trial, 10 after;
1088 /// staff can override it.
1089 pub max_concurrent_agents: u32,
1090 /// The longest one run may take: 60 minutes in the first month or on
1091 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
1092 pub max_run_minutes: u32,
1093 /// One run's spend cap (`RUN_CAP_MICROS`, $2 by default); staff can
1094 /// override it.
1095 pub run_cap_micros: i64,
1096 /// What agents may spend on one issue in all (`ISSUE_CAP_MICROS`, $10
1097 /// by default); the owners can set it (`set_caps`), and staff override.
1098 pub issue_cap_micros: i64,
1099 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
1100 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
1101 /// which has no on-demand usage.
1102 pub ceiling_micros: i64,
1103 /// Usage not yet paid for this month, with prepayment taken off.
1104 pub exposure_micros: i64,
1105 /// Why new compute is paused, for the owner: the limit is reached, a
1106 /// spend spike is waiting for an owner to confirm it, or g1t staff put
1107 /// a hold on it. None when it is not.
1108 pub paused: Option<String>,
1109 // What the workspace's plan gives it, for its pages.
1110 /// What open reservations hold now.
1111 #[serde(default)]
1112 pub held_micros: i64,
1113 /// Paid in advance and not used yet.
1114 #[serde(default)]
1115 pub prepaid_micros: i64,
1116 /// The plan's included usage each month, and what of it is used.
1117 #[serde(default)]
1118 pub included_micros: i64,
1119 #[serde(default)]
1120 pub included_used_micros: i64,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1121 /// How far back the audit log can be read and exported, and what is
1122 /// kept: the plan's days, or what g1t staff set for the account.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1123 pub audit_retention_days: u32,
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1124 /// Whether `audit_retention_days` is what staff set for the account
1125 /// rather than the plan's.
1126 #[serde(default)]
1127 pub audit_retention_custom: bool,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1128 /// Private repository storage that is free for every workspace: past
1129 /// it, the plan pays for it and a free workspace's pushes stop.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1130 pub free_private_storage_bytes: i64,
1131 /// The last daily measure of the workspace's private repositories.
1132 pub private_storage_bytes: i64,
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member1133 /// On a paid plan (not Free): storage past the free amounts below is
1134 /// charged, so nothing is refused for it.
1135 #[serde(default)]
1136 pub has_plan: bool,
1137 /// Package storage free for every workspace, public and private: past
1138 /// it, the plan pays for it and a free workspace's pushes are refused.
1139 #[serde(default)]
1140 pub package_public_free_bytes: i64,
1141 #[serde(default)]
1142 pub package_private_free_bytes: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1143 /// What g1t's open-source pool paid for the workspace this month.
1144 pub oss_paid_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1145 /// Deploy build time this month, every second of it metered.
1146 #[serde(default)]
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1147 pub build_seconds_used: u32,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1148 /// Git operations this month, and how many are free for every
1149 /// workspace (past it: metered on the plan, slowed when free).
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1150 #[serde(default)]
1151 pub git_operations: u64,
1152 #[serde(default)]
1153 pub git_operations_included: u64,
1154 /// The smallest amount a card is charged when a month closes.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1155 pub min_charge_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1156 /// A spend spike waiting for an owner, or decided.
1157 #[serde(default)]
1158 pub spike: Option<Spike>,
1159 /// Where usage stands against what is included and the limits, from 50%.
1160 #[serde(default)]
1161 pub alerts: Vec<UsageAlert>,
1162}
1163
1164/// One level reached: 50, 75, 90 or 100 percent of something.
1165#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1166#[serde(rename_all = "camelCase")]
1167pub struct UsageAlert {
1168 /// `included` (the plan's included usage), `spend_limit` (the owners'
1169 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
1170 pub meter: String,
1171 pub level: u32,
1172 pub used_micros: i64,
1173 pub limit_micros: i64,
1174 pub message: String,
Billing accounts, terms and enterprises; g1t is no longer free1175}
1176
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1177/// An hour's spend well above the workspace's usual pace: new compute
1178/// waits until an owner says to keep going.
1179#[derive(Clone, Debug, Serialize, Deserialize)]
1180#[serde(rename_all = "camelCase")]
1181pub struct Spike {
1182 pub id: String,
1183 /// `open` (waiting for an owner), `continued` (an owner said keep
1184 /// going) or `stopped` (an owner said stop).
1185 pub status: String,
1186 /// The hour's spend when it was found, and the usual hour's.
1187 pub hour_micros: i64,
1188 pub average_micros: i64,
1189 pub detected_at: String,
1190 #[serde(default)]
1191 pub decided_by: Option<String>,
1192 #[serde(default)]
1193 pub decided_at: Option<String>,
1194 /// While continued: until when, unless spend doubles again first.
1195 #[serde(default)]
1196 pub until: Option<String>,
1197}
1198
1199/// `reserve`: holds an estimate of a start's cost before the work starts.
1200/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1201///
1202/// - `paused`: a spend spike waiting for an owner, or a hold.
1203/// - `limit`: the spend limit or g1t's ceiling would be passed.
1204/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1205/// no card check yet).
1206/// - `trial_used`: the one-time trial is spent.
1207/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1208/// it, is spent this month.
1209///
1210/// The message says exactly what to do, with the page to do it on (such as
1211/// `/acme/-/billing`).
1212#[derive(Debug, Serialize, Deserialize)]
1213#[serde(rename_all = "camelCase")]
1214pub struct ReserveArgs {
1215 pub workspace: String,
1216 pub repo: RepoPath,
1217 /// Whether the repository is public: the open-source pool pays only for
1218 /// public repositories' checks, workflows and merge queue.
1219 pub public: bool,
1220 pub kind: ComputeKind,
1221 /// The most the work is expected to cost g1t, before the margin, in
1222 /// millionths of a dollar (billing adds the margin, as it does to every
1223 /// charge). For an agent, its model's average plus its sandbox for its
1224 /// whole time cap.
1225 #[serde(alias = "estimate_micros")]
1226 pub estimate_micros: i64,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1227 /// An agent run on g1t's hosted models (not the workspace's own
1228 /// provider). Unsaid, an agent run is taken to be one. g1t's daily
1229 /// spend breaker pauses these when g1t is paying for them.
1230 #[serde(default, alias = "hosted_model")]
1231 pub hosted_model: Option<bool>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1232}
1233
1234#[derive(Clone, Debug, Serialize, Deserialize)]
1235#[serde(rename_all = "camelCase")]
1236pub struct Reservation {
1237 pub id: String,
1238 pub paid_by: PaidBy,
1239 /// What is held, at cost; less than the estimate when a free
1240 /// workspace's last bit of trial credit is all there is.
1241 #[serde(default)]
1242 pub held_micros: i64,
1243 /// RFC 3339: when the hold lapses if never settled.
1244 #[serde(default)]
1245 pub expires_at: String,
1246}
1247
1248/// `settle`: releases a reservation's hold with what the work cost. The
1249/// charge goes on the ledger the usual way. Safe to repeat. Returns
1250/// `Outcome<bool>`: false if it was settled or had lapsed before.
1251#[derive(Debug, Serialize, Deserialize)]
1252#[serde(rename_all = "camelCase")]
1253pub struct SettleArgs {
1254 #[serde(alias = "reservation_id")]
1255 pub reservation_id: String,
1256 /// What the work cost g1t, before the margin.
1257 #[serde(alias = "actual_micros")]
1258 pub actual_micros: i64,
1259}
1260
1261// --- Card checks, the plan, prepayment -------------------------------------
1262
1263/// `card_check`: starts Stripe's page to save and verify a card: a setup
1264/// with 3-D Secure where the card supports it, which the card's bank sees
1265/// as a $0 or $1 authorization that is never charged. The trial and the
1266/// open-source pool need it, and it is the card the plan uses. Owners only.
1267/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1268/// `session`, for `confirm_card_check`.
1269#[derive(Debug, Serialize, Deserialize)]
1270#[serde(rename_all = "camelCase")]
1271pub struct CardCheckArgs {
1272 pub actor: User,
1273 pub workspace: String,
1274 #[serde(alias = "return_url")]
1275 pub return_url: String,
1276}
1277
1278/// `confirm_card_check`: records the check once Stripe says the card was
1279/// verified, and grants the trial if the month's pool has room and the card
1280/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1281#[derive(Debug, Serialize, Deserialize)]
1282pub struct ConfirmCardCheckArgs {
1283 pub workspace: String,
1284 pub viewer: Viewer,
1285 pub session: String,
1286}
1287
1288// --- Limits: raising them, and spikes ---------------------------------------
1289
1290/// A request to g1t: a higher limit, or help with usage that went past
1291/// what was meant.
1292#[derive(Clone, Debug, Serialize, Deserialize)]
1293#[serde(rename_all = "camelCase")]
1294pub struct LimitRequest {
1295 pub id: String,
1296 pub workspace: String,
1297 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1298 pub kind: String,
1299 /// The limit asked for; for an overage, what they think went wrong.
1300 pub amount_micros: i64,
1301 pub reason: String,
1302 pub expected_monthly_micros: i64,
1303 /// `open`, `approved` or `declined`.
1304 pub status: String,
1305 /// What was approved, which may differ from what was asked.
1306 #[serde(default)]
1307 pub decided_micros: Option<i64>,
1308 #[serde(default)]
1309 pub decided_by: Option<String>,
1310 /// The answer, as the owner sees it.
1311 #[serde(default)]
1312 pub answer: Option<String>,
1313 pub created_by: String,
1314 pub created_at: String,
1315 #[serde(default)]
1316 pub decided_at: Option<String>,
1317}
1318
1319/// `request_limit`: an owner asks g1t for more, or for help with usage past
1320/// what they meant. Answered within one business day, in the app and by
1321/// email. Owners only. Returns `Outcome<LimitRequest>`.
1322#[derive(Debug, Serialize, Deserialize)]
1323#[serde(rename_all = "camelCase")]
1324pub struct RequestLimitArgs {
1325 pub actor: User,
1326 pub workspace: String,
1327 /// `limit` or `overage`.
1328 pub kind: String,
1329 #[serde(alias = "amount_micros")]
1330 pub amount_micros: i64,
1331 pub reason: String,
1332 #[serde(default, alias = "expected_monthly_micros")]
1333 pub expected_monthly_micros: i64,
1334}
1335
1336/// `limit_requests`: a workspace's requests, newest first. Members only.
1337/// Returns `Outcome<Vec<LimitRequest>>`.
1338#[derive(Debug, Serialize, Deserialize)]
1339pub struct LimitRequestsArgs {
1340 pub workspace: String,
1341 pub viewer: Viewer,
1342}
1343
1344/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1345/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1346/// new compute paused until an owner says to keep going. Owners only.
1347/// Returns `Outcome<Entitlements>`.
1348#[derive(Debug, Serialize, Deserialize)]
1349#[serde(rename_all = "camelCase")]
1350pub struct ConfirmSpikeArgs {
1351 pub actor: User,
1352 pub workspace: String,
1353 #[serde(alias = "keep_going")]
1354 pub keep_going: bool,
1355}
1356
1357/// `set_caps`: the owners' own caps on agents: one run's spend ($0.10 to
1358/// $100) and what the agents on one issue may spend in all ($1 to $1,000).
1359/// None goes back to the default ($2 and $10). A cap g1t staff set for the
1360/// account wins over both. Owners only. Returns `Outcome<Entitlements>`.
1361#[derive(Debug, Serialize, Deserialize)]
1362#[serde(rename_all = "camelCase")]
1363pub struct SetCapsArgs {
1364 pub actor: User,
1365 pub workspace: String,
1366 #[serde(default, alias = "run_cap_micros")]
1367 pub run_cap_micros: Option<i64>,
1368 #[serde(default, alias = "issue_cap_micros")]
1369 pub issue_cap_micros: Option<i64>,
1370}
1371
1372/// What staff see beside a request: the workspace's history with g1t.
1373#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1374#[serde(rename_all = "camelCase")]
1375pub struct WorkspaceHistory {
1376 pub plan: Option<PlanKind>,
1377 /// The last six months, oldest first.
1378 pub months: Vec<MonthFigures>,
1379 /// Live payments that have cleared, and how many.
1380 pub paid_cleared_micros: i64,
1381 pub payments: u32,
1382 pub disputes: u32,
1383 pub declines: u32,
1384 /// The first time the workspace appears in billing, RFC 3339.
1385 pub first_seen: Option<String>,
1386 pub ceiling_micros: Option<i64>,
1387 pub max_ceiling_micros: Option<i64>,
1388 pub spend_limit_micros: Option<i64>,
1389 /// Recent velocity: the last hour, the usual hour over the last week,
1390 /// and the last 24 hours, at price.
1391 pub last_hour_micros: i64,
1392 pub average_hour_micros: i64,
1393 pub last_day_micros: i64,
1394}
1395
1396#[derive(Clone, Debug, Serialize, Deserialize)]
1397#[serde(rename_all = "camelCase")]
1398pub struct LimitRequestReview {
1399 pub request: LimitRequest,
1400 pub history: WorkspaceHistory,
1401}
1402
1403/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1404/// `Vec<LimitRequestReview>`.
1405#[derive(Debug, Default, Serialize, Deserialize)]
1406pub struct AdminLimitRequestsArgs {
1407 /// `open` (the default), `approved`, `declined` or `all`.
1408 #[serde(default)]
1409 pub status: Option<String>,
1410}
1411
1412/// `admin_decide_limit_request`: approve (at the amount asked, or
1413/// `amount_micros`) or decline. The owner is told in the app and by email.
1414/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1415#[derive(Debug, Serialize, Deserialize)]
1416pub struct AdminDecideLimitRequestArgs {
1417 pub id: String,
1418 /// `approve` or `decline`.
1419 pub decision: String,
1420 #[serde(default)]
1421 pub amount_micros: Option<i64>,
1422 /// What the owner is told, beside the decision.
1423 #[serde(default)]
1424 pub note: String,
1425 pub by: String,
1426}
1427
1428/// `admin_record_payment`: money that reached g1t outside the card pages,
1429/// such as a bank transfer, entered as a payment (it raises the limit like
1430/// one). Recorded with who and the transfer's reference. Returns
1431/// `Outcome<LedgerEntry>`.
1432#[derive(Debug, Serialize, Deserialize)]
1433pub struct AdminRecordPaymentArgs {
1434 pub workspace: String,
1435 pub amount_micros: i64,
1436 /// The bank's reference for the transfer, or Stripe's payment id.
1437 pub reference: String,
1438 pub note: String,
1439 pub by: String,
1440}
1441
1442// --- Overages and goodwill (sudo) --------------------------------------------
1443
1444/// What a one-time goodwill credit would come to: g1t's margin on the
1445/// overage, always, plus as much of its underlying cost as the cap allows.
1446#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1447#[serde(rename_all = "camelCase")]
1448pub struct Goodwill {
1449 /// This month's charges above the workspace's typical month.
1450 pub overage_micros: i64,
1451 /// The part of the overage that is g1t's margin.
1452 pub margin_micros: i64,
1453 /// The part that is what g1t paid its providers.
1454 pub cost_micros: i64,
1455 /// The one-click credit: the margin plus the cost up to the cap.
1456 pub credit_micros: i64,
1457 /// Of the credit, the real cost g1t absorbs.
1458 pub absorbed_micros: i64,
1459}
1460
1461/// A workspace whose month went well past its usual, or hit a spike.
1462#[derive(Clone, Debug, Serialize, Deserialize)]
1463#[serde(rename_all = "camelCase")]
1464pub struct Overage {
1465 pub workspace: String,
1466 pub plan: PlanKind,
1467 /// The median of its last three months' charges.
1468 pub typical_month_micros: i64,
1469 pub this_month_micros: i64,
1470 /// What this month cost g1t, and what g1t keeps of it.
1471 pub cost_micros: i64,
1472 pub margin_micros: i64,
1473 /// A spike this month, if there was one.
1474 pub spike: Option<Spike>,
1475 /// The runs that cost the most this month.
1476 pub top_entries: Vec<LedgerEntry>,
1477 pub goodwill: Goodwill,
1478 /// False when a goodwill credit was given in the last 12 months.
1479 pub goodwill_available: bool,
1480 pub last_goodwill_at: Option<String>,
1481 /// An open overage request from the owner, if there is one.
1482 pub request: Option<LimitRequest>,
1483}
1484
1485/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
1486#[derive(Debug, Default, Serialize, Deserialize)]
1487pub struct AdminOveragesArgs {}
1488
1489/// `admin_goodwill`: credits a workspace for accidental usage. With no
1490/// amount, the one-click credit (`Goodwill::credit_micros`), once per
1491/// workspace in 12 months. A larger amount, or a second within 12 months,
1492/// needs a typed reason. It shows on the statement as "Credit from g1t:
1493/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
1494#[derive(Debug, Serialize, Deserialize)]
1495pub struct AdminGoodwillArgs {
1496 pub workspace: String,
1497 #[serde(default)]
1498 pub amount_micros: Option<i64>,
1499 /// Why, typed by staff; needed past the one-click credit.
1500 #[serde(default)]
1501 pub reason: String,
1502 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
1503 #[serde(default)]
1504 pub day: Option<String>,
1505 pub by: String,
1506}
1507
1508/// One workspace's recent pace, for sudo's velocity view.
1509#[derive(Clone, Debug, Serialize, Deserialize)]
1510#[serde(rename_all = "camelCase")]
1511pub struct Velocity {
1512 pub workspace: String,
1513 pub plan: PlanKind,
1514 pub last_hour_micros: i64,
1515 pub average_hour_micros: i64,
1516 pub last_day_micros: i64,
1517 pub this_month_micros: i64,
1518 /// The last hour over the usual hour; 0 with no history.
1519 pub ratio: f64,
1520 pub spike: Option<Spike>,
1521 pub first_seen: Option<String>,
1522}
1523
1524/// `admin_velocity`: workspaces spending in the last day, fastest first.
1525/// Returns `Vec<Velocity>`.
1526#[derive(Debug, Default, Serialize, Deserialize)]
1527pub struct AdminVelocityArgs {}
1528
Billing accounts, terms and enterprises; g1t is no longer free1529#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1530#[serde(rename_all = "snake_case")]
1531pub enum AccountKind {
1532 Workspace,
1533 Enterprise,
1534}
1535
1536/// How an account is charged. Standard unless g1t set otherwise in sudo.
1537#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1538#[serde(rename_all = "camelCase")]
1539pub struct Terms {
1540 pub kind: TermsKind,
1541 /// Off every usage charge, in percent. Custom terms only.
1542 #[serde(default)]
1543 pub discount_percent: u32,
1544 /// A ceiling on unpaid usage that replaces the one trust would give.
1545 #[serde(default)]
1546 pub ceiling_micros: Option<i64>,
1547 /// Why, for whoever looks next.
1548 #[serde(default)]
1549 pub note: String,
1550 /// When the terms end and the account goes back to standard.
1551 #[serde(default)]
1552 pub until: Option<String>,
1553 #[serde(default)]
1554 pub set_by: Option<String>,
1555 #[serde(default)]
1556 pub set_at: Option<String>,
1557}
1558
1559impl Terms {
1560 pub fn standard() -> Self {
1561 Terms {
1562 kind: TermsKind::Standard,
1563 discount_percent: 0,
1564 ceiling_micros: None,
1565 note: String::new(),
1566 until: None,
1567 set_by: None,
1568 set_at: None,
1569 }
1570 }
1571
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1572 /// The discount in percent, 0 to 100. Terms from before discounts
1573 /// replaced "comped" read as 100%.
1574 pub fn percent_off(&self) -> u32 {
1575 match self.kind {
1576 TermsKind::Comped => 100,
1577 TermsKind::Custom => self.discount_percent.min(100),
1578 TermsKind::Standard => 0,
1579 }
1580 }
1581
1582 /// A 100% discount: nothing is charged, usage is recorded at its price
1583 /// and discounted in full. g1t's own workspaces and partners. Paid
1584 /// features are on without a plan, and g1t's own spend on it is held to
1585 /// a monthly budget (the terms' ceiling, at cost).
1586 pub fn full_discount(&self) -> bool {
1587 self.percent_off() >= 100
1588 }
1589
Billing accounts, terms and enterprises; g1t is no longer free1590 /// What a charge becomes under these terms.
1591 pub fn apply(&self, charge_micros: i64) -> i64 {
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1592 charge_micros * i64::from(100 - self.percent_off()) / 100
Billing accounts, terms and enterprises; g1t is no longer free1593 }
Merge branch 'worktree-agent-a633ac0f7f66d419d'1594
1595 /// What a charge at cost plus the margin becomes under these terms, and
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1596 /// what the discount took off it (`ledger.discount_micros`), so the
1597 /// statement shows the usage at its price and the discount beside it,
1598 /// and a discount below cost plus the margin is counted as given, never
1599 /// lost. A 100% discount takes it all.
Merge branch 'worktree-agent-a633ac0f7f66d419d'1600 pub fn discounted(&self, charge_micros: i64) -> (i64, i64) {
1601 let charged = self.apply(charge_micros);
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1602 (charged, (charge_micros - charged).max(0))
1603 }
1604
1605 /// How the statement and sudo name the terms: `100% discount`, `30% off`.
1606 pub fn discount_label(&self) -> Option<String> {
1607 match self.percent_off() {
1608 0 => None,
1609 100 => Some("100% discount".to_owned()),
1610 percent => Some(format!("{percent}% off")),
Merge branch 'worktree-agent-a633ac0f7f66d419d'1611 }
1612 }
Billing accounts, terms and enterprises; g1t is no longer free1613}
1614
1615#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1616#[serde(rename_all = "snake_case")]
1617pub enum TermsKind {
1618 /// Prices as published, limits by trust.
1619 Standard,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1620 /// Before discounts: what a 100% discount is now. Read as one
1621 /// (`Terms::percent_off`); billing never writes it (migration 0039).
Billing accounts, terms and enterprises; g1t is no longer free1622 Comped,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1623 /// A discount (up to 100%), a ceiling, or both.
Billing accounts, terms and enterprises; g1t is no longer free1624 Custom,
1625}
1626
Stripe webhooks, enterprise invoices, and sudo for both1627/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
1628/// body and its `Stripe-Signature` header. Billing checks the signature
1629/// against the secret of the endpoint it registered, and handles each
1630/// event once. Returns `Outcome<bool>`: false for one already handled.
1631#[derive(Debug, Serialize, Deserialize)]
1632pub struct StripeWebhookArgs {
1633 pub payload: String,
1634 pub signature: String,
1635}
1636
1637/// `admin_stripe`: where billing stands with Stripe. Staff only. Returns
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1638/// `StripeStatus`. With `fix: true`, first enables the destination at
1639/// billing's address and gives it the events billing needs.
Stripe webhooks, enterprise invoices, and sudo for both1640#[derive(Debug, Default, Serialize, Deserialize)]
1641pub struct AdminStripeArgs {
1642 #[serde(default)]
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1643 pub fix: bool,
Stripe webhooks, enterprise invoices, and sudo for both1644 #[serde(default)]
1645 pub by: Option<String>,
1646}
1647
1648#[derive(Clone, Debug, Serialize, Deserialize)]
1649#[serde(rename_all = "camelCase")]
1650pub struct StripeStatus {
1651 /// `test` or `live`, from the key; `off` without one.
1652 pub mode: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1653 /// Whether `STRIPE_WEBHOOK_SECRET` is set, so events can be checked.
1654 pub secret_set: bool,
1655 /// The destination at billing's address in Stripe, as Stripe has it.
Stripe webhooks, enterprise invoices, and sudo for both1656 pub webhook: Option<StripeWebhook>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1657 /// Events billing handles that the destination does not send.
1658 pub missing_events: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both1659 /// The latest events handled, newest first.
1660 pub recent_events: Vec<StripeEventSummary>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1661 /// What went wrong reading or fixing the destination, if it did.
Stripe webhooks, enterprise invoices, and sudo for both1662 pub error: Option<String>,
1663}
1664
1665#[derive(Clone, Debug, Serialize, Deserialize)]
1666#[serde(rename_all = "camelCase")]
1667pub struct StripeWebhook {
1668 pub url: String,
1669 pub endpoint_id: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1670 /// `enabled` or `disabled`.
1671 pub status: String,
Stripe webhooks, enterprise invoices, and sudo for both1672 pub events: Vec<String>,
1673 pub created_at: String,
1674}
1675
1676#[derive(Clone, Debug, Serialize, Deserialize)]
1677#[serde(rename_all = "camelCase")]
1678pub struct StripeEventSummary {
1679 pub id: String,
1680 pub kind: String,
1681 pub outcome: String,
1682 pub received_at: String,
1683}
1684
1685/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
1686/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
1687#[derive(Debug, Serialize, Deserialize)]
1688pub struct AdminEnterpriseBillingArgs {
1689 pub id: String,
1690 pub email: String,
1691 pub by: String,
1692}
1693
1694/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
1695/// what its workspaces owe, rather than waiting for the month to close.
1696/// Returns `Outcome<EnterpriseInvoice>`.
1697#[derive(Debug, Serialize, Deserialize)]
1698pub struct AdminInvoiceEnterpriseArgs {
1699 pub id: String,
1700 pub by: String,
1701}
1702
1703/// An enterprise's invoice: one line per workspace, paid on Stripe.
1704#[derive(Clone, Debug, Serialize, Deserialize)]
1705#[serde(rename_all = "camelCase")]
1706pub struct EnterpriseInvoice {
1707 pub invoice_id: String,
1708 /// Stripe's page for it, where it is paid.
1709 pub hosted_url: Option<String>,
1710 pub amount_micros: i64,
1711 /// `open`, `paid`, `overdue` or `void`.
1712 pub status: String,
1713 pub period: String,
1714 pub lines: Vec<InvoiceLine>,
1715 pub created_at: String,
1716}
1717
1718#[derive(Clone, Debug, Serialize, Deserialize)]
1719#[serde(rename_all = "camelCase")]
1720pub struct InvoiceLine {
1721 pub workspace: String,
1722 pub amount_micros: i64,
1723}
1724
Two limits, real invoices, trust that grows by itself, sales signals1725/// A workspace's invoice from g1t: one per month, and one each time it is
1726/// charged near its limit. Itemised, charged to the card on file, and kept
1727/// in Stripe's billing page with its PDF.
1728#[derive(Clone, Debug, Serialize, Deserialize)]
1729#[serde(rename_all = "camelCase")]
1730pub struct WorkspaceInvoice {
1731 pub invoice_id: String,
1732 pub workspace: String,
1733 /// `month` (2026-10) or `threshold`.
1734 pub reason: String,
1735 pub period: String,
1736 pub amount_micros: i64,
1737 /// `paid`, `open`, `failed` or `void`.
1738 pub status: String,
1739 pub hosted_url: Option<String>,
1740 pub pdf_url: Option<String>,
1741 pub lines: Vec<InvoiceItem>,
1742 pub created_at: String,
1743}
1744
1745#[derive(Clone, Debug, Serialize, Deserialize)]
1746#[serde(rename_all = "camelCase")]
1747pub struct InvoiceItem {
1748 pub description: String,
1749 pub amount_micros: i64,
1750}
1751
1752/// `invoices`: a workspace's invoices from g1t, newest first. Members
1753/// only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
1754#[derive(Debug, Serialize, Deserialize)]
1755pub struct InvoicesArgs {
1756 pub workspace: String,
1757 pub viewer: Viewer,
1758}
1759
1760/// `admin_workspace_invoices`: the same, for staff. Returns
1761/// `Vec<WorkspaceInvoice>`.
1762#[derive(Debug, Serialize, Deserialize)]
1763pub struct AdminWorkspaceInvoicesArgs {
1764 pub workspace: String,
1765}
1766
The statement is a month at a time, a line per kind of charge1767/// `statement`: a month of a workspace's ledger, grouped by day (or by
1768/// project) with a line per kind of charge. Members only. Returns
1769/// `Outcome<Statement>`.
1770#[derive(Debug, Serialize, Deserialize)]
1771pub struct StatementArgs {
1772 pub workspace: String,
1773 pub viewer: Viewer,
1774 /// YYYY-MM; this month when absent.
1775 #[serde(default)]
1776 pub month: Option<String>,
1777 /// `day` (the default) or `project`.
1778 #[serde(default)]
1779 pub group: Option<String>,
1780}
1781
1782#[derive(Clone, Debug, Serialize, Deserialize)]
1783#[serde(rename_all = "camelCase")]
1784pub struct Statement {
1785 pub month: String,
1786 /// Months with any entries, newest first.
1787 pub months: Vec<String>,
1788 pub groups: Vec<StatementGroup>,
1789 pub totals: StatementTotals,
1790}
1791
1792#[derive(Clone, Debug, Serialize, Deserialize)]
1793#[serde(rename_all = "camelCase")]
1794pub struct StatementGroup {
1795 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
1796 pub key: String,
1797 pub label: String,
1798 pub lines: Vec<StatementLine>,
1799 /// What the group's charges come to.
1800 pub charged_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1801 /// Its usage at price, and what the discount took off it.
1802 #[serde(default)]
1803 pub price_micros: i64,
1804 #[serde(default)]
1805 pub discount_micros: i64,
The statement is a month at a time, a line per kind of charge1806}
1807
1808#[derive(Clone, Debug, Serialize, Deserialize)]
1809#[serde(rename_all = "camelCase")]
1810pub struct StatementLine {
1811 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
Prices are what g1t pays plus 20%, from the first second1812 /// Refunds, and, for older entries, Runs on your own model provider.
The statement is a month at a time, a line per kind of charge1813 pub kind: String,
1814 pub count: u32,
1815 /// Charges positive; money in (payments, credits) negative.
1816 pub charged_micros: i64,
1817 pub cost_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1818 /// Of the usage on the line, what was paid for before it was charged:
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1819 /// by the plan's included usage, the trial credit, g1t's open-source
1820 /// pool, or g1t itself. Not in `charged_micros`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1821 #[serde(default)]
1822 pub covered_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1823 /// Usage at its price: charged, plus what paid for it and what the
1824 /// discount took off. Zero for money in.
1825 #[serde(default)]
1826 pub price_micros: i64,
1827 /// What the account's discount took off the line's price.
1828 #[serde(default)]
1829 pub discount_micros: i64,
The statement is a month at a time, a line per kind of charge1830}
1831
1832#[derive(Clone, Debug, Serialize, Deserialize)]
1833#[serde(rename_all = "camelCase")]
1834pub struct StatementTotals {
1835 pub charged_micros: i64,
1836 pub paid_micros: i64,
1837 pub cost_micros: i64,
1838 pub entries: u32,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging1839 /// Usage at price, and what the discount took off it: charged is the
1840 /// price less the discount and what paid for it.
1841 #[serde(default)]
1842 pub price_micros: i64,
1843 #[serde(default)]
1844 pub discount_micros: i64,
1845 /// The account's discount now, in percent; absent without one.
1846 #[serde(default)]
1847 pub discount_percent: Option<u32>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1848 /// What paid for usage before it was charged, one line per source,
1849 /// such as "Paid by g1t's open-source pool".
1850 #[serde(default)]
1851 pub covered: Vec<Covered>,
1852 /// Owed when the month closed but under the minimum charge, so it
1853 /// carries over to the next invoice. Zero when nothing carried.
1854 #[serde(default)]
1855 pub carried_micros: i64,
1856}
1857
1858/// One source that paid for usage before it was charged.
1859#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1860#[serde(rename_all = "camelCase")]
1861pub struct Covered {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1862 /// `included`, `trial`, `oss_pool` or `given`.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1863 pub source: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1864 /// "Paid by your plan's included usage", "Paid by your trial credit",
1865 /// "Paid by g1t's open-source pool", "Covered by g1t".
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1866 pub label: String,
1867 pub micros: i64,
The statement is a month at a time, a line per kind of charge1868}
1869
1870/// `statement_entries`: one statement line's entries, newest first, 50 at
1871/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
1872#[derive(Debug, Serialize, Deserialize)]
1873pub struct StatementEntriesArgs {
1874 pub workspace: String,
1875 pub viewer: Viewer,
1876 pub month: String,
1877 pub kind: String,
1878 #[serde(default)]
1879 pub day: Option<String>,
1880 #[serde(default)]
1881 pub project: Option<String>,
1882 #[serde(default)]
1883 pub before: Option<String>,
1884}
1885
Two limits, real invoices, trust that grows by itself, sales signals1886// --- Sales (sudo.g1t.sh) ------------------------------------------------------
1887//
1888// What staff need to know to reach out: who is growing, who is close to
1889// their limit, who was declined, who has become a steady customer. And what
1890// was done about it: a stage, an owner on g1t's side, a next step, notes.
1891
1892/// Why a workspace is worth a look.
1893#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1894#[serde(rename_all = "snake_case")]
1895pub enum SignalKind {
1896 /// At its limit, or its own spend limit: work is stopped.
1897 AtLimit,
1898 /// Past 80% of what is available to it: about to need more.
1899 NearCeiling,
1900 /// Its card was declined or a payment disputed.
1901 Declined,
1902 /// This month is well ahead of last month.
1903 Growing,
1904 /// Became Established: the ceiling now follows its spend.
1905 Established,
1906 /// Paid g1t for the first time.
1907 FirstPayment,
1908 /// Spending enough that custom terms or an enterprise may suit it.
1909 HighSpend,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1910 /// Costs g1t more on Cloudflare than it pays, over 30 days: a pricing
1911 /// gap or abuse to look at (billing's `margin`).
1912 CostOverRevenue,
Two limits, real invoices, trust that grows by itself, sales signals1913}
1914
1915#[derive(Clone, Debug, Serialize, Deserialize)]
1916#[serde(rename_all = "camelCase")]
1917pub struct Signal {
1918 pub workspace: String,
1919 pub kind: SignalKind,
1920 /// One sentence, with the figures.
1921 pub detail: String,
1922 /// The figure that matters, such as this month's spend.
1923 pub value_micros: i64,
1924 /// Its sales stage, if staff gave it one.
1925 pub stage: Option<String>,
1926 pub owner: Option<String>,
Billing lists every invoice and every staff change; signals carry follow-ups1927 #[serde(default)]
1928 pub next_step: Option<String>,
1929 /// When the next step is due, `YYYY-MM-DD`.
1930 #[serde(default)]
1931 pub next_at: Option<String>,
1932}
1933
1934/// `admin_invoices`: every invoice g1t has sent, workspaces' and
1935/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
1936#[derive(Debug, Default, Serialize, Deserialize)]
1937pub struct AdminInvoicesArgs {
1938 /// `paid`, `open`, `failed`, `overdue` or `void`.
1939 #[serde(default)]
1940 pub status: Option<String>,
1941 /// YYYY-MM, by when it was sent.
1942 #[serde(default)]
1943 pub month: Option<String>,
1944}
1945
1946#[derive(Clone, Debug, Serialize, Deserialize)]
1947#[serde(rename_all = "camelCase")]
1948pub struct InvoiceSummary {
1949 pub invoice_id: String,
1950 /// `workspace` or `enterprise`.
1951 pub kind: String,
1952 /// The workspace's slug, or the enterprise's account id.
1953 pub account: String,
1954 /// What to call it: the workspace, or the enterprise's name.
1955 pub name: String,
1956 pub reason: String,
1957 pub period: String,
1958 pub amount_micros: i64,
1959 pub status: String,
1960 pub hosted_url: Option<String>,
1961 pub created_at: String,
1962 pub paid_at: Option<String>,
1963}
1964
1965/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
1966/// Returns `Vec<AdminAction>`.
1967#[derive(Debug, Default, Serialize, Deserialize)]
1968pub struct AdminAuditArgs {
1969 #[serde(default)]
1970 pub by: Option<String>,
1971 #[serde(default)]
1972 pub action: Option<String>,
1973 /// Only those before this time, for paging.
1974 #[serde(default)]
1975 pub before: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals1976}
1977
1978/// `admin_signals`: every workspace worth reaching out to, most urgent
1979/// first. Returns `Vec<Signal>`.
1980#[derive(Debug, Default, Serialize, Deserialize)]
1981pub struct AdminSignalsArgs {}
1982
1983/// What staff are doing about a workspace.
1984#[derive(Clone, Debug, Serialize, Deserialize)]
1985#[serde(rename_all = "camelCase")]
1986pub struct SalesRecord {
1987 pub workspace: String,
1988 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
1989 pub stage: String,
1990 /// The staff member looking after it.
1991 pub owner: Option<String>,
1992 pub next_step: Option<String>,
1993 /// RFC 3339 date.
1994 pub next_at: Option<String>,
1995 pub notes: Vec<SalesNote>,
1996 pub updated_at: Option<String>,
1997}
1998
1999#[derive(Clone, Debug, Serialize, Deserialize)]
2000#[serde(rename_all = "camelCase")]
2001pub struct SalesNote {
2002 pub id: String,
2003 pub text: String,
2004 pub by: String,
2005 pub created_at: String,
2006}
2007
2008/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
2009#[derive(Debug, Serialize, Deserialize)]
2010pub struct AdminSalesArgs {
2011 pub workspace: String,
2012}
2013
2014/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
2015#[derive(Debug, Serialize, Deserialize)]
2016pub struct AdminSetSalesArgs {
2017 pub workspace: String,
2018 pub stage: String,
2019 #[serde(default)]
2020 pub owner: Option<String>,
2021 #[serde(default)]
2022 pub next_step: Option<String>,
2023 #[serde(default)]
2024 pub next_at: Option<String>,
2025 pub by: String,
2026}
2027
2028/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
2029#[derive(Debug, Serialize, Deserialize)]
2030pub struct AdminAddNoteArgs {
2031 pub workspace: String,
2032 pub text: String,
2033 pub by: String,
2034}
2035
2036/// `admin_overview`: the business at a glance. Returns `Overview`.
2037#[derive(Debug, Default, Serialize, Deserialize)]
2038pub struct AdminOverviewArgs {}
2039
2040#[derive(Clone, Debug, Serialize, Deserialize)]
2041#[serde(rename_all = "camelCase")]
2042pub struct Overview {
2043 /// YYYY-MM.
2044 pub month: String,
2045 /// The last six months, oldest first, all workspaces together.
2046 pub months: Vec<MonthFigures>,
2047 /// This month by kind of usage: models, sandbox, deployments, plans.
2048 pub by_kind: Vec<KindFigures>,
2049 pub paying_workspaces: u32,
2050 pub stopped: u32,
2051 pub near_ceiling: u32,
2052 pub declined: u32,
2053 /// Sent and not yet paid, workspaces and enterprises.
2054 pub open_invoices_micros: i64,
2055 /// Follow-ups due today or earlier.
2056 pub follow_ups_due: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2057 /// The capped budgets g1t pays from, this month.
2058 #[serde(default)]
2059 pub pools: Option<Pools>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2060 /// This month's revenue: usage charged plus the plan's price paid.
2061 #[serde(default)]
2062 pub revenue_micros: i64,
2063 /// Workspaces on the paid plan now, and what their price comes to a
2064 /// month.
2065 #[serde(default)]
2066 pub active_plans: u32,
2067 #[serde(default)]
2068 pub plan_mrr_micros: i64,
2069 /// What g1t gave this month, by source, apart from its margin.
2070 #[serde(default)]
2071 pub given: Vec<GivenFigures>,
2072 /// g1t's own and Flagon's workspaces this month: what their use cost,
2073 /// and why they are not charged.
2074 #[serde(default)]
2075 pub internal: Vec<InternalUse>,
2076 /// Open limit requests, and workspaces in the Overages queue.
2077 #[serde(default)]
2078 pub open_requests: u32,
2079 #[serde(default)]
2080 pub overages: u32,
2081 /// Spend spikes waiting for an owner.
2082 #[serde(default)]
2083 pub open_spikes: u32,
Two limits, real invoices, trust that grows by itself, sales signals2084}
2085
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2086/// What g1t gave this month from one source.
2087#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2088#[serde(rename_all = "camelCase")]
2089pub struct GivenFigures {
2090 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
2091 pub source: String,
2092 pub label: String,
2093 /// At price, and what it cost g1t.
2094 pub micros: i64,
2095 pub cost_micros: i64,
2096}
2097
2098/// One internal workspace's use this month.
2099#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2100#[serde(rename_all = "camelCase")]
2101pub struct InternalUse {
2102 pub workspace: String,
2103 /// Why it is not charged: its terms' note.
2104 pub reason: String,
2105 pub cost_micros: i64,
2106 pub entries: u32,
2107}
2108
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2109/// g1t's capped budgets for free usage, this calendar month (UTC).
2110#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2111#[serde(rename_all = "camelCase")]
2112pub struct Pools {
2113 /// YYYY-MM.
2114 pub month: String,
2115 /// Trial grants made this month, against the month's pool.
2116 pub trial_granted_micros: i64,
2117 pub trial_pool_micros: i64,
2118 pub trial_grants: u32,
2119 /// What the open-source pool paid this month, against its cap.
2120 pub oss_used_micros: i64,
2121 pub oss_pool_micros: i64,
2122 /// Each public repository's monthly cap on the pool.
2123 pub oss_repo_micros: i64,
2124}
2125
Two limits, real invoices, trust that grows by itself, sales signals2126#[derive(Clone, Debug, Serialize, Deserialize)]
2127#[serde(rename_all = "camelCase")]
2128pub struct KindFigures {
2129 pub kind: String,
2130 pub charged_micros: i64,
2131 pub cost_micros: i64,
2132}
2133
Billing accounts, terms and enterprises; g1t is no longer free2134// --- Staff (sudo.g1t.sh) ------------------------------------------------------
2135//
2136// Called only by the sudo app, which only g1t staff can reach (behind
2137// Cloudflare Access). Each change names who made it, and is kept in the
2138// audit log.
2139
2140/// `admin_accounts`: every billing account, with where each stands this
2141/// month. Returns `Vec<AccountSummary>`.
2142#[derive(Debug, Default, Serialize, Deserialize)]
2143pub struct AdminAccountsArgs {
2144 #[serde(default)]
2145 pub query: Option<String>,
Stripe webhooks, enterprise invoices, and sudo for both2146 /// Exactly these workspaces' accounts, such as one page of sudo's
2147 /// list; every account with activity when absent.
2148 #[serde(default)]
2149 pub workspaces: Option<Vec<String>>,
Billing accounts, terms and enterprises; g1t is no longer free2150}
2151
2152#[derive(Clone, Debug, Serialize, Deserialize)]
2153#[serde(rename_all = "camelCase")]
2154pub struct AccountSummary {
2155 pub account: BillingAccount,
2156 pub limit: Limit,
2157 /// Charged this month, after terms.
2158 pub charged_micros: i64,
2159 /// What this month's usage cost g1t.
2160 pub cost_micros: i64,
2161 /// Paid, ever.
2162 pub paid_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2163 /// The same figures for each of the account's workspaces that has
2164 /// any, so staff can see what one member of an enterprise used.
2165 #[serde(default)]
2166 pub by_workspace: Vec<WorkspaceFigures>,
Two limits, real invoices, trust that grows by itself, sales signals2167 /// The last six months, oldest first, for trends.
2168 #[serde(default)]
2169 pub months: Vec<MonthFigures>,
2170}
2171
2172/// One month of an account's billing.
2173#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2174#[serde(rename_all = "camelCase")]
2175pub struct MonthFigures {
2176 /// YYYY-MM.
2177 pub month: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2178 /// Usage charged, after what paid for it first.
Two limits, real invoices, trust that grows by itself, sales signals2179 pub charged_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2180 /// What usage cost g1t: only what g1t paid for, never a workspace's own
2181 /// model provider.
Two limits, real invoices, trust that grows by itself, sales signals2182 pub cost_micros: i64,
2183 pub paid_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2184 /// The plan's monthly price, paid.
2185 #[serde(default)]
2186 pub plans_micros: i64,
2187 /// What g1t gave, at price: internal (comped) use, trials, the
2188 /// open-source pool, goodwill credits and what g1t covered. Not margin.
2189 #[serde(default)]
2190 pub given_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2191}
2192
2193/// One workspace's share of an [`AccountSummary`].
2194#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2195#[serde(rename_all = "camelCase")]
2196pub struct WorkspaceFigures {
2197 pub workspace: String,
2198 pub charged_micros: i64,
2199 pub cost_micros: i64,
2200 pub paid_micros: i64,
Billing accounts, terms and enterprises; g1t is no longer free2201}
2202
2203/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
2204#[derive(Debug, Serialize, Deserialize)]
2205pub struct AdminAccountArgs {
2206 /// An account id, or a workspace slug.
2207 pub id: String,
2208}
2209
2210#[derive(Clone, Debug, Serialize, Deserialize)]
2211#[serde(rename_all = "camelCase")]
2212pub struct AccountDetail {
2213 pub summary: AccountSummary,
2214 /// Each workspace's limit, for an enterprise.
2215 pub workspaces: Vec<Limit>,
2216 pub ledger: Vec<LedgerEntry>,
2217 pub audit: Vec<AdminAction>,
2218}
2219
2220/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
2221#[derive(Debug, Serialize, Deserialize)]
2222pub struct AdminSetTermsArgs {
2223 pub id: String,
2224 pub terms: Terms,
2225 pub by: String,
2226}
2227
2228/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
2229#[derive(Debug, Serialize, Deserialize)]
2230pub struct AdminCreateEnterpriseArgs {
2231 pub name: String,
2232 pub workspaces: Vec<String>,
2233 pub by: String,
2234}
2235
2236/// `admin_attach`: moves a workspace onto an enterprise account, or back
2237/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
2238#[derive(Debug, Serialize, Deserialize)]
2239pub struct AdminAttachArgs {
2240 pub workspace: String,
2241 pub account: Option<String>,
2242 pub by: String,
2243}
2244
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2245/// Why g1t gave a workspace credit.
2246#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
2247#[serde(rename_all = "snake_case")]
2248pub enum CreditKind {
2249 /// Marketing: a welcome, a referral, an event. Given away when spent.
2250 Promotional,
2251 /// An apology, or accidental usage forgiven. Given away when spent.
2252 #[default]
2253 Goodwill,
2254 /// Money back for something that went wrong. Not given away: it gives
2255 /// back money already paid, so it comes off what was paid on the day
2256 /// it refunds, and what it pays for later is paid for.
2257 Refund,
2258 /// Bought by the workspace (prepaid AI): money paid in up front, owed
2259 /// as usage until spent. What it pays for is paid for, never given.
2260 /// Staff never give it; its ledger line is a payment, not `crd…`.
2261 Purchased,
2262}
2263
2264impl CreditKind {
2265 pub fn as_str(self) -> &'static str {
2266 match self {
2267 CreditKind::Promotional => "promotional",
2268 CreditKind::Goodwill => "goodwill",
2269 CreditKind::Refund => "refund",
2270 CreditKind::Purchased => "purchased",
2271 }
2272 }
2273
2274 pub fn parse(text: &str) -> Option<CreditKind> {
2275 match text {
2276 "promotional" => Some(CreditKind::Promotional),
2277 "goodwill" => Some(CreditKind::Goodwill),
2278 "refund" => Some(CreditKind::Refund),
2279 "purchased" => Some(CreditKind::Purchased),
2280 _ => None,
2281 }
2282 }
2283
2284 /// As people read it: `Promotional`.
2285 pub fn label(self) -> &'static str {
2286 match self {
2287 CreditKind::Promotional => "Promotional",
2288 CreditKind::Goodwill => "Goodwill",
2289 CreditKind::Refund => "Refund",
2290 CreditKind::Purchased => "Purchased",
2291 }
2292 }
2293}
2294
2295/// `admin_credit`: credit g1t gives a workspace: promotional, goodwill or a
2296/// refund, with a note, and optionally an expiry. It is spent before
2297/// anything paid in advance, the soonest-expiring first. The workspace's
2298/// owners are emailed. Returns `Outcome<LedgerEntry>`.
Billing accounts, terms and enterprises; g1t is no longer free2299#[derive(Debug, Serialize, Deserialize)]
2300pub struct AdminCreditArgs {
2301 pub workspace: String,
2302 pub amount_micros: i64,
2303 pub note: String,
2304 pub by: String,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2305 #[serde(default)]
2306 pub kind: CreditKind,
2307 /// RFC 3339; unused credit stops counting then. Never for a refund.
2308 #[serde(default)]
2309 pub expires_at: Option<String>,
2310 /// A refund: what it refunds, in a line, and the day of it
2311 /// (`YYYY-MM-DD`; today if absent).
2312 #[serde(default)]
2313 pub refund_for: Option<String>,
2314 #[serde(default)]
2315 pub refund_day: Option<String>,
2316}
2317
2318/// One credit g1t gave, with what of it was used: spent on usage, the
2319/// soonest-expiring grant first, before anything paid in advance.
2320#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2321#[serde(rename_all = "camelCase")]
2322pub struct CreditGrant {
2323 /// `crd_…`, the grant's ledger reference.
2324 pub id: String,
2325 pub workspace: String,
2326 pub kind: CreditKind,
2327 pub amount_micros: i64,
2328 pub used_micros: i64,
2329 /// What can still be spent: nothing once it expired or was revoked.
2330 pub left_micros: i64,
2331 pub note: String,
2332 #[serde(default)]
2333 pub refund_for: Option<String>,
2334 #[serde(default)]
2335 pub refund_day: Option<String>,
2336 pub expires_at: Option<String>,
2337 pub created_by: String,
2338 pub created_at: String,
2339 /// `open`, `used`, `expired` or `revoked`.
2340 pub state: String,
2341 #[serde(default)]
2342 pub closed_at: Option<String>,
2343 #[serde(default)]
2344 pub closed_note: Option<String>,
2345 #[serde(default)]
2346 pub closed_by: Option<String>,
2347 /// What expiring or revoking took off the balance.
2348 #[serde(default)]
2349 pub closed_micros: i64,
2350 /// What it pays for: `all` usage, or `models` only (agent runs' model
2351 /// cost), which is spent first.
2352 #[serde(default)]
2353 pub scope: String,
2354 /// Where it came from: `staff`, `purchase` or `promo_code`.
2355 #[serde(default)]
2356 pub source: String,
2357}
2358
2359/// `credits` (`Outcome<Credits>`, `AccountArgs`): a workspace's credits from
2360/// g1t, newest first, for its members.
2361#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2362#[serde(rename_all = "camelCase")]
2363pub struct Credits {
2364 pub grants: Vec<CreditGrant>,
2365 /// What is left to spend, in all.
2366 pub left_micros: i64,
2367}
2368
2369/// `admin_credits`: every credit g1t gave, newest first, filtered. Returns
2370/// `AdminCredits`.
2371#[derive(Debug, Default, Serialize, Deserialize)]
2372pub struct AdminCreditsArgs {
2373 #[serde(default)]
2374 pub workspace: Option<String>,
2375 #[serde(default)]
2376 pub kind: Option<CreditKind>,
2377 /// `YYYY-MM`: given that month.
2378 #[serde(default)]
2379 pub month: Option<String>,
2380 /// Given by this member of staff.
2381 #[serde(default)]
2382 pub by: Option<String>,
2383}
2384
2385/// One month's credits of one kind: given, used on usage that month, and
2386/// taken back unused (expired or revoked).
2387#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2388#[serde(rename_all = "camelCase")]
2389pub struct CreditMonth {
2390 pub month: String,
2391 pub kind: CreditKind,
2392 pub given_micros: i64,
2393 pub grants: u32,
2394 pub used_micros: i64,
2395 pub expired_micros: i64,
2396 pub revoked_micros: i64,
2397}
2398
2399#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2400#[serde(rename_all = "camelCase")]
2401pub struct AdminCredits {
2402 /// At most 200.
2403 pub grants: Vec<CreditGrant>,
2404 /// The last 12 months, newest first, whatever the month filter.
2405 pub months: Vec<CreditMonth>,
2406 /// Who has given credit, for the filter.
2407 pub staff: Vec<String>,
2408}
2409
2410/// `admin_revoke_credit`: what is left of a grant, taken off the balance,
2411/// with why. Returns `Outcome<CreditGrant>`.
2412#[derive(Debug, Serialize, Deserialize)]
2413pub struct AdminRevokeCreditArgs {
2414 pub id: String,
2415 pub note: String,
2416 pub by: String,
Billing accounts, terms and enterprises; g1t is no longer free2417}
2418
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's2419/// `admin_reset_billing`: a test workspace's billing wiped, so it starts
2420/// again as a new customer. Only while billing runs on Stripe's test key;
2421/// never a comped workspace or one an enterprise pays for. `confirm` is the
2422/// workspace's slug typed out. Returns `Outcome<BillingReset>`.
2423#[derive(Debug, Serialize, Deserialize)]
2424pub struct AdminResetBillingArgs {
2425 pub workspace: String,
2426 pub confirm: String,
2427 pub note: String,
2428 pub by: String,
2429}
2430
2431/// What a reset removed.
2432#[derive(Clone, Debug, Serialize, Deserialize)]
2433#[serde(rename_all = "camelCase")]
2434pub struct BillingReset {
2435 pub workspace: String,
2436 pub rows: u32,
sudo: a billing reset runs the costs analysis again so every figure is fresh; every submit button shows it is working (CSS only); no margin percentage on less than a cent sold2437 /// Whether the costs analysis ran again after it, so the margin
2438 /// figures no longer hold the workspace's past usage.
2439 #[serde(default)]
2440 pub refreshed: bool,
sudo: reset a test workspace's billing so it starts again as a new customer; refused on a live Stripe key, for comped workspaces and for an enterprise's2441}
2442
Billing accounts, terms and enterprises; g1t is no longer free2443/// One change made in sudo.
2444#[derive(Clone, Debug, Serialize, Deserialize)]
2445#[serde(rename_all = "camelCase")]
2446pub struct AdminAction {
2447 pub id: String,
2448 pub account: String,
2449 pub action: String,
2450 pub detail: String,
2451 pub by: String,
2452 pub created_at: String,
2453}
2454
Paid features: a workspace turns on Deployments with a monthly plan2455/// What a feature's plan costs and includes.
2456#[derive(Clone, Debug, Serialize, Deserialize)]
2457#[serde(rename_all = "camelCase")]
2458pub struct Plan {
2459 pub feature: Feature,
2460 pub title: String,
2461 /// Charged every month while the plan is on, in cents.
2462 pub monthly_cents: u32,
2463 /// What the monthly price includes, one line each, for people to read.
2464 pub includes: Vec<String>,
2465 /// How usage past the allowance is charged, for people to read.
2466 pub overage: String,
2467}
2468
2469#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2470#[serde(rename_all = "snake_case")]
2471pub enum SubscriptionStatus {
2472 /// Paid up; the feature works.
2473 Active,
2474 /// Paid up to the end of the period, and ends then.
2475 Canceling,
2476 /// The last payment failed; the feature is off until it is paid.
2477 PastDue,
2478 /// Ended.
2479 Canceled,
2480}
2481
2482impl SubscriptionStatus {
2483 /// Whether the feature works in this state.
2484 pub fn on(self) -> bool {
2485 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
2486 }
2487}
2488
2489/// A workspace's plan for one feature.
2490#[derive(Clone, Debug, Serialize, Deserialize)]
2491#[serde(rename_all = "camelCase")]
2492pub struct Subscription {
2493 pub feature: Feature,
2494 pub status: SubscriptionStatus,
2495 /// RFC 3339: when the period paid for ends, and the plan renews or
2496 /// ends.
2497 pub period_end: Option<String>,
2498 /// Username of whoever turned it on.
2499 pub started_by: String,
2500 /// RFC 3339.
2501 pub started_at: String,
2502}
2503
2504/// A feature as a workspace sees it: what it costs, and its plan if it has
2505/// one.
2506#[derive(Clone, Debug, Serialize, Deserialize)]
2507#[serde(rename_all = "camelCase")]
2508pub struct FeatureState {
2509 pub plan: Plan,
2510 pub subscription: Option<Subscription>,
2511 /// Whether the feature works for the workspace now.
2512 pub on: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2513 /// On without a plan: comped terms, or given by g1t. Nothing to pay
2514 /// and nothing to turn off.
2515 #[serde(default)]
2516 pub included: bool,
Paid features: a workspace turns on Deployments with a monthly plan2517}
2518
2519/// `features`: every paid feature and the workspace's plan for each.
2520/// Members only. Returns `Outcome<Vec<FeatureState>>`.
2521#[derive(Debug, Serialize, Deserialize)]
2522pub struct FeaturesArgs {
2523 pub workspace: String,
2524 pub viewer: Viewer,
2525}
2526
2527/// `subscribe`: starts the card page for a feature's monthly plan. Owners
2528/// only. Returns `Outcome<Checkout>`; the page's id comes back to
2529/// `return_url` as `session`, for `confirm_subscription`.
2530#[derive(Debug, Serialize, Deserialize)]
2531#[serde(rename_all = "camelCase")]
2532pub struct SubscribeArgs {
2533 pub actor: User,
2534 pub workspace: String,
2535 pub feature: Feature,
2536 pub return_url: String,
2537}
2538
2539/// `confirm_subscription`: turns the feature on once the processor says
2540/// the plan was paid for. Safe to call any number of times. Returns
2541/// `Outcome<FeatureState>`.
2542#[derive(Debug, Serialize, Deserialize)]
2543pub struct ConfirmSubscriptionArgs {
2544 pub workspace: String,
2545 pub viewer: Viewer,
2546 pub session: String,
2547}
2548
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member2549/// `admin_log`: a staff change another service made to a workspace, kept
2550/// in sudo's audit log with billing's own (`admin_audit`). For identity's
2551/// restores and purges of deleted workspaces. Returns `bool`.
2552#[derive(Debug, Serialize, Deserialize)]
2553pub struct AdminLogArgs {
2554 pub workspace: String,
2555 pub action: String,
2556 pub detail: String,
2557 /// The staff member's email.
2558 pub by: String,
2559}
2560
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2561/// `close_workspace`: settles a workspace that is about to be deleted.
2562/// Owners only. Refused while it has an invoice that failed, while it
2563/// holds prepaid credit, or while it owes money it cannot be charged for
2564/// now; otherwise what it owes is invoiced to its card at once (no
2565/// minimum), its plan is cancelled at Stripe straight away, and its
2566/// account is marked closed, so the month-end close, autopay and limit
2567/// warnings pass it by. Its ledger, invoices and statements stay. With
2568/// `dry_run`, only says whether it could, changing nothing. Returns
2569/// `Outcome<bool>`.
2570#[derive(Debug, Serialize, Deserialize)]
2571#[serde(rename_all = "camelCase")]
2572pub struct CloseWorkspaceArgs {
2573 pub actor: User,
2574 pub workspace: String,
2575 #[serde(default)]
2576 pub dry_run: bool,
2577}
2578
Paid features: a workspace turns on Deployments with a monthly plan2579/// `cancel_subscription` (`resume` false) ends a plan at the end of the
2580/// period paid for; with `resume` true, takes that back. Owners only.
2581/// Returns `Outcome<FeatureState>`.
2582#[derive(Debug, Serialize, Deserialize)]
2583pub struct CancelSubscriptionArgs {
2584 pub actor: User,
2585 pub workspace: String,
2586 pub feature: Feature,
2587 #[serde(default)]
2588 pub resume: bool,
2589}
2590
2591/// `has_feature`: whether a feature works for a workspace now, asked by the
2592/// service that provides it before doing paid work. Returns
2593/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
2594/// True everywhere when no card processor is configured.
2595#[derive(Debug, Serialize, Deserialize)]
2596pub struct HasFeatureArgs {
2597 pub workspace: String,
2598 pub feature: Feature,
2599}
2600
2601/// `charge_feature`: usage of a feature past its plan's allowance, charged
2602/// from the workspace's credit at cost plus the margin, whatever
2603/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
2604/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
2605/// reference was charged before.
2606#[derive(Debug, Serialize, Deserialize)]
2607#[serde(rename_all = "camelCase")]
2608pub struct ChargeFeatureArgs {
2609 pub workspace: String,
2610 pub feature: Feature,
2611 /// What it cost g1t, in millionths of a dollar, before the margin.
2612 pub cost_micros: i64,
2613 pub description: String,
2614 /// `namespace/name`, when the usage was one repository's.
2615 pub repo: Option<String>,
2616 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
2617 pub reference: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2618 /// For a build: how long it ran. The plan's included build time this
2619 /// month pays for what it can, and only the rest of `cost_micros` is
2620 /// charged.
2621 #[serde(default)]
2622 pub build_seconds: Option<u32>,
Paid features: a workspace turns on Deployments with a monthly plan2623}
2624
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2625// ---------------------------------------------------------------------
2626// Costs and margin: what Cloudflare charges g1t against what g1t
2627// charges (billing's costs.rs, margin.rs and pricing.rs). Staff only.
2628// ---------------------------------------------------------------------
2629
2630/// `admin_costs`: the Costs & margin page. Returns `CostsReport`.
2631#[derive(Debug, Default, Serialize, Deserialize)]
2632pub struct AdminCostsArgs {
2633 /// How many days back, 7 to 90; 30 when absent.
2634 #[serde(default)]
2635 pub days: Option<u32>,
2636}
2637
2638/// One of g1t's products on one day.
2639#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2640#[serde(rename_all = "camelCase")]
2641pub struct CostDay {
2642 pub day: String,
2643 pub bucket: String,
2644 /// What Cloudflare charged g1t.
2645 pub cf_cost_micros: i64,
2646 /// What g1t's meters recorded it cost, at the price book's cost.
2647 pub own_cost_micros: i64,
2648 /// What customers were charged for it at price, before included
2649 /// usage, trials and pools paid for some.
2650 pub value_micros: i64,
2651 /// Of that, what workspaces paid.
2652 pub cash_micros: i64,
2653}
2654
2655/// One product over the range.
2656#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2657#[serde(rename_all = "camelCase")]
2658pub struct ProductMargin {
2659 pub bucket: String,
2660 pub title: String,
2661 /// The cost the margin is taken from: Cloudflare's bill, or g1t's own
2662 /// figure for what Cloudflare does not bill (models).
2663 pub cost_micros: i64,
2664 pub cf_cost_micros: i64,
2665 pub own_cost_micros: i64,
2666 pub value_micros: i64,
2667 pub margin_micros: i64,
2668 pub margin_percent: Option<f64>,
2669 /// `cloudflare` or `ledger`.
2670 pub cost_source: String,
2671 /// Running g1t itself, paid for by the plan.
2672 pub overhead: bool,
2673}
2674
Costs: margin is measured on what was sold; comped workspaces, free periods, the trial and the pools are given away, a budget shown beside it2675/// All of g1t over the range: money in against every cost, and against
2676/// the cost of what was sold (every cost less what g1t gave away).
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2677#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2678#[serde(rename_all = "camelCase")]
2679pub struct OverallMargin {
2680 /// What workspaces paid for usage, and for the plan.
2681 pub usage_micros: i64,
2682 pub plans_micros: i64,
2683 pub cost_micros: i64,
2684 pub margin_micros: i64,
2685 pub margin_percent: Option<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 it2686 /// Of `cost_micros`, what went on usage g1t gave away on purpose:
2687 /// comped workspaces, free periods, the trial and the open-source pool.
2688 #[serde(default)]
2689 pub given_micros: i64,
2690 /// Money in against `cost_micros - given_micros`.
2691 #[serde(default)]
2692 pub sold_margin_micros: i64,
2693 #[serde(default)]
2694 pub sold_margin_percent: Option<f64>,
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 running2695 /// What was sold, apart: usage (`usage_micros` against what that usage
2696 /// cost, less what was given), running g1t (`plans_micros` against the
2697 /// platform's cost, less its given share) and what no mapping names.
2698 #[serde(default)]
2699 pub usage_cost_micros: i64,
2700 #[serde(default)]
2701 pub usage_margin_micros: i64,
2702 #[serde(default)]
2703 pub usage_margin_percent: Option<f64>,
2704 #[serde(default)]
2705 pub running_cost_micros: i64,
2706 #[serde(default)]
2707 pub unmapped_cost_micros: i64,
2708 /// `given_micros` by why: comped workspaces, free use (free periods,
2709 /// free allowances, overruns g1t covered), the trial, the open-source pool.
2710 #[serde(default)]
2711 pub given_comped_micros: i64,
2712 #[serde(default)]
2713 pub given_free_micros: i64,
2714 #[serde(default)]
2715 pub given_trial_micros: i64,
2716 #[serde(default)]
2717 pub given_pool_micros: i64,
Merge branch 'worktree-agent-a633ac0f7f66d419d'2718 /// What discounts on an account's terms took below cost plus the
2719 /// margin: given, so a discounted sale is not margin lost.
2720 #[serde(default)]
2721 pub given_discount_micros: i64,
Billing: credits with a kind and expiry, discounts instead of comped, and safer charging2722 /// Credits from g1t spent on usage, by kind: given, so usage paid for
2723 /// with them is never money in. Refunds are not here: they come off
2724 /// money in on the day they refund.
2725 #[serde(default)]
2726 pub given_credit_promotional_micros: i64,
2727 #[serde(default)]
2728 pub given_credit_goodwill_micros: i64,
2729 /// Credits over the range: given (every kind), spent on usage, and
2730 /// refunds' money given back.
2731 #[serde(default)]
2732 pub credits_given_micros: i64,
2733 #[serde(default)]
2734 pub credits_used_micros: i64,
2735 #[serde(default)]
2736 pub credits_refunded_micros: 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 running2737 /// `cost_micros` by who g1t pays: Cloudflare's bill (billed amounts,
2738 /// after the included allowances), and model providers (the ledger's
2739 /// cost of the tokens, which Cloudflare's bill does not show).
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)2740 /// What the plan's included usage paid for, at price (the ledger's
2741 /// `credit_micros`, comped workspaces left out): money in for usage,
2742 /// paid out of `plans_micros`.
2743 #[serde(default)]
2744 pub included_micros: 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 running2745 #[serde(default)]
2746 pub cloudflare_cost_micros: i64,
2747 #[serde(default)]
2748 pub models_cost_micros: i64,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2749}
2750
2751/// A count, cost or leak that does not add up.
2752#[derive(Clone, Debug, Serialize, Deserialize)]
2753#[serde(rename_all = "camelCase")]
2754pub struct CostDrift {
2755 pub bucket: String,
2756 pub title: String,
2757 /// `count` (units g1t counted against Cloudflare's), `cost` (the bill
Merge branch 'worktree-agent-a633ac0f7f66d419d'2758 /// against the price book's cost of the same usage; for models, what AI
2759 /// Gateway priced g1t's provider traffic at against the ledger's model
2760 /// cost), `unpriced` (model usage AI Gateway put no price on, so its
2761 /// cost is not the providers'), or `leak`.
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2762 pub kind: String,
2763 pub ours: f64,
2764 pub cloudflare: f64,
2765 pub delta_percent: Option<f64>,
2766 pub detail: String,
2767 pub found_at: String,
2768}
2769
2770/// A margin alert, open while its condition lasts.
2771#[derive(Clone, Debug, Serialize, Deserialize)]
2772#[serde(rename_all = "camelCase")]
2773pub struct MarginAlert {
2774 pub id: String,
2775 /// `margin`, `overall`, `leak`, `drift` or `workspace`.
2776 pub kind: String,
2777 /// The product, or the workspace.
2778 pub subject: String,
2779 pub detail: String,
2780 pub since: String,
2781 pub opened_at: String,
2782 pub emailed_at: Option<String>,
2783}
2784
2785/// A change to a price the reconciler measured.
2786#[derive(Clone, Debug, Serialize, Deserialize)]
2787#[serde(rename_all = "camelCase")]
2788pub struct PriceProposal {
2789 pub id: String,
2790 pub meter: String,
2791 pub title: String,
2792 pub unit: String,
2793 pub current_cost_micros: f64,
2794 pub proposed_cost_micros: f64,
2795 pub change_percent: f64,
2796 pub markup_percent: u32,
2797 pub reason: String,
2798 /// `keeper` or `reconciler`.
2799 pub source: String,
2800 /// Far off the current cost: look before approving.
2801 pub suspect: bool,
2802 /// `open`, `applied`, `approved`, `rejected` or `superseded`.
2803 pub status: String,
2804 pub created_at: String,
2805 pub decided_at: Option<String>,
2806 pub decided_by: Option<String>,
2807 pub note: Option<String>,
2808 /// When it takes or took effect, once approved or applied.
2809 pub effective_at: Option<String>,
2810}
2811
2812/// One version of one meter's price. Never changed once written.
2813#[derive(Clone, Debug, Serialize, Deserialize)]
2814#[serde(rename_all = "camelCase")]
2815pub struct PriceVersion {
2816 pub id: String,
2817 pub meter: String,
2818 pub version: u32,
2819 pub cost_micros: f64,
2820 pub markup_percent: u32,
2821 pub price_micros: f64,
2822 pub effective_at: String,
2823 pub reason: String,
2824 pub created_by: String,
2825 /// When the price book took it on; absent while it waits for its date.
2826 pub applied_at: Option<String>,
2827}
2828
2829/// What a workspace cost g1t over the range, Cloudflare's costs shared
2830/// out by g1t's own meters, against what it paid.
2831#[derive(Clone, Debug, Serialize, Deserialize)]
2832#[serde(rename_all = "camelCase")]
2833pub struct WorkspaceCost {
2834 pub workspace: String,
2835 pub cost_micros: i64,
2836 pub revenue_micros: 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 it2837 /// Of `cost_micros`, what g1t gave away.
2838 #[serde(default)]
2839 pub given_micros: i64,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2840 /// One of g1t's own (comped) workspaces.
2841 pub internal: bool,
2842}
2843
2844/// One Cloudflare meter over the range, and the product it is a cost of.
2845#[derive(Clone, Debug, Serialize, Deserialize)]
2846#[serde(rename_all = "camelCase")]
2847pub struct CostLineSummary {
2848 pub product: String,
2849 pub meter: String,
2850 pub raw_name: String,
2851 pub unit: String,
2852 pub source: String,
2853 pub quantity: f64,
2854 pub cost_micros: i64,
2855 /// Absent when no mapping claims it.
2856 pub bucket: Option<String>,
2857}
2858
2859/// A row of the mapping from Cloudflare's meters to g1t's products.
2860#[derive(Clone, Debug, Serialize, Deserialize)]
2861#[serde(rename_all = "camelCase")]
2862pub struct CostMapping {
2863 pub product: String,
2864 pub meter: String,
2865 pub bucket: String,
2866 pub price_meter: Option<String>,
2867 pub own_meter: Option<String>,
2868 pub scale_to_own: bool,
2869 pub drift_percent: f64,
2870 pub note: String,
2871 pub updated_at: String,
2872 pub updated_by: String,
2873}
2874
2875/// The guardrails on prices and the alerts.
2876#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2877#[serde(rename_all = "camelCase")]
2878pub struct CostSettings {
2879 /// Apply small moves without staff.
2880 pub auto_apply: bool,
2881 /// The largest move applied without staff, either way, in percent.
2882 pub auto_apply_percent: f64,
2883 /// Days between telling customers of a rise and charging it.
2884 pub notice_days: u32,
2885 /// Below this margin, in percent, for `alert_days` days in a row, alert.
2886 pub margin_floor_percent: f64,
2887 pub alert_days: u32,
2888 /// Days with less cost than this say nothing about a margin.
2889 pub min_daily_cost_micros: i64,
2890 /// A workspace costing more than its revenue times this, over 30 days,
2891 /// and at least `anomaly_floor_micros`, is flagged.
2892 pub anomaly_factor: f64,
2893 pub anomaly_floor_micros: i64,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit2894 /// Pass Stripe's card fee on as its own line when AI credit is bought
2895 /// by card (`card_fee_percent` and `card_fee_fixed` in the price book).
2896 #[serde(default = "yes")]
2897 pub card_fee: bool,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2898}
2899
2900impl Default for CostSettings {
2901 fn default() -> Self {
2902 CostSettings {
2903 auto_apply: true,
2904 auto_apply_percent: 25.0,
2905 notice_days: 14,
2906 margin_floor_percent: 10.0,
2907 alert_days: 3,
2908 min_daily_cost_micros: 100_000,
2909 anomaly_factor: 1.0,
2910 anomaly_floor_micros: 1_000_000,
Usage, Billing settings and prepaid AI credit; fixes from the UX audit2911 card_fee: true,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2912 }
2913 }
2914}
2915
2916/// The Costs & margin page.
2917#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2918#[serde(rename_all = "camelCase")]
2919pub struct CostsReport {
2920 /// A token to read Cloudflare's bill is set.
2921 pub configured: bool,
2922 /// When Cloudflare's bill was last read.
2923 pub fetched_at: Option<String>,
2924 /// The days shown, YYYY-MM-DD.
2925 pub since: String,
2926 pub until: String,
2927 pub days: Vec<CostDay>,
2928 pub products: Vec<ProductMargin>,
2929 pub overall: OverallMargin,
2930 pub drift: Vec<CostDrift>,
2931 pub alerts: Vec<MarginAlert>,
2932 pub proposals: Vec<PriceProposal>,
2933 pub versions: Vec<PriceVersion>,
2934 pub top_workspaces: Vec<WorkspaceCost>,
2935 pub lines: Vec<CostLineSummary>,
2936 pub mappings: Vec<CostMapping>,
2937 pub settings: CostSettings,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2938 /// g1t's own spend against its two caps.
2939 #[serde(default)]
2940 pub caps: SpendCaps,
2941}
2942
2943/// What g1t itself pays for, against its caps (billing's `budget`): the
2944/// daily breaker on all of it, and each comped account's monthly budget.
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing2945/// One of Cloudflare's subscriptions, at what it comes to a month.
2946#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2947#[serde(rename_all = "camelCase")]
2948pub struct FixedCost {
2949 pub name: String,
2950 pub monthly_micros: i64,
2951}
2952
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2953/// At cost, never at price. What sudo's Costs page and its red bar show.
2954#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2955#[serde(rename_all = "camelCase")]
2956pub struct SpendCaps {
2957 /// Today (UTC), YYYY-MM-DD, and this month, YYYY-MM.
2958 pub day: String,
2959 pub month: String,
2960 /// What g1t paid for itself today across every workspace: comped work,
2961 /// the trial and open-source pools, free workspaces' overruns, and
2962 /// anything charged without real money behind it.
2963 pub today_micros: i64,
2964 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`. Zero: no breaker.
2965 pub daily_cap_micros: i64,
2966 /// The breaker is open: new hosted-model agent runs that g1t would pay
2967 /// for wait until tomorrow (UTC) or until staff lift it.
2968 pub tripped: bool,
2969 pub tripped_at: Option<String>,
2970 /// Staff lifted it for the rest of the day.
2971 pub lifted_by: Option<String>,
2972 pub lifted_at: Option<String>,
2973 pub lift_note: Option<String>,
2974 /// This month so far, by what paid: `comped`, `trial`, `oss`, `given`,
2975 /// `unpaid`.
2976 pub month_buckets: Vec<SpendBucket>,
2977 /// Each comped account's monthly budget.
2978 pub comped: Vec<CompedBudget>,
2979 /// Free workspaces' share of this month's reconciled costs (git,
2980 /// storage, platform), through yesterday.
2981 pub free_tier_micros: i64,
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing2982 /// Cloudflare's subscriptions a month: as read from Cloudflare each
2983 /// day, else `CLOUDFLARE_FIXED_MONTHLY_MICROS`, an estimate.
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2984 pub fixed_monthly_micros: i64,
Costs: Cloudflare's subscriptions read from Cloudflare each day, the estimate only until then; sudo's costs split into Costs & margin and Bill & pricing2985 /// `cloudflare` or `estimate`.
2986 #[serde(default)]
2987 pub fixed_source: String,
2988 #[serde(default)]
2989 pub fixed_read_at: Option<String>,
2990 /// Each subscription, when read from Cloudflare.
2991 #[serde(default)]
2992 pub fixed_items: Vec<FixedCost>,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2993 /// Money in this month, through the last reconciled day.
2994 pub revenue_micros: i64,
2995}
2996
2997#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2998#[serde(rename_all = "camelCase")]
2999pub struct SpendBucket {
3000 pub bucket: String,
3001 pub title: String,
3002 pub micros: i64,
3003}
3004
3005/// A comped account's monthly budget: what its work cost g1t this month.
3006#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3007#[serde(rename_all = "camelCase")]
3008pub struct CompedBudget {
3009 pub account: String,
3010 pub name: String,
3011 pub used_micros: i64,
3012 /// Zero: no budget.
3013 pub ceiling_micros: i64,
3014 /// The ceiling is `COMPED_MONTHLY_CEILING_MICROS`, not the account's own.
3015 pub default_ceiling: bool,
3016 /// 50, 75, 90, 100, or 0.
3017 pub level: u32,
3018}
3019
3020/// `admin_spend_caps`: g1t's own spend against its caps. Returns `SpendCaps`.
3021#[derive(Debug, Default, Serialize, Deserialize)]
3022pub struct AdminSpendCapsArgs {}
3023
3024/// `admin_lift_breaker`: lets hosted-model runs start again for the rest
3025/// of today (UTC), with why. Recorded in the audit log. Returns
3026/// `Outcome<SpendCaps>`.
3027#[derive(Debug, Serialize, Deserialize)]
3028pub struct AdminLiftBreakerArgs {
3029 pub note: String,
3030 pub by: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3031}
3032
3033/// `admin_cost_alerts`: the open margin alerts, for sudo's banner.
3034/// Returns `Vec<MarginAlert>`.
3035#[derive(Debug, Default, Serialize, Deserialize)]
3036pub struct AdminCostAlertsArgs {}
3037
3038/// `admin_decide_proposal`: approve or reject a price proposal. An
3039/// approved rise takes effect after the notice period. Returns
3040/// `Outcome<PriceProposal>`.
3041#[derive(Debug, Serialize, Deserialize)]
3042pub struct AdminDecideProposalArgs {
3043 pub id: String,
3044 /// `approve` or `reject`.
3045 pub decision: String,
3046 #[serde(default)]
3047 pub note: String,
3048 pub by: String,
3049}
3050
3051/// `admin_set_cost_settings`. Returns `Outcome<CostSettings>`.
3052#[derive(Debug, Serialize, Deserialize)]
3053pub struct AdminSetCostSettingsArgs {
3054 pub settings: CostSettings,
3055 pub by: String,
3056}
3057
3058/// `admin_set_cost_mapping`: adds, changes or (with `remove`) removes a
3059/// mapping row. Returns `Outcome<CostMapping>`.
3060#[derive(Debug, Serialize, Deserialize)]
3061pub struct AdminSetCostMappingArgs {
3062 pub product: String,
3063 pub meter: String,
3064 #[serde(default)]
3065 pub bucket: String,
3066 #[serde(default)]
3067 pub price_meter: Option<String>,
3068 #[serde(default)]
3069 pub own_meter: Option<String>,
3070 #[serde(default)]
3071 pub scale_to_own: bool,
3072 #[serde(default)]
3073 pub drift_percent: Option<f64>,
3074 #[serde(default)]
3075 pub note: String,
3076 #[serde(default)]
3077 pub remove: bool,
3078 pub by: String,
3079}
3080
3081/// `admin_run_costs`: reads Cloudflare's bill and reconciles now, as the
3082/// daily run does. Returns `Outcome<CostsRun>`.
3083#[derive(Debug, Default, Serialize, Deserialize)]
3084pub struct AdminRunCostsArgs {
3085 #[serde(default)]
3086 pub by: String,
3087}
3088
3089#[derive(Clone, Debug, Default, Serialize, Deserialize)]
3090#[serde(rename_all = "camelCase")]
3091pub struct CostsRun {
3092 pub lines: u32,
3093 pub days: u32,
3094 pub proposals: u32,
3095 pub alerts: u32,
3096 /// What could not be read, in words.
3097 pub problems: Vec<String>,
3098}
3099
Usage, Billing settings and prepaid AI credit; fixes from the UX audit3100// --- The Usage page ----------------------------------------------------------
3101
3102/// The product families the Usage page groups meters into, in order, with
3103/// their names.
3104pub const PRODUCTS: [(&str, &str); 8] = [
3105 ("agent", "Agent"),
3106 ("sandboxes", "Sandboxes"),
3107 ("gateway", "AI Gateway"),
3108 ("deployments", "Deployments"),
3109 ("git_storage", "Git & storage"),
3110 ("packages", "Packages"),
3111 ("security", "Security & quality"),
3112 ("search", "Search"),
3113];
3114
3115/// `usage_report`: a workspace's usage over a range of days, at price, by
3116/// product, meter, project and day. The figures are the ledger's: the same
3117/// lines the statement and invoices read, so every page agrees. Members
3118/// only. Returns `Outcome<UsageReport>`.
3119#[derive(Debug, Serialize, Deserialize)]
3120#[serde(rename_all = "camelCase")]
3121pub struct UsageReportArgs {
3122 pub workspace: String,
3123 pub viewer: Viewer,
3124 /// The first day, `YYYY-MM-DD` (UTC).
3125 pub from: String,
3126 /// The last day, `YYYY-MM-DD`, included.
3127 pub until: String,
3128 /// Only these product families (`agent`, `sandboxes`…); all when empty.
3129 #[serde(default)]
3130 pub products: Vec<String>,
3131 /// Only these projects (repositories, `owner/name`); all when empty.
3132 #[serde(default)]
3133 pub projects: Vec<String>,
3134}
3135
3136/// What usage came to over a range, and what paid for it. `price_micros`
3137/// less `discount_micros`, `included_micros` and `credits_micros` is
3138/// `charged_micros`.
3139#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3140#[serde(rename_all = "camelCase")]
3141pub struct UsageTotals {
3142 /// Usage at price, metered usage not yet charged (`pending_micros`)
3143 /// included.
3144 pub price_micros: i64,
3145 /// What the account's discount took off.
3146 pub discount_micros: i64,
3147 /// What the plan's included usage, the trial and g1t's pools paid.
3148 pub included_micros: i64,
3149 /// What credit paid: AI credit, credit from g1t.
3150 pub credits_micros: i64,
3151 /// What is left for the workspace to pay.
3152 pub charged_micros: i64,
3153 /// Metered this month and charged when it closes (storage, git
3154 /// operations, scans, embeddings, domains), at price.
3155 pub pending_micros: i64,
3156 /// What it cost g1t, before any markup.
3157 pub cost_micros: i64,
3158}
3159
3160/// One day's usage of one product, at price.
3161#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3162#[serde(rename_all = "camelCase")]
3163pub struct UsageDay {
3164 /// `YYYY-MM-DD`.
3165 pub day: String,
3166 pub product: String,
3167 pub micros: i64,
3168}
3169
3170/// How much of an allowance is used, in the meter's unit.
3171#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3172#[serde(rename_all = "camelCase")]
3173pub struct Allowance {
3174 pub used: f64,
3175 pub of: f64,
3176 /// `bytes`, `operations`, `dollars`…
3177 pub unit: String,
3178}
3179
3180/// One project's part of a meter.
3181#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3182#[serde(rename_all = "camelCase")]
3183pub struct ProjectUsage {
3184 /// `owner/name`, or empty for usage that is not one project's.
3185 pub project: String,
3186 pub micros: i64,
3187 pub quantity: f64,
3188}
3189
3190/// One meter over the range.
3191#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3192#[serde(rename_all = "camelCase")]
3193pub struct MeterLine {
3194 /// `agent_models`, `agent_rate`, `sandbox`, `builds`…
3195 pub key: String,
3196 pub label: String,
3197 pub product: String,
3198 /// What `quantity` counts: `tokens`, `seconds`, `bytes`, `operations`,
3199 /// `entries`.
3200 pub unit: String,
3201 pub quantity: f64,
3202 /// At price.
3203 pub micros: i64,
3204 /// Of `micros`, metered this month and charged when it closes.
3205 #[serde(default)]
3206 pub pending_micros: i64,
3207 /// Every day of the range, oldest first, at price: the sparkline.
3208 pub daily: Vec<i64>,
3209 #[serde(default)]
3210 pub allowance: Option<Allowance>,
3211 pub by_project: Vec<ProjectUsage>,
3212}
3213
3214/// A part of a product, such as the agent's runs, reviews and plans.
3215#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3216#[serde(rename_all = "camelCase")]
3217pub struct FeatureUsage {
3218 pub key: String,
3219 pub label: String,
3220 pub micros: i64,
3221 pub count: u32,
3222}
3223
3224/// One product family over the range.
3225#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3226#[serde(rename_all = "camelCase")]
3227pub struct ProductUsage {
3228 pub key: String,
3229 pub label: String,
3230 pub micros: i64,
3231 pub meters: Vec<MeterLine>,
3232 /// For the agent: by what it was doing (runs, reviews, plans, checks).
3233 #[serde(default)]
3234 pub features: Vec<FeatureUsage>,
3235}
3236
3237#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3238#[serde(rename_all = "camelCase")]
3239pub struct UsageReport {
3240 pub from: String,
3241 pub until: String,
3242 pub totals: UsageTotals,
3243 /// Each day and product with usage, oldest first.
3244 pub days: Vec<UsageDay>,
3245 /// Every product family, in order, even with nothing used.
3246 pub products: Vec<ProductUsage>,
3247 /// Every project with usage in the range, for the filter.
3248 pub projects: Vec<String>,
3249 /// The plan's included usage this month, when the workspace has it.
3250 #[serde(default)]
3251 pub included: Option<Allowance>,
3252 /// The account's discount, in percent, when it has one.
3253 #[serde(default)]
3254 pub discount_percent: Option<u32>,
3255 /// AI credit left now, and credit from g1t for everything.
3256 pub ai_credit_micros: i64,
3257 pub credit_micros: i64,
3258 /// The trial credit left, for a workspace on its trial.
3259 #[serde(default)]
3260 pub trial_micros: Option<i64>,
3261 pub plan: PlanKind,
3262 /// Nothing is charged while g1t is being built out.
3263 pub free: bool,
3264}
3265
3266// --- AI credit -----------------------------------------------------------------
3267
3268/// Auto-reload: when AI credit falls below `threshold_micros`, the saved
3269/// card is charged to bring it back to `target_micros`, at most
3270/// `monthly_max_micros` in a calendar month. Off by default. A failed
3271/// charge turns it off and tells the owners.
3272#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3273#[serde(rename_all = "camelCase")]
3274pub struct AiReload {
3275 pub enabled: bool,
3276 pub threshold_micros: i64,
3277 pub target_micros: i64,
3278 pub monthly_max_micros: i64,
3279 /// Reloaded this month so far.
3280 #[serde(default)]
3281 pub reloaded_micros: i64,
3282 /// When it last failed and was turned off, and why.
3283 #[serde(default)]
3284 pub failed_at: Option<String>,
3285 #[serde(default)]
3286 pub error: Option<String>,
3287}
3288
3289/// The card fee passed on when AI credit is bought by card.
3290#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3291#[serde(rename_all = "camelCase")]
3292pub struct CardFee {
3293 pub on: bool,
3294 /// Per dollar charged, in millionths: 29,000 is 2.9%.
3295 pub percent_micros: f64,
3296 pub fixed_cents: u32,
3297}
3298
3299/// `ai_credit` (`AccountArgs`): a workspace's prepaid AI credit, what it
3300/// pays for and how it is bought. Members only. Returns `Outcome<AiCredit>`.
3301#[derive(Clone, Debug, Serialize, Deserialize)]
3302#[serde(rename_all = "camelCase")]
3303pub struct AiCredit {
3304 /// What is left to spend on Agent and AI Gateway usage.
3305 pub balance_micros: i64,
3306 /// Of it, bought (paid) and given (promotional).
3307 pub purchased_micros: i64,
3308 pub given_micros: i64,
3309 /// Its grants, newest first.
3310 pub grants: Vec<CreditGrant>,
3311 /// A 100% discount: AI usage is free, shown at its price then the
3312 /// discount. Nothing to buy.
3313 pub free_via_discount: bool,
3314 /// Invoiced terms (an enterprise): models are billed after use, so no
3315 /// credit is needed.
3316 pub postpaid: bool,
3317 /// Whether new runs on g1t's models are refused now for want of credit.
3318 pub blocked: bool,
3319 /// Whether the workspace may buy it: on the plan, not free.
3320 pub can_buy: bool,
3321 pub presets_cents: Vec<u32>,
3322 pub min_cents: u32,
3323 pub max_cents: u32,
3324 pub card_fee: CardFee,
3325 pub reload: AiReload,
3326 /// The agent rate per million tokens, now, at price.
3327 pub agent_rate_micros: f64,
3328 /// The markup on models' provider price, in percent.
3329 pub model_markup_percent: u32,
3330 /// The markup on AI Gateway's provider price, in percent.
3331 pub gateway_markup_percent: u32,
3332 /// The AI credit given once on starting the plan.
3333 pub upgrade_credit_micros: i64,
3334 /// How long bought credit lasts, in days.
3335 pub expires_days: u32,
3336}
3337
3338/// `buy_ai_credit`: Stripe's page to buy AI credit, one payment by card,
3339/// with the card fee as its own line. Owners only. Returns
3340/// `Outcome<Checkout>`; the page's id comes back to `return_url` as
3341/// `ai_credit`, for `confirm_ai_credit`.
3342#[derive(Debug, Serialize, Deserialize)]
3343#[serde(rename_all = "camelCase")]
3344pub struct BuyAiCreditArgs {
3345 pub actor: User,
3346 pub workspace: String,
3347 /// The credit, in cents; the card fee is added on top.
3348 #[serde(alias = "amount_cents")]
3349 pub amount_cents: u32,
3350 #[serde(alias = "return_url")]
3351 pub return_url: String,
3352}
3353
3354/// `confirm_ai_credit`: credits a purchase once Stripe says it was paid,
3355/// once. Safe to repeat; the webhook does the same. Returns
3356/// `Outcome<AiCredit>`.
3357#[derive(Debug, Serialize, Deserialize)]
3358pub struct ConfirmAiCreditArgs {
3359 pub workspace: String,
3360 pub viewer: Viewer,
3361 pub session: String,
3362}
3363
3364/// `set_ai_reload`: auto-reload's settings. Owners only. Returns
3365/// `Outcome<AiCredit>`.
3366#[derive(Debug, Serialize, Deserialize)]
3367#[serde(rename_all = "camelCase")]
3368pub struct SetAiReloadArgs {
3369 pub actor: User,
3370 pub workspace: String,
3371 pub enabled: bool,
3372 #[serde(alias = "threshold_micros")]
3373 pub threshold_micros: i64,
3374 #[serde(alias = "target_micros")]
3375 pub target_micros: i64,
3376 #[serde(alias = "monthly_max_micros")]
3377 pub monthly_max_micros: i64,
3378}
3379
3380// --- Budgets ------------------------------------------------------------------
3381
3382/// `set_budget`: the monthly budget on usage after included usage: the
3383/// owners' spend limit, its alerts, whether usage pauses at 100%, and an
3384/// optional webhook. Owners only. Returns `Outcome<Limit>`.
3385#[derive(Debug, Serialize, Deserialize)]
3386#[serde(rename_all = "camelCase")]
3387pub struct SetBudgetArgs {
3388 pub actor: User,
3389 pub workspace: String,
3390 /// The amount; None keeps the automatic one.
3391 #[serde(default, alias = "amount_micros")]
3392 pub amount_micros: Option<i64>,
3393 /// Some of 50, 75, 90 and 100.
3394 #[serde(default)]
3395 pub alerts: Vec<u32>,
3396 #[serde(default = "yes", alias = "pause_at_limit")]
3397 pub pause_at_limit: bool,
3398 /// An HTTPS address, or None for no webhook.
3399 #[serde(default)]
3400 pub webhook: Option<String>,
3401 /// Leave the spend limit as it is and change only the alerts, the
3402 /// pause and the webhook.
3403 #[serde(default, alias = "keep_limit")]
3404 pub keep_limit: bool,
3405}
3406
3407// --- Billing details -----------------------------------------------------------
3408
3409/// A postal address, as Stripe keeps it.
3410#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3411#[serde(rename_all = "camelCase")]
3412pub struct PostalAddress {
3413 #[serde(default)]
3414 pub line1: String,
3415 #[serde(default)]
3416 pub line2: String,
3417 #[serde(default)]
3418 pub city: String,
3419 #[serde(default)]
3420 pub state: String,
3421 #[serde(default)]
3422 pub postal_code: String,
3423 /// Two letters, `US`.
3424 #[serde(default)]
3425 pub country: String,
3426}
3427
3428/// The default way the workspace pays, as far as it is safe to show.
3429#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3430#[serde(rename_all = "camelCase")]
3431pub struct PaymentMethod {
3432 /// `card`, or another kind Stripe has.
3433 pub kind: String,
3434 #[serde(default)]
3435 pub brand: Option<String>,
3436 #[serde(default)]
3437 pub last4: Option<String>,
3438 #[serde(default)]
3439 pub exp_month: Option<u32>,
3440 #[serde(default)]
3441 pub exp_year: Option<u32>,
3442}
3443
3444/// One of the customer's invoices at Stripe: the plan, activations, AI
3445/// credit and month-end usage.
3446#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
3447#[serde(rename_all = "camelCase")]
3448pub struct StripeInvoice {
3449 pub id: String,
3450 #[serde(default)]
3451 pub number: Option<String>,
3452 /// `paid`, `open`, `void`, `uncollectible` or `draft`.
3453 pub status: String,
3454 pub total_cents: i64,
3455 pub currency: String,
3456 /// RFC 3339.
3457 pub created_at: String,
3458 #[serde(default)]
3459 pub description: Option<String>,
3460 #[serde(default)]
3461 pub hosted_url: Option<String>,
3462 #[serde(default)]
3463 pub pdf_url: Option<String>,
3464}
3465
3466/// What the next invoice will be, from g1t's own ledger.
3467#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3468#[serde(rename_all = "camelCase")]
3469pub struct UpcomingInvoice {
3470 /// When the month closes, RFC 3339.
3471 pub closes_at: String,
3472 /// The plan and activations, at their monthly price.
3473 pub subscriptions_micros: i64,
3474 /// Usage still owed, after included usage, credit and any discount.
3475 pub usage_micros: i64,
3476 pub total_micros: i64,
3477}
3478
3479/// `billing_details` (`AccountArgs`): who the invoices are for, the default
3480/// payment method, and the invoices, from the Stripe customer. Members see
3481/// it; owners change it. Returns `Outcome<BillingDetails>`.
3482#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
3483#[serde(rename_all = "camelCase")]
3484pub struct BillingDetails {
3485 /// Whether the workspace has a Stripe customer yet.
3486 pub customer: bool,
3487 pub email: Option<String>,
3488 pub name: Option<String>,
3489 pub address: Option<PostalAddress>,
3490 /// `eu_vat`, `us_ein`…, and its value.
3491 pub tax_id_type: Option<String>,
3492 pub tax_id: Option<String>,
3493 /// Printed on invoices.
3494 pub po_number: Option<String>,
3495 /// The invoices' language, such as `en` or `fr`.
3496 pub language: Option<String>,
3497 pub payment_method: Option<PaymentMethod>,
3498 pub invoices: Vec<StripeInvoice>,
3499 pub upcoming: UpcomingInvoice,
3500 /// Stripe could not be read: what is shown is what g1t keeps.
3501 #[serde(default)]
3502 pub unavailable: Option<String>,
3503}
3504
3505/// `set_billing_details`: saves the invoice details on the Stripe customer.
3506/// Owners only. Absent fields are left as they are; an empty string clears
3507/// one. Returns `Outcome<BillingDetails>`.
3508#[derive(Debug, Serialize, Deserialize)]
3509#[serde(rename_all = "camelCase")]
3510pub struct SetBillingDetailsArgs {
3511 pub actor: User,
3512 pub workspace: String,
3513 #[serde(default)]
3514 pub email: Option<String>,
3515 #[serde(default)]
3516 pub name: Option<String>,
3517 #[serde(default)]
3518 pub address: Option<PostalAddress>,
3519 #[serde(default, alias = "tax_id_type")]
3520 pub tax_id_type: Option<String>,
3521 #[serde(default, alias = "tax_id")]
3522 pub tax_id: Option<String>,
3523 #[serde(default, alias = "po_number")]
3524 pub po_number: Option<String>,
3525 #[serde(default)]
3526 pub language: Option<String>,
3527}
3528
Models per workspace: several providers, routed by kind of work3529#[cfg(test)]
3530mod tests {
3531 use super::*;
3532
3533 #[test]
Prices are what g1t pays plus 20%, from the first second3534 fn an_account_carries_no_run_fee() {
3535 let account = Account {
3536 workspace: "acme".into(),
3537 balance_micros: 0,
3538 status: Status { enabled: true, live: false, free: false },
3539 margin_percent: 20,
3540 card: None,
3541 };
3542 let json = serde_json::to_value(account).unwrap();
3543 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
3544 keys.sort_unstable();
3545 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
3546 }
3547
3548 #[test]
3549 fn a_price_change_says_when_the_markup_moved() {
3550 let change = PriceChange {
3551 meter: "sandbox_second".into(),
3552 old_cost_micros: 21.0,
3553 new_cost_micros: 21.0,
3554 markup_percent: 20,
3555 old_markup_percent: Some(138),
3556 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
3557 created_at: "2026-10-05T00:00:00Z".into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily3558 effective_at: None,
Prices are what g1t pays plus 20%, from the first second3559 };
3560 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
3561 let cost_only = PriceChange { old_markup_percent: None, ..change };
3562 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
3563 }
3564
3565 #[test]
Paid features: a workspace turns on Deployments with a monthly plan3566 fn features_are_named_as_the_site_sends_them() {
3567 assert_eq!(
3568 serde_json::to_value(Feature::Deployments).unwrap(),
3569 serde_json::json!("deployments")
3570 );
3571 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look3572 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
3573 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
3574 // Older readers named the plan Team.
3575 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
3576 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
Teams and CODEOWNERS, labels and milestones, dependency updates, the security suite, and a clearer top bar3577 assert_eq!(Feature::ALL, [Feature::Plan, Feature::Security]);
3578 assert_eq!(Feature::parse("security"), Some(Feature::Security));
3579 assert_eq!(serde_json::to_value(Feature::Security).unwrap(), serde_json::json!("security"));
Paid features: a workspace turns on Deployments with a monthly plan3580 assert!(SubscriptionStatus::Canceling.on());
3581 assert!(!SubscriptionStatus::PastDue.on());
3582 }
3583
3584 #[test]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look3585 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
3586 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
3587 "workspace": "acme",
3588 "repo": { "namespace": "acme", "name": "web" },
3589 "public": true,
3590 "kind": "check",
3591 "estimateMicros": 2_000_000,
3592 }))
3593 .unwrap();
3594 assert_eq!(asked.kind, ComputeKind::Check);
3595 assert!(asked.kind.open_source_pool());
3596 assert!(!ComputeKind::Agent.open_source_pool());
3597 // Rust callers that write snake_case are read too.
3598 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
3599 "workspace": "acme",
3600 "repo": { "namespace": "acme", "name": "web" },
3601 "public": false,
3602 "kind": "agent",
3603 "estimate_micros": 1,
3604 }))
3605 .unwrap();
3606 assert_eq!(snake.estimate_micros, 1);
3607 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
3608 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
3609 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
3610 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
3611 }
3612
3613 #[test]
3614 fn a_refusal_carries_its_own_code() {
3615 let refused: crate::Outcome<Reservation> =
3616 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
3617 let json = serde_json::to_value(&refused).unwrap();
3618 assert_eq!(json["error"]["code"], "oss_pool_empty");
3619 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
3620 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
3621 }
3622
3623 #[test]
Models per workspace: several providers, routed by kind of work3624 fn who_pays_is_read_as_the_runner_sends_it() {
3625 let run: StartRunArgs = serde_json::from_value(serde_json::json!({
3626 "workspace": "acme",
3627 "repo": { "namespace": "acme", "name": "web" },
3628 "number": 7,
3629 "task": "implement",
3630 "model": "Claude Sonnet 5.5",
3631 "billedTo": "workspace",
3632 }))
3633 .unwrap();
3634 assert_eq!(run.billed_to, "workspace");
3635 }
3636}

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