feat(sandbox): add Orca per-workspace environment - #78
Merged
Conversation
Add `createos setup orca`, which lets Orca run a workspace on a disposable sandbox instead of the user's laptop. The command has two halves: a human one that checks prerequisites and prints install instructions, and a hidden `--recipe` one that Orca calls for each lifecycle phase, selected by ORCA_VM_MODE. Create provisions a microVM, wires SSH through the gateway, waits for sshd, then packs and uploads the working tree and checks it out on a branch named after the workspace. The checkout is pushed rather than cloned, so no git token reaches the sandbox and private repositories need no extra setup; uncommitted edits travel with it. Suspend and resume are not declared. SSH does not reliably come back after a resume, and Orca requires the pair or neither. Coding agents are opt-in via --agents/CREATEOS_AGENTS. Orca passes no agent identity to a recipe, so this cannot be inferred. Installs run over SSH rather than the exec API: the two do not share a mount namespace outside /workspace, so anything the exec API installs is invisible to the session Orca launches the agent in. Each agent links into /usr/local/bin and is skipped when already present. Also pin ControlPath per sandbox in renderSSHBlock. Every tunnel-mode sandbox resolves to the same HostName/User, so a ControlPath built from %h/%r/%p -- including a common personal `Host *` default -- collides across sandboxes, and ssh reuses a stale multiplexed connection to a different, often destroyed, box. This affects `sandbox editor` today, independently of Orca. %n is the one token unique per sandbox. Same family as #76. probeSSH takes the wait as a parameter so the recipe can allow longer than the editor's default for a cold box.
errcheck runs with check-blank, so blank-assigned errors need an explicit nolint and a reason. Reuse the outer err instead of shadowing it, and drop the client argument orcaInstallAgents stopped using when agent installs moved from the exec API to SSH. No behaviour change.
`createos setup orca` becomes `createos sandbox setup orca`. The command only ever configures sandboxes, so it belongs in the sandbox tree rather than as a second top-level group beside it. root.go is no longer touched by this feature: the group registers itself through NewSandboxCommand like every other sandbox subcommand, and NewSetupCommand becomes unexported. Document the shape in CLAUDE.md, since a harness integration is not like the other commands: it has a human half and a hidden machine half, the machine half owns stdout, and the command string is duplicated in a plugin that lives in another repo.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
createos setup orca, which lets Orca run a workspace on a disposable CreateOS Sandbox instead of the user's laptop.The companion Orca plugin lives in
NodeOps-app/createos-pluginsunderpackages/orca-plugin. It contains no code — it registers one VM recipe that shells out to this command.How it works
Orca drives a per-workspace environment by running one command for every lifecycle phase and selecting the phase with
ORCA_VM_MODE. The command prints one JSON object on stdout; everything else goes to stderr.createos setup orcahas two halves:--doctorchecks login,git, andssh, then prints install instructions.--recipe(hidden) runs one lifecycle phase.On create it provisions a microVM, wires SSH through the gateway, waits for
sshdto actually accept connections, packs the working tree, uploads it, and checks it out on a branch named after the workspace.The checkout is pushed, not cloned, so no git token ever reaches the sandbox and private repositories work with no extra setup. Uncommitted edits travel with it, which is the point of the path.
Suspend and resume are not declared
SSH does not reliably come back after a sandbox resume. Orca requires the pair or neither, so this recipe declares neither and every workspace is destroy-and-recreate.
Coding agents (
--agents/CREATEOS_AGENTS)Opt-in, nothing by default:
claude,codex,cursor,opencode,pi.Orca passes no agent identity to a recipe (
ORCA_VM_MODE,ORCA_REPO_*,ORCA_WORKSPACE_NAME, and nothing naming the agent), so this cannot be inferred and has to be asked for.Two details worth review attention:
ExecSandbox. Those two do not share a mount namespace outside/workspace. Verified directly: a file written via the exec API to/tmpor/usr/local/bincannot be read back over SSH, while/workspace/repocan. Installing via the exec API produced agents that reported success and were then invisible to the session Orca launches them in.set -epropagates a vendor installer's own exit status, so a status reused as "already present" would silently turn a failed install into a skip.Each agent links into
/usr/local/binand is skipped when already present. Notecursorinstalls a binary namedcursor-agent.These run the vendors' own install scripts, unpinned and not checksum-verified. Documented in the plugin README, with a custom
CREATEOS_ROOTFSimage as the way to control it.Also: ControlPath fix in
editor.goThis one affects
sandbox editortoday, independently of Orca.Every tunnel-mode sandbox resolves to the same
HostName 127.0.0.1+User root, so aControlPathbuilt from%h/%r/%p— including the common personalHost *default — collides across sandboxes. WithControlMaster auto, ssh silently reuses a stale multiplexed connection to a different, often already-destroyed, sandbox instead of opening one to the box actually requested.That was the cause of assorted "sshd didn't answer" and hang symptoms, reproduced against the stock
sandbox editorcommand with no Orca involved.%n(the alias) is the one token guaranteed unique per sandbox. Same family as #76.probeSSHnow takes the wait as a parameter so the recipe can allow longer than the editor's default for a cold box.Testing
Verified end-to-end against a real sandbox, through Orca's UI and standalone:
createos sandbox listchecked after each run)--agentsvalue fails before any sandbox is created10 unit tests cover the argument-injection regexes, sandbox-name sanitizing, shell quoting, agent parsing/dedup/rejection, the agent table's name→binary mapping, marker parsing, and that the generated script links into
/usr/local/bin.go build,go vet,go test ./..., andgosecall pass;gosecreports 0 issues inorca.go.Notes for the reviewer
exec.Commandor a git refspec is pinned by regex (orcaSHARE,orcaBranchRE,orcaRootRE) — argv stops shell injection but not argument injection, and git reads a leading-as an option.