Commit Graph
578 Commits
Author SHA1 Message Date
Neil 63b6999dee feat(cloud): pdx1 default region, iad1 fallback, golden per region; Standard = 8 vCPU / 16 GB (U17, U18) (#194)
* feat(cloud): pdx1 default region with iad1 fallback and one golden per region; Standard is 8 vCPU / 16 GB (U17, U18)

- golden_images and environment_builds record their region (0040, backfilled iad1); rotation,
  git-source builds, smoke and activation run per region; lifecycle picks the region's golden.
- New creates default to pdx1; a create with no pinned region falls back to iad1 automatically on a
  capacity/provider error (VERCEL_FALLBACK_REGIONS). Until pdx1 has an image, creates use iad1.
- Environment builds boot and smoke in their own region; workspaces from a build boot there.
- One size: Standard = 8 vCPU / 16 GB (ORCA_CLOUD_SANDBOX_SPEC). Legacy 'large' requests map to
  standard; 0040 rewrites existing large rows.

* merge cloud/integration (ORCA_CLOUD_SANDBOX_SPEC landed in #196)
2026-11-10 01:07:56 -08:00
Neil b22512a759 Merge pull request #196 from nwparker/cloud/standard-8vcpu-copy
feat(cloud): one sandbox size (8 vCPU · 16 GB, 1×): billing and copy
2026-11-10 01:00:13 -08:00
Neil c997f9cf3c fix(cloud): orcad and its terminals trust the egress proxy's CA (Gemini CLI hung in every cloud terminal)
Hosts with credential rules (model APIs, api.github.com) are answered by Vercel's egress proxy with a
certificate from its CA. Vercel's command env trusts it (NODE_USE_SYSTEM_CA, NODE_EXTRA_CA_CERTS,
SSL_CERT_FILE) but orcad starts under env -i, so Node clients in terminals used Node's bundled roots:
Gemini CLI retried forever ('This is taking a bit longer') while the same call from a command
returned at once. Found on the live proof of the new agents; NODE_USE_SYSTEM_CA=1 in the terminal
fixed it there (1 s to the provider's answer).
2026-11-10 00:57:32 -08:00
Neil ff12245a82 feat(cloud): one sandbox size (8 vCPU · 16 GB, 1×) — billing counts every size 1×, plans/usage/run-on/env/CLI/phone copy from ORCA_CLOUD_SANDBOX_SPEC, size pickers removed 2026-11-10 00:54:28 -08:00
Neil 0ca83ef860 feat(sandbox-control): dev control plane also trusts staging auth sandbox tokens
Staging sign-in now works end to end in dev without a dev operator key on the user's machine.
Same account ids as billing dev (which trusts staging already); never honored when SANDBOX_ENV=prod;
a staging key cannot pass as the prod issuer.
2026-11-10 00:46:50 -08:00
Neil 64fbff83f1 Merge pull request #190 from nwparker/cloud/agents-in-image
feat(cloud): OpenCode, Gemini CLI, Cursor agent and Copilot CLI in the image; their keys swapped at egress
2026-11-10 00:33:06 -08:00
Neil 4286eb9cfc fix(cloud): no swappable key reaches the VM unrouted; Cursor's exchange reply passes only the access token
Security review: a Copilot BYOK key without a usable base URL (or with one from another scope) had
no route, so it went to the VM as itself. Every swappable name now becomes a placeholder, routed or
not, and the Copilot pair resolves like the other agent keys (shared beats personal, as a pair).
The Cursor exchange reply is parsed whatever its content type and cut down to accessToken; headers
(set-cookie) are not passed. opencode.ai is not allowlisted (anonymous share uploads).
2026-11-10 00:24:34 -08:00
Neil a1d4aad723 build(cloud): source-build smoke prints used disk and /opt/orca size 2026-11-10 00:20:05 -08:00
Neil 095c4ed960 build(cloud): fail the golden build when a new agent CLI does not start; smoke checks them 2026-11-10 00:19:31 -08:00
Neil 2086f40305 feat(cloud): OpenCode, Gemini CLI, Cursor agent and Copilot CLI in the golden image, keys swapped at egress
Image: pinned opencode-ai 1.19.5, @google/gemini-cli 0.67.0, @github/copilot 1.0.108 (npm) and
cursor-agent 2026.11.05-86047df (checksummed tarball). opencode's unused linux build variant is
dropped (~205 MB). First-run screens are answered at every boot/secret refresh (folder trust for
Gemini, Cursor and Copilot; Gemini's auth picker when a Gemini key is set; OpenCode's gateway model
from ANTHROPIC_MODEL/OPENAI_MODEL); self-update is off for all four.

Egress: GEMINI_API_KEY / GOOGLE_GENERATIVE_AI_API_KEY (generativelanguage.googleapis.com,
x-goog-api-key), CURSOR_API_KEY (api2.cursor.sh, only POST /auth/exchange_user_api_key; the
refresh token is stripped so nothing long-lived lands on the VM) and COPILOT_PROVIDER_API_KEY /
BEARER_TOKEN (host of COPILOT_PROVIDER_BASE_URL, same scope only) are placeholders in the VM and
swapped by the proxy. Allowlist adds the agents' API and sign-in hosts. API/MCP accept the image's
agents; routines stay on claude/codex.
2026-11-10 00:19:04 -08:00
Neil 4b3e0b62d3 docs(cloud): lift download/remote-desktop audit caps after real-UI run on m4air 2026-11-09 23:50:14 -08:00
Neil f8058b55a5 feat(cloud): desktop UI checks for acc.download, acc.remote-desktop and local.core-workflow (W9D_ONLY=ui-audit)
- local.core-workflow: composer local worktree (Blank Terminal), edit in its terminal, diff in
  Source Control and the diff tab, Stage All, commit
- acc.download: Explorer Download… (20 MiB file) and Download as ZIP… (folder) with progress and
  success toasts and sha256 checks; only the native Save panel's answer is stubbed in main via the
  inspector (W9D_MAIN_INSPECT)
- acc.remote-desktop: workspace menu Open remote desktop -> orcaDesktop connected, noVNC canvas
  sampled, a red xterm launched on DISPLAY=:1 shows up; guest screenshots over CDP
- local rows without a host badge count as local
2026-11-09 23:50:14 -08:00
Neil 1e2dc4f00f fix(cloud): acceptance report applies independent-audit caps (partial) and adds local core workflow, run actions, read-while-asleep and checkpoints rows 2026-11-09 23:31:50 -08:00
Neil d9b9e23ca3 docs(cloud): acceptance wake delivery on snap_6kSW (#173/#175) 2026-11-09 23:31:50 -08:00
Neil 20338e08c5 fix(sandbox-control): routine comments mint an issues:write token so plain issues get comments (falls back when the installation lacks it) 2026-11-09 23:05:42 -08:00
Neil 543cff405e perf(cloud): seal the ready offer into the start line; poll ready every 10 ms (#175)
The wake phase "orcad ready" (W9) runs from orcad's spawn to the control plane
having the pairing offer. After orca-sandbox-start exits, the control plane
made a second Vercel round trip (files API, ~120 ms from here) to read the
offer, because command stdout is kept as logs.

startOrcad now passes a fresh 32-byte key per call in env (env is not kept).
The start script adds "sealedOffer": "<iv>.<base64 AES-256-CTR(offer line)>"
to the pointer line it already prints, so the logs only hold ciphertext and
the control plane opens the offer locally. The key is unset first thing in the
script and in the prelude, so nothing the script starts inherits it. If the
offer is missing or does not open (older image, no openssl, wrong key), the
control plane reads the file as before. Older Workers send no key, and
current images then print the plain pointer.

The ready poll in the start script drops from 50 ms to 10 ms (~20 ms mean),
keeping the 60 s minimum timeout.

Verified on a VM from snap_aQmW, driving the start script like the control
plane: all four starts, including an idempotent second start, opened without
the files API, and the stdout never held the pairing code.
2026-11-09 22:01:32 -08:00
Neil 213ce633e5 perf(cloud): start orcad from a full V8 code cache (~0.3 s off ready) (#173)
NODE_COMPILE_CACHE only stores what V8 compiles before a script runs: 5.4 MB of
orcad-server.js's 15 MB. Every function startup runs still compiled lazily on
every boot. Measured on a VM from golden snap_aQmW, orcad ready went from
0.95-1.06 s to 0.70-0.78 s (5 pairs, alternated), and from ~1.25 s to ~0.95 s
on a slower host.

image/vm/orcad-boot runs orcad-server.js through a vm.Script whose code cache
is taken as the image's smoke run exits, so it covers what startup compiled.
orcad sees the same argv, __filename, require and import() resolution as under
plain node. The cache file starts with a header (Node version, arch, bundle
length and crc32): V8 itself only checks the source length, so a cache from
other bundle bytes would otherwise run stale code. Without a matching cache
orcad loads through require exactly as before.

Verified in that VM: the cache is accepted, the experimental-loader warning
stays out of orcad.log, and a terminal created through the CLI runs (node-pty
resolves via import() from the bundle directory).
2026-11-09 21:52:21 -08:00
Neil 4ec131ade1 docs(cloud): acceptance findings — asleep delete 1.5 s after #171 2026-11-09 21:40:54 -08:00
Neil 515bc10542 feat(cloud): desktop acceptance stamps app log lines with wall-clock time and records Delete click/done times 2026-11-09 21:40:54 -08:00
Neil dd0cd5b001 feat(cloud): acceptance report p99 column; wake delivery on the #151/#162 golden (n=22) 2026-11-09 21:27:41 -08:00
Neil 6732b345b2 feat(cloud): acceptance env.setup-script verifies orca.yaml setup in the VM exactly once (core #168) 2026-11-09 21:20:26 -08:00
Neil 5a7484cf20 Merge pull request #169 from nwparker/cloud/w9-eval
feat(cloud): acceptance — billing gate via billing-test accounts, CLI --on cloud, asleep move-delete, staging teams
2026-11-09 21:17:28 -08:00
Neil 78c874ee1f feat(cloud): run orca.yaml setup once for API/CLI/phone/routine workspaces (#168)
Desktop-created cloud workspaces already run setup through orcad's worktree
create after the setup prompt. Workspaces made through createWorkspace use the
VM's primary checkout, which no worktree create touches, so their setup never
ran. Start it detached on create (once per workspace, orcad's env, skipped on
environment builds) and have routine runners wait for it.
2026-11-09 21:16:28 -08:00
Neil 22f8d5abb4 docs(cloud): acceptance findings — CLI create and asleep move-delete closed 2026-11-09 21:15:16 -08:00
Neil d9cd1bac93 feat(cloud): acceptance api.cli runs orca worktree create --on cloud headlessly (W5 #167) 2026-11-09 21:14:53 -08:00
Neil 3b8867d3de feat(cloud): acceptance control-plane billing gate end to end via dev:billing-test-* (#166) 2026-11-09 21:14:53 -08:00
Neil e506a7bca7 Merge pull request #167 from nwparker/cloud/w5-cli-worktree-on
feat(cli): orca worktree create --on cloud (private build) with --env, JSON output and clear plan/at-cap/sign-in errors
2026-11-09 21:13:22 -08:00
Neil 70e9b2a6c4 feat(cli): orca worktree create --on cloud[:<project>] [--env] creates an Orca Cloud workspace (private build); clear plan/at-cap/payment/sign-in errors; API accepts a team environment 2026-11-09 21:08:55 -08:00
Neil f2386f9a37 feat(sandbox-control): dev-only SANDBOX_DEV_BILLING_ENFORCE_PREFIX sends one dev account prefix through the real billing gate (#166)
dev:* accounts skip billing on dev, so W9 could not test the gate (no plan refused, active allowed,
grace expiry suspends, at-cap refused) with dev tokens. Dev accounts under the configured prefix now go
to the billing Worker. Honored only when SANDBOX_ENV is exactly 'dev' and only for a specific
sub-prefix (dev:<word>- or dev:<word>:), never all dev accounts; tests cover prod/staging/test being
ignored and over-broad prefixes being rejected. Dev sets dev:billing-test-.
2026-11-09 21:07:28 -08:00
Neil f04525d7db feat(cloud): acceptance closes idle sleep, agent sign-in persistence, wake-on-message, scheduled routines, file search, usage metering, overage cap and full dunning; web client post-MVP 2026-11-09 21:03:10 -08:00
Neil c77ab2b16c feat(cloud): desktop keystroke n=W9D_KEY_SAMPLES with p99/outlier timeline; life.auto-resume desktop check
- W9D_KEY_SAMPLES (default 10): stats gain p99; each sample records time since the workspace's
  first terminal output and wall clock, plus a histogram and the outliers
- life.auto-resume (W9D_AUTO_RESUME=1, W9D_ONLY=auto-resume): personal AI Gateway secrets for the
  dev user (kimi-k2, never logged, deleted after), Claude runs a 40 s loop, status panel Restart,
  Orca must send continue once and the turn must finish; mapped to catalog life.auto-resume
2026-11-09 21:03:10 -08:00
Neil 1772627e6e fix(cloud): CLI/API workspace sleep (POST /v1/workspaces/:id/sleep) records the deliberate sleep too 2026-11-09 20:47:22 -08:00
Neil a878062983 Merge pull request #163 from nwparker/cloud/w2-predict-gaps
fix(cloud): predictive wake gaps: drag, cross-client sleep cooldown, seat cap
2026-11-09 20:43:41 -08:00
Neil 66170d3213 fix(cloud): a failed user-sleep hint write never fails the sleep (worker deployed before migration 0038) 2026-11-09 20:41:55 -08:00
Neil 0a7785d4b5 fix(cloud): predictive wake gaps: a drag never wakes (press waits for release, drag cancels pending), the sleep cooldown reads the server's last chosen sleep from any client, and a wake is skipped with the owner's seat or team at or one below its running cap 2026-11-09 20:41:25 -08:00
Neil b98feda7f8 feat(cloud): targeted desktop acceptance for #157 (picker sign-in twice, Move then Delete)
- W9D_MODE=targeted: picker sign-in on a fresh profile and again after a sign-out, create +
  keystroke, Move to this computer with Archive afterwards then Delete within seconds (destroyed,
  no suspend/archive error toasts), normal delete
- asleep Delete through the dialog without a Force delete offer is intended: graded pass
- cleanup removes local copies a run made; catalog row + import mapping for move-then-delete
2026-11-09 20:34:07 -08:00
Neil 52f1ba7796 docs+copy(cloud): the per-seat running cap counts the workspace owner's seat; at-cap errors a teammate sees name the owner 2026-11-09 20:11:19 -08:00
Neil aedfb7f7de feat(cloud): desktop acceptance covers #149 (picker sign-in, asleep Force delete, cross-team delete, sign-in card)
- auth.picker-sign-in: picking the signed-out Orca Cloud row must leave Orca Cloud selected
- life.delete-asleep(-after-move): no wake, Force delete offer, destroyed; life.destroy now a
  normal delete of an awake workspace; D-2 passes on a cross-team destroy or a kept
  "couldn't confirm" row
- vercel-check.ts: confirms each run sandbox is 404 on Vercel and records it in results.json
- catalog rows and import mapping for the new steps
2026-11-09 19:59:49 -08:00
Neil 6bc1fffc67 W8: teammates wake a team workspace from the phone (team-route wake; owner route not widened) (#155)
* feat(w8): teammates wake a team workspace from the phone (team route long-poll wake, same access/billing/cap; owner route stays the fallback)

* test(w8): team wake refuses an admin on a private row, never returns the endpoint; phone never falls back on 401/403
2026-11-09 19:59:10 -08:00
Neil 78fce9977d fix(sandbox-control): the orphan sweep no longer stops warm-pool VMs (their record is the pool row) 2026-11-09 19:49:24 -08:00
Neil d5e57508cf Merge pull request #152 from nwparker/cloud/activity-keepalive
feat(cloud): VM activity, not the desktop, keeps a sandbox's lease alive
2026-11-09 19:44:57 -08:00
Neil 4dee4cc7c6 feat(cloud): VM activity keeps a sandbox alive without a desktop - orcad publishes its work verdict, the cron renews busy leases and sleeps idle ones 2026-11-09 19:41:36 -08:00
Neil 893be16f77 perf(terminal): prove an idle Linux daemon shell from /proc so Enter skips the paste settle (#151)
terminal.send text+Enter to a daemon-backed shell always waited the 500 ms agent paste
settle: the daemon's shell-foreground answer is lifecycle ownership, which is only proven
after a recovery or full-screen exit, never for a fresh or plain-command shell. On Linux
the idle-prompt claim now also accepts the shell's own /proc stat (process group is the
terminal foreground group, i.e. ps '+'), scoped to the target PTY; other hosts unchanged.

The acceptance wake split uses one persistent paired connection per wake (the desktop's
transport) by default; transport=cli keeps the per-call socket the CLI dials.
2026-11-09 19:38:55 -08:00
Neil 05d43602c8 Merge pull request #149 from nwparker/cloud/delete-unconfirmed-fix
fix(cloud): P0 delete leak - never drop a row for an unconfirmed sandbox; creator can delete across teams; asleep delete without wake
2026-11-09 19:33:17 -08:00
Neil 5bdec31000 fix(cloud): address review - asleep delete needs Force delete and runs ownership checks, destroyed sandboxes forget their row, removed members get no cross-team delete, destroy errors always surface 2026-11-09 19:27:29 -08:00
Neil cb7d58bbc0 fix(cloud): deletes never drop a row for a sandbox Orca Cloud can't confirm; asleep cloud delete skips the wake; creator can delete across teams; picker sign-in selects Orca Cloud; signed-out create shows the sign-in card 2026-11-09 19:20:44 -08:00
Neil 1417e042fc feat(cloud): desktop acceptance covers the #127 fixes and runs on a remote Mac
- auth.signed-out-create (D-1/D-4), proj.cloud-name (D-3), life.delete-unconfirmed (D-2,
  team switch -> not_found), life.delete-after-oob-destroy; life.destroy now requires destroyed
  and records the asleep-delete refusal
- starts signed out (dev auth) with no sign-in-then-relaunch workaround; mock keychain so a
  fresh dev app identity does not block on a macOS Keychain prompt
- cloud vs local rows disambiguated by badge (Move to this computer reuses the name)
- upload/download helpers for staging and collecting a run on a paired Orca environment
2026-11-09 19:08:55 -08:00
Neil f2df1de717 feat(cloud): acceptance wake-delivery split benchmark, persistent-connection downloads, golden status helper 2026-11-09 19:08:55 -08:00
Neil d7552c51a0 feat(cloud): acceptance remote-desktop check (ticketed websockify upgrade on the VM loopback) 2026-11-09 19:08:55 -08:00
Neil 75f7919107 docs(cloud): acceptance findings — desktop fixes #127 awaiting UI re-run 2026-11-09 19:08:55 -08:00