billing: GET /v1/internal/accounts/:accountId/subscription -> {status, open}
(MEMBERSHIP_SYNC_TOKEN), so auth refuses to delete a team with an open subscription.
sandbox-control (auth's MEMBER_REMOVED_TOKEN, never the operator routes):
- GET /v1/internal/accounts/:accountId/delete-check -> {workspaces}: the team's
workspaces in any state but destroyed.
- POST /v1/internal/accounts/:accountId/secrets/purge -> {purged}: deletes the
team's org- and repo-scoped cloud secrets; 409 workspaces_exist while any remain.
Found by the dev shadow run: Stripe test clocks put current_period_start a month ahead, so the
grant read as not started and the resolver denied (subscription_inactive) where legacy allowed.
starts_at is now min(period start, first sync) and stays stable across syncs.
- catalog: Team v2 (same entitlements and policy as v1, so v1 subscribers see no change) adds the
sandbox_spec and ready_in_seconds features the desktop already localizes; currentVersion 2
- catalog plans take an optional supportUrl (https: or mailto: only) and GET /v1/plans returns it
per plan; clients fall back to the Enterprise contact
Neil's Q1 answer: a plan id on the Stripe Price/Product maps to the repo catalog's entitlements.
seatPriceMatch reads price.metadata.orca_plan (after env ids and price history, before lookup key
and product); the catalog sync logs a lookup-key/tag mismatch; stripe-test-setup tags every price.
- planForPrice fix: seat prices map through the configured ids, the price history (plan_prices),
the plan's lookup key, or the plan's Stripe product; a known subscriber on an unmappable price
still gets status/period/cancel applied, so a price change can never leave a canceled
subscriber with access (test: lifecycle.test.ts, all modes)
- catalog prices synced from Stripe by lookup key (cron + price.*/product.* webhooks); every seat
price a subscription is on is kept as history; Checkout sells the lookup-key holder when on and
refuses (409 plan_unavailable) rather than guess a price
- webhook: one processStripeEvent for webhooks and replay; customer.deleted revokes; the mirror
upsert ignores older Stripe reads
- reconcile.ts (cron): Stripe event replay from a cursor, rolling re-sync of stale subscriptions
(404 = canceled), catalog price refresh; grant backfill after it
- GET /v1/plans (public, ETag/304, prices only from Stripe, entitlements, features, policy) and
GET /v1/entitlements (effective values, access, grants); legacy offers built from the catalog
when on (identical for the launch catalog, plus warnAtPercent)
- seats and overage read the price history and plan policy (seat bounds, true-down window)
- migration 0008: plans, immutable plan_versions, plan_prices (Stripe mirror), entitlement_grants,
entitlement_audit, entitlement_shadow_diffs, stripe_sync_state; subscriptions.seat_price_id
- catalog/catalog.json (catalog-as-code: Team, Enterprise, Trial; no prices) + scripts/catalog-apply.ts
- typed entitlement registry (entitlement-keys.ts) with sum/max/union/or aggregation
- grants.ts: idempotent projection of each subscription (Stripe mirror) and trial into grants,
hold/release/revoke, out-of-order guard, terminal revocation, audit; manual/promo grants
- entitlements.ts: one builder fed by legacy terms or grant terms; ENTITLEMENTS_MODE off|shadow|on;
shadow records every disagreement; lazy re-projection; cron backfill
- trial hours come from the catalog's trial plan when on (granted trials keep their hours)
- the whole suite runs in legacy, shadow (zero diffs enforced) and on modes
- orcad: a delivered keystroke frame from a paired device records that device as the terminal's driver (the same registry agent prompts use), so the VM git shim's orca-git-as answers for plain terminals too. The device is the transport-attested caller, never the client id. Local windows never record.
- desktop: typing in an Orca Cloud terminal reports "I'm driving" (noteCloudSend, throttled), so the egress honors the typist's GitHub identity. Only xterm user input counts; query replies, focus and mouse reports, replay and resubscribes don't.
- Fallback author email cloud@users.noreply.onorca.dev becomes cloud@users.noreply.orca.dev; existing workspaces on the old default follow on next boot.
Help menu docs/changelog, the telemetry privacy link, the share page's
"Get Orca" link and the MCP server's website URL now use orca.dev. The site
moved there; the old onorca.dev hosts redirect. Service hosts are unchanged.
Boot-time installs were swallowed and the boot cleared gitSetup, so a restart whose install failed again dropped the desktop strip while git still lacked identities. Every boot install now goes through settleMemberGit: failure marks the VM and the row, a later success (or a boot with nobody to install) clears them. The git shim refuses on a failed mark even with nothing pending. Restart is the retry (it re-runs the install before it answers).
POST /v1/sandboxes kept the response open ~2.3 s after orcad was ready while syncMemberGit ran (and again inside boot when the creator had GitHub connected). A create now records the owner device, answers, and installs member identities in ctx.waitUntil. The VM is told the setup is pending (ORCA_GIT_SETUP=pending -> ~/.orca-sandbox/git-setup.pending); its git shim holds commit/push/merge/... up to 15 s until orca-sandbox-members writes git-setup.ready, and on a failed or stalled setup stops them with what to do instead of committing as the workspace identity. Read-only git and fetches never wait: the boot already routes the workspace credential. A failed install is kept on the row (0053 git_setup_failed_at) and shown as sandbox.gitSetup 'failed' to clients sending feature git-setup; any later boot or successful sync clears it. Wake, resume, restart and grants are unchanged (synchronous).