g1t/services/billing/migrations/0018_one_plan.sql
| 1 | -- One paid plan, "g1t", and the limits, checks and protections around it. |
| 2 | -- See src/compute.rs (entitlements, reservations, spikes), src/cards.rs |
| 3 | -- (card checks), src/requests.rs (raising a limit), src/overages.rs |
| 4 | -- (goodwill) and src/limits.rs (ceilings). |
| 5 | |
| 6 | -- What g1t covered itself on a usage entry, such as the part of a free |
| 7 | -- workspace's last trial run past its trial credit. Like credit_micros, |
| 8 | -- trial_micros and oss_micros, it is not in amount_micros. |
| 9 | ALTER TABLE ledger ADD COLUMN given_micros INTEGER NOT NULL DEFAULT 0; |
| 10 | |
| 11 | -- Ceilings: the highest the workspace has ever had (owners may set their |
| 12 | -- spend limit up to it without asking), a ceiling g1t granted (an approved |
| 13 | -- request, or the owners' one-time raise), and when that raise was used. |
| 14 | ALTER TABLE limits ADD COLUMN max_ceiling_micros INTEGER; |
| 15 | ALTER TABLE limits ADD COLUMN granted_ceiling_micros INTEGER; |
| 16 | ALTER TABLE limits ADD COLUMN raised_at TEXT; |
| 17 | -- The owners' own caps on agents: one run's spend, and what the agents on |
| 18 | -- one issue may spend in all. Null: the defaults ($2 and $10). |
| 19 | ALTER TABLE limits ADD COLUMN run_cap_micros INTEGER; |
| 20 | ALTER TABLE limits ADD COLUMN issue_cap_micros INTEGER; |
| 21 | |
| 22 | -- Overrides g1t staff set per account in sudo: agents at once, one run's |
| 23 | -- spend cap, and a hold on new compute (with why). |
| 24 | ALTER TABLE billing_accounts ADD COLUMN max_concurrent_agents INTEGER; |
| 25 | ALTER TABLE billing_accounts ADD COLUMN run_cap_micros INTEGER; |
| 26 | ALTER TABLE billing_accounts ADD COLUMN issue_cap_micros INTEGER; |
| 27 | ALTER TABLE billing_accounts ADD COLUMN hold TEXT; |
| 28 | |
| 29 | -- Estimates held before compute starts, so starts at the same moment |
| 30 | -- cannot overshoot a ceiling together. Released by `settle`, or after |
| 31 | -- three hours if never settled. |
| 32 | CREATE TABLE reservations ( |
| 33 | id TEXT PRIMARY KEY, |
| 34 | workspace TEXT NOT NULL, |
| 35 | repo TEXT NOT NULL, |
| 36 | kind TEXT NOT NULL, |
| 37 | public INTEGER NOT NULL DEFAULT 0, |
| 38 | -- At cost to g1t, as asked. |
| 39 | estimate_micros INTEGER NOT NULL, |
| 40 | -- What is held, at price (cost plus the margin), which is what limits |
| 41 | -- and pools are measured in. |
| 42 | hold_micros INTEGER NOT NULL, |
| 43 | -- credit, trial, oss or on_demand. |
| 44 | paid_by TEXT NOT NULL, |
| 45 | created_at TEXT NOT NULL, |
| 46 | expires_at TEXT NOT NULL, |
| 47 | settled_at TEXT, |
| 48 | -- At cost, as settled. |
| 49 | actual_micros INTEGER |
| 50 | ); |
| 51 | CREATE INDEX reservations_open ON reservations (workspace, settled_at, expires_at); |
| 52 | |
| 53 | -- Card checks: a card saved and verified with Stripe (a setup with 3-D |
| 54 | -- Secure where the card supports it; never charged). The trial and the |
| 55 | -- open-source pool need one. One trial per card, by its fingerprint. |
| 56 | CREATE TABLE card_checks ( |
| 57 | workspace TEXT PRIMARY KEY, |
| 58 | setup_intent TEXT NOT NULL, |
| 59 | payment_method TEXT NOT NULL, |
| 60 | fingerprint TEXT, |
| 61 | brand TEXT, |
| 62 | last4 TEXT, |
| 63 | funding TEXT, |
| 64 | country TEXT, |
| 65 | checked_by TEXT NOT NULL, |
| 66 | checked_at TEXT NOT NULL |
| 67 | ); |
| 68 | CREATE INDEX card_checks_by_fingerprint ON card_checks (fingerprint); |
| 69 | |
| 70 | -- Spend spikes: an hour well above the workspace's usual. New compute |
| 71 | -- waits for an owner: keep going (for 24 hours, or until the hour's spend |
| 72 | -- doubles again) or stop. |
| 73 | CREATE TABLE spikes ( |
| 74 | id TEXT PRIMARY KEY, |
| 75 | workspace TEXT NOT NULL, |
| 76 | -- open, continued or stopped. |
| 77 | status TEXT NOT NULL, |
| 78 | hour_micros INTEGER NOT NULL, |
| 79 | average_micros INTEGER NOT NULL, |
| 80 | detected_at TEXT NOT NULL, |
| 81 | told_at TEXT, |
| 82 | decided_by TEXT, |
| 83 | decided_at TEXT, |
| 84 | until TEXT |
| 85 | ); |
| 86 | CREATE INDEX spikes_by_workspace ON spikes (workspace, detected_at); |
| 87 | |
| 88 | -- Requests to g1t: raise my limit, or spent more than I meant to. |
| 89 | CREATE TABLE limit_requests ( |
| 90 | id TEXT PRIMARY KEY, |
| 91 | workspace TEXT NOT NULL, |
| 92 | -- limit or overage. |
| 93 | kind TEXT NOT NULL, |
| 94 | amount_micros INTEGER NOT NULL, |
| 95 | reason TEXT NOT NULL, |
| 96 | expected_monthly_micros INTEGER NOT NULL DEFAULT 0, |
| 97 | -- open, approved or declined. |
| 98 | status TEXT NOT NULL DEFAULT 'open', |
| 99 | decided_micros INTEGER, |
| 100 | decided_by TEXT, |
| 101 | answer TEXT, |
| 102 | created_by TEXT NOT NULL, |
| 103 | created_at TEXT NOT NULL, |
| 104 | decided_at TEXT |
| 105 | ); |
| 106 | CREATE INDEX limit_requests_by_workspace ON limit_requests (workspace, created_at); |
| 107 | CREATE INDEX limit_requests_by_status ON limit_requests (status, created_at); |
| 108 | |
| 109 | -- Alerts sent: 50, 75, 90 and 100% of the plan's included usage |
| 110 | -- (`included`), the owners' spend limit (`spend_limit`) and g1t's ceiling |
| 111 | -- (`ceiling`), once each a month. |
| 112 | CREATE TABLE alerts_sent ( |
| 113 | workspace TEXT NOT NULL, |
| 114 | -- YYYY-MM. |
| 115 | month TEXT NOT NULL, |
| 116 | meter TEXT NOT NULL, |
| 117 | level INTEGER NOT NULL, |
| 118 | sent_at TEXT NOT NULL, |
| 119 | PRIMARY KEY (workspace, month, meter, level) |
| 120 | ); |
| 121 | |
| 122 | -- The plan's monthly price, as each invoice for it is paid: revenue that |
| 123 | -- never goes through the ledger. |
| 124 | CREATE TABLE plan_payments ( |
| 125 | invoice_id TEXT PRIMARY KEY, |
| 126 | workspace TEXT NOT NULL, |
| 127 | amount_micros INTEGER NOT NULL, |
| 128 | paid_at TEXT NOT NULL |
| 129 | ); |
| 130 | CREATE INDEX plan_payments_by_month ON plan_payments (paid_at); |
| 131 | |
| 132 | -- New meters: |
| 133 | -- - Git operations through g1t (clones, fetches, pushes), at Cloudflare |
| 134 | -- Artifacts' price from 2026-10-14: $0.15 per 1,000. |
| 135 | -- - A sandbox second's parts, for runs that report their own CPU: memory, |
| 136 | -- disk and the Durable Object behind the container per second; vCPU per |
| 137 | -- vCPU-second. |
| 138 | INSERT INTO prices (meter, title, unit, cost_micros, markup_percent, source, updated_at) VALUES |
| 139 | ('git_operations', 'Git operations past the included amount', '1,000 operations', 150000, 20, 'list', '2026-10-05T00:00:00Z'), |
| 140 | ('sandbox_base_second', 'Sandbox time: memory, disk and its Durable Object', 'second', 12.1225, 20, 'list', '2026-10-05T00:00:00Z'), |
| 141 | ('sandbox_cpu_second', 'Sandbox time: CPU in use', 'vCPU-second', 20, 20, 'list', '2026-10-05T00:00:00Z') |
| 142 | ON CONFLICT (meter) DO NOTHING; |
| 143 | |
| 144 | -- The Durable Object behind each sandbox is billed for as long as the |
| 145 | -- container runs: 128 MB at $12.50 per million GB-seconds, 1.5625 |
| 146 | -- millionths of a dollar a second, which the sandbox second left out. |
| 147 | INSERT INTO price_changes (id, meter, old_cost_micros, new_cost_micros, markup_percent, reason, created_at) |
| 148 | SELECT 'prc_do_' || meter, meter, cost_micros, cost_micros + 1.5625, markup_percent, |
| 149 | 'Now includes the Durable Object behind each sandbox, which Cloudflare bills for as long as the container runs', |
| 150 | '2026-10-05T00:00:00Z' |
| 151 | FROM prices WHERE meter IN ('sandbox_second', 'build_second') |
| 152 | ON CONFLICT (id) DO NOTHING; |
| 153 | UPDATE prices SET cost_micros = cost_micros + 1.5625, updated_at = '2026-10-05T00:00:00Z' |
| 154 | WHERE meter IN ('sandbox_second', 'build_second'); |
| 155 | |
| 156 | INSERT INTO price_changes (id, meter, old_cost_micros, new_cost_micros, markup_percent, reason, created_at) VALUES |
| 157 | ('prc_new_git_operations', 'git_operations', 150000, 150000, 20, 'Now metered: git operations past the included amount, at Cloudflare Artifacts'' price, from 14 October 2026', '2026-10-05T00:00:00Z'), |
| 158 | ('prc_new_sandbox_parts', 'sandbox_cpu_second', 20, 20, 20, 'Runs that report their own CPU are priced on it: memory, disk and the Durable Object by the second, CPU by the vCPU-second', '2026-10-05T00:00:00Z') |
| 159 | ON CONFLICT (id) DO NOTHING; |