Commit Graph
76 Commits
Author SHA1 Message Date
Neil c1c1cb387f Merge remote-tracking branch 'private/cloud/integration' into cloud/read-snapshot-signed
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
#	cloud/apps/sandbox-control/image/vm/orca-sandbox-grant
2026-11-11 00:08:43 -08:00
Neil bfc8849b22 fix(cloud): grant sealer records the device only when the script provides the ledger path 2026-11-10 23:21:47 -08:00
Neil 766e0b23b6 Merge remote-tracking branch 'private/cloud/integration' into cloud/read-snapshot-signed
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
#	src/main/ipc/cloud-workspaces.ts
2026-11-10 23:20:55 -08:00
Neil d6b3646081 Merge remote-tracking branch 'private/cloud/integration' into cloud/local-command
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
#	src/renderer/src/i18n/locales/en.json
2026-11-10 21:04:43 -08:00
Neil 3f5f314efc fix(cloud): local commands — security review fixes
- 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
2026-11-10 20:11:56 -08:00
Neil d029218f18 fix(w3): keep pid-less user listeners visible; hide only declared platform ports
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.
2026-11-10 20:10:47 -08:00
Neil 0b986f1fc6 fix(w3): hide the sandbox's own services from External ports in cloud workspaces 2026-11-10 20:04:36 -08:00
Neil a86a8d6850 feat(cloud-create): automatic environments — repos come prebuilt after the first create
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).
2026-11-10 19:10:29 -08:00
Neil 61fd3ecf0f fix(cloud): read snapshot reads the workspace's own checkout (branch worktree), not the base clone; profile-cloud-service under max-lines 2026-11-10 18:01:58 -08:00
Neil 47395617b2 Merge remote-tracking branch 'private/cloud/integration' into cloud/read-snapshot-signed
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 17:02:00 -08:00
Neil 19cb3f2eac fix(cloud-create): blobless repo clone, optional background clone with progress, bounded create failures
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)
2026-11-10 15:51:46 -08:00
Neil 99c52ba889 Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-a9-recipe
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 05:53:41 -08:00
Neil 732c04ddd7 Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-a9-recipe
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 05:43:06 -08:00
Neil e6bb0a9a52 Merge remote-tracking branch 'private/cloud/integration' into cloud/read-snapshot-signed
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 05:34:29 -08:00
Neil 4c1ee40ffe Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-codex-broker
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 05:32:29 -08:00
Neil 849a12773b fix(cloud): find agent CLIs in /opt/orca/tools/bin; delete note only for running sandboxes; test retry skip
Verified on a dev sandbox (Claude Code 2.1.319, codex-cli 0.166.0): runCommand runs as ubuntu with
HOME=/vercel (same user as orcad); `claude auth logout` and `codex logout` exist and exit 0; the
script reported {loggedOut:2,left:0}, removed both credential files and kept other ~/.claude.json keys.
2026-11-10 05:27:41 -08:00
Neil 5b9d2735e0 fix(cloud): OpenCode with no credentials shows 'Connect a provider' instead of failing on Copilot or Zen
With nothing configured, OpenCode picked its GitHub Copilot provider (GITHUB_TOKEN is the workspace
GitHub App stand-in: 'Authorization header is badly formatted') or OpenCode Zen (opencode.ai is not
on the egress allowlist). The VM seeding keeps both in OpenCode's disabled_providers unless the user
connected them in OpenCode (auth.json); the user's own list is kept (ledger of what Orca added).
Checked on a dev VM: the composer opens on 'Connect a provider'.
2026-11-10 05:23:31 -08:00
Neil 1bca45b536 fix(sandbox-control): Codex broker review fixes (M1-M4, L5-L8)
- M1: a permanent refresh failure revokes only the login it refreshed (CAS on updated_at).
- M2: dead logins (refresh_token_expired/reused/invalidated) are never retried; a new login clears it.
- M3: the Codex account route only carries the token to /backend-api/codex/*, the plugin catalog and
  wham/usage (GET), seen live; plain segments only.
- M4: one atomic INSERT enforces one live broker per person (409 broker_in_progress) and 6 starts/hour;
  broker sandboxes use 2 vCPUs.
- L5: auth.json is read only after Codex exited 0.
- L6: the 401 hook respects the refresher's backoff and dead logins.
- L7: a cancel while the broker is starting wins: the sandbox goes and POST answers 409 cancelled.
- L8: orca-sandbox-env writes the marker before the swap, recognises any Orca placeholder, and never
  overwrites a login already set aside.
2026-11-10 05:22:43 -08:00
Neil 4af8f7fe04 feat(sandbox-control): Codex account broker (contract D11, flag S2)
Codex signs in once via the person's Orca account:
- POST /v1/agent-credentials/codex/broker starts a short-lived broker sandbox (golden image, egress
  auth.openai.com + chatgpt.com only, non-persistent, tagged orcaCodexBroker/orcaInfra so the workspace
  orphan sweep and billing never see it) running `codex login --device-auth` (Codex 0.166), and returns
  {brokerId, verificationUrl, userCode}. GET .../broker/:id -> {status: pending|active|failed}; DELETE
  cancels. On completion the Worker reads auth.json through the files API, stores the tokens
  envelope-encrypted and Personal-only in agent_credentials, and deletes the broker sandbox.
- Lease refresher (cron, every 5 min): refreshes the access token with the refresh token before expiry,
  single-flight per person (D1 lease) with a compare-and-swap write, so a refresh token is spent once and
  a delete or new login is never overwritten (no refresh_token_reused). Failures mark expiring/expired
  (by the real access-token expiry) or revoked (refresh_token_reused/invalidated). A chatgpt.com 401
  triggers a refresh instead of revoking.
- codexAccount AccountProvider: placeholder access token bound to the sandbox id (D14), swapped for the
  real one on chatgpt.com with ChatGPT-Account-Id; never in a shared workspace (D13); kill switch
  AGENT_ACCOUNTS_CODEX_DISABLED=1.
- VM: orca-sandbox-env writes a placeholder ~/.codex/auth.json (no refresh token) from
  ORCA_CODEX_AUTH_PLACEHOLDER, sets a managed HTTPS ChatGPT provider (Codex's Responses WebSocket can't
  pass the egress proxy; verified live), and sets a person's own login aside / restores it.
- Migration 0045_codex_broker (codex_broker_sessions, codex_leases; no token columns).
- Flag S2 on in wrangler.dev.jsonc; absent in prod.
2026-11-10 05:03:50 -08:00
Neil a81afb57e0 Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-a9-recipe
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 04:49:31 -08:00
Neil 5e1a5d7ac5 refactor(cloud): drop env-build scrub-on-clone (spec v4 D14); keep sign-out before delete 2026-11-10 04:33:45 -08:00
Neil a25934cf64 feat(cloud): sign in once via your Orca account (contract D10/D11/D13/D14) — Claude setup-token store (Personal only, flag S1, kill switch), egress-time Source with one stable account placeholder per sandbox, never in shared workspaces; migration 0044 2026-11-10 04:22:16 -08:00
Neil 9c50f9ae40 fix(cloud): grants only fill an empty signer pin; explicit forget; atomic pin merge; atomic VM key file 2026-11-10 04:15:52 -08:00
Neil be30702701 Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-a9-recipe
# Conflicts:
#	cloud/apps/sandbox-control/image/provision-golden.sh
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 04:11:07 -08:00
Neil 12fe0c867f feat(cloud): sign agents out before sandbox delete, scrub env-build images, recipe env warning (A9, P-4)
- Worker: run orca-agent-signout (each agent's logout, then credential files) on a running sandbox
  before the provider delete; bounded (20 s) and best effort, never blocks the delete.
- Environment builds: scrub agent auth (--scrub-only) before the snapshot; fail closed.
- New VM script registered in VM_SCRIPTS + provision loop; vm-scripts regenerated.
- Main IPC ephemeralVm:forwardedEnvSummary returns names only, grouped by credential-looking.
- P-4 warning component (+ fixture) shown under Settings > recipes; delete dialog line for awake
  Orca Cloud sandboxes.
2026-11-10 04:07:13 -08:00
Neil 9019db6822 feat(cloud): VM-signed read snapshots (Ed25519 per workspace), signer in sealed grants, shared pin policy 2026-11-10 03:36:27 -08:00
Neil e02edd279f Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-secret-request
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 03:34:32 -08:00
Neil f8e362259a fix(cloud): harden agent secret requests (review of #214)
- Agent and worktree come from the terminal orcad verifies from launch
  evidence (verifiedCallerTerminalHandle, shared with turn-sender); the
  terminalHandle param is gone and a worktree hint is kept only when the
  host knows it.
- secrets.* are accepted, and their events sent, only on cloud orcad
  (ORCA_IS_CLOUD=1 + ORCA_SANDBOX_ID); already-set never reads orcad's env.
- touchRequest emits secrets.requestUpdated; natural expiry emits
  secrets.requestSettled {status: expired}.
- orca-env skips the control plane's reserved names and prefixes, with a
  test that reads them from secrets.ts.
- reason drops bidi controls and turns C0/C1 controls into spaces.
2026-11-10 03:34:04 -08:00
Neil 86ccf4eb29 fix(cloud): read snapshot lists chats from orcad's session store (the local socket can't stream subscribeStatus)
Live on dev the first capture sealed zero sessions: agentSession.subscribeStatus needs a streaming
transport and the unix socket refuses it, and worktree.ps lists only agents live right now. The
collector now reads orcad's own session records (agent-session-journal.db, read-only): provider,
name and last turn, open tabs first; live worktree.ps rows override status; history still comes
from agentSession.history, with the stored last turn as fallback.
2026-11-10 03:10:34 -08:00
Neil 7dbd33a1ad Merge remote-tracking branch 'private/cloud/integration' into cloud/w6-secret-request
# Conflicts:
#	cloud/apps/sandbox-control/image/provision-golden.sh
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-10 03:05:52 -08:00
Neil 81577b1ac7 feat(cloud): agent secret requests (secrets.request RPC, orca secret request, orca-env)
orcad keeps agent-initiated secret requests in memory with the contract's
limits (3 pending, 10/hour, 15 min TTL, touch capped at 60 min, dedupe per
name), derives the agent from the calling terminal, and announces
secrets.requested on runtime.clientEvents (replayed on attach; phones get
status only). The CLI waits on it and prints the D4 reload hint; the VM
ships orca-env and makes zsh read the session env hook.
2026-11-10 02:55:15 -08:00
Neil 6fb8497a1a Merge remote-tracking branch 'private/cloud/integration' into cloud/sleeping-read-snapshot
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
#	mobile/src/cloud/cloud-control-plane-client.ts
#	mobile/src/i18n/en-runtime-required.json
#	mobile/src/i18n/locales/en.json
#	src/renderer/src/components/cloud-workspaces/CloudWorkspaceRow.tsx
#	src/renderer/src/components/cloud-workspaces/CloudWorkspacesHome.tsx
2026-11-10 02:21:17 -08:00
Neil e35ff0844a feat(cloud): COPILOT_GITHUB_TOKEN and GOOGLE_API_KEY swapped at egress; routines run all six image agents; AWS keys marked stored on workspace
- COPILOT_GITHUB_TOKEN: placeholder in the VM, swapped on api.github.com (Bearer or token form, next to
  the workspace token's own route) and on the Copilot API hosts (left open for Copilot's own session
  token). GOOGLE_API_KEY: swapped on generativelanguage.googleapis.com and aiplatform.googleapis.com
  (Vertex express). A credential may now route to several hosts.
- Routines accept claude, codex, opencode, gemini, cursor and copilot; the in-VM runner has a
  headless command for each (checked against a local stand-in model: prompts starting with '-' stay
  prompts). The editor picks the agent from a list.
- AWS_* keys (SigV4, signed in the VM) and Bedrock API keys can't be swapped: the secrets UI marks
  them 'Stored on workspace' with the reason, in the list, the .env preview, the effective view and
  the editor.
- Gemini's auth method is seeded for GOOGLE_API_KEY too (vertex-ai with GOOGLE_GENAI_USE_VERTEXAI);
  headless Gemini refuses to start without one.
2026-11-10 02:08:00 -08:00
Neil 1095b509fc fix(cloud): Copilot CLI sign-in works in cloud workspaces (GitHub placeholder no longer overrides /login)
Copilot prefers GH_TOKEN/GITHUB_TOKEN over a stored /login. In Orca Cloud those hold the workspace
GitHub App placeholder, so every Copilot start said 'Resource not accessible by integration' and a
user who signed in could never use it. A wrapper drops the two placeholders for Copilot only, unless
BYOK is configured (no GitHub sign-in needed; gh in Copilot's shell keeps working).
2026-11-10 01:33:29 -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 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 cedec8398b fix(cloud): read snapshot security review follow-ups (trust wording, timing-only stats, live phone keys, races) 2026-11-10 00:17:45 -08:00
Neil 1bc418da8a feat(cloud): seal a read snapshot at drain; reader keys, read endpoint, revocation pruning 2026-11-10 00:13:19 -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 2a075764a7 fix(sandbox-control): golden builds retry apt downloads and wait for the dpkg lock (provision_exit_100) (#147) 2026-11-09 19:06:04 -08:00
Neil 4c1b26e67e Merge remote-tracking branch 'private/cloud/integration' into w6-agent-firstrun
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-09 18:45:39 -08:00
Neil 0a625740cc perf(orcad): skip the disposable startup preflight on Orca Cloud boots (#141)
On every Orca Cloud start and wake, orcad ran its disposable startup preflight child. That child is a
second full load of the 15 MB bundle plus the SQLite and node-pty probes, and it ran before main().
The cloud slot could never be qualified: qualification needed the managed-activation env, and the
marker lives in the slot directory, which is root-owned in the image. In-VM profiles showed the main
thread idle for about 580 ms waiting on that child.

- ORCA_ORCAD_STARTUP_QUALIFICATION_DIR names a writable directory for the startup-qualified marker
  and opts the slot into qualification. The key is unchanged: artifact version, file stat identity,
  runtime, ABI, kernel and launch env. It survives a stop and resume: inode, mtime and ctime are
  identical after resume, and the first launch after resume skipped the preflight.
- The image creates /opt/orca/startup-qualification. The provisioning smoke run records the marker,
  and orca-sandbox-start passes the env.
- Agent-state-rules live updates and speech-model warm-up now start after the ready line instead of
  before it.
- Once the ready line is published, a terminal list warms the terminal path (worktree catalog,
  daemon inventory) before the reconnecting desktop asks for it.

Real 4-vCPU VMs, readyMs median of 5: idle 1475 -> 903 ms; with 3 busy cores 1921 -> 1200 ms.
2026-11-09 18:44:16 -08:00
Neil d98b730df9 fix(cloud): agents start working from the phone/API — Claude Code first-run screens and Codex trust/sandbox pre-answered in the sandbox, no self-update mid-session; 'Open terminal to sign in' opens the terminal (paneView rides with paneKey) 2026-11-09 18:42:25 -08:00
Neil 8ef12e5729 fix(cloud): desktop P2 review — unarchiving state, dismissible progress, late/thrown wake failures, one failure message 2026-11-09 18:12:17 -08:00
Neil 724e8bce0b feat(cloud): remote desktop P2 — Unarchive action, wake then open, light reconnect card, palette entry 2026-11-09 18:12:13 -08:00
Neil 63377c810d fix(sandbox-control): source-built goldens provision with the built commit's image files, not the Worker's copies (#137)
The Worker-started source build used the VM scripts, provision script and manifest pins embedded in
the Worker that started it, so a golden built from a newer commit could miss that commit's new VM
scripts (seen live: snap_HTsU from 19f900e has #122's orcad but not orca-desktop-proxy or
orca-desktop-page.js). source-build.sh now sources image/commit-provision-env.mjs from the fetched
checkout (no dependencies; mirrors provisionEnv, enforced by a test), so the image ships exactly that
commit. Tests also require every image/vm file to be a declared VM script and installed by provision.
2026-11-09 18:10:35 -08:00
Neil c68f06864a fix(cloud): name the VM project after its repo (orca repo add --display-name); golden builder uses wait=true exit probe; image-only merges rebuild (#133)
* feat(cli,sandbox-control): orca repo add --display-name; cloud sandboxes name the project after its repository

The VM checkout lives at /vercel/sandbox/project, so boot-time registration (#104) named the repo
'project'. repo.add already accepts displayName; the CLI now exposes it as --display-name and
orca-sandbox-start passes the repository basename from ORCA_REPO_URL.

* fix(sandbox-control): golden builder polls detached builds with a bounded wait=true read; image-only merges trigger a rebuild

The provider reports a detached command's exit code only to a wait=true read (W1's finding, #123):
stepBuild's plain GET is what kept finished golden builds 'running' until the deadline. stepBuild and
the operator rotation now use the same bounded wait probe (4 s); the exit-file marker stays as a
fallback. golden-source's VM path list now includes cloud/apps/sandbox-control/image/ (VM scripts,
provision, manifest), so a script-only merge (e.g. orca-desktop-proxy) rebuilds the golden too.
2026-11-09 17:56:20 -08:00
Neil 60377d3364 feat(cloud): remote desktop Beta — single-use tickets, fit, clipboard, keyboard release (W10 RD-1..6) (#122)
* feat(cloud): remote desktop single-use tickets, fit, clipboard and keyboard release

- VM: Xvnc on a 0600 socket with no VNC auth plus an X cookie; orca-desktop-proxy (websockify
  subclass) admits one WebSocket per unused 60 s ticket from the same localhost origin, refuses
  rebinding Hosts, scrubs tickets from logs, serves the page with connect-src 'self'.
- Worker: POST /desktop mints a fresh ticket per call (schemaVersion 2).
- Desktop: no secret in the tab URL; main hands the page tickets on load and reconnect (backoff,
  floor, pause with Reconnect), only for the exact URL of a live forward it opened; keyboard stays
  in the desktop until Ctrl+Cmd / Ctrl+Alt; a paste key pushes this computer's clipboard; sandbox
  copies reach this computer after a copy gesture.
- UI: keyboard hint, asleep/starting neutral tone and mapping, persistent error toasts, Beta badge.

* fix(cloud): remote desktop review fixes

- Security review: noVNC host/port/encrypt pinned to the page's origin, CSP connect-src 'self',
  exact-URL + live-forward trust, mint floor and per-minute cap, copy gate on copy gestures only,
  X display cookie.
- Design review: archived is its own neutral state, starting covers waking, page messages say
  "Wake it in Orca", pause overlay is a status for calm states and focuses Reconnect, hint wraps
  and hides from assistive tech when faded.
- FE review: paste follows the keyboard layout, AltGr never releases, page calls settle on
  navigation, state notices only on change, hint once per load/release, paste always pushes,
  no committed bytecode, session tests.

* fix(cloud): remote desktop archived page message, hint layout, fixtures

* fix(cloud): remote desktop copy gate counts double-clicks (pointer events have no click count)

* chore(cloud): remote desktop lint (exhaustive pause reasons, no test casts or wall clock)
2026-11-09 17:36:19 -08:00