flagon-io/g1t

public

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

g1t/crates/contracts/src/billing.rs

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