HOLD: paid Cloud plan entitlements on in production (CLOUD_PLAN_ENTITLEMENTS=on)

TE
2d ago
+6-2

HOLD: outward step, flips paid Cloud entitlements in production; p0-machines' coordinator decides when

What

This PR turns paid NEXT Cloud entitlements on in production (cmux-api):

  • backend/apps/api/wrangler.jsonc env.production vars: "CLOUD_PLAN_ENTITLEMENTS": "on".
  • scripts/cmux-next/release/production-approvals.json guarded_vars: "CLOUD_PLAN_ENTITLEMENTS=on". The production promote's G7 needs this entry (#26727, merged).

Once this lands and the promote runs, a team with a paid cmuxPlan gets Cloud machines at its PAID_PLAN_LIMITS:

  • Pro, Founders and Team: 5 active, 20 saved, up to 8 vCPU / 16 GB / 128 GB;
  • Max: 20 active, 50 saved, up to 32 vCPU / 64 GB / 512 GB;
  • Go: 1 active, 2 vCPU / 4 GB.

Every other team keeps getting cloud.plan.required, which the Mac app answers with the cmux Pro offer (Upgrade opens cmux.com/pricing). The plan comes from web billing's Stripe-written cmuxPlan, read through TeamDO.inferencePlan, the same read the model router already uses in production.

Not ready yet: what must land before this one can

  1. A production Cloud image and provider. Production has no CLOUD_FREESTYLE_SNAPSHOT (no cmuxnp-prod-vmimg- image; the image promote workflow only has a dev channel) and no vm-api transport. Until it has one, every paid create would answer cloud.no_snapshot_configured. test/cloud-plan-production.test.ts (#26727) fails on this PR for that reason, by design. The snapshot var (or the vm-api vars) goes into this PR, or lands before it.
  2. The provider key secret on cmux-api: CLOUD_FREESTYLE_API_KEY for the direct transport, or CLOUD_VM_API_SERVICE_KEY for vm-api. Without it the promote holds at G5 (#26727).
  3. Whoever removes the HOLD line also owns the guarded_vars entry text above. G7 lets the production promote carry this value only while the entry exists.

Known gaps that ship with this flip (none of them blocks entitlements; each is a money or product call)

  • No VM-hours meter. NEXT limits machine shape and count, not hours. vm_hours_used is always 0, and Go's vm_hours_included: 40 is shown but not enforced. The only time bound is the idle pause (30 min default, plus the 24 h no-report backstop).
  • Team is 5 machines flat. Classic web gives 5 per paid seat (cmuxSeats); NEXT does not read seats.
  • A lapsed plan does not pause running machines. Create, start, resize and snapshot are refused at the next request. A busy machine keeps running until it goes idle.
  • The CLI and Chief cannot open live checkouts. cmux billing checkout refuses a live Stripe session while BillingLivePolicy.shipped.liveCheckout is false (a separate app-side flip). The GUI offer is not affected.

Why

Leo, 2026-10-28: "whatever's convenient for cloud, like that can be productionized, operationalized, we ought to just put out there"
Leo, 2026-10-28: "'pay money to us'"

Risk

Outward and money-moving. This flip starts Freestyle spend for paying teams in production, and it makes NEXT Cloud part of what Pro, Max, Team, Founders and Go sell. Rollback: unset the var (G7 needs CLOUD_PLAN_ENTITLEMENTS=<unset> approved), or run the one-command production rollback. Machines that already exist keep running until they pause or are deleted. Deletes always run.

Tested

The production behaviour with the flag on is covered by cloud-plan-production.test.ts, the Durable Object cases by cloud-plan-entitlements.test.ts, and the promote gates by prod-promote.test.ts (#26727). The production-provider invariant in that test is red on this PR until item 1 above lands.

Agent session: agent session

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

HOLD: turns paid Cloud plan entitlements on in production (CLOUD_PLAN_ENTITLEMENTS=on). Teams with a paid cmuxPlan now get Cloud machines at their PAID_PLAN_LIMITS; all other teams keep getting cloud.plan.required. This PR is held until the coordinator decides when it lands.

Rollout

  • Sets the var in the production worker config and approves it in the production promote gates.
  • Until a production Cloud image and provider key secret exist, every paid create fails with cloud.no_snapshot_configured.

Known gaps

  • Limits apply to machine shape and count, not hours; Go's 40 included hours are shown but not enforced.
  • A lapsed plan doesn't pause running machines; only the next create, start, resize, or snapshot is refused.
  • The CLI and Chief can't open live checkouts while BillingLivePolicy.shipped.liveCheckout is off.