Auth pushes PropelAuth offboarding and Orca-side member removals here (stablyai/orca-cloud#739);
it should not need the full operator token for that. The operator token is still accepted.
Opting out left the metered overage item on the Stripe subscription, so every later
invoice carried a '0 std hour x Orca sandbox overage' $0 line (confirmed on a
throwaway test-mode subscription). The item now comes off at once when nothing was
metered this Stripe period, or at the first sync after the period rolls over when
there is usage still to invoice. Re-opting in within 24h got the removed item's id
replayed by Stripe idempotency (also confirmed live); the add now retries under a
key naming the removed item. Nothing meters while opted out or with a $0 cap.
Terminal prompts read ubuntu@<random VM id> and changed after every wake. Each start (create and wake)
now sets the hostname to the workspace's title (else its repo's name) as a hostname label, plus an
/etc/hosts entry, inline before orcad starts: no image change. Verified on a dev VM: hostname ja-hello,
127.0.1.1 ja-hello, no sudo warnings.
- orcad: only the owner's desktop may attach/settle; devices paired through a grant are listed
by orca-sandbox-grant and refused (forks keep their offer's origin id)
- desktop: per-workspace limits (3 waiting, 60/hour), always-allow runs one at a time and 30/hour,
one banner per burst, refusal floods logged once a minute
- dialog: commands over 20 lines refused; long ones must be scrolled to the end; agent name muted;
Turn off for this workspace; read delay counts from when a prompt reaches the front
- lookalike spaces and combining marks refused; settled events only close their own workspace's prompt
- executor reports soon after the shell exits even if an escaped process holds the pipes; hard stop
- audit log keeps 3 files, long commands stored as prefix + sha256
- rules expire after 30 days and are cleared when a shared workspace is allowed
Vercel's sandbox-init listens on 23456 as root (pid invisible to the user); the
VM declares it via ORCA_HOST_MANAGED_UNOWNED_PORTS. Any other pid-less
listener (root-run postgres, Docker-published ports) stays under External.
Plan & billing now reads '<Team> · Team plan', shows the last 30 days of sandbox
hours as a bar chart, and (owners/admins) the top workspaces and members. Members
see the team chart and their own hours. The overage toggle becomes one 'Monthly
spend cap' field ($0 = off, 'Turn on with a $25 cap'); the rate is a one-line
hint; alert levels are read-only values. The manual refresh icon is gone.
Billing Worker: additive GET /v1/billing/usage (read-only over usage_events,
no migration).
- POST /v1/sandboxes takes an Idempotency-Key (api_idempotency, 24 h, scoped per account and member):
the same key and body return the first create's sandbox instead of starting another; same key with a
different body is a 422. The desktop sends context.idempotencyKey when its caller provides one.
- The desktop sends the workspace name as the create's title, so lists show it, not an untitled row.
- GET /v1/sandboxes/boot-plan?repo= says whether a create would clone or update (environment build);
the driver asks in parallel with the create, so the build sheet names 'Clone repository' or 'Update
repository' once, with a placeholder until it knows, and never relabels mid-run.
Home's team list leaves routine-run VMs out unless the new Routine runs switch is on. The switch is
offered only when the team has routines, counts the running runs it hides, and is remembered per
account. Hidden runs leave the workspace count but keep their sandbox hours in the total. Listed
runs carry a routine mark and 'Routine · <name>'. Control plane: GET /v1/team/workspaces
?routines=include, plus fromRoutine/routineName per row and a routineRuns summary.
Design-squad review of #336: a teammate's heatmap is shown only if they opted in (same
Experimental switch as Pokes, renamed 'Pokes and activity'); otherwise their profile says they
haven't shared it. Your own heatmap always shows. Team rows get 'View profile' first in the menu.
Own-profile hint: 'Hover a day to see your sandbox hours.'
cloud/apps/acceptance/live/checkpoints-vm.ts runs a real Claude Code chat in orcad on a dev VM,
model pointed at a no-cost stand-in on the VM, over the desktop's own E2EE request connection.
Checks one checkpoint ref per turn, refs and files unchanged across sleep/wake and rollover,
Rewind + restore files + Undo from a fresh connection, the agent's context rewound too, and a
restore after an orcad restart. Always deletes the VM. Adds catalog row cloud.checkpoints.
Conductor 0.91 parity. Clicking a teammate opens their profile, which shows their role, a
26-week daily usage heatmap and their recent team workspaces. Teammates see only 0-4 levels,
and amounts are shown only to you. Pokes are opt-in in Settings > Experimental and need both
people to have them on. They are rate-limited to 1 per minute per teammate and 20 per hour, and
delivered in-app by a 30 s poll that runs only while Pokes are on and Orca is in front.
Control plane: migration 0050 (member_usage_hours, member_poke_prefs, member_pokes),
src/team-presence.ts routes. The heatmap reads usage_outbox sandbox time plus dated
token_usd_micros deltas. A member's poke opt-in is dropped when they are removed.
The first create of a repo that no environment of the account lists makes a default environment for it
(just the repo, cloned blobless, no install script) and builds it in the background with the existing
environment build pipeline (EnvironmentBuildRunner). Later creates boot from its active build and fetch
only what changed, in the background, shown as 'Update repository' with git's real progress.
- environments.kind 'org' | 'auto' + last_used_at (migration 0049, applied to dev); an admin's edit
adopts an auto environment; an admin environment listing the repo always wins
- auto environments unused for 14 days are deleted with their snapshots (cron); per account only
- golden refresh, staleness, pruning and the warm pool apply as for any environment
- the caller's machine size stands for auto environments; an explicit other region skips them
- AUTO_ENVIRONMENTS=off disables it
- desktop: feature repo-update, 'Update repository' / 'Fetching changes…' / sidebar 'Updating the
repository…'
Measured on dev (stablyai/orca, staging OWNER): before 6 s to ready + 58 s clone; the background build
took 79 s; after, ready in 6.6-8.7 s with the repo current 0.7 s later (7.4-9.4 s total).
Runs the confirm-before-revoke auth check on demand: a pass within 10 min answers at once (verified_at, migration 0048), otherwise one check rate-limited to one a minute per person, revoking (audited) only on a definite authentication_error. Storing a new token forgets the old verification.
A single 401 on an Orca-account request no longer revokes the person's Claude account everywhere. The hook only fires after the real credential was swapped in and sent. The control plane then makes one free auth check with the real token (Anthropic count_tokens, oauth beta) and revokes only on a definite authentication_error; anything else (passes, 429, 5xx, other 401 types, network) keeps the account. Checks are rate-limited to one per person and agent per minute (agent_credentials.checked_at), and every keep/revoke is audited (agent_credential_audit, migration 0047). Codex keeps its lease-refresh path.
The team and per-member concurrency counts (billing's checkStart input and the guardrail backstop)
counted 'suspending' rows, which drain for up to 5 min after someone chose Sleep and read as
asleep everywhere, and 'creating' rows whose Worker was cut off (until the cron released them).
Both could refuse a start that the user's view says fits. The global capacity cap still counts them.
A repo-backed create of stablyai/orca (2.7 GB) did a full git clone inside the 120 s orcad start
command, failed at ~127 s with a bare create_failed, then the pooled path retried on a fresh VM
(~255 s total). When the desktop gave up, the cut-off Worker left the retry row in 'creating' for
15 min.
- workspace prelude clones --filter=blob:none (stablyai/orca: 68 s instead of >120 s timeout)
- feature clone-progress: the clone runs detached after ready; GET /v1/sandboxes/:id/clone reports
{state, percent, stage} and records a failed clone's reason in last_error; 15 min clone timeout
- failed creates record why (orcad_start_failed, provider_<status>, ...) instead of create_failed
- a pooled boot that failed after 30 s is not retried on a fresh VM
- 'creating' rows whose Worker was cut off are released after 5 min (create_abandoned)
#305 moved billing's GitHub install-url call onto a service binding because same-account Workers
can't fetch each other (Cloudflare error 1042). No prod billing config exists yet (launch runbook
step 1.2); this test fails if the new wrangler.prod.jsonc names SANDBOX_CONTROL_URL without the
binding, or binds another environment's control plane.
- The composer read the setup session with a bare readCloudSetupSession() call in render. The React
Compiler memoizes that on the session id, so the panel kept its first (empty) read: rows never
moved past 'In browser', the finished session never auto-started the workspace, and the GitHub
row stayed 'Needed' after the browser said 'You're all set'. Read it through
useCloudSetupSession (useSyncExternalStore with a per-poll cached snapshot) instead.
- Billing asked the control plane for the GitHub install URL with a plain fetch to its workers.dev
origin. Same-account Workers can't do that (Cloudflare error 1042), so every setup GitHub step
showed 'Orca Cloud setup is unavailable'. Bind the control plane as a service (SANDBOX_CONTROL)
in the dev config and send the request through it when bound.
Found by the NP-4 end-to-end run (np4-e2e) on m4air against staging.
GET /v1/agent-credentials adds providerState {claude, codex: on|off|disabled} next to providerEnabled.
Clients read it through orcaCloudProviderState (older servers: disabled = kill switch; a state that
contradicts providerEnabled is never trusted). Flag off is a new 'unavailable' kind: Settings says
it isn't available in Orca Cloud yet (no 'right now / until it's back'), and the composer hides the
Agent row like an agent without an account path. Kill-switch copy is unchanged.
Live m4air run: a signed-in owner whose active team never had a trial was sent
to T3/Checkout. Only teams bound in the browser got the trial. Session creation
now runs the same grant (N11 limits) for the active team, so the chain skips
the plan page.
The boot resolves the session env before the VM is up, and a secret change only refreshes running sandboxes, so a value saved from the build-sheet Secrets row during the boot never reached the workspace (found in the W6 live e2e). Once running, the boot re-resolves the env and refreshes the session when it changed.