W9 F4/F5. The build starts from vercel/sandbox/node:24 (Ubuntu) instead of the deprecated node24
runtime, so sandboxes cloned from the golden get 64 GB instead of 32 GB. The bundle's orca-cli.cjs
becomes /opt/orca/bin/orca (ORCA_USER_DATA_PATH defaults to orcad's data dir); a pinned Google Chrome
.deb provides headless Chrome and orcad gets ORCA_BROWSER_EXECUTABLE. Live smoke of the built image:
64G disk, all tools on PATH, orca status reaches orcad, chrome headless renders, create 2.3 s, orcad
2.1 s, resume 2.0 s.
Control plane (cloud/apps/sandbox-control):
- Provider presets as secrets: AI_GATEWAY_API_KEY (Vercel AI Gateway) derives Claude Code + Codex env;
custom ANTHROPIC_BASE_URL/OPENAI_BASE_URL (https, public host only) + *_MODEL. Provider hosts are
allowlisted per workspace from its agent connection (migration 0026 egress_allow_hosts, additive).
- Egress placeholder swap is host-scoped per credential (SECRET@host) incl. ai-gateway.vercel.sh and
custom hosts; staged rollout SECRETS_EGRESS_PLACEHOLDERS=accounts:<ids> (or on/off).
- GET /v1/secrets/effective, POST /v1/secrets/:scope/batch, POST /v1/sandboxes/:id/env/refresh; every
secret write refreshes running sessions of that owner (no restart, no rebuild).
- VM: orca-sandbox-env writes a tmpfs-only session env file re-read by every new shell
(/etc/profile.d hook); Codex gets a managed provider block when OPENAI_BASE_URL is set; managed env
ORCA_IS_CLOUD, ORCA_ROOT_PATH/WORKTREE_PATH/WORKSPACE_PATH/WORKSPACE_NAME/DEFAULT_BRANCH/PORT +
CONDUCTOR_* aliases. image/patch-golden-image.ts re-images scripts in ~10s.
- Redaction: pairing offer read via the files API (Vercel retains command stdout), log tails strip
injected values/token shapes, runningOrcad no longer prints the offer, no GitHub placeholder for
non-github.com repos.
Desktop: token-pattern + injected-value redaction for logs, crash reports, daemon log, emulator probe,
relayed worker spans.
Live (Vercel, W6 worker, placeholders on): org-shared gateway key, real Claude Code 2.1.319 and Codex
0.166.0 fixed+committed code through the swap; SSE long stream 978 chunks/25s and streamed tool_use
intact; real key absent from VM disk, tmpfs and every process env; secret edit visible in a new
shell 0.6s after PUT.
- pool.ts: N standard VMs booted from the current golden with egress denied; create claims one atomically
(DELETE..RETURNING), applies the caller policy + full lease in parallel, bills from the claim; cron and
each claim refill; recycled at 20 min, on golden change, pool off or kill switch; fallback to a fresh VM.
- F1: orca-sandbox-start leaked the start-lock fd into orcad, so a second start (resume of a running
sandbox, retried create) waited 60 s and 502'd. Children now close fd 9; the Worker also reads the
serving orcad's ready line without the lock (fixes VMs on older images).
- F2: a github.com repo with no App token and no anonymous access fails 409 github_not_connected before
any VM work.
Auth mints the live org role as orgRole on sandbox tokens (same claim as billing
tokens, stablyai/orca-cloud#695); the verifier read org_role, so admins would
never have been recognized.
* feat(sidebar): replace footer Settings and Help icons with an account menu
* fix: keep account menu header on primitive spacing; move focus-read allowance
* fix(sidebar): address account menu review findings
* feat(billing): Cloudflare Worker for Stripe checkout, portal, webhooks, entitlements and usage (#7)
Test-mode-only foundation for paid Orca plans: hosted Checkout/Portal,
signed + idempotent Stripe webhooks that re-read subscriptions, a D1 ledger
for sandbox usage with overage-only Billing meter events, and entitlement
checks for the desktop and the sandbox control plane.
* feat(billing): Plan & billing in the Orca account settings and account menu (#9)
Shows the signed-in account's plan, sandbox hours used and left, and opens
hosted Stripe Checkout (Upgrade) or the Customer Portal (Manage billing) in
the browser. Main mints a short-lived billing token from Orca Cloud auth and
only opens checkout.stripe.com / billing.stripe.com URLs. Hidden until
ORCA_BILLING_API_URL is set; the account menu item is gated on the 'billing'
cloud capability and registers through a local adapter that mirrors the
upcoming account menu API.
* feat(billing): plans and prices from config, opt-in Stripe Tax, real account menu
- Worker: BILLING_PLANS JSON overrides the plan catalog (labels, display price,
hours, limits); entitlements return the plan list so the desktop shows no
hardcoded prices. STRIPE_AUTOMATIC_TAX=true adds automatic_tax and tax id
collection to Checkout (default off). Setup script takes prices from env.
- Desktop: register Plan & billing through the sidebar account menu
(merged nwparker/sidebar-account-menu) and drop the local adapter.
- Register the billing Worker's tests with the test-runner check.
* feat(billing): Team pricing, opt-in overage, Stripe test-mode e2e, dev deploy (#18)
* feat(billing): Team-only pricing, opt-in overage with spend cap, live Stripe test-mode e2e
- Catalog: Team $50/seat/mo or $480/seat/yr, 60 Standard h/seat/month pooled
(monthly windows on annual plans), Large 2x, 3 running/seat, 15-min idle sleep;
Enterprise is contact-only. All numbers/labels in BILLING_PLANS defaults.
- Overage is opt-in (adds the metered item), $0.75/h, monthly spend cap (default
$25, admin-raisable) enforced in the meter outbox; 80%/100% usage warnings.
- Checkout: plan/interval/seats with adjustable quantity; portal configuration var.
- Workers reject fetch redirect 'error': use 'manual'. Log route errors.
- scripts/stripe-test-setup.ts creates the test catalog + portal configuration;
scripts/e2e-test-mode.ts drives Checkout (headless, test card), overage, meter,
cap, annual, test-clock renewal, payment failure and cancel.
- Desktop: Team/Enterprise cards with interval + seats, usage warnings, overage
toggle + cap; split into copy/offers/usage modules.
* feat(billing): dev deployment config, dev operator keys, return page
- wrangler.dev.jsonc for orca-billing-dev (workers.dev, dev D1, test-mode prices).
- BILLING_DEV_JWKS (issuer orca-billing-dev-test) accepted only when BILLING_ENV
is not prod, for e2e runs until auth mints billing tokens.
- GET /return: static 'return to Orca' page for Checkout/Portal redirects.
- Design doc: final pricing, dev URL and resources for the control plane.
* feat(billing): team seats follow membership, payment grace + suspend signal, Team UI
Worker
- Seats = org members from Orca Cloud auth (single source of truth; pending invites
never count; min 1): auth pushes POST /v1/internal/seats, billing pulls
/v1/internal/billing/org-members at Checkout and in a reconcile cron, fails closed
(503) if auth is configured but unreachable. No token claim carries a count.
- Proration: adds always_invoice, removals create_prorations (Stripe default).
- Billing admin actions require a token minted in the last 6 minutes.
- Payment failure: past_due_since (migration 0003); new starts stop at once;
after 96 h (paymentGraceHours) reason payment_grace_expired + suspendRunning on
check/usage, and GET /v1/internal/suspensions for the control plane.
- Portal config: monthly<->annual + cancel only (seats follow membership).
- e2e: seat add/remove proration, grace expiry -> suspend -> pay -> resume.
Desktop
- Team section + account menu item: members/roles, invite, change role, remove
with confirm, revoke invites (owners/admins, auth role rules mirrored).
- Payment issue banner with suspension date and Update payment method.
* feat(billing): in-app plans page (#22)
A pricing/plans page reachable from the account menu ("Plan & billing"), the
Settings plan section ("Choose a plan" / "Compare plans"), and any upsell via
openPlansPage(). Everything comes from the billing service's offers: Team
monthly/annual (with the computed annual saving), pooled hours, Large weight,
concurrency, idle sleep, opt-in overage with spend cap, and Enterprise contact.
- States: choose (admin), locked (member), current plan with usage, payment
failure (status-first), checkout in progress, loading and failure with retry.
- Seats are read-only: one per team member (billing re-reads the count).
- Shared card shell keeps both cards' footers level; sliding interval toggle and
price fade respect reduced motion; dialog opens without a pre-focused button.
- Worker offers gain per-seat cents and the Large weight for totals.
- Copy matches the marketing /cloud promises: agents keep running, wake in
seconds, secrets never stored in the VM.
* wip(cloud): Orca Cloud built-in provider wiring
* feat(cloud): no-plan upsell opens the plans page
* feat(cloud): Orca Cloud dogfood fixes (provider persistence, picker label, any-input activity, idle override)
* fix(cloud): live dogfood fixes (snapshot already-deleted, agent egress wildcards, golden image id) + results
* fix(cloud): i18n keys, typed dev JWK, verdict typing
* feat(cloud): billing caps from billing (maxConcurrentSandboxes, per-member), userId on usage; concurrency copy
* feat(cloud): golden image can pin a released orca-server tarball (--release), source build stays the fallback
* fix(cloud): async dev key read, runtime localization catalog
- Seats billed = the period's peak membership: adds above paid seats are charged now
(always_invoice); removals never credit mid-period and only lower the quantity inside
seatTrueDownHours of renewal (proration none), so remove/re-add churn can't dodge or
double-charge. The cron applies deferred decreases from the last pushed count.
- Included hours and caps come from billed seats; per-member concurrency cap
(member_concurrency_limit); usage attributed to the member who started it.
- Start check returns billed `seats` and the derived caps for the control plane.
- Desktop shows when paid seats drop at renewal; proration copy updated.
- Design doc: seat-fairness decision with the GitHub/Cursor/Slack/Linear comparison.
Security (B1): repo tokens were minted for any repo.url where the app was installed. Now an
installation must be bound to the caller's account (signed single-use connect state + OAuth proof
that the installer owns the account or is an org admin), tokens are scoped to the one repo and the
binding is re-checked on cached tokens. Operator binding route for support/dev.
Guardrails (B3/G4): D1 kill switch + static flag refuse create/resume/rollover and drain running
sandboxes; global running cap; 3/seat per-account backstop; per-account create rate limit.
Retention (B4/B6): hourly orphan sweep (stop, then delete after grace; partial listings delete
nothing), 30-day deletion after cancel/unpaid with a T-7 warning hook, 90-day idle retention,
104-day provider snapshot expiry backstop, repo_url tombstoned on destroy.
D1 secrets table (migration 0003), AES-256-GCM per-secret DEK wrapped by SECRETS_KEK with
(scope, owner, name) AAD and key-id rotation; write-only /v1/secrets CRUD with org owner/admin
checks; precedence-resolved injection into orcad's env at every boot (no disk, no rebuild);
optional egress placeholder swap for api.anthropic.com / api.openai.com via the forwardURL proxy.
* fix(cloud): provision bundle download no longer overwrites its source; shortened session deadline for staging rehearsals
* docs(cloud): live Vercel Pro results