- Account/billing cards use glossary titles (Out of sandbox hours, Spend cap reached, Payment failed,
running-sandbox limits), role-aware next steps, and 'Nothing was created, and you weren't charged.'
- 409 github_not_connected: 'GitHub isn't connected for <owner/repo>' + Connect GitHub (POST
/v1/github/connect, opens the GitHub install page) + Try again.
- A repo without a GitHub remote is refused in main before the control plane is called, with
Start empty / Push to GitHub first.
- GET /v1/config serves createTiming (p50/p95 of recent create-to-ready); the sheet's 'usually ~Ns'
reads it and shows no hint without a measurement.
- Orca Cloud servers display as 'Orca Cloud' in sidebar host labels and the usage segment; legacy
'<repo> VM <id>' names are renamed at startup.
* fix(sandbox-control): read the orgRole claim that Orca Cloud auth mints (#29)
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.
* wip(cloud): environment builds control plane
* wip(cloud): environment build boot path, staleness, routes
* wip(cloud): env build tests, anonymous staleness fallback
* wip(cloud): migration 0008 (claimed), quieter recovered clone errors
* fix(cloud): env build review: interrupted-snapshot recovery, bounded log reads, secret redaction, activation guard, hook/prelude fixes
* fix(cloud): wait briefly for the build exit code after its log ends; print the base image and package manager
* wip(desktop): org environments client, IPC, CLI, settings UI, orcad disk usage + low-disk banner
* perf(cloud): smoke-boot each environment build before activating it; skip the redundant prelude fetch
* fix(cloud): a GitHub token error fails the build as github_unavailable instead of cloning anonymously
* perf(cloud): warm pool VMs per hot environment build, so a create from an org environment skips the provider boot
* wip(desktop): cloud environment UI round-3 fixes
* fix(cloud): pool-per-build review: smoke race safety, provider hiccups skip smoke, usage-based hot builds with caps, bounded boots, index
* fix(desktop): Configure with an agent hands the prompt over at launch (trust dialog swallowed the pasted Enter); CLI builds table widths
* polish(desktop): W10 P2s — InlineError action slot, shared focus-ring, copy
* feat(cloud): build event stream heartbeat every 20 s
* fix(desktop): FE review — stable virtualizer, shadow-buffer replays, heartbeat-aware build watch, CLI follower restarts, repo import path checks, poll races
* feat(cloud): meter environment build sandbox time to the member who started it; no setup-script nudge for workspaces booted from an env build
Plans said 15 minutes, the desktop slept at 20. The shared wire module now owns the value; the
control plane serves it at GET /v1/config (public product constants), the desktop driver's idleStopMs,
billing's Team plan idleSleepMinutes and the billing fixtures read the constant, and the idle-sleep
toast already prints the scheduler's value from the event.
## W4: team collaboration on Orca Cloud workspaces
### Control plane (`cloud/apps/sandbox-control`)
- **Registry** (`0024_workspace_registry.sql`, applied on dev D1): owner (reassignable), `created_by`, branch, `visibility` (private default / team), section, last activity, token spend. Reuses 0020's `name`/`archived_at`.
- **Routes under `/v1/team/*`** (W5 owns `/v1/workspaces`): list with Mine/Team/All, By, In, Archived filters; PATCH (rename, share or make private, section, archive, reassign owner); presence heartbeats; wake and lease for teammates; links resolve; org prompts/PR instructions (compare-and-set).
- **Security fix:** `/v1/sandboxes/:id` used to be account-scoped, so any org member could resume a teammate's sandbox and receive its orcad pairing code. It is now owner-only; admins can still suspend and destroy.
- **Per-member, per-device E2EE grants:**
- The VM mints a single-device orcad offer and seals it to the requesting device's X25519 key.
- The control plane stores only the orcad device id.
- Revocation runs `orca pair --revoke`, which closes the device's live sockets, plus an on-disk registry removal.
- When the VM is asleep, the revoke is queued and applied at boot before orcad starts.
- Triggers: explicit revoke, making a workspace private, archive, reassign, member removal (internal route), and phone device-session revoke (W8).
- VMs from older images get the entry points installed on demand.
- **Git as the sender:** a member's own GitHub user-to-server token (App OAuth, envelope-encrypted) is swapped in at egress only for the workspace repo. The VM gets per-member git configs and `orca-git-as`.
- **Cost:** metered compute × billing rate, plus optional token spend.
### orcad (generic)
- New owner-only RPC `pairing.revokeDevice` and `orca pair --revoke <device-id>`: revokes a device and closes its open connections immediately.
### Desktop
- A "Cloud workspaces" section on Home: Team/Mine, By, In and Archived filters; sections; live presence; cost; last activity. Single-click Open on a teammate workspace pairs this device through an E2EE grant. Also a row menu (share/private, move to section, reassign, archive) and copy link.
- Deep links `orca://cloud/w/<id>[/c/<chat>]` (open-url, argv, second instance, sidecar handoff) and the `cloudWorkspace.copyLink` keybinding.
- Messages to agents in shared workspaces carry a sender envelope.
- Uses the W10 shared primitives.
- Two rounds of adversarial design and frontend review.
### Evidence
Real Vercel VMs; see the W4 report. Numbers:
- grant issued in 0.55 s, paired in 1.35 s
- revoke closes the live desktop socket ~30 ms after the PATCH returns
- two members' pushes land on GitHub with their own author identities (shared dev Worker, preview alias w4); a push to another repo gets 403
* feat(cloud): SSH into Orca Cloud workspaces (orca ssh, ssh-proxy, VS Code/Cursor Remote-SSH)
- VM: unprivileged key-only sshd on 127.0.0.1:2222 (orca-sshd), host key and Orca-managed
authorized_keys on the persistent disk; orca-ssh-keys entry point; provision extracts only the
server binaries (dnf or apt, +1.5-2 MB) and unlocks the sandbox user for key logins.
- Control plane: POST/DELETE /v1/sandboxes/:id/ssh-keys runs the fixed entry point and answers
the pinned host key; editor Remote-SSH server hosts on the egress allowlist.
- Desktop: per-workspace ed25519 keys in userData (0600), Orca-owned ssh_config + pinned
known_hosts, ~/.ssh/config Include only after consent; wake -> key -> runtime port forward
(openRuntimePortForward reason 'ssh'); revoke (deferred while asleep); SSH traffic keeps the
workspace awake.
- CLI: orca ssh-proxy <workspace> (ProxyCommand) and orca ssh <workspace>.
- UI: Open in -> VS Code / Cursor over Remote SSH, SSH submenu (Copy SSH Command, Revoke),
include consent dialog, progress/error toasts.
* fix(cloud-ssh): review fixes — no agent forwarding, reinstall per VM boot, wake to revoke
- ForwardAgent/ForwardX11 no in Orca's host blocks; AllowAgentForwarding no in the VM's sshd.
- Reinstall the key (idempotent; restarts sshd, re-pins the host key) once per VM boot, so a
wiped disk, rollover or dead sshd heals on the next connection.
- Revoke wakes the VM to remove its copy; only an unreachable VM falls back to the queue.
- orca-ssh-keys rejects multi-line keys; sshd log truncated per start; audit log entries for
ssh key add/remove; state parsed per entry; % escaped in ssh_config paths; ProxyCommand pinned
to this app's profile; ssh-proxy flushes stdout before exit; --print shell-quotes; editors
open via --folder-uri with a checked path; "Remove from ~/.ssh/config" + one-time backup.
GOLDEN_IMAGE_SOURCE=git:<repo>#<branch>: each cron tick resolves the branch HEAD through the GitHub
App installation on that repo (read-only single-repo token, operator-only request surface) and, when
HEAD differs from the active image's commit and the compare touches VM paths, starts a source build:
fetch the commit, build orcad + bundled orca CLI, provision, then the existing wipe, snapshot, smoke,
atomic activate (previous image kept as rollback). The commit is recorded as bundle_sha256 git:<sha>
(kept through clone refreshes) and in /opt/orca/runtime/orcad/SOURCE_COMMIT. Also (W2): openssh-server
in the image (on demand only), https apt sources, unlocked password field for key-only sshd.
Follow-up to #49 (W1 environment builds), control plane only.
- **Warm pool per hot environment build** (`pool.ts`): alongside the golden pool, keep 1 ready VM booted from the active build of each environment that was used or built in the last 2 hours (standard size, at most 20 builds). A create from that environment claims it. Pool deletes never remove the image a VM booted from.
- **Smoke-boot every build before activating it**: orcad must answer on the new image. If the install script broke Orca, that build fails as `smoke_failed` and never reaches a workspace. The boot also takes the first-boot cost so users don't.
- **Prelude**: skips the fetch when the build is already on the requested branch, because Orca fetches the base right before `worktree add`.
- **GitHub token errors**: a minting error now fails the build as `github_unavailable` (step clone). Before, it cloned anonymously and failed with a misleading 404, seen live.
Evidence: shared dev preview alias, private repo, sequential, n=12.
| Create → orcad ready | p50 | p95 |
|---|---|---|
| Before | 4.99 s | 6.71 s |
| After | 3.76 s | 5.67 s |
The only sample above 3.9 s was the first one, which missed the pool while it was cold. Pool hits put the VM phase at 0.25 s. Tests: 221 pass, plus typecheck.
Control-plane half of W1, org environment builds (Conductor "Cloud Computer" parity). The desktop settings UI, CLI and disk banner follow in a second PR.
- **Org environment**: repos (GitHub, up to 50, each with a default branch), an install script, env var names, and a machine size (Standard 4/8 or Large 8/16; Large bills 2× through the existing usage path). Members can read; owners and admins edit. Edits use optimistic concurrency.
- **Versioned builds** (`#N`), with status, live log stream (ndjson), stored log, failure step, duration and snapshot.
- Each build boots a throwaway VM from the active golden image, clones every repo, and runs the install script once with `set -euo pipefail`.
- It records commits and lockfile blob ids, checks that orcad survived, then saves a manual snapshot.
- **Failed builds never replace the active build.** Rollback is instant (`activate`). The newest 5 builds plus the active one keep their snapshots.
- **New workspaces boot from the active build** (or a pinned `environmentBuildId`). Existing workspaces keep their build, and destroying a workspace never deletes the shared image.
- A first-boot prelude links the prebuilt repo as the project and fast-forwards the requested branch.
- A post-checkout hook hard-links the installed dependency folders into each new Orca worktree (about 250 ms).
- **Rebuild prompt** (`/staleness`): reports a lockfile change, a moved default branch, a config edit, or a newer base image. It uses the GitHub compare API, or anonymous smart-HTTP plus raw blob hashing when the REST API refuses Worker IPs.
- **Repos with no commits** build and open; Orca adds an empty first commit.
- **Hardening from the review**:
- Recovers a snapshot whose finalize was cut off (adopted by box name).
- Bounded log reads on cancel/timeout.
- Org secret values are redacted from live and stored logs.
- Activation is guarded in SQL.
- Build-number races return 409.
- Migration `0008_environments.sql`: number claimed in /tmp/vdrv/migrations.claims and already applied to dev D1. It is additive.
## Evidence (real Vercel VMs)
Full write-up: `.context/cloud/reports/w1-env-builds.md`.
- vercel/ms build: 15.9 s (boot 1.8 s, clone+install 6 s, snapshot 8 s).
- Private `nwparker/orca-sandbox-push-test` plus public `vercel/ms` in one environment on the shared dev preview: build succeeded in 18 s, and a workspace created from it was ready in 6.3 s.
- Create → orcad ready:
| Boots from | p50 | p95 |
|---|---|---|
| Environment build (n=10, deps installed) | 4.37 s | 8.41 s |
| Bare golden image (n=9) | 4.79 s | 7.61 s |
- Rollback, creating from build N-1, an empty repo (`rsc/empty`), and staleness `deps_changed` → Rebuild → `up_to_date` were all verified live.
## Tests
`cloud/apps/sandbox-control`: 99 vitest tests and typecheck pass. The new `test/environments.test.ts` covers RBAC, build/fail/keep-last-good, concurrent build 409, cancel, live stream, boot from active and pinned builds, rollback, prune, interrupted-snapshot recovery, and staleness.
Reported by W5: when a pooled VM fails to adopt and its destroy stops short (the provider's delete
snapshot still being written), the row stays 'destroying' and keeps the workspace's live unique index,
so the fresh-VM retry hit conflict:sandbox_destroying. The abandoned row (never held user data) now
detaches from the workspace; the cron finishes destroying it. Regression test forces both failures.
* feat(cloud): per-device Orca Cloud access for phones
A signed-in desktop enrolls a paired phone's Ed25519 key with the sandbox
control plane (orcaCloud.enrollDevice, phone -> paired desktop -> /v1/device-sessions).
The phone then signs its own short-lived EdDSA tokens and reaches the control
plane directly with its user's identity; no bearer secret is stored anywhere.
Sessions are revocable by the user or the phone itself (401 on the next
request), expire after 30 idle days, and cannot enroll devices, manage
secrets or connect GitHub. onDeviceSessionRevoked lets the workspace
registry cascade grant revocation.
* fix(cloud): device sessions re-vouched every 14 days, revoked keys stay revoked, unpairing a phone ends its cloud access, race-safe enroll, dev namespace
* fix(cloud): phones are 'device' API callers: no API keys or OAuth approvals
Live dev after a golden rotation showed 1 hit in 10 creates: stale pooled VMs only recycled on the next
cron, and refills ran only after a hit. Now every create (hit or miss) refills in the background, the
cron maintains the pool after the golden tick with fresh deps, and a lease row keeps overlapping
refills from overshooting.