Commit Graph
467 Commits
Author SHA1 Message Date
Neil 53dced42c8 fix(cloud): MCP sync review — generic OAuth relay accepts only a loopback GET for its port (2-min TTL, never displaces a sign-in), first sync is an explicit consent before tokens leave this computer, no '.' on the image PATH 2026-11-09 15:09:45 -08:00
Neil 3c553acc77 feat(cloud): MCP servers panel on Orca Cloud workspaces; resolve stdio commands like agent launches; image puts node/npx on agents' PATH 2026-11-09 07:59:02 -08:00
Neil 07f607d414 fix(cloud): default workspace git author on images older than the start-script default 2026-11-09 07:48:30 -08:00
Neil 5c53102202 Merge pull request #90 from nwparker/cloud/w6-mcp-egress
W6: per-workspace egress hosts for synced remote MCP servers
2026-11-09 07:46:17 -08:00
Neil 6c2a37c83b feat(sandbox-control): per-workspace tool egress hosts for synced remote MCP servers (PUT/GET /v1/sandboxes/:id/egress/hosts, migration 0032) 2026-11-09 07:46:04 -08:00
Neil 1ba34366c9 chore(cloud): regenerate VM scripts
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-09 07:30:56 -08:00
Neil 6afde0d63d fix(cloud): attest plain terminals by handle+pane (agent terminals still prove their launch token); default workspace git author 2026-11-09 07:30:50 -08:00
Neil baef8f91e0 feat(cloud): member git identity only within a window after the member's own client reports a send (0031); entry points kept current per boot
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
2026-11-09 07:30:39 -08:00
Neil a3c5f1ed50 fix(cloud): owner device joins the VM member map as soon as it is recorded 2026-11-09 07:30:27 -08:00
Neil fc50ad9466 feat(cloud): per-device sender attestation (orcad orchestration.turnSender, orca sender), root-only member identities + sudo helper, sender-aware git in VMs (default on); terminal-agent sender envelope; follow teammates into chats and chat links; name claim
# Conflicts:
#	cloud/apps/sandbox-control/image/vm-scripts.generated.ts
#	src/renderer/src/components/cloud-workspaces/CloudMemberAvatars.tsx
2026-11-09 07:30:27 -08:00
Neil 2d01a3d1b2 feat(w3): remote desktop polish — glossary copy, InlineError toasts with one next action, fixtures (#83) 2026-11-09 07:27:19 -08:00
Neil e141a74ead feat(cloud): one idle-sleep constant (ORCA_CLOUD_IDLE_SLEEP_MINUTES = 15) for control plane, desktop scheduler, billing plans and toasts; GET /v1/config serves it (#80)
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.
2026-11-09 07:15:51 -08:00
Neil 0272e323b3 docs(sandbox-control): glossary pass on OpenAPI, /docs and MCP tool descriptions (sandbox, team, sleep/wake/delete, Orca API keys) 2026-11-09 07:12:44 -08:00
Neil 5ab167c0d1 Merge pull request #76 from nwparker/cloud/w2-transfer
feat(cloud): move a cloud workspace to this computer (worktree, changes, chats) + preview wake helper
2026-11-09 07:04:00 -08:00
Neil 165d6813fe Merge remote-tracking branch 'private/cloud/integration' into cloud/w2-transfer
# Conflicts:
#	src/main/hosted-sandbox/hosted-sandbox-install.ts
#	src/preload/api-types.ts
#	src/renderer/src/components/sidebar/workspace-actions/context-menu/WorktreeContextMenuView.tsx
2026-11-09 07:02:59 -08:00
Neil b0b593ed45 Merge remote-tracking branch 'private/cloud/integration' into cloud/w2-transfer
# Conflicts:
#	cloud/apps/sandbox-control/src/index.ts
#	cloud/apps/sandbox-control/src/lifecycle.ts
#	cloud/apps/sandbox-control/src/pool.ts
#	cloud/apps/sandbox-control/src/sandbox-vm-commands.ts
#	cloud/apps/sandbox-control/test/fake-vercel.ts
#	cloud/apps/sandbox-control/test/lifecycle-flow.test.ts
#	src/main/hosted-sandbox/hosted-sandbox-control-plane-client.ts
#	src/preload/api-types.ts
#	src/renderer/src/components/sidebar/workspace-actions/context-menu/WorktreeContextMenuView.tsx
#	src/shared/hosted-sandbox/hosted-sandbox-wire.ts
2026-11-09 07:01:38 -08:00
Neil 702ce21a21 feat(billing): dev Worker trusts staging auth and reads staging seat counts (dev only) 2026-11-09 07:01:19 -08:00
Neil 32d8f0589c Merge pull request #68 from nwparker/cloud/accounts-ux
feat(team): Team page, upgrade moments and in-app checkout for Orca Cloud
2026-11-09 06:57:01 -08:00
Neil 7f65f2bdf5 refactor(sandbox-control): API access uses W4's canSee/canOpen; teammate messages carry the sender envelope (spoofed envelopes stripped) 2026-11-09 06:56:04 -08:00
Neil 8b070704e8 fix(team): review round 2 polish (design P2s, FE P2s) 2026-11-09 06:55:56 -08:00
Neil 45caa0c93a fix(team): review round 1 (design + FE): copy, focus, live regions, roster/overlay races, summary generations, checkout watch + single live session 2026-11-09 06:55:56 -08:00
Neil dde40cbdeb wip(team): W10 round-1 fixes, return page, docs 2026-11-09 06:55:55 -08:00
Neil 90adbc6601 wip(team): team page, checkout flow, dev billing token 2026-11-09 06:55:54 -08:00
Neil e7c1831e60 Merge pull request #58 from nwparker/cloud/w4-multiplayer
feat(cloud): W4 team collaboration on cloud workspaces
2026-11-09 06:53:38 -08:00
Neil 777f078c76 feat(cloud): W4 team collaboration on cloud workspaces
## 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
2026-11-09 06:53:24 -08:00
Neil b0b224de2b Merge pull request #67 from nwparker/cloud/w5-grant-host
feat(mcp): connected-app grants show the client's redirect hosts
2026-11-09 06:50:46 -08:00
Neil 8a657067bd fix(cloud): SSH/transfer security review: owner-only SSH setup + DELETE, managed authorized_keys block, stable bridge token via subprotocol, boot-scoped pids + required healthz, owner re-check on ssh-wake, safe tar/gzip extraction, transcript-only chats, VM cleanup in finally, transfer sweep, unique aliases, shell-safe ProxyCommand launcher (Windows/AppImage), login-user fix-up 2026-11-09 06:50:38 -08:00
Neil 8ff954b08b feat(mcp): connected-app grants show the client's redirect hosts 2026-11-09 06:50:34 -08:00
Neil 309245d0d4 feat(w3): remote desktop for Orca Cloud workspaces (Xvnc + noVNC over the port forward) (#61) 2026-11-09 06:43:19 -08:00
Neil 984f29fc37 feat(cloud): SSH into Orca Cloud workspaces (orca ssh, ssh-proxy, VS Code/Cursor Remote-SSH) (#55)
* 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.
2026-11-09 06:37:23 -08:00
Neil 1737dcdfbd feat(cloud): share links for Orca Cloud workspace ports (W3) (#51)
* feat(cloud): share links for Orca Cloud workspace ports (gateway, in-VM share proxy, wake interstitial, Ports panel UI)

* fix(cloud): share-link security review (no cookie fallback, path mode dev-only, per-link cookie scoping, SW and Referer blocked, proxy key via stdin, connection deadline, link-bound viewer, wake cap, rate limit, secret rotation)

* fix(cloud): share links re-review (host-only app cookies in subdomain mode, previous-secret proxy retry, 30 min connection cap, share-domain pinning on the desktop)
2026-11-09 06:30:40 -08:00
Neil 484b999dc8 feat(sandbox-control): golden image tracks cloud/integration HEAD (orcad built from source, rebuilt when HEAD touches VM code); sshd in image (#57)
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.
2026-11-09 06:17:08 -08:00
Neil eb844e7ed3 Merge pull request #56 from nwparker/cloud/w2-lifecycle
fix(cloud): archive capture keeps the first path char; OOM victim reports the process, not its thread
2026-11-09 06:14:36 -08:00
Neil d6679e8e93 feat(cloud): move a cloud workspace to this computer, wake on SSH (sshd over a wss bridge + desktop ProxyCommand), preview wake helper 2026-11-09 06:11:42 -08:00
Neil e70317fa7c perf(cloud): warm pool per hot environment build; smoke-boot builds; clearer GitHub failures (#54)
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.
2026-11-09 06:08:11 -08:00
Neil e2d32ce606 fix(cloud): archive capture keeps porcelain's leading status column; OOM victim reports node, not MainThread 2026-11-09 05:56:07 -08:00
Neil c1cf0cb760 ## What (#49)
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.
2026-11-09 05:45:23 -08:00
Neil dcf7c01216 fix(routines): security review — creator-only edits/runs (admins disable/delete), trusted author_association for GitHub triggers, disable departed members' routines, fair + atomic start caps and queue limit, validated PR URLs, private run detail; drop pool workaround (fixed in #44) 2026-11-09 05:37:30 -08:00
Neil 9ba97b6aaa fix(routines): retry a start once when a half-released VM holds the run key (pool adopt fallback), clear retry marker on running 2026-11-09 05:37:30 -08:00
Neil 76a5d9e703 fix(routines): compact runner status parsing (pretty-printed result), trigger view key order, status parse test 2026-11-09 05:37:29 -08:00
Neil e9f52875cd feat(sandbox-control): routines — schedule/GitHub/webhook triggers, hidden run VMs as the creator, detached headless agent runner, PR/comment posting, per-minute tick with caps, dedupe and crash recovery 2026-11-09 05:37:29 -08:00
Neil f90518bb66 fix(sandbox-control): per-member workspace access on the public API and MCP (W4 rule: private unless team-visible; owner-only screens; admins metadata + sleep/archive/destroy) 2026-11-09 05:31:37 -08:00
Neil 6eec750fe2 fix(sandbox-control): a failed pool adopt no longer blocks its own fallback (sandbox_destroying conflict) (#44)
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.
2026-11-09 05:28:11 -08:00
Neil ee6c84f883 feat(cloud): per-device Orca Cloud access for phones (W8) (#42)
* 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
2026-11-09 05:22:51 -08:00
Neil 9426b62da0 fix(sandbox-control): pool refills on a miss too and right after a golden activation; one maintainer at a time (#43)
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.
2026-11-09 05:16:06 -08:00
Neil 291394f954 Merge pull request #41 from nwparker/cloud/w6-fixes
W6: node on Orca Cloud terminal PATH; correct workspace name/default branch
2026-11-09 05:06:57 -08:00
Neil 16f598ee6a Merge pull request #35 from nwparker/cloud/w2-lifecycle
feat(cloud): workspace lifecycle API (wake, restart, status, archive) + desktop service
2026-11-09 05:06:27 -08:00
Neil e180ab330f Merge pull request #39 from nwparker/cloud/w5-mcp
feat(sandbox-control): hosted MCP server with OAuth 2.1 (DCR + PKCE) over the public API
2026-11-09 05:06:07 -08:00
Neil 30d9684148 Merge remote-tracking branch 'private/cloud/integration' into cloud/w2-lifecycle
# Conflicts:
#	cloud/apps/sandbox-control/src/index.ts
#	cloud/apps/sandbox-control/src/sandbox-provider.ts
#	cloud/apps/sandbox-control/src/store.ts
#	cloud/apps/sandbox-control/src/vercel-sandbox-api.ts
#	cloud/apps/sandbox-control/test/fake-vercel.ts
#	src/main/hosted-sandbox/hosted-sandbox-driver.ts
#	src/main/hosted-sandbox/hosted-sandbox-recipe.ts
2026-11-09 05:05:53 -08:00
Neil d360b916f9 fix(mcp): grant list shows the effective end for grants minted before 0028 2026-11-09 05:05:27 -08:00