g1t/crates/contracts/src/billing.rs

2,800 lines100,923 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,
Agents as a team: lifecycle, merge queue, billing and a new shell193}
194
Integrations: your own model provider, alerts that open issues, tickets agents read195fn g1t() -> String {
196 "g1t".to_owned()
197}
198
Agents as a team: lifecycle, merge queue, billing and a new shell199/// `account` (`Outcome<Account>`) and `ledger` (`Outcome<Vec<LedgerEntry>>`,
200/// newest first). Members of the workspace only.
201#[derive(Debug, Serialize, Deserialize)]
202pub struct AccountArgs {
203 pub workspace: String,
204 pub viewer: Viewer,
205}
206
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look207/// `checkout`: prepays usage: money paid in advance, drawn down by usage
208/// after the plan's included usage, which raises what can be used before
209/// work stops by the same amount at once. $25 at the least. By card, with
210/// 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 shell211/// only. Returns `Outcome<Checkout>`.
212#[derive(Debug, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct CheckoutArgs {
215 pub actor: User,
216 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look217 /// How much to prepay, in cents.
Agents as a team: lifecycle, merge queue, billing and a new shell218 pub amount_cents: u32,
219 /// Where the payment page sends the person afterwards. The payment's
220 /// id is appended as `session`.
221 pub return_url: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look222 /// `card` (the default) or `bank_transfer` (from $1,000): Stripe gives
223 /// the account details, and the money counts once it arrives.
224 #[serde(default)]
225 pub method: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell226}
227
228#[derive(Debug, Serialize, Deserialize)]
229pub struct Checkout {
230 /// The payment page to send the person to.
231 pub url: String,
232}
233
234/// `confirm`: credits a payment once the provider says it was made. Safe
235/// to call any number of times. Returns `Outcome<Account>`.
236#[derive(Debug, Serialize, Deserialize)]
237pub struct ConfirmArgs {
238 pub workspace: String,
239 pub viewer: Viewer,
240 /// The payment's id, as returned to `return_url`.
241 pub session: String,
242}
243
244/// `can_start`: whether a workspace may start an agent now, asked before
245/// anything is opened for it. Returns `Outcome<bool>`: a failure, with the
246/// reason to show, when it has no credit.
247#[derive(Debug, Serialize, Deserialize)]
248pub struct CanStartArgs {
249 pub workspace: String,
250}
251
252/// `start_run`: asks whether a workspace may start an agent, and opens the
253/// run it will be charged for. Called by the runner service. Returns
254/// `Outcome<Option<RunTicket>>`: no ticket when billing is off, a failure
255/// when the workspace has no credit.
256#[derive(Debug, Serialize, Deserialize)]
257pub struct StartRunArgs {
258 pub workspace: String,
259 pub repo: RepoPath,
260 pub number: u32,
261 /// `implement`, `review` or `update`.
262 pub task: String,
263 /// The model, by its public name.
264 pub model: String,
Integrations: your own model provider, alerts that open issues, tickets agents read265 /// `workspace` when the run uses the workspace's own model provider.
Models per workspace: several providers, routed by kind of work266 /// The runner, which is TypeScript, sends it as `billedTo`.
267 #[serde(default = "g1t", alias = "billedTo")]
Integrations: your own model provider, alerts that open issues, tickets agents read268 pub billed_to: String,
Prices keep themselves current with what g1t pays269 /// The model session's id, when its requests go through g1t's AI
270 /// Gateway: settling charges the run what the gateway priced them at.
271 #[serde(default)]
272 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 tier273 /// `small` or `large`: the tier g1t routed the run to, when g1t pays
274 /// for its model. None on the workspace's own provider.
275 #[serde(default)]
276 pub tier: Option<String>,
Agents as a team: lifecycle, merge queue, billing and a new shell277}
278
279#[derive(Clone, Debug, Serialize, Deserialize)]
280#[serde(rename_all = "camelCase")]
281pub struct RunTicket {
282 pub run_id: String,
283 /// Lets the sandbox, and nothing else, report what this run cost.
284 pub token: String,
285}
286
287/// `finish_run`: what a run cost, as its sandbox reports it. Charged once.
288/// Returns `Outcome<bool>`.
289#[derive(Debug, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct FinishRunArgs {
292 pub run_id: String,
293 pub token: String,
294 /// What the model provider charged, in US dollars.
295 pub cost_usd: f64,
296 #[serde(default)]
297 pub turns: u32,
298}
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request299
300
301/// `usage`: what a workspace's agents cost over a period, broken down.
302/// Members only. Returns `Outcome<Usage>`.
303#[derive(Debug, Serialize, Deserialize)]
304pub struct UsageArgs {
305 pub workspace: String,
306 pub viewer: Viewer,
307 /// RFC 3339: the start of the period. The period runs to now.
308 pub since: String,
309}
310
311/// One slice of usage: what it was for, what it cost, how many runs.
312#[derive(Clone, Debug, Serialize, Deserialize)]
313#[serde(rename_all = "camelCase")]
314pub struct UsageSlice {
315 pub key: String,
316 pub micros: i64,
317 pub runs: u32,
318}
319
320/// What a workspace's agents cost over a period.
321#[derive(Clone, Debug, Serialize, Deserialize)]
322#[serde(rename_all = "camelCase")]
323pub struct Usage {
324 pub since: String,
325 /// Charged, including g1t's margin.
326 pub spent_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read327 /// What g1t's model provider charged, before the margin.
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request328 pub cost_micros: i64,
Integrations: your own model provider, alerts that open issues, tickets agents read329 /// What runs on the workspace's own provider cost there, as the harness
330 /// estimated it. Not charged by g1t.
331 pub provider_micros: i64,
Usage while free is shown at cost; agents get rustfmt and clippy332 /// What the runs used, at cost: g1t's models and the workspace's own
333 /// provider together, whatever was charged for them.
334 pub used_micros: i64,
335 /// g1t charges nothing for now. The slices then measure usage at cost,
336 /// since every charge is zero.
337 pub free: bool,
Usage, like a hosting provider's: what agents cost, per day, task, repository and pull request338 pub runs: u32,
339 /// Spend per day (`YYYY-MM-DD`) and task, as `day/task` keys.
340 pub by_day: Vec<UsageSlice>,
341 /// Per task: implement, review, revise, update, plan.
342 pub by_task: Vec<UsageSlice>,
343 /// Per repository, `namespace/name`.
344 pub by_repo: Vec<UsageSlice>,
345 /// The pull requests that cost most, as `namespace/name#number`.
346 pub by_pull: Vec<UsageSlice>,
347 /// Per model, by its public name.
348 pub by_model: Vec<UsageSlice>,
349 /// Credit bought in the period.
350 pub added_micros: i64,
351}
Models per workspace: several providers, routed by kind of work352
Mission control shows model usage, yours and the workspace's: tokens, cost, active days, cache share, each day, and the mix353/// `record_tokens`: what one model answer used, added to the day's count
354/// for its run. The model proxy sends it after each answer. For usage
355/// views only: runs are still priced from AI Gateway. Returns
356/// `Outcome<bool>`: false when there was nothing to count.
357#[derive(Debug, Serialize, Deserialize)]
358#[serde(rename_all = "camelCase")]
359pub struct RecordTokensArgs {
360 pub workspace: String,
361 /// The model session's id (`ModelSession::id`), one per run.
362 pub session: String,
363 /// The person the run is for, by username. Absent when nobody asked.
364 #[serde(default)]
365 pub person: Option<String>,
366 pub model: String,
367 /// On g1t's hosted models: `small` or `large`.
368 #[serde(default)]
369 pub tier: Option<String>,
370 #[serde(default)]
371 pub input: u64,
372 #[serde(default)]
373 pub output: u64,
374 #[serde(default)]
375 pub cache_read: u64,
376 #[serde(default)]
377 pub cache_write: u64,
378}
379
380/// `token_usage`: the model tokens a workspace's runs used, day by day,
381/// for the whole workspace or for one person. Members only; a member may
382/// ask only for themselves, an owner for anyone. Returns
383/// `Outcome<TokenUsage>`.
384#[derive(Debug, Serialize, Deserialize)]
385pub struct TokenUsageArgs {
386 pub workspace: String,
387 pub viewer: Viewer,
388 /// A username: only the runs for them.
389 #[serde(default)]
390 pub person: Option<String>,
391 /// How many days, to today: 42 when absent, 366 at most.
392 #[serde(default)]
393 pub days: Option<u32>,
394}
395
396/// One day's tokens.
397#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
398pub struct DayTokens {
399 /// `YYYY-MM-DD`, UTC.
400 pub day: String,
401 pub tokens: u64,
402}
403
404/// The model tokens runs used over a window of days.
405#[derive(Clone, Debug, Serialize, Deserialize)]
406#[serde(rename_all = "camelCase")]
407pub struct TokenUsage {
408 /// `YYYY-MM-DD`: the first day counted.
409 pub since: String,
410 pub days: u32,
411 /// Null for the whole workspace.
412 pub person: Option<String>,
413 pub total_tokens: u64,
414 pub input_tokens: u64,
415 pub output_tokens: u64,
416 pub cache_read_tokens: u64,
417 pub cache_write_tokens: u64,
418 /// What those runs were charged, as `usage` measures it.
419 pub cost_micros: i64,
420 /// Days in the window with any tokens.
421 pub active_days: u32,
422 /// Every day in the window, oldest first, zeros included.
423 pub by_day: Vec<DayTokens>,
424}
425
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look426/// What a workspace pays a monthly price for. There is one plan, `plan`
427/// ("g1t"): a flat price per workspace, never per person, with included
428/// usage each month, more private storage, and deployments. Never free:
429/// `FREE_WHILE_BUILDING` does not cover it.
430///
431/// `deployments` is not sold on its own any more: it comes with the plan.
432/// A service that asks `has_feature` for it is told whether the workspace
433/// has the plan, and a Deployments subscription bought before the change
434/// keeps working until its period ends.
Paid features: a workspace turns on Deployments with a monthly plan435#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
436#[serde(rename_all = "snake_case")]
437pub enum Feature {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look438 /// The g1t plan. Older readers called it `team`.
439 #[serde(alias = "team")]
440 Plan,
441 /// Previews per pull request and production on g1t.page: part of the
442 /// plan.
Paid features: a workspace turns on Deployments with a monthly plan443 Deployments,
444}
445
446impl Feature {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look447 /// What is sold: the plan alone.
448 pub const ALL: [Feature; 1] = [Feature::Plan];
Paid features: a workspace turns on Deployments with a monthly plan449
450 pub fn as_str(self) -> &'static str {
451 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look452 Feature::Plan => "plan",
Paid features: a workspace turns on Deployments with a monthly plan453 Feature::Deployments => "deployments",
454 }
455 }
456
457 pub fn parse(name: &str) -> Option<Feature> {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look458 match name {
459 "plan" | "team" => Some(Feature::Plan),
460 "deployments" => Some(Feature::Deployments),
461 _ => None,
462 }
Paid features: a workspace turns on Deployments with a monthly plan463 }
464
465 pub fn title(self) -> &'static str {
466 match self {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look467 Feature::Plan => "g1t",
Paid features: a workspace turns on Deployments with a monthly plan468 Feature::Deployments => "Deployments",
469 }
470 }
471}
472
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas473/// What deployments cost g1t, in millionths of a dollar: fallbacks for
474/// when billing's price book cannot be read. Nothing here is an allowance:
475/// on the plan every unit is metered from the first, at cost plus the
476/// margin, and drawn from the plan's included usage before anything is
477/// charged. Projects, previews and the apps behind them are not metered at
478/// all: Cloudflare's Workers for Platforms includes far more scripts than
479/// g1t runs, so an app costs g1t only the requests and CPU it answers with.
480pub mod deployment_costs {
481 /// Workers for Platforms: $0.30 per million requests.
Paid features: a workspace turns on Deployments with a monthly plan482 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 quotas483 /// $0.02 per million CPU milliseconds.
Paid features: a workspace turns on Deployments with a monthly plan484 pub const MICROS_PER_MILLION_CPU_MS: i64 = 20_000;
Deployments: a preview for every pull request, production on g1t.page485 /// 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 look486 /// Containers, standard-1: half a vCPU, 4 GiB, 8 GB disk), rounded up,
487 /// as the price keeper measured it on 2026-10-05 (14.5). Only a
488 /// fallback: billing charges builds at the price book's `build_second`,
489 /// which the keeper keeps current.
490 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 quotas491 /// What one custom hostname costs g1t a month (Cloudflare for SaaS):
492 /// $0.10.
Agents and memory, checks and conflicts, profiles, slug renames, custom domains493 pub const MICROS_PER_DOMAIN_MONTH: i64 = 100_000;
Paid features: a workspace turns on Deployments with a monthly plan494}
495
Every sandbox is metered by the second496/// `record_sandbox`: how long one sandbox ran for a workspace, reported by
Prices are what g1t pays plus 20%, from the first second497/// the runner when it stops. Every sandbox g1t starts for a workspace
498/// (agents, reviews, checks, the merge queue, workflow jobs) is metered by
499/// the second, from the first: recorded once per `reference`, with what it
500/// cost g1t, and charged at the price book's `sandbox_second` price unless
501/// `FREE_WHILE_BUILDING`. Deploy builds are charged by the Deployments plan
502/// instead.
Every sandbox is metered by the second503/// Returns `Outcome<bool>`: false if that reference was recorded before.
504#[derive(Debug, Serialize, Deserialize)]
505#[serde(rename_all = "camelCase")]
506pub struct RecordSandboxArgs {
507 pub workspace: String,
508 pub seconds: u32,
509 /// What ran, e.g. `Checks on acme/api#12`.
510 pub description: String,
511 /// `namespace/name`.
512 pub repo: Option<String>,
513 /// Unique to the run.
514 pub reference: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look515 /// What ran: `agent`, `check`, `workflow` or `queue`. Decides whether
516 /// g1t's open-source pool may pay for it (checks, workflows and the
517 /// merge queue on public repositories). Absent: not the pool.
518 #[serde(default)]
519 pub kind: Option<ComputeKind>,
520 /// The vCPU-seconds the sandbox used, when it can tell. With it, the
521 /// run is priced on its own CPU (`sandbox_base_second` per second plus
522 /// `sandbox_cpu_second` per vCPU-second); without it, at the average
523 /// (`sandbox_second`).
524 #[serde(default, alias = "cpu_seconds")]
525 pub cpu_seconds: Option<f64>,
526 /// The reservation the work started under, settled with this cost.
527 #[serde(default, alias = "reservation_id")]
528 pub reservation_id: Option<String>,
Fast pages, required checks on the branch, self-hosted runners, honest incidents529 /// It ran on one of the workspace's self-hosted runners: recorded as
530 /// self-hosted time, for the minutes, at $0.
531 #[serde(default, alias = "self_hosted")]
532 pub self_hosted: bool,
533 /// The machine it ran on, by label (`g1t-4core`); absent, the standard
534 /// one. A larger machine's memory and disk cost more each second.
535 #[serde(default)]
536 pub instance: Option<String>,
Every sandbox is metered by the second537}
538
Usage limits: unpaid usage can only go so far539/// How much a workspace has earned g1t's trust with money, which sets how
540/// far its unpaid usage can go before its work stops.
541#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
542#[serde(rename_all = "snake_case")]
543pub enum Trust {
544 /// No live payment yet: only a little past the free allowances.
545 New,
546 /// Has paid g1t real money: the ceiling grows with what it has paid.
547 Paid,
Two limits, real invoices, trust that grows by itself, sales signals548 /// Has paid steadily for months, with nothing disputed or declined:
549 /// the ceiling follows its monthly spend, up to $10,000, by itself.
550 Established,
Usage limits: unpaid usage can only go so far551 /// A ceiling g1t set by hand, after talking to the workspace.
552 Reviewed,
553 /// g1t's own workspaces: no ceiling.
554 Internal,
555}
556
557#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
558#[serde(rename_all = "snake_case")]
559pub enum LimitState {
560 Ok,
561 /// Past 80% of the ceiling.
562 Warning,
563 /// At or past it: no new sandboxes, builds or app requests.
564 Stopped,
565}
566
567/// How far a workspace's unpaid usage has gone this month, and where its
568/// work stops: like Fly's or Cloudflare's limits for new accounts, so no
569/// one runs up costs g1t cannot collect. Usage counts at what it cost g1t
570/// or what it is charged, whichever is more, so it counts while g1t is
571/// free too.
572#[derive(Clone, Debug, Serialize, Deserialize)]
573#[serde(rename_all = "camelCase")]
574pub struct Limit {
575 pub workspace: String,
Billing accounts, terms and enterprises; g1t is no longer free576 /// The account that pays, whose usage and payments the limit counts:
577 /// the workspace's own, or its enterprise's.
578 #[serde(default)]
579 pub account: String,
580 #[serde(default)]
581 pub account_name: String,
Usage limits: unpaid usage can only go so far582 pub trust: Trust,
583 /// Usage this month (UTC) less what was paid this month.
584 pub exposure_micros: i64,
585 /// Where work stops: the lower of g1t's ceiling and the owner's own
586 /// spend limit. None for g1t's own workspaces.
587 pub ceiling_micros: Option<i64>,
588 /// The ceiling g1t sets from `trust`.
589 pub trust_ceiling_micros: Option<i64>,
590 /// The owner's own monthly limit, if they set one.
591 pub spend_limit_micros: Option<i64>,
592 pub state: LimitState,
593 /// What to tell people when work is stopped or close to it.
594 pub message: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals595 /// Charged this month, which the spend limit is measured against.
596 #[serde(default)]
597 pub spent_micros: i64,
598 /// True while the owners have not chosen a spend limit of their own, so
599 /// the automatic one applies: $200, or twice last month's spend.
600 #[serde(default)]
601 pub default_spend_limit: bool,
602 /// The most the owners may set their own limit to: g1t's ceiling. To
603 /// go past it, they contact g1t.
604 #[serde(default)]
605 pub available_micros: Option<i64>,
606 /// How the ceiling grows from here, in a sentence.
607 #[serde(default)]
608 pub growth: Option<String>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look609 /// Money paid in advance and not used yet. It raises what can be used
610 /// before work stops by the same amount, at once.
611 #[serde(default)]
612 pub prepaid_micros: i64,
613 /// The highest ceiling the workspace has ever had. Owners may set their
614 /// spend limit anywhere up to it (plus what is prepaid) without asking.
615 #[serde(default)]
616 pub max_ceiling_micros: Option<i64>,
617 /// The most the owners may raise the limit to themselves, once, with
618 /// `raise_once`: twice the highest ceiling. None once it is used.
619 #[serde(default)]
620 pub raise_once_micros: Option<i64>,
621 /// When the one-time raise was used, RFC 3339.
622 #[serde(default)]
623 pub raised_at: Option<String>,
624 /// True in a paid workspace's first billing cycle, when the ceiling is
625 /// the starting one (`LIMIT_PAID_START_MICROS`).
626 #[serde(default)]
627 pub first_month: bool,
Usage limits: unpaid usage can only go so far628}
629
630/// `limit`: a workspace's limit, for its members. Returns `Outcome<Limit>`.
631#[derive(Debug, Serialize, Deserialize)]
632pub struct LimitArgs {
633 pub workspace: String,
634 pub viewer: Viewer,
635}
636
637/// `check_limit`: the same, for the services that enforce it. Returns
638/// `Outcome<Limit>`.
639#[derive(Debug, Serialize, Deserialize)]
640pub struct CheckLimitArgs {
641 pub workspace: String,
642}
643
Prices keep themselves current with what g1t pays644/// `note_pending`: usage this month that will be charged later, such as
645/// app traffic past a plan, so the workspace's limit counts it now. Each
646/// report replaces the last for that workspace, source and month. Called
647/// by the service that meters it. Returns `bool`.
648#[derive(Debug, Serialize, Deserialize)]
649#[serde(rename_all = "camelCase")]
650pub struct NotePendingArgs {
651 pub workspace: String,
Fast pages, required checks on the branch, self-hosted runners, honest incidents652 /// `deployments`, `security` (scans), `context` (search embeddings),
653 /// `storage` or `cache` (actions/cache, plan only). Billing charges
654 /// `security`, `context`, `storage` and `cache` itself once the month
655 /// is over; `deployments` charges its own.
Prices keep themselves current with what g1t pays656 pub source: String,
657 /// What it cost g1t so far this month, before the margin.
658 pub cost_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas659 /// How much of it, for the Billing page: `1.2 million requests and
660 /// 3.4 million CPU ms`, `2 custom domains`.
661 #[serde(default)]
662 pub detail: Option<String>,
Prices keep themselves current with what g1t pays663}
664
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas665/// `usage_meters`: this month's usage for a workspace, one line per kind
666/// of meter, at what it is charged (cost plus the margin, on the account's
667/// terms) before the plan's included usage, the trial or g1t's pools paid
668/// for any of it. Members only. Returns `Outcome<Vec<MeterUsage>>`.
669#[derive(Debug, Serialize, Deserialize)]
670pub struct UsageMetersArgs {
671 pub workspace: String,
672 pub viewer: Viewer,
673}
674
675/// One kind of meter's usage this month.
676#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
677#[serde(rename_all = "camelCase")]
678pub struct MeterUsage {
679 /// `agents` (agent runs, models and sandboxes for checks, workflows
680 /// and the merge queue), `builds`, `requests` (app requests and CPU),
681 /// `domains`, `git_storage` (git operations and private storage) or
682 /// `search_scans` (search embeddings and security scans).
683 pub key: String,
684 pub label: String,
685 /// At price, before what paid for it.
686 pub micros: i64,
687 /// How much, when it is known: `12 runs`, `41 build minutes`.
688 #[serde(default)]
689 pub quantity: Option<String>,
690}
691
Usage limits: unpaid usage can only go so far692/// `set_spend_limit`: the owner's own monthly ceiling, under g1t's; None
693/// removes it. Owners only. Returns `Outcome<Limit>`.
694#[derive(Debug, Serialize, Deserialize)]
695#[serde(rename_all = "camelCase")]
696pub struct SetSpendLimitArgs {
697 pub actor: User,
698 pub workspace: String,
Two limits, real invoices, trust that grows by itself, sales signals699 /// A monthly limit, at most what is available; None goes back to the
700 /// default.
Usage limits: unpaid usage can only go so far701 pub spend_limit_micros: Option<i64>,
Two limits, real invoices, trust that grows by itself, sales signals702 /// Use everything available, with no limit of their own.
703 #[serde(default)]
704 pub use_full_limit: bool,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look705 /// Use the one-time raise: up to twice the highest ceiling the
706 /// workspace has had, without asking. Once per workspace.
707 #[serde(default, alias = "raiseOnce")]
708 pub raise_once: bool,
Usage limits: unpaid usage can only go so far709}
710
Prices keep themselves current with what g1t pays711/// One metered unit: what it costs g1t, and what it is sold at. The price
712/// is always `cost × (100 + markup) / 100`, so it follows the cost.
713#[derive(Clone, Debug, Serialize, Deserialize)]
714#[serde(rename_all = "camelCase")]
715pub struct Price {
716 /// `sandbox_second`, `build_second`, `app_requests`, `app_cpu`, `app_month`.
717 pub meter: String,
718 pub title: String,
719 pub unit: String,
720 /// Millionths of a dollar per unit; may have a fraction.
721 pub cost_micros: f64,
722 pub markup_percent: u32,
723 pub price_micros: f64,
724 /// `list`: Cloudflare's published price. `cloudflare`: what Cloudflare
725 /// actually billed g1t, measured.
726 pub source: String,
727 /// When it was last checked against Cloudflare's bill.
728 pub checked_at: Option<String>,
729 pub updated_at: String,
730}
731
732impl Price {
733 pub fn price_for(cost_micros: f64, markup_percent: u32) -> f64 {
734 cost_micros * f64::from(100 + markup_percent) / 100.0
735 }
736}
737
738/// A cost that moved.
739#[derive(Clone, Debug, Serialize, Deserialize)]
740#[serde(rename_all = "camelCase")]
741pub struct PriceChange {
742 pub meter: String,
743 pub old_cost_micros: f64,
744 pub new_cost_micros: f64,
745 pub markup_percent: u32,
Prices are what g1t pays plus 20%, from the first second746 /// The markup before, when the change was to the markup rather than
747 /// to the cost. Absent when the markup stayed `markup_percent`.
748 #[serde(default, skip_serializing_if = "Option::is_none")]
749 pub old_markup_percent: Option<u32>,
Prices keep themselves current with what g1t pays750 pub reason: String,
751 pub created_at: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily752 /// When a change still to come takes effect: a rise is announced
753 /// before it is charged. Absent for changes already made.
754 #[serde(default, skip_serializing_if = "Option::is_none")]
755 pub effective_at: Option<String>,
Prices keep themselves current with what g1t pays756}
757
758/// `prices`: every metered price and the recent changes. Public. Returns
759/// `PriceBook`.
760#[derive(Clone, Debug, Serialize, Deserialize)]
761#[serde(rename_all = "camelCase")]
762pub struct PriceBook {
763 pub prices: Vec<Price>,
764 pub changes: Vec<PriceChange>,
765 /// The margin on model usage, which is charged at what AI Gateway
766 /// priced each request at.
767 pub model_margin_percent: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put768 /// Every plan, as it is sold now.
769 #[serde(default)]
770 pub plans: Vec<Plan>,
771 /// What is free, and what pays for it.
772 #[serde(default)]
773 pub free: Option<FreeTier>,
Prices keep themselves current with what g1t pays774}
775
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put776/// What g1t gives without a plan, each with what pays for it: a capped
777/// budget, never an open-ended allowance.
778#[derive(Clone, Debug, Default, Serialize, Deserialize)]
779#[serde(rename_all = "camelCase")]
780pub struct FreeTier {
781 /// Each new workspace's trial credit, once.
782 pub trial_workspace_micros: i64,
783 /// Trial grants each month, in all; new trials wait when it is spent.
784 pub trial_monthly_pool_micros: i64,
785 /// g1t's open-source pool each month, and any one repository's share.
786 pub oss_pool_micros: i64,
787 pub oss_repo_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas788 /// Private repository storage that is free for every workspace. Past
789 /// it, the plan pays at cost plus the margin; a free workspace's pushes
790 /// to private repositories stop instead.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put791 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 sudo792 /// 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 put793 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 sudo794 /// Days of audit log the g1t plan keeps, and g1t's own and enterprise
795 /// workspaces. Longer is by arrangement, set per account in sudo.
796 #[serde(default)]
797 pub plan_audit_retention_days: u32,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look798 /// The smallest amount a card is charged when a month closes; less
799 /// 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 put800 pub min_charge_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas801 /// Git operations (clones, fetches and pushes through g1t) that are
802 /// free for every workspace each month. Past it, the plan pays at cost
803 /// plus the margin and is never slowed; a free workspace is slowed
804 /// down, never charged.
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look805 #[serde(default)]
806 pub git_operations_included: u64,
807 /// A new paid workspace's ceiling in its first month.
808 #[serde(default)]
809 pub paid_start_ceiling_micros: i64,
810 /// The most a one-click goodwill credit can cost g1t.
811 #[serde(default)]
812 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 put813}
814
Billing accounts, terms and enterprises; g1t is no longer free815/// Who pays: a billing account. Every workspace has one; by default its
816/// own. An enterprise account pays for several workspaces at once, as
817/// GitHub Enterprise does: one bill, one limit, one set of terms.
818#[derive(Clone, Debug, Serialize, Deserialize)]
819#[serde(rename_all = "camelCase")]
820pub struct BillingAccount {
821 /// `ws_<slug>` for a workspace's own account; `ent_…` for an enterprise.
822 pub id: String,
823 pub kind: AccountKind,
824 pub name: String,
825 pub terms: Terms,
826 /// The workspaces it pays for.
827 pub workspaces: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both828 /// Where an enterprise's invoices go.
829 #[serde(default)]
830 pub billing_email: Option<String>,
831 /// An enterprise's invoices, newest first. Empty for a workspace's own.
832 #[serde(default)]
833 pub invoices: Vec<EnterpriseInvoice>,
Billing accounts, terms and enterprises; g1t is no longer free834 pub created_at: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put835 /// What g1t staff set for the account beyond its terms.
836 #[serde(default)]
837 pub allowances: Allowances,
838}
839
840/// Set per account by g1t staff in sudo, on top of its terms.
841#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
842#[serde(rename_all = "camelCase")]
843pub struct Allowances {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look844 /// The g1t plan without paying for its monthly price, such as for a
845 /// partner. Usage is charged as usual. Comped accounts have it anyway.
846 #[serde(default, alias = "team")]
847 pub plan: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put848 /// Each of the account's public repositories' monthly cap on g1t's
849 /// open-source pool, in place of `OSS_REPO_MICROS`. None: the default.
850 #[serde(default)]
851 pub oss_repo_micros: Option<i64>,
852 /// The trial credit each of its workspaces gets, in place of
853 /// `TRIAL_WORKSPACE_MICROS`, outside the monthly pool. None: the default.
854 #[serde(default)]
855 pub trial_micros: Option<i64>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look856 /// Agents at once, in place of the plan's (2 in the first month or on
857 /// the trial, then 10). None: the default.
858 #[serde(default)]
859 pub max_concurrent_agents: Option<u32>,
860 /// One run's spend cap, in place of `RUN_CAP_MICROS` and the owners'
861 /// own. None: theirs, or the default.
862 #[serde(default)]
863 pub run_cap_micros: Option<i64>,
864 /// What the agents on one issue may spend in all, in place of
865 /// `ISSUE_CAP_MICROS` and the owners' own. None: theirs, or the default.
866 #[serde(default)]
867 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 sudo868 /// Days of audit log its workspaces keep, in place of the plan's (7
869 /// free, 90 on the plan), longer or shorter. None: the plan's.
870 #[serde(default)]
871 pub audit_retention_days: Option<u32>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look872 /// A hold g1t staff put on new compute, with why. None: no hold.
873 #[serde(default)]
874 pub hold: Option<String>,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put875}
876
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look877/// `admin_set_allowances`: the plan on or off without charge, overrides of
878/// the plan's caps, a hold, and the account's share of g1t's pools.
879/// 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 put880#[derive(Debug, Serialize, Deserialize)]
881pub struct AdminSetAllowancesArgs {
882 pub id: String,
883 pub allowances: Allowances,
884 pub note: String,
885 pub by: String,
886}
887
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look888// --- Entitlements, and compute started under a reservation -----------------
889//
890// Every service that starts compute (sandboxes for agents, checks,
891// workflows and the merge queue; builds; models; semantic search) asks
892// billing first:
893//
894// 1. `entitlements { workspace }` says what the workspace may do at all:
895// its plan, whether it may start compute, its caps, and whether compute
896// is paused.
897// 2. `reserve { workspace, repo, public, kind, estimate_micros }` holds the
898// work's estimated cost against what may pay for it, so that starts at
899// the same moment cannot overshoot the ceiling together. It answers who
900// pays first, or refuses with a stable code and a message for the owner.
901// 3. `settle { reservation_id, actual_micros }` releases the hold once the
902// work is done. The charge itself goes on the ledger the usual way
903// (`finish_run`, `record_sandbox`, `charge_feature`, `note_pending`).
904//
905// A reservation never settled expires after `RESERVATION_HOURS`.
906
907/// A reservation that is never settled stops holding after this long.
908pub const RESERVATION_HOURS: u64 = 3;
909/// What a ceiling reads as when there is none (g1t's own workspaces): a
910/// billion dollars, which JavaScript holds exactly.
911pub const UNLIMITED_MICROS: i64 = 1_000_000_000_000_000;
912
913/// What a workspace pays g1t on, as far as compute is concerned.
914#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
915#[serde(rename_all = "snake_case")]
916pub enum PlanKind {
917 /// No plan: the forge is free; compute only from a trial or g1t's
918 /// open-source pool, after a card check.
919 Free,
920 /// The g1t plan, paid for (or given by g1t staff without its price).
921 Paid,
922 /// g1t's own workspaces and Flagon's (comped terms): the plan without
923 /// being charged. Usage is still recorded at what it cost.
924 Internal,
925 /// Paid for by an enterprise account, invoiced.
926 Enterprise,
927}
928
929impl PlanKind {
930 pub fn as_str(self) -> &'static str {
931 match self {
932 PlanKind::Free => "free",
933 PlanKind::Paid => "paid",
934 PlanKind::Internal => "internal",
935 PlanKind::Enterprise => "enterprise",
936 }
937 }
938
939 /// Whether usage past what is included may be charged (on demand).
940 pub fn on_demand(self) -> bool {
941 !matches!(self, PlanKind::Free)
942 }
943}
944
945/// What compute is for.
946#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
947#[serde(rename_all = "snake_case")]
948pub enum ComputeKind {
949 /// An agent's run: its sandbox and its model.
950 Agent,
951 /// Checks on a pull request.
952 Check,
953 /// A workflow job.
954 Workflow,
955 /// The merge queue's checks.
956 Queue,
957 /// A deployment's build.
958 Deploy,
959 /// Semantic search: embeddings in the context hub.
960 Embedding,
961}
962
963impl ComputeKind {
964 pub fn as_str(self) -> &'static str {
965 match self {
966 ComputeKind::Agent => "agent",
967 ComputeKind::Check => "check",
968 ComputeKind::Workflow => "workflow",
969 ComputeKind::Queue => "queue",
970 ComputeKind::Deploy => "deploy",
971 ComputeKind::Embedding => "embedding",
972 }
973 }
974
975 /// Whether g1t's open-source pool may pay for it on a public
976 /// repository: checks, workflows and the merge queue only.
977 pub fn open_source_pool(self) -> bool {
978 matches!(self, ComputeKind::Check | ComputeKind::Workflow | ComputeKind::Queue)
979 }
980}
981
982/// Who pays first for reserved work. What the first source cannot cover
983/// falls to the next, in this order.
984#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
985#[serde(rename_all = "snake_case")]
986pub enum PaidBy {
987 /// The plan's included usage this month.
988 Credit,
989 /// The workspace's one-time trial credit.
990 Trial,
991 /// g1t's open-source pool.
992 Oss,
993 /// Charged to the workspace, at cost plus the margin.
994 OnDemand,
995}
996
997/// `entitlements`: what a workspace may do now, for the services that
998/// start compute and the pages that show it. Takes `EntitlementsArgs`;
999/// 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 put1000#[derive(Debug, Serialize, Deserialize)]
1001pub struct EntitlementsArgs {
1002 pub workspace: String,
1003}
1004
Audit logs are kept by plan: a week on free, 90 days on the plan, and what staff set for an account in sudo1005/// `audit_retention`: how many days of audit log each workspace keeps, for
1006/// the events service's daily purge. Takes `AuditRetentionArgs`; returns
1007/// `Vec<AuditRetention>`, one for each workspace asked about.
1008#[derive(Debug, Serialize, Deserialize)]
1009pub struct AuditRetentionArgs {
1010 pub workspaces: Vec<String>,
1011}
1012
1013#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1014pub struct AuditRetention {
1015 pub workspace: String,
1016 pub days: u32,
1017}
1018
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1019#[derive(Clone, Debug, Serialize, Deserialize)]
1020#[serde(rename_all = "camelCase")]
1021pub struct Entitlements {
1022 pub workspace: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1023 pub plan: PlanKind,
1024 /// May start sandboxes, models, deployments and semantic search at all:
1025 /// paid, internal and enterprise workspaces, or a free one with trial
1026 /// credit left. A free workspace may still use the open-source pool
1027 /// for checks, workflows and the merge queue on public repositories
1028 /// after a card check; `reserve` decides that per start.
1029 pub compute: bool,
1030 /// The one-time trial credit left; 0 if none was granted or it is used.
1031 pub trial_micros_left: i64,
1032 /// A card check has been done. The trial and the open-source pool need
1033 /// it.
1034 pub trial_verified: bool,
1035 /// A paid workspace still in its first billing cycle.
1036 pub first_month: bool,
1037 /// Agents at once: 2 in the first month or on the trial, 10 after;
1038 /// staff can override it.
1039 pub max_concurrent_agents: u32,
1040 /// The longest one run may take: 60 minutes in the first month or on
1041 /// the trial; otherwise the guardrails' own caps (`MAX_MINUTES`).
1042 pub max_run_minutes: u32,
1043 /// One run's spend cap (`RUN_CAP_MICROS`, $2 by default); staff can
1044 /// override it.
1045 pub run_cap_micros: i64,
1046 /// What agents may spend on one issue in all (`ISSUE_CAP_MICROS`, $10
1047 /// by default); the owners can set it (`set_caps`), and staff override.
1048 pub issue_cap_micros: i64,
1049 /// Where on-demand work stops: g1t's ceiling on usage not yet paid
1050 /// for. `UNLIMITED_MICROS` for g1t's own workspaces; 0 for a free one,
1051 /// which has no on-demand usage.
1052 pub ceiling_micros: i64,
1053 /// Usage not yet paid for this month, with prepayment taken off.
1054 pub exposure_micros: i64,
1055 /// Why new compute is paused, for the owner: the limit is reached, a
1056 /// spend spike is waiting for an owner to confirm it, or g1t staff put
1057 /// a hold on it. None when it is not.
1058 pub paused: Option<String>,
1059 // What the workspace's plan gives it, for its pages.
1060 /// What open reservations hold now.
1061 #[serde(default)]
1062 pub held_micros: i64,
1063 /// Paid in advance and not used yet.
1064 #[serde(default)]
1065 pub prepaid_micros: i64,
1066 /// The plan's included usage each month, and what of it is used.
1067 #[serde(default)]
1068 pub included_micros: i64,
1069 #[serde(default)]
1070 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 sudo1071 /// How far back the audit log can be read and exported, and what is
1072 /// 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 put1073 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 sudo1074 /// Whether `audit_retention_days` is what staff set for the account
1075 /// rather than the plan's.
1076 #[serde(default)]
1077 pub audit_retention_custom: bool,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1078 /// Private repository storage that is free for every workspace: past
1079 /// 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 put1080 pub free_private_storage_bytes: i64,
1081 /// The last daily measure of the workspace's private repositories.
1082 pub private_storage_bytes: i64,
1083 /// What g1t's open-source pool paid for the workspace this month.
1084 pub oss_paid_micros: i64,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1085 /// Deploy build time this month, every second of it metered.
1086 #[serde(default)]
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1087 pub build_seconds_used: u32,
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas1088 /// Git operations this month, and how many are free for every
1089 /// 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 look1090 #[serde(default)]
1091 pub git_operations: u64,
1092 #[serde(default)]
1093 pub git_operations_included: u64,
1094 /// 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 put1095 pub min_charge_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1096 /// A spend spike waiting for an owner, or decided.
1097 #[serde(default)]
1098 pub spike: Option<Spike>,
1099 /// Where usage stands against what is included and the limits, from 50%.
1100 #[serde(default)]
1101 pub alerts: Vec<UsageAlert>,
1102}
1103
1104/// One level reached: 50, 75, 90 or 100 percent of something.
1105#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1106#[serde(rename_all = "camelCase")]
1107pub struct UsageAlert {
1108 /// `included` (the plan's included usage), `spend_limit` (the owners'
1109 /// own limit) or `ceiling` (g1t's, on usage not yet paid for).
1110 pub meter: String,
1111 pub level: u32,
1112 pub used_micros: i64,
1113 pub limit_micros: i64,
1114 pub message: String,
Billing accounts, terms and enterprises; g1t is no longer free1115}
1116
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1117/// An hour's spend well above the workspace's usual pace: new compute
1118/// waits until an owner says to keep going.
1119#[derive(Clone, Debug, Serialize, Deserialize)]
1120#[serde(rename_all = "camelCase")]
1121pub struct Spike {
1122 pub id: String,
1123 /// `open` (waiting for an owner), `continued` (an owner said keep
1124 /// going) or `stopped` (an owner said stop).
1125 pub status: String,
1126 /// The hour's spend when it was found, and the usual hour's.
1127 pub hour_micros: i64,
1128 pub average_micros: i64,
1129 pub detected_at: String,
1130 #[serde(default)]
1131 pub decided_by: Option<String>,
1132 #[serde(default)]
1133 pub decided_at: Option<String>,
1134 /// While continued: until when, unless spend doubles again first.
1135 #[serde(default)]
1136 pub until: Option<String>,
1137}
1138
1139/// `reserve`: holds an estimate of a start's cost before the work starts.
1140/// Returns `Outcome<Reservation>`, or a failure whose code says why not:
1141///
1142/// - `paused`: a spend spike waiting for an owner, or a hold.
1143/// - `limit`: the spend limit or g1t's ceiling would be passed.
1144/// - `not_paid`: no plan, and nothing else pays for this kind of work (or
1145/// no card check yet).
1146/// - `trial_used`: the one-time trial is spent.
1147/// - `oss_pool_empty`: the open-source pool, or the repository's share of
1148/// it, is spent this month.
1149///
1150/// The message says exactly what to do, with the page to do it on (such as
1151/// `/acme/-/billing`).
1152#[derive(Debug, Serialize, Deserialize)]
1153#[serde(rename_all = "camelCase")]
1154pub struct ReserveArgs {
1155 pub workspace: String,
1156 pub repo: RepoPath,
1157 /// Whether the repository is public: the open-source pool pays only for
1158 /// public repositories' checks, workflows and merge queue.
1159 pub public: bool,
1160 pub kind: ComputeKind,
1161 /// The most the work is expected to cost g1t, before the margin, in
1162 /// millionths of a dollar (billing adds the margin, as it does to every
1163 /// charge). For an agent, its model's average plus its sandbox for its
1164 /// whole time cap.
1165 #[serde(alias = "estimate_micros")]
1166 pub estimate_micros: i64,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays1167 /// An agent run on g1t's hosted models (not the workspace's own
1168 /// provider). Unsaid, an agent run is taken to be one. g1t's daily
1169 /// spend breaker pauses these when g1t is paying for them.
1170 #[serde(default, alias = "hosted_model")]
1171 pub hosted_model: Option<bool>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1172}
1173
1174#[derive(Clone, Debug, Serialize, Deserialize)]
1175#[serde(rename_all = "camelCase")]
1176pub struct Reservation {
1177 pub id: String,
1178 pub paid_by: PaidBy,
1179 /// What is held, at cost; less than the estimate when a free
1180 /// workspace's last bit of trial credit is all there is.
1181 #[serde(default)]
1182 pub held_micros: i64,
1183 /// RFC 3339: when the hold lapses if never settled.
1184 #[serde(default)]
1185 pub expires_at: String,
1186}
1187
1188/// `settle`: releases a reservation's hold with what the work cost. The
1189/// charge goes on the ledger the usual way. Safe to repeat. Returns
1190/// `Outcome<bool>`: false if it was settled or had lapsed before.
1191#[derive(Debug, Serialize, Deserialize)]
1192#[serde(rename_all = "camelCase")]
1193pub struct SettleArgs {
1194 #[serde(alias = "reservation_id")]
1195 pub reservation_id: String,
1196 /// What the work cost g1t, before the margin.
1197 #[serde(alias = "actual_micros")]
1198 pub actual_micros: i64,
1199}
1200
1201// --- Card checks, the plan, prepayment -------------------------------------
1202
1203/// `card_check`: starts Stripe's page to save and verify a card: a setup
1204/// with 3-D Secure where the card supports it, which the card's bank sees
1205/// as a $0 or $1 authorization that is never charged. The trial and the
1206/// open-source pool need it, and it is the card the plan uses. Owners only.
1207/// Returns `Outcome<Checkout>`; the page's id comes back to `return_url` as
1208/// `session`, for `confirm_card_check`.
1209#[derive(Debug, Serialize, Deserialize)]
1210#[serde(rename_all = "camelCase")]
1211pub struct CardCheckArgs {
1212 pub actor: User,
1213 pub workspace: String,
1214 #[serde(alias = "return_url")]
1215 pub return_url: String,
1216}
1217
1218/// `confirm_card_check`: records the check once Stripe says the card was
1219/// verified, and grants the trial if the month's pool has room and the card
1220/// has not had one before. Safe to repeat. Returns `Outcome<Entitlements>`.
1221#[derive(Debug, Serialize, Deserialize)]
1222pub struct ConfirmCardCheckArgs {
1223 pub workspace: String,
1224 pub viewer: Viewer,
1225 pub session: String,
1226}
1227
1228// --- Limits: raising them, and spikes ---------------------------------------
1229
1230/// A request to g1t: a higher limit, or help with usage that went past
1231/// what was meant.
1232#[derive(Clone, Debug, Serialize, Deserialize)]
1233#[serde(rename_all = "camelCase")]
1234pub struct LimitRequest {
1235 pub id: String,
1236 pub workspace: String,
1237 /// `limit` (raise my limit) or `overage` (spent more than meant to).
1238 pub kind: String,
1239 /// The limit asked for; for an overage, what they think went wrong.
1240 pub amount_micros: i64,
1241 pub reason: String,
1242 pub expected_monthly_micros: i64,
1243 /// `open`, `approved` or `declined`.
1244 pub status: String,
1245 /// What was approved, which may differ from what was asked.
1246 #[serde(default)]
1247 pub decided_micros: Option<i64>,
1248 #[serde(default)]
1249 pub decided_by: Option<String>,
1250 /// The answer, as the owner sees it.
1251 #[serde(default)]
1252 pub answer: Option<String>,
1253 pub created_by: String,
1254 pub created_at: String,
1255 #[serde(default)]
1256 pub decided_at: Option<String>,
1257}
1258
1259/// `request_limit`: an owner asks g1t for more, or for help with usage past
1260/// what they meant. Answered within one business day, in the app and by
1261/// email. Owners only. Returns `Outcome<LimitRequest>`.
1262#[derive(Debug, Serialize, Deserialize)]
1263#[serde(rename_all = "camelCase")]
1264pub struct RequestLimitArgs {
1265 pub actor: User,
1266 pub workspace: String,
1267 /// `limit` or `overage`.
1268 pub kind: String,
1269 #[serde(alias = "amount_micros")]
1270 pub amount_micros: i64,
1271 pub reason: String,
1272 #[serde(default, alias = "expected_monthly_micros")]
1273 pub expected_monthly_micros: i64,
1274}
1275
1276/// `limit_requests`: a workspace's requests, newest first. Members only.
1277/// Returns `Outcome<Vec<LimitRequest>>`.
1278#[derive(Debug, Serialize, Deserialize)]
1279pub struct LimitRequestsArgs {
1280 pub workspace: String,
1281 pub viewer: Viewer,
1282}
1283
1284/// `confirm_spike`: an owner's answer to a spend spike. Keep going lifts the
1285/// pause for 24 hours, or until the hour's spend doubles again; stop keeps
1286/// new compute paused until an owner says to keep going. Owners only.
1287/// Returns `Outcome<Entitlements>`.
1288#[derive(Debug, Serialize, Deserialize)]
1289#[serde(rename_all = "camelCase")]
1290pub struct ConfirmSpikeArgs {
1291 pub actor: User,
1292 pub workspace: String,
1293 #[serde(alias = "keep_going")]
1294 pub keep_going: bool,
1295}
1296
1297/// `set_caps`: the owners' own caps on agents: one run's spend ($0.10 to
1298/// $100) and what the agents on one issue may spend in all ($1 to $1,000).
1299/// None goes back to the default ($2 and $10). A cap g1t staff set for the
1300/// account wins over both. Owners only. Returns `Outcome<Entitlements>`.
1301#[derive(Debug, Serialize, Deserialize)]
1302#[serde(rename_all = "camelCase")]
1303pub struct SetCapsArgs {
1304 pub actor: User,
1305 pub workspace: String,
1306 #[serde(default, alias = "run_cap_micros")]
1307 pub run_cap_micros: Option<i64>,
1308 #[serde(default, alias = "issue_cap_micros")]
1309 pub issue_cap_micros: Option<i64>,
1310}
1311
1312/// What staff see beside a request: the workspace's history with g1t.
1313#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1314#[serde(rename_all = "camelCase")]
1315pub struct WorkspaceHistory {
1316 pub plan: Option<PlanKind>,
1317 /// The last six months, oldest first.
1318 pub months: Vec<MonthFigures>,
1319 /// Live payments that have cleared, and how many.
1320 pub paid_cleared_micros: i64,
1321 pub payments: u32,
1322 pub disputes: u32,
1323 pub declines: u32,
1324 /// The first time the workspace appears in billing, RFC 3339.
1325 pub first_seen: Option<String>,
1326 pub ceiling_micros: Option<i64>,
1327 pub max_ceiling_micros: Option<i64>,
1328 pub spend_limit_micros: Option<i64>,
1329 /// Recent velocity: the last hour, the usual hour over the last week,
1330 /// and the last 24 hours, at price.
1331 pub last_hour_micros: i64,
1332 pub average_hour_micros: i64,
1333 pub last_day_micros: i64,
1334}
1335
1336#[derive(Clone, Debug, Serialize, Deserialize)]
1337#[serde(rename_all = "camelCase")]
1338pub struct LimitRequestReview {
1339 pub request: LimitRequest,
1340 pub history: WorkspaceHistory,
1341}
1342
1343/// `admin_limit_requests`: requests for staff, oldest open first. Returns
1344/// `Vec<LimitRequestReview>`.
1345#[derive(Debug, Default, Serialize, Deserialize)]
1346pub struct AdminLimitRequestsArgs {
1347 /// `open` (the default), `approved`, `declined` or `all`.
1348 #[serde(default)]
1349 pub status: Option<String>,
1350}
1351
1352/// `admin_decide_limit_request`: approve (at the amount asked, or
1353/// `amount_micros`) or decline. The owner is told in the app and by email.
1354/// Recorded with who and why. Returns `Outcome<LimitRequest>`.
1355#[derive(Debug, Serialize, Deserialize)]
1356pub struct AdminDecideLimitRequestArgs {
1357 pub id: String,
1358 /// `approve` or `decline`.
1359 pub decision: String,
1360 #[serde(default)]
1361 pub amount_micros: Option<i64>,
1362 /// What the owner is told, beside the decision.
1363 #[serde(default)]
1364 pub note: String,
1365 pub by: String,
1366}
1367
1368/// `admin_record_payment`: money that reached g1t outside the card pages,
1369/// such as a bank transfer, entered as a payment (it raises the limit like
1370/// one). Recorded with who and the transfer's reference. Returns
1371/// `Outcome<LedgerEntry>`.
1372#[derive(Debug, Serialize, Deserialize)]
1373pub struct AdminRecordPaymentArgs {
1374 pub workspace: String,
1375 pub amount_micros: i64,
1376 /// The bank's reference for the transfer, or Stripe's payment id.
1377 pub reference: String,
1378 pub note: String,
1379 pub by: String,
1380}
1381
1382// --- Overages and goodwill (sudo) --------------------------------------------
1383
1384/// What a one-time goodwill credit would come to: g1t's margin on the
1385/// overage, always, plus as much of its underlying cost as the cap allows.
1386#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
1387#[serde(rename_all = "camelCase")]
1388pub struct Goodwill {
1389 /// This month's charges above the workspace's typical month.
1390 pub overage_micros: i64,
1391 /// The part of the overage that is g1t's margin.
1392 pub margin_micros: i64,
1393 /// The part that is what g1t paid its providers.
1394 pub cost_micros: i64,
1395 /// The one-click credit: the margin plus the cost up to the cap.
1396 pub credit_micros: i64,
1397 /// Of the credit, the real cost g1t absorbs.
1398 pub absorbed_micros: i64,
1399}
1400
1401/// A workspace whose month went well past its usual, or hit a spike.
1402#[derive(Clone, Debug, Serialize, Deserialize)]
1403#[serde(rename_all = "camelCase")]
1404pub struct Overage {
1405 pub workspace: String,
1406 pub plan: PlanKind,
1407 /// The median of its last three months' charges.
1408 pub typical_month_micros: i64,
1409 pub this_month_micros: i64,
1410 /// What this month cost g1t, and what g1t keeps of it.
1411 pub cost_micros: i64,
1412 pub margin_micros: i64,
1413 /// A spike this month, if there was one.
1414 pub spike: Option<Spike>,
1415 /// The runs that cost the most this month.
1416 pub top_entries: Vec<LedgerEntry>,
1417 pub goodwill: Goodwill,
1418 /// False when a goodwill credit was given in the last 12 months.
1419 pub goodwill_available: bool,
1420 pub last_goodwill_at: Option<String>,
1421 /// An open overage request from the owner, if there is one.
1422 pub request: Option<LimitRequest>,
1423}
1424
1425/// `admin_overages`: the Overages queue. Returns `Vec<Overage>`.
1426#[derive(Debug, Default, Serialize, Deserialize)]
1427pub struct AdminOveragesArgs {}
1428
1429/// `admin_goodwill`: credits a workspace for accidental usage. With no
1430/// amount, the one-click credit (`Goodwill::credit_micros`), once per
1431/// workspace in 12 months. A larger amount, or a second within 12 months,
1432/// needs a typed reason. It shows on the statement as "Credit from g1t:
1433/// accidental usage on <date>". Returns `Outcome<LedgerEntry>`.
1434#[derive(Debug, Serialize, Deserialize)]
1435pub struct AdminGoodwillArgs {
1436 pub workspace: String,
1437 #[serde(default)]
1438 pub amount_micros: Option<i64>,
1439 /// Why, typed by staff; needed past the one-click credit.
1440 #[serde(default)]
1441 pub reason: String,
1442 /// The day the accidental usage happened, `YYYY-MM-DD`; today if absent.
1443 #[serde(default)]
1444 pub day: Option<String>,
1445 pub by: String,
1446}
1447
1448/// One workspace's recent pace, for sudo's velocity view.
1449#[derive(Clone, Debug, Serialize, Deserialize)]
1450#[serde(rename_all = "camelCase")]
1451pub struct Velocity {
1452 pub workspace: String,
1453 pub plan: PlanKind,
1454 pub last_hour_micros: i64,
1455 pub average_hour_micros: i64,
1456 pub last_day_micros: i64,
1457 pub this_month_micros: i64,
1458 /// The last hour over the usual hour; 0 with no history.
1459 pub ratio: f64,
1460 pub spike: Option<Spike>,
1461 pub first_seen: Option<String>,
1462}
1463
1464/// `admin_velocity`: workspaces spending in the last day, fastest first.
1465/// Returns `Vec<Velocity>`.
1466#[derive(Debug, Default, Serialize, Deserialize)]
1467pub struct AdminVelocityArgs {}
1468
Billing accounts, terms and enterprises; g1t is no longer free1469#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1470#[serde(rename_all = "snake_case")]
1471pub enum AccountKind {
1472 Workspace,
1473 Enterprise,
1474}
1475
1476/// How an account is charged. Standard unless g1t set otherwise in sudo.
1477#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1478#[serde(rename_all = "camelCase")]
1479pub struct Terms {
1480 pub kind: TermsKind,
1481 /// Off every usage charge, in percent. Custom terms only.
1482 #[serde(default)]
1483 pub discount_percent: u32,
1484 /// A ceiling on unpaid usage that replaces the one trust would give.
1485 #[serde(default)]
1486 pub ceiling_micros: Option<i64>,
1487 /// Why, for whoever looks next.
1488 #[serde(default)]
1489 pub note: String,
1490 /// When the terms end and the account goes back to standard.
1491 #[serde(default)]
1492 pub until: Option<String>,
1493 #[serde(default)]
1494 pub set_by: Option<String>,
1495 #[serde(default)]
1496 pub set_at: Option<String>,
1497}
1498
1499impl Terms {
1500 pub fn standard() -> Self {
1501 Terms {
1502 kind: TermsKind::Standard,
1503 discount_percent: 0,
1504 ceiling_micros: None,
1505 note: String::new(),
1506 until: None,
1507 set_by: None,
1508 set_at: None,
1509 }
1510 }
1511
1512 /// What a charge becomes under these terms.
1513 pub fn apply(&self, charge_micros: i64) -> i64 {
1514 match self.kind {
1515 TermsKind::Comped => 0,
1516 TermsKind::Custom => charge_micros * i64::from(100 - self.discount_percent.min(100)) / 100,
1517 TermsKind::Standard => charge_micros,
1518 }
1519 }
1520}
1521
1522#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1523#[serde(rename_all = "snake_case")]
1524pub enum TermsKind {
1525 /// Prices as published, limits by trust.
1526 Standard,
1527 /// Nothing charged; usage still recorded with its cost. Paid features
1528 /// are on without a plan. For g1t's own workspaces, partners, and the
1529 /// like.
1530 Comped,
1531 /// A discount, a ceiling, or both.
1532 Custom,
1533}
1534
Stripe webhooks, enterprise invoices, and sudo for both1535/// `stripe_webhook`: an event from Stripe, as the API received it: the raw
1536/// body and its `Stripe-Signature` header. Billing checks the signature
1537/// against the secret of the endpoint it registered, and handles each
1538/// event once. Returns `Outcome<bool>`: false for one already handled.
1539#[derive(Debug, Serialize, Deserialize)]
1540pub struct StripeWebhookArgs {
1541 pub payload: String,
1542 pub signature: String,
1543}
1544
1545/// `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 dashboard1546/// `StripeStatus`. With `fix: true`, first enables the destination at
1547/// billing's address and gives it the events billing needs.
Stripe webhooks, enterprise invoices, and sudo for both1548#[derive(Debug, Default, Serialize, Deserialize)]
1549pub struct AdminStripeArgs {
1550 #[serde(default)]
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1551 pub fix: bool,
Stripe webhooks, enterprise invoices, and sudo for both1552 #[serde(default)]
1553 pub by: Option<String>,
1554}
1555
1556#[derive(Clone, Debug, Serialize, Deserialize)]
1557#[serde(rename_all = "camelCase")]
1558pub struct StripeStatus {
1559 /// `test` or `live`, from the key; `off` without one.
1560 pub mode: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1561 /// Whether `STRIPE_WEBHOOK_SECRET` is set, so events can be checked.
1562 pub secret_set: bool,
1563 /// The destination at billing's address in Stripe, as Stripe has it.
Stripe webhooks, enterprise invoices, and sudo for both1564 pub webhook: Option<StripeWebhook>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1565 /// Events billing handles that the destination does not send.
1566 pub missing_events: Vec<String>,
Stripe webhooks, enterprise invoices, and sudo for both1567 /// The latest events handled, newest first.
1568 pub recent_events: Vec<StripeEventSummary>,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1569 /// What went wrong reading or fixing the destination, if it did.
Stripe webhooks, enterprise invoices, and sudo for both1570 pub error: Option<String>,
1571}
1572
1573#[derive(Clone, Debug, Serialize, Deserialize)]
1574#[serde(rename_all = "camelCase")]
1575pub struct StripeWebhook {
1576 pub url: String,
1577 pub endpoint_id: String,
Stripe's webhook secret is a Worker secret, STRIPE_WEBHOOK_SECRET, from a destination made in Stripe's dashboard1578 /// `enabled` or `disabled`.
1579 pub status: String,
Stripe webhooks, enterprise invoices, and sudo for both1580 pub events: Vec<String>,
1581 pub created_at: String,
1582}
1583
1584#[derive(Clone, Debug, Serialize, Deserialize)]
1585#[serde(rename_all = "camelCase")]
1586pub struct StripeEventSummary {
1587 pub id: String,
1588 pub kind: String,
1589 pub outcome: String,
1590 pub received_at: String,
1591}
1592
1593/// `admin_enterprise_billing`: where an enterprise's invoices go. Creates
1594/// or updates its Stripe customer. Returns `Outcome<BillingAccount>`.
1595#[derive(Debug, Serialize, Deserialize)]
1596pub struct AdminEnterpriseBillingArgs {
1597 pub id: String,
1598 pub email: String,
1599 pub by: String,
1600}
1601
1602/// `admin_invoice_enterprise`: sends an enterprise its invoice now, for
1603/// what its workspaces owe, rather than waiting for the month to close.
1604/// Returns `Outcome<EnterpriseInvoice>`.
1605#[derive(Debug, Serialize, Deserialize)]
1606pub struct AdminInvoiceEnterpriseArgs {
1607 pub id: String,
1608 pub by: String,
1609}
1610
1611/// An enterprise's invoice: one line per workspace, paid on Stripe.
1612#[derive(Clone, Debug, Serialize, Deserialize)]
1613#[serde(rename_all = "camelCase")]
1614pub struct EnterpriseInvoice {
1615 pub invoice_id: String,
1616 /// Stripe's page for it, where it is paid.
1617 pub hosted_url: Option<String>,
1618 pub amount_micros: i64,
1619 /// `open`, `paid`, `overdue` or `void`.
1620 pub status: String,
1621 pub period: String,
1622 pub lines: Vec<InvoiceLine>,
1623 pub created_at: String,
1624}
1625
1626#[derive(Clone, Debug, Serialize, Deserialize)]
1627#[serde(rename_all = "camelCase")]
1628pub struct InvoiceLine {
1629 pub workspace: String,
1630 pub amount_micros: i64,
1631}
1632
Two limits, real invoices, trust that grows by itself, sales signals1633/// A workspace's invoice from g1t: one per month, and one each time it is
1634/// charged near its limit. Itemised, charged to the card on file, and kept
1635/// in Stripe's billing page with its PDF.
1636#[derive(Clone, Debug, Serialize, Deserialize)]
1637#[serde(rename_all = "camelCase")]
1638pub struct WorkspaceInvoice {
1639 pub invoice_id: String,
1640 pub workspace: String,
1641 /// `month` (2026-10) or `threshold`.
1642 pub reason: String,
1643 pub period: String,
1644 pub amount_micros: i64,
1645 /// `paid`, `open`, `failed` or `void`.
1646 pub status: String,
1647 pub hosted_url: Option<String>,
1648 pub pdf_url: Option<String>,
1649 pub lines: Vec<InvoiceItem>,
1650 pub created_at: String,
1651}
1652
1653#[derive(Clone, Debug, Serialize, Deserialize)]
1654#[serde(rename_all = "camelCase")]
1655pub struct InvoiceItem {
1656 pub description: String,
1657 pub amount_micros: i64,
1658}
1659
1660/// `invoices`: a workspace's invoices from g1t, newest first. Members
1661/// only. Returns `Outcome<Vec<WorkspaceInvoice>>`.
1662#[derive(Debug, Serialize, Deserialize)]
1663pub struct InvoicesArgs {
1664 pub workspace: String,
1665 pub viewer: Viewer,
1666}
1667
1668/// `admin_workspace_invoices`: the same, for staff. Returns
1669/// `Vec<WorkspaceInvoice>`.
1670#[derive(Debug, Serialize, Deserialize)]
1671pub struct AdminWorkspaceInvoicesArgs {
1672 pub workspace: String,
1673}
1674
The statement is a month at a time, a line per kind of charge1675/// `statement`: a month of a workspace's ledger, grouped by day (or by
1676/// project) with a line per kind of charge. Members only. Returns
1677/// `Outcome<Statement>`.
1678#[derive(Debug, Serialize, Deserialize)]
1679pub struct StatementArgs {
1680 pub workspace: String,
1681 pub viewer: Viewer,
1682 /// YYYY-MM; this month when absent.
1683 #[serde(default)]
1684 pub month: Option<String>,
1685 /// `day` (the default) or `project`.
1686 #[serde(default)]
1687 pub group: Option<String>,
1688}
1689
1690#[derive(Clone, Debug, Serialize, Deserialize)]
1691#[serde(rename_all = "camelCase")]
1692pub struct Statement {
1693 pub month: String,
1694 /// Months with any entries, newest first.
1695 pub months: Vec<String>,
1696 pub groups: Vec<StatementGroup>,
1697 pub totals: StatementTotals,
1698}
1699
1700#[derive(Clone, Debug, Serialize, Deserialize)]
1701#[serde(rename_all = "camelCase")]
1702pub struct StatementGroup {
1703 /// The day (YYYY-MM-DD) or the project (`owner/name`, or empty).
1704 pub key: String,
1705 pub label: String,
1706 pub lines: Vec<StatementLine>,
1707 /// What the group's charges come to.
1708 pub charged_micros: i64,
1709}
1710
1711#[derive(Clone, Debug, Serialize, Deserialize)]
1712#[serde(rename_all = "camelCase")]
1713pub struct StatementLine {
1714 /// Agent runs, Sandbox time, Deployments, Payments, Credits from g1t,
Prices are what g1t pays plus 20%, from the first second1715 /// Refunds, and, for older entries, Runs on your own model provider.
The statement is a month at a time, a line per kind of charge1716 pub kind: String,
1717 pub count: u32,
1718 /// Charges positive; money in (payments, credits) negative.
1719 pub charged_micros: i64,
1720 pub cost_micros: i64,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1721 /// 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 look1722 /// by the plan's included usage, the trial credit, g1t's open-source
1723 /// 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 put1724 #[serde(default)]
1725 pub covered_micros: i64,
The statement is a month at a time, a line per kind of charge1726}
1727
1728#[derive(Clone, Debug, Serialize, Deserialize)]
1729#[serde(rename_all = "camelCase")]
1730pub struct StatementTotals {
1731 pub charged_micros: i64,
1732 pub paid_micros: i64,
1733 pub cost_micros: i64,
1734 pub entries: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1735 /// What paid for usage before it was charged, one line per source,
1736 /// such as "Paid by g1t's open-source pool".
1737 #[serde(default)]
1738 pub covered: Vec<Covered>,
1739 /// Owed when the month closed but under the minimum charge, so it
1740 /// carries over to the next invoice. Zero when nothing carried.
1741 #[serde(default)]
1742 pub carried_micros: i64,
1743}
1744
1745/// One source that paid for usage before it was charged.
1746#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
1747#[serde(rename_all = "camelCase")]
1748pub struct Covered {
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1749 /// `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 put1750 pub source: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1751 /// "Paid by your plan's included usage", "Paid by your trial credit",
1752 /// "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 put1753 pub label: String,
1754 pub micros: i64,
The statement is a month at a time, a line per kind of charge1755}
1756
1757/// `statement_entries`: one statement line's entries, newest first, 50 at
1758/// a time (`before` = the last id seen). Returns `Outcome<Vec<LedgerEntry>>`.
1759#[derive(Debug, Serialize, Deserialize)]
1760pub struct StatementEntriesArgs {
1761 pub workspace: String,
1762 pub viewer: Viewer,
1763 pub month: String,
1764 pub kind: String,
1765 #[serde(default)]
1766 pub day: Option<String>,
1767 #[serde(default)]
1768 pub project: Option<String>,
1769 #[serde(default)]
1770 pub before: Option<String>,
1771}
1772
Two limits, real invoices, trust that grows by itself, sales signals1773// --- Sales (sudo.g1t.sh) ------------------------------------------------------
1774//
1775// What staff need to know to reach out: who is growing, who is close to
1776// their limit, who was declined, who has become a steady customer. And what
1777// was done about it: a stage, an owner on g1t's side, a next step, notes.
1778
1779/// Why a workspace is worth a look.
1780#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1781#[serde(rename_all = "snake_case")]
1782pub enum SignalKind {
1783 /// At its limit, or its own spend limit: work is stopped.
1784 AtLimit,
1785 /// Past 80% of what is available to it: about to need more.
1786 NearCeiling,
1787 /// Its card was declined or a payment disputed.
1788 Declined,
1789 /// This month is well ahead of last month.
1790 Growing,
1791 /// Became Established: the ceiling now follows its spend.
1792 Established,
1793 /// Paid g1t for the first time.
1794 FirstPayment,
1795 /// Spending enough that custom terms or an enterprise may suit it.
1796 HighSpend,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily1797 /// Costs g1t more on Cloudflare than it pays, over 30 days: a pricing
1798 /// gap or abuse to look at (billing's `margin`).
1799 CostOverRevenue,
Two limits, real invoices, trust that grows by itself, sales signals1800}
1801
1802#[derive(Clone, Debug, Serialize, Deserialize)]
1803#[serde(rename_all = "camelCase")]
1804pub struct Signal {
1805 pub workspace: String,
1806 pub kind: SignalKind,
1807 /// One sentence, with the figures.
1808 pub detail: String,
1809 /// The figure that matters, such as this month's spend.
1810 pub value_micros: i64,
1811 /// Its sales stage, if staff gave it one.
1812 pub stage: Option<String>,
1813 pub owner: Option<String>,
Billing lists every invoice and every staff change; signals carry follow-ups1814 #[serde(default)]
1815 pub next_step: Option<String>,
1816 /// When the next step is due, `YYYY-MM-DD`.
1817 #[serde(default)]
1818 pub next_at: Option<String>,
1819}
1820
1821/// `admin_invoices`: every invoice g1t has sent, workspaces' and
1822/// enterprises', newest first. Returns `Vec<InvoiceSummary>`.
1823#[derive(Debug, Default, Serialize, Deserialize)]
1824pub struct AdminInvoicesArgs {
1825 /// `paid`, `open`, `failed`, `overdue` or `void`.
1826 #[serde(default)]
1827 pub status: Option<String>,
1828 /// YYYY-MM, by when it was sent.
1829 #[serde(default)]
1830 pub month: Option<String>,
1831}
1832
1833#[derive(Clone, Debug, Serialize, Deserialize)]
1834#[serde(rename_all = "camelCase")]
1835pub struct InvoiceSummary {
1836 pub invoice_id: String,
1837 /// `workspace` or `enterprise`.
1838 pub kind: String,
1839 /// The workspace's slug, or the enterprise's account id.
1840 pub account: String,
1841 /// What to call it: the workspace, or the enterprise's name.
1842 pub name: String,
1843 pub reason: String,
1844 pub period: String,
1845 pub amount_micros: i64,
1846 pub status: String,
1847 pub hosted_url: Option<String>,
1848 pub created_at: String,
1849 pub paid_at: Option<String>,
1850}
1851
1852/// `admin_audit`: every change made in sudo, and by Stripe, newest first.
1853/// Returns `Vec<AdminAction>`.
1854#[derive(Debug, Default, Serialize, Deserialize)]
1855pub struct AdminAuditArgs {
1856 #[serde(default)]
1857 pub by: Option<String>,
1858 #[serde(default)]
1859 pub action: Option<String>,
1860 /// Only those before this time, for paging.
1861 #[serde(default)]
1862 pub before: Option<String>,
Two limits, real invoices, trust that grows by itself, sales signals1863}
1864
1865/// `admin_signals`: every workspace worth reaching out to, most urgent
1866/// first. Returns `Vec<Signal>`.
1867#[derive(Debug, Default, Serialize, Deserialize)]
1868pub struct AdminSignalsArgs {}
1869
1870/// What staff are doing about a workspace.
1871#[derive(Clone, Debug, Serialize, Deserialize)]
1872#[serde(rename_all = "camelCase")]
1873pub struct SalesRecord {
1874 pub workspace: String,
1875 /// `none`, `lead`, `contacted`, `negotiating`, `won`, `lost` or `churn_risk`.
1876 pub stage: String,
1877 /// The staff member looking after it.
1878 pub owner: Option<String>,
1879 pub next_step: Option<String>,
1880 /// RFC 3339 date.
1881 pub next_at: Option<String>,
1882 pub notes: Vec<SalesNote>,
1883 pub updated_at: Option<String>,
1884}
1885
1886#[derive(Clone, Debug, Serialize, Deserialize)]
1887#[serde(rename_all = "camelCase")]
1888pub struct SalesNote {
1889 pub id: String,
1890 pub text: String,
1891 pub by: String,
1892 pub created_at: String,
1893}
1894
1895/// `admin_sales`: a workspace's sales record. Returns `SalesRecord`.
1896#[derive(Debug, Serialize, Deserialize)]
1897pub struct AdminSalesArgs {
1898 pub workspace: String,
1899}
1900
1901/// `admin_set_sales`: its stage, owner and next step. Returns `Outcome<SalesRecord>`.
1902#[derive(Debug, Serialize, Deserialize)]
1903pub struct AdminSetSalesArgs {
1904 pub workspace: String,
1905 pub stage: String,
1906 #[serde(default)]
1907 pub owner: Option<String>,
1908 #[serde(default)]
1909 pub next_step: Option<String>,
1910 #[serde(default)]
1911 pub next_at: Option<String>,
1912 pub by: String,
1913}
1914
1915/// `admin_add_note`. Returns `Outcome<SalesRecord>`.
1916#[derive(Debug, Serialize, Deserialize)]
1917pub struct AdminAddNoteArgs {
1918 pub workspace: String,
1919 pub text: String,
1920 pub by: String,
1921}
1922
1923/// `admin_overview`: the business at a glance. Returns `Overview`.
1924#[derive(Debug, Default, Serialize, Deserialize)]
1925pub struct AdminOverviewArgs {}
1926
1927#[derive(Clone, Debug, Serialize, Deserialize)]
1928#[serde(rename_all = "camelCase")]
1929pub struct Overview {
1930 /// YYYY-MM.
1931 pub month: String,
1932 /// The last six months, oldest first, all workspaces together.
1933 pub months: Vec<MonthFigures>,
1934 /// This month by kind of usage: models, sandbox, deployments, plans.
1935 pub by_kind: Vec<KindFigures>,
1936 pub paying_workspaces: u32,
1937 pub stopped: u32,
1938 pub near_ceiling: u32,
1939 pub declined: u32,
1940 /// Sent and not yet paid, workspaces and enterprises.
1941 pub open_invoices_micros: i64,
1942 /// Follow-ups due today or earlier.
1943 pub follow_ups_due: u32,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1944 /// The capped budgets g1t pays from, this month.
1945 #[serde(default)]
1946 pub pools: Option<Pools>,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1947 /// This month's revenue: usage charged plus the plan's price paid.
1948 #[serde(default)]
1949 pub revenue_micros: i64,
1950 /// Workspaces on the paid plan now, and what their price comes to a
1951 /// month.
1952 #[serde(default)]
1953 pub active_plans: u32,
1954 #[serde(default)]
1955 pub plan_mrr_micros: i64,
1956 /// What g1t gave this month, by source, apart from its margin.
1957 #[serde(default)]
1958 pub given: Vec<GivenFigures>,
1959 /// g1t's own and Flagon's workspaces this month: what their use cost,
1960 /// and why they are not charged.
1961 #[serde(default)]
1962 pub internal: Vec<InternalUse>,
1963 /// Open limit requests, and workspaces in the Overages queue.
1964 #[serde(default)]
1965 pub open_requests: u32,
1966 #[serde(default)]
1967 pub overages: u32,
1968 /// Spend spikes waiting for an owner.
1969 #[serde(default)]
1970 pub open_spikes: u32,
Two limits, real invoices, trust that grows by itself, sales signals1971}
1972
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look1973/// What g1t gave this month from one source.
1974#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1975#[serde(rename_all = "camelCase")]
1976pub struct GivenFigures {
1977 /// `internal`, `trial`, `oss_pool`, `goodwill` or `covered`.
1978 pub source: String,
1979 pub label: String,
1980 /// At price, and what it cost g1t.
1981 pub micros: i64,
1982 pub cost_micros: i64,
1983}
1984
1985/// One internal workspace's use this month.
1986#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
1987#[serde(rename_all = "camelCase")]
1988pub struct InternalUse {
1989 pub workspace: String,
1990 /// Why it is not charged: its terms' note.
1991 pub reason: String,
1992 pub cost_micros: i64,
1993 pub entries: u32,
1994}
1995
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1996/// g1t's capped budgets for free usage, this calendar month (UTC).
1997#[derive(Clone, Debug, Default, Serialize, Deserialize)]
1998#[serde(rename_all = "camelCase")]
1999pub struct Pools {
2000 /// YYYY-MM.
2001 pub month: String,
2002 /// Trial grants made this month, against the month's pool.
2003 pub trial_granted_micros: i64,
2004 pub trial_pool_micros: i64,
2005 pub trial_grants: u32,
2006 /// What the open-source pool paid this month, against its cap.
2007 pub oss_used_micros: i64,
2008 pub oss_pool_micros: i64,
2009 /// Each public repository's monthly cap on the pool.
2010 pub oss_repo_micros: i64,
2011}
2012
Two limits, real invoices, trust that grows by itself, sales signals2013#[derive(Clone, Debug, Serialize, Deserialize)]
2014#[serde(rename_all = "camelCase")]
2015pub struct KindFigures {
2016 pub kind: String,
2017 pub charged_micros: i64,
2018 pub cost_micros: i64,
2019}
2020
Billing accounts, terms and enterprises; g1t is no longer free2021// --- Staff (sudo.g1t.sh) ------------------------------------------------------
2022//
2023// Called only by the sudo app, which only g1t staff can reach (behind
2024// Cloudflare Access). Each change names who made it, and is kept in the
2025// audit log.
2026
2027/// `admin_accounts`: every billing account, with where each stands this
2028/// month. Returns `Vec<AccountSummary>`.
2029#[derive(Debug, Default, Serialize, Deserialize)]
2030pub struct AdminAccountsArgs {
2031 #[serde(default)]
2032 pub query: Option<String>,
Stripe webhooks, enterprise invoices, and sudo for both2033 /// Exactly these workspaces' accounts, such as one page of sudo's
2034 /// list; every account with activity when absent.
2035 #[serde(default)]
2036 pub workspaces: Option<Vec<String>>,
Billing accounts, terms and enterprises; g1t is no longer free2037}
2038
2039#[derive(Clone, Debug, Serialize, Deserialize)]
2040#[serde(rename_all = "camelCase")]
2041pub struct AccountSummary {
2042 pub account: BillingAccount,
2043 pub limit: Limit,
2044 /// Charged this month, after terms.
2045 pub charged_micros: i64,
2046 /// What this month's usage cost g1t.
2047 pub cost_micros: i64,
2048 /// Paid, ever.
2049 pub paid_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2050 /// The same figures for each of the account's workspaces that has
2051 /// any, so staff can see what one member of an enterprise used.
2052 #[serde(default)]
2053 pub by_workspace: Vec<WorkspaceFigures>,
Two limits, real invoices, trust that grows by itself, sales signals2054 /// The last six months, oldest first, for trends.
2055 #[serde(default)]
2056 pub months: Vec<MonthFigures>,
2057}
2058
2059/// One month of an account's billing.
2060#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2061#[serde(rename_all = "camelCase")]
2062pub struct MonthFigures {
2063 /// YYYY-MM.
2064 pub month: String,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2065 /// Usage charged, after what paid for it first.
Two limits, real invoices, trust that grows by itself, sales signals2066 pub charged_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2067 /// What usage cost g1t: only what g1t paid for, never a workspace's own
2068 /// model provider.
Two limits, real invoices, trust that grows by itself, sales signals2069 pub cost_micros: i64,
2070 pub paid_micros: i64,
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2071 /// The plan's monthly price, paid.
2072 #[serde(default)]
2073 pub plans_micros: i64,
2074 /// What g1t gave, at price: internal (comped) use, trials, the
2075 /// open-source pool, goodwill credits and what g1t covered. Not margin.
2076 #[serde(default)]
2077 pub given_micros: i64,
Billing on Stripe's pages, month-end charges, warnings; sudo by workspace2078}
2079
2080/// One workspace's share of an [`AccountSummary`].
2081#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2082#[serde(rename_all = "camelCase")]
2083pub struct WorkspaceFigures {
2084 pub workspace: String,
2085 pub charged_micros: i64,
2086 pub cost_micros: i64,
2087 pub paid_micros: i64,
Billing accounts, terms and enterprises; g1t is no longer free2088}
2089
2090/// `admin_account`: one account in full. Returns `Outcome<AccountDetail>`.
2091#[derive(Debug, Serialize, Deserialize)]
2092pub struct AdminAccountArgs {
2093 /// An account id, or a workspace slug.
2094 pub id: String,
2095}
2096
2097#[derive(Clone, Debug, Serialize, Deserialize)]
2098#[serde(rename_all = "camelCase")]
2099pub struct AccountDetail {
2100 pub summary: AccountSummary,
2101 /// Each workspace's limit, for an enterprise.
2102 pub workspaces: Vec<Limit>,
2103 pub ledger: Vec<LedgerEntry>,
2104 pub audit: Vec<AdminAction>,
2105}
2106
2107/// `admin_set_terms`. Returns `Outcome<BillingAccount>`.
2108#[derive(Debug, Serialize, Deserialize)]
2109pub struct AdminSetTermsArgs {
2110 pub id: String,
2111 pub terms: Terms,
2112 pub by: String,
2113}
2114
2115/// `admin_create_enterprise`. Returns `Outcome<BillingAccount>`.
2116#[derive(Debug, Serialize, Deserialize)]
2117pub struct AdminCreateEnterpriseArgs {
2118 pub name: String,
2119 pub workspaces: Vec<String>,
2120 pub by: String,
2121}
2122
2123/// `admin_attach`: moves a workspace onto an enterprise account, or back
2124/// onto its own with `account: None`. Returns `Outcome<BillingAccount>`.
2125#[derive(Debug, Serialize, Deserialize)]
2126pub struct AdminAttachArgs {
2127 pub workspace: String,
2128 pub account: Option<String>,
2129 pub by: String,
2130}
2131
2132/// `admin_credit`: money g1t gives a workspace, such as a refund or a
2133/// goodwill credit. Returns `Outcome<LedgerEntry>`.
2134#[derive(Debug, Serialize, Deserialize)]
2135pub struct AdminCreditArgs {
2136 pub workspace: String,
2137 pub amount_micros: i64,
2138 pub note: String,
2139 pub by: String,
2140}
2141
2142/// One change made in sudo.
2143#[derive(Clone, Debug, Serialize, Deserialize)]
2144#[serde(rename_all = "camelCase")]
2145pub struct AdminAction {
2146 pub id: String,
2147 pub account: String,
2148 pub action: String,
2149 pub detail: String,
2150 pub by: String,
2151 pub created_at: String,
2152}
2153
Paid features: a workspace turns on Deployments with a monthly plan2154/// What a feature's plan costs and includes.
2155#[derive(Clone, Debug, Serialize, Deserialize)]
2156#[serde(rename_all = "camelCase")]
2157pub struct Plan {
2158 pub feature: Feature,
2159 pub title: String,
2160 /// Charged every month while the plan is on, in cents.
2161 pub monthly_cents: u32,
2162 /// What the monthly price includes, one line each, for people to read.
2163 pub includes: Vec<String>,
2164 /// How usage past the allowance is charged, for people to read.
2165 pub overage: String,
2166}
2167
2168#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
2169#[serde(rename_all = "snake_case")]
2170pub enum SubscriptionStatus {
2171 /// Paid up; the feature works.
2172 Active,
2173 /// Paid up to the end of the period, and ends then.
2174 Canceling,
2175 /// The last payment failed; the feature is off until it is paid.
2176 PastDue,
2177 /// Ended.
2178 Canceled,
2179}
2180
2181impl SubscriptionStatus {
2182 /// Whether the feature works in this state.
2183 pub fn on(self) -> bool {
2184 matches!(self, SubscriptionStatus::Active | SubscriptionStatus::Canceling)
2185 }
2186}
2187
2188/// A workspace's plan for one feature.
2189#[derive(Clone, Debug, Serialize, Deserialize)]
2190#[serde(rename_all = "camelCase")]
2191pub struct Subscription {
2192 pub feature: Feature,
2193 pub status: SubscriptionStatus,
2194 /// RFC 3339: when the period paid for ends, and the plan renews or
2195 /// ends.
2196 pub period_end: Option<String>,
2197 /// Username of whoever turned it on.
2198 pub started_by: String,
2199 /// RFC 3339.
2200 pub started_at: String,
2201}
2202
2203/// A feature as a workspace sees it: what it costs, and its plan if it has
2204/// one.
2205#[derive(Clone, Debug, Serialize, Deserialize)]
2206#[serde(rename_all = "camelCase")]
2207pub struct FeatureState {
2208 pub plan: Plan,
2209 pub subscription: Option<Subscription>,
2210 /// Whether the feature works for the workspace now.
2211 pub on: bool,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2212 /// On without a plan: comped terms, or given by g1t. Nothing to pay
2213 /// and nothing to turn off.
2214 #[serde(default)]
2215 pub included: bool,
Paid features: a workspace turns on Deployments with a monthly plan2216}
2217
2218/// `features`: every paid feature and the workspace's plan for each.
2219/// Members only. Returns `Outcome<Vec<FeatureState>>`.
2220#[derive(Debug, Serialize, Deserialize)]
2221pub struct FeaturesArgs {
2222 pub workspace: String,
2223 pub viewer: Viewer,
2224}
2225
2226/// `subscribe`: starts the card page for a feature's monthly plan. Owners
2227/// only. Returns `Outcome<Checkout>`; the page's id comes back to
2228/// `return_url` as `session`, for `confirm_subscription`.
2229#[derive(Debug, Serialize, Deserialize)]
2230#[serde(rename_all = "camelCase")]
2231pub struct SubscribeArgs {
2232 pub actor: User,
2233 pub workspace: String,
2234 pub feature: Feature,
2235 pub return_url: String,
2236}
2237
2238/// `confirm_subscription`: turns the feature on once the processor says
2239/// the plan was paid for. Safe to call any number of times. Returns
2240/// `Outcome<FeatureState>`.
2241#[derive(Debug, Serialize, Deserialize)]
2242pub struct ConfirmSubscriptionArgs {
2243 pub workspace: String,
2244 pub viewer: Viewer,
2245 pub session: String,
2246}
2247
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2248/// `close_workspace`: settles a workspace that is about to be deleted.
2249/// Owners only. Refused while it has an invoice that failed, while it
2250/// holds prepaid credit, or while it owes money it cannot be charged for
2251/// now; otherwise what it owes is invoiced to its card at once (no
2252/// minimum), its plan is cancelled at Stripe straight away, and its
2253/// account is marked closed, so the month-end close, autopay and limit
2254/// warnings pass it by. Its ledger, invoices and statements stay. With
2255/// `dry_run`, only says whether it could, changing nothing. Returns
2256/// `Outcome<bool>`.
2257#[derive(Debug, Serialize, Deserialize)]
2258#[serde(rename_all = "camelCase")]
2259pub struct CloseWorkspaceArgs {
2260 pub actor: User,
2261 pub workspace: String,
2262 #[serde(default)]
2263 pub dry_run: bool,
2264}
2265
Paid features: a workspace turns on Deployments with a monthly plan2266/// `cancel_subscription` (`resume` false) ends a plan at the end of the
2267/// period paid for; with `resume` true, takes that back. Owners only.
2268/// Returns `Outcome<FeatureState>`.
2269#[derive(Debug, Serialize, Deserialize)]
2270pub struct CancelSubscriptionArgs {
2271 pub actor: User,
2272 pub workspace: String,
2273 pub feature: Feature,
2274 #[serde(default)]
2275 pub resume: bool,
2276}
2277
2278/// `has_feature`: whether a feature works for a workspace now, asked by the
2279/// service that provides it before doing paid work. Returns
2280/// `Outcome<bool>`: a failure, with the reason to show, when it does not.
2281/// True everywhere when no card processor is configured.
2282#[derive(Debug, Serialize, Deserialize)]
2283pub struct HasFeatureArgs {
2284 pub workspace: String,
2285 pub feature: Feature,
2286}
2287
2288/// `charge_feature`: usage of a feature past its plan's allowance, charged
2289/// from the workspace's credit at cost plus the margin, whatever
2290/// `FREE_WHILE_BUILDING` says. Called by the service that provides it.
2291/// Charged once per `reference`. Returns `Outcome<bool>`: false if that
2292/// reference was charged before.
2293#[derive(Debug, Serialize, Deserialize)]
2294#[serde(rename_all = "camelCase")]
2295pub struct ChargeFeatureArgs {
2296 pub workspace: String,
2297 pub feature: Feature,
2298 /// What it cost g1t, in millionths of a dollar, before the margin.
2299 pub cost_micros: i64,
2300 pub description: String,
2301 /// `namespace/name`, when the usage was one repository's.
2302 pub repo: Option<String>,
2303 /// Unique to this charge, e.g. `deployments/acme/2026-10`.
2304 pub reference: String,
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put2305 /// For a build: how long it ran. The plan's included build time this
2306 /// month pays for what it can, and only the rest of `cost_micros` is
2307 /// charged.
2308 #[serde(default)]
2309 pub build_seconds: Option<u32>,
Paid features: a workspace turns on Deployments with a monthly plan2310}
2311
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2312// ---------------------------------------------------------------------
2313// Costs and margin: what Cloudflare charges g1t against what g1t
2314// charges (billing's costs.rs, margin.rs and pricing.rs). Staff only.
2315// ---------------------------------------------------------------------
2316
2317/// `admin_costs`: the Costs & margin page. Returns `CostsReport`.
2318#[derive(Debug, Default, Serialize, Deserialize)]
2319pub struct AdminCostsArgs {
2320 /// How many days back, 7 to 90; 30 when absent.
2321 #[serde(default)]
2322 pub days: Option<u32>,
2323}
2324
2325/// One of g1t's products on one day.
2326#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2327#[serde(rename_all = "camelCase")]
2328pub struct CostDay {
2329 pub day: String,
2330 pub bucket: String,
2331 /// What Cloudflare charged g1t.
2332 pub cf_cost_micros: i64,
2333 /// What g1t's meters recorded it cost, at the price book's cost.
2334 pub own_cost_micros: i64,
2335 /// What customers were charged for it at price, before included
2336 /// usage, trials and pools paid for some.
2337 pub value_micros: i64,
2338 /// Of that, what workspaces paid.
2339 pub cash_micros: i64,
2340}
2341
2342/// One product over the range.
2343#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2344#[serde(rename_all = "camelCase")]
2345pub struct ProductMargin {
2346 pub bucket: String,
2347 pub title: String,
2348 /// The cost the margin is taken from: Cloudflare's bill, or g1t's own
2349 /// figure for what Cloudflare does not bill (models).
2350 pub cost_micros: i64,
2351 pub cf_cost_micros: i64,
2352 pub own_cost_micros: i64,
2353 pub value_micros: i64,
2354 pub margin_micros: i64,
2355 pub margin_percent: Option<f64>,
2356 /// `cloudflare` or `ledger`.
2357 pub cost_source: String,
2358 /// Running g1t itself, paid for by the plan.
2359 pub overhead: bool,
2360}
2361
2362/// All of g1t over the range: money in against every cost.
2363#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2364#[serde(rename_all = "camelCase")]
2365pub struct OverallMargin {
2366 /// What workspaces paid for usage, and for the plan.
2367 pub usage_micros: i64,
2368 pub plans_micros: i64,
2369 pub cost_micros: i64,
2370 pub margin_micros: i64,
2371 pub margin_percent: Option<f64>,
2372}
2373
2374/// A count, cost or leak that does not add up.
2375#[derive(Clone, Debug, Serialize, Deserialize)]
2376#[serde(rename_all = "camelCase")]
2377pub struct CostDrift {
2378 pub bucket: String,
2379 pub title: String,
2380 /// `count` (units g1t counted against Cloudflare's), `cost` (the bill
2381 /// against the price book's cost of the same usage), or `leak`.
2382 pub kind: String,
2383 pub ours: f64,
2384 pub cloudflare: f64,
2385 pub delta_percent: Option<f64>,
2386 pub detail: String,
2387 pub found_at: String,
2388}
2389
2390/// A margin alert, open while its condition lasts.
2391#[derive(Clone, Debug, Serialize, Deserialize)]
2392#[serde(rename_all = "camelCase")]
2393pub struct MarginAlert {
2394 pub id: String,
2395 /// `margin`, `overall`, `leak`, `drift` or `workspace`.
2396 pub kind: String,
2397 /// The product, or the workspace.
2398 pub subject: String,
2399 pub detail: String,
2400 pub since: String,
2401 pub opened_at: String,
2402 pub emailed_at: Option<String>,
2403}
2404
2405/// A change to a price the reconciler measured.
2406#[derive(Clone, Debug, Serialize, Deserialize)]
2407#[serde(rename_all = "camelCase")]
2408pub struct PriceProposal {
2409 pub id: String,
2410 pub meter: String,
2411 pub title: String,
2412 pub unit: String,
2413 pub current_cost_micros: f64,
2414 pub proposed_cost_micros: f64,
2415 pub change_percent: f64,
2416 pub markup_percent: u32,
2417 pub reason: String,
2418 /// `keeper` or `reconciler`.
2419 pub source: String,
2420 /// Far off the current cost: look before approving.
2421 pub suspect: bool,
2422 /// `open`, `applied`, `approved`, `rejected` or `superseded`.
2423 pub status: String,
2424 pub created_at: String,
2425 pub decided_at: Option<String>,
2426 pub decided_by: Option<String>,
2427 pub note: Option<String>,
2428 /// When it takes or took effect, once approved or applied.
2429 pub effective_at: Option<String>,
2430}
2431
2432/// One version of one meter's price. Never changed once written.
2433#[derive(Clone, Debug, Serialize, Deserialize)]
2434#[serde(rename_all = "camelCase")]
2435pub struct PriceVersion {
2436 pub id: String,
2437 pub meter: String,
2438 pub version: u32,
2439 pub cost_micros: f64,
2440 pub markup_percent: u32,
2441 pub price_micros: f64,
2442 pub effective_at: String,
2443 pub reason: String,
2444 pub created_by: String,
2445 /// When the price book took it on; absent while it waits for its date.
2446 pub applied_at: Option<String>,
2447}
2448
2449/// What a workspace cost g1t over the range, Cloudflare's costs shared
2450/// out by g1t's own meters, against what it paid.
2451#[derive(Clone, Debug, Serialize, Deserialize)]
2452#[serde(rename_all = "camelCase")]
2453pub struct WorkspaceCost {
2454 pub workspace: String,
2455 pub cost_micros: i64,
2456 pub revenue_micros: i64,
2457 /// One of g1t's own (comped) workspaces.
2458 pub internal: bool,
2459}
2460
2461/// One Cloudflare meter over the range, and the product it is a cost of.
2462#[derive(Clone, Debug, Serialize, Deserialize)]
2463#[serde(rename_all = "camelCase")]
2464pub struct CostLineSummary {
2465 pub product: String,
2466 pub meter: String,
2467 pub raw_name: String,
2468 pub unit: String,
2469 pub source: String,
2470 pub quantity: f64,
2471 pub cost_micros: i64,
2472 /// Absent when no mapping claims it.
2473 pub bucket: Option<String>,
2474}
2475
2476/// A row of the mapping from Cloudflare's meters to g1t's products.
2477#[derive(Clone, Debug, Serialize, Deserialize)]
2478#[serde(rename_all = "camelCase")]
2479pub struct CostMapping {
2480 pub product: String,
2481 pub meter: String,
2482 pub bucket: String,
2483 pub price_meter: Option<String>,
2484 pub own_meter: Option<String>,
2485 pub scale_to_own: bool,
2486 pub drift_percent: f64,
2487 pub note: String,
2488 pub updated_at: String,
2489 pub updated_by: String,
2490}
2491
2492/// The guardrails on prices and the alerts.
2493#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
2494#[serde(rename_all = "camelCase")]
2495pub struct CostSettings {
2496 /// Apply small moves without staff.
2497 pub auto_apply: bool,
2498 /// The largest move applied without staff, either way, in percent.
2499 pub auto_apply_percent: f64,
2500 /// Days between telling customers of a rise and charging it.
2501 pub notice_days: u32,
2502 /// Below this margin, in percent, for `alert_days` days in a row, alert.
2503 pub margin_floor_percent: f64,
2504 pub alert_days: u32,
2505 /// Days with less cost than this say nothing about a margin.
2506 pub min_daily_cost_micros: i64,
2507 /// A workspace costing more than its revenue times this, over 30 days,
2508 /// and at least `anomaly_floor_micros`, is flagged.
2509 pub anomaly_factor: f64,
2510 pub anomaly_floor_micros: i64,
2511}
2512
2513impl Default for CostSettings {
2514 fn default() -> Self {
2515 CostSettings {
2516 auto_apply: true,
2517 auto_apply_percent: 25.0,
2518 notice_days: 14,
2519 margin_floor_percent: 10.0,
2520 alert_days: 3,
2521 min_daily_cost_micros: 100_000,
2522 anomaly_factor: 1.0,
2523 anomaly_floor_micros: 1_000_000,
2524 }
2525 }
2526}
2527
2528/// The Costs & margin page.
2529#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2530#[serde(rename_all = "camelCase")]
2531pub struct CostsReport {
2532 /// A token to read Cloudflare's bill is set.
2533 pub configured: bool,
2534 /// When Cloudflare's bill was last read.
2535 pub fetched_at: Option<String>,
2536 /// The days shown, YYYY-MM-DD.
2537 pub since: String,
2538 pub until: String,
2539 pub days: Vec<CostDay>,
2540 pub products: Vec<ProductMargin>,
2541 pub overall: OverallMargin,
2542 pub drift: Vec<CostDrift>,
2543 pub alerts: Vec<MarginAlert>,
2544 pub proposals: Vec<PriceProposal>,
2545 pub versions: Vec<PriceVersion>,
2546 pub top_workspaces: Vec<WorkspaceCost>,
2547 pub lines: Vec<CostLineSummary>,
2548 pub mappings: Vec<CostMapping>,
2549 pub settings: CostSettings,
Spend caps: a monthly budget for comped workspaces and a daily breaker on what g1t pays2550 /// g1t's own spend against its two caps.
2551 #[serde(default)]
2552 pub caps: SpendCaps,
2553}
2554
2555/// What g1t itself pays for, against its caps (billing's `budget`): the
2556/// daily breaker on all of it, and each comped account's monthly budget.
2557/// At cost, never at price. What sudo's Costs page and its red bar show.
2558#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2559#[serde(rename_all = "camelCase")]
2560pub struct SpendCaps {
2561 /// Today (UTC), YYYY-MM-DD, and this month, YYYY-MM.
2562 pub day: String,
2563 pub month: String,
2564 /// What g1t paid for itself today across every workspace: comped work,
2565 /// the trial and open-source pools, free workspaces' overruns, and
2566 /// anything charged without real money behind it.
2567 pub today_micros: i64,
2568 /// `PLATFORM_DAILY_SPEND_CAP_MICROS`. Zero: no breaker.
2569 pub daily_cap_micros: i64,
2570 /// The breaker is open: new hosted-model agent runs that g1t would pay
2571 /// for wait until tomorrow (UTC) or until staff lift it.
2572 pub tripped: bool,
2573 pub tripped_at: Option<String>,
2574 /// Staff lifted it for the rest of the day.
2575 pub lifted_by: Option<String>,
2576 pub lifted_at: Option<String>,
2577 pub lift_note: Option<String>,
2578 /// This month so far, by what paid: `comped`, `trial`, `oss`, `given`,
2579 /// `unpaid`.
2580 pub month_buckets: Vec<SpendBucket>,
2581 /// Each comped account's monthly budget.
2582 pub comped: Vec<CompedBudget>,
2583 /// Free workspaces' share of this month's reconciled costs (git,
2584 /// storage, platform), through yesterday.
2585 pub free_tier_micros: i64,
2586 /// `CLOUDFLARE_FIXED_MONTHLY_MICROS`: Cloudflare subscriptions, an estimate.
2587 pub fixed_monthly_micros: i64,
2588 /// Money in this month, through the last reconciled day.
2589 pub revenue_micros: i64,
2590}
2591
2592#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2593#[serde(rename_all = "camelCase")]
2594pub struct SpendBucket {
2595 pub bucket: String,
2596 pub title: String,
2597 pub micros: i64,
2598}
2599
2600/// A comped account's monthly budget: what its work cost g1t this month.
2601#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2602#[serde(rename_all = "camelCase")]
2603pub struct CompedBudget {
2604 pub account: String,
2605 pub name: String,
2606 pub used_micros: i64,
2607 /// Zero: no budget.
2608 pub ceiling_micros: i64,
2609 /// The ceiling is `COMPED_MONTHLY_CEILING_MICROS`, not the account's own.
2610 pub default_ceiling: bool,
2611 /// 50, 75, 90, 100, or 0.
2612 pub level: u32,
2613}
2614
2615/// `admin_spend_caps`: g1t's own spend against its caps. Returns `SpendCaps`.
2616#[derive(Debug, Default, Serialize, Deserialize)]
2617pub struct AdminSpendCapsArgs {}
2618
2619/// `admin_lift_breaker`: lets hosted-model runs start again for the rest
2620/// of today (UTC), with why. Recorded in the audit log. Returns
2621/// `Outcome<SpendCaps>`.
2622#[derive(Debug, Serialize, Deserialize)]
2623pub struct AdminLiftBreakerArgs {
2624 pub note: String,
2625 pub by: String,
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2626}
2627
2628/// `admin_cost_alerts`: the open margin alerts, for sudo's banner.
2629/// Returns `Vec<MarginAlert>`.
2630#[derive(Debug, Default, Serialize, Deserialize)]
2631pub struct AdminCostAlertsArgs {}
2632
2633/// `admin_decide_proposal`: approve or reject a price proposal. An
2634/// approved rise takes effect after the notice period. Returns
2635/// `Outcome<PriceProposal>`.
2636#[derive(Debug, Serialize, Deserialize)]
2637pub struct AdminDecideProposalArgs {
2638 pub id: String,
2639 /// `approve` or `reject`.
2640 pub decision: String,
2641 #[serde(default)]
2642 pub note: String,
2643 pub by: String,
2644}
2645
2646/// `admin_set_cost_settings`. Returns `Outcome<CostSettings>`.
2647#[derive(Debug, Serialize, Deserialize)]
2648pub struct AdminSetCostSettingsArgs {
2649 pub settings: CostSettings,
2650 pub by: String,
2651}
2652
2653/// `admin_set_cost_mapping`: adds, changes or (with `remove`) removes a
2654/// mapping row. Returns `Outcome<CostMapping>`.
2655#[derive(Debug, Serialize, Deserialize)]
2656pub struct AdminSetCostMappingArgs {
2657 pub product: String,
2658 pub meter: String,
2659 #[serde(default)]
2660 pub bucket: String,
2661 #[serde(default)]
2662 pub price_meter: Option<String>,
2663 #[serde(default)]
2664 pub own_meter: Option<String>,
2665 #[serde(default)]
2666 pub scale_to_own: bool,
2667 #[serde(default)]
2668 pub drift_percent: Option<f64>,
2669 #[serde(default)]
2670 pub note: String,
2671 #[serde(default)]
2672 pub remove: bool,
2673 pub by: String,
2674}
2675
2676/// `admin_run_costs`: reads Cloudflare's bill and reconciles now, as the
2677/// daily run does. Returns `Outcome<CostsRun>`.
2678#[derive(Debug, Default, Serialize, Deserialize)]
2679pub struct AdminRunCostsArgs {
2680 #[serde(default)]
2681 pub by: String,
2682}
2683
2684#[derive(Clone, Debug, Default, Serialize, Deserialize)]
2685#[serde(rename_all = "camelCase")]
2686pub struct CostsRun {
2687 pub lines: u32,
2688 pub days: u32,
2689 pub proposals: u32,
2690 pub alerts: u32,
2691 /// What could not be read, in words.
2692 pub problems: Vec<String>,
2693}
2694
Models per workspace: several providers, routed by kind of work2695#[cfg(test)]
2696mod tests {
2697 use super::*;
2698
2699 #[test]
Prices are what g1t pays plus 20%, from the first second2700 fn an_account_carries_no_run_fee() {
2701 let account = Account {
2702 workspace: "acme".into(),
2703 balance_micros: 0,
2704 status: Status { enabled: true, live: false, free: false },
2705 margin_percent: 20,
2706 card: None,
2707 };
2708 let json = serde_json::to_value(account).unwrap();
2709 let mut keys: Vec<&str> = json.as_object().unwrap().keys().map(String::as_str).collect();
2710 keys.sort_unstable();
2711 assert_eq!(keys, ["balanceMicros", "card", "marginPercent", "status", "workspace"]);
2712 }
2713
2714 #[test]
2715 fn a_price_change_says_when_the_markup_moved() {
2716 let change = PriceChange {
2717 meter: "sandbox_second".into(),
2718 old_cost_micros: 21.0,
2719 new_cost_micros: 21.0,
2720 markup_percent: 20,
2721 old_markup_percent: Some(138),
2722 reason: "Sandbox time is now charged at cost plus 20% from the first second".into(),
2723 created_at: "2026-10-05T00:00:00Z".into(),
Git storage hardened, pages in tens of milliseconds, honest security alerts, and costs reconciled daily2724 effective_at: None,
Prices are what g1t pays plus 20%, from the first second2725 };
2726 assert_eq!(serde_json::to_value(&change).unwrap()["oldMarkupPercent"], 138);
2727 let cost_only = PriceChange { old_markup_percent: None, ..change };
2728 assert!(serde_json::to_value(&cost_only).unwrap().get("oldMarkupPercent").is_none());
2729 }
2730
2731 #[test]
Paid features: a workspace turns on Deployments with a monthly plan2732 fn features_are_named_as_the_site_sends_them() {
2733 assert_eq!(
2734 serde_json::to_value(Feature::Deployments).unwrap(),
2735 serde_json::json!("deployments")
2736 );
2737 assert_eq!(Feature::parse("deployments"), Some(Feature::Deployments));
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2738 assert_eq!(serde_json::to_value(Feature::Plan).unwrap(), serde_json::json!("plan"));
2739 assert_eq!(Feature::parse("plan"), Some(Feature::Plan));
2740 // Older readers named the plan Team.
2741 assert_eq!(Feature::parse("team"), Some(Feature::Plan));
2742 assert_eq!(serde_json::from_value::<Feature>(serde_json::json!("team")).unwrap(), Feature::Plan);
2743 assert_eq!(Feature::ALL, [Feature::Plan]);
Paid features: a workspace turns on Deployments with a monthly plan2744 assert!(SubscriptionStatus::Canceling.on());
2745 assert!(!SubscriptionStatus::PastDue.on());
2746 }
2747
2748 #[test]
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look2749 fn a_reservation_is_asked_for_and_answered_in_camel_case() {
2750 let asked: ReserveArgs = serde_json::from_value(serde_json::json!({
2751 "workspace": "acme",
2752 "repo": { "namespace": "acme", "name": "web" },
2753 "public": true,
2754 "kind": "check",
2755 "estimateMicros": 2_000_000,
2756 }))
2757 .unwrap();
2758 assert_eq!(asked.kind, ComputeKind::Check);
2759 assert!(asked.kind.open_source_pool());
2760 assert!(!ComputeKind::Agent.open_source_pool());
2761 // Rust callers that write snake_case are read too.
2762 let snake: ReserveArgs = serde_json::from_value(serde_json::json!({
2763 "workspace": "acme",
2764 "repo": { "namespace": "acme", "name": "web" },
2765 "public": false,
2766 "kind": "agent",
2767 "estimate_micros": 1,
2768 }))
2769 .unwrap();
2770 assert_eq!(snake.estimate_micros, 1);
2771 let answer = Reservation { id: "rsv_1".into(), paid_by: PaidBy::OnDemand, held_micros: 5, expires_at: String::new() };
2772 assert_eq!(serde_json::to_value(&answer).unwrap()["paidBy"], "on_demand");
2773 assert_eq!(serde_json::to_value(PlanKind::Internal).unwrap(), "internal");
2774 assert!(!PlanKind::Free.on_demand() && PlanKind::Enterprise.on_demand());
2775 }
2776
2777 #[test]
2778 fn a_refusal_carries_its_own_code() {
2779 let refused: crate::Outcome<Reservation> =
2780 crate::Outcome::fail(crate::FailureCode::OssPoolEmpty, "The open-source pool is spent.");
2781 let json = serde_json::to_value(&refused).unwrap();
2782 assert_eq!(json["error"]["code"], "oss_pool_empty");
2783 assert_eq!(crate::FailureCode::NotPaid.http_status(), 402);
2784 assert_eq!(crate::FailureCode::Paused.http_status(), 409);
2785 }
2786
2787 #[test]
Models per workspace: several providers, routed by kind of work2788 fn who_pays_is_read_as_the_runner_sends_it() {
2789 let run: StartRunArgs = serde_json::from_value(serde_json::json!({
2790 "workspace": "acme",
2791 "repo": { "namespace": "acme", "name": "web" },
2792 "number": 7,
2793 "task": "implement",
2794 "model": "Claude Sonnet 5.5",
2795 "billedTo": "workspace",
2796 }))
2797 .unwrap();
2798 assert_eq!(run.billed_to, "workspace");
2799 }
2800}