Repository navigation
Add an on-demand browser editor for running VMs - #599
thomas-chauchefoin-tob wants to merge 5 commits into
Conversation
Add `coop web-editor` to install and run code-server inside an existing VM. Print a local URL and session password for the user to open in their browser. Editing, extensions, and terminals run in the guest so this brings a stronger isolation than `coop editor`.
0994a1d to
33c4b02
Compare
|
Review summary: I left two inline suggestions: reject a conflicting I checked correctness, design, conventions, security, API usage, tests, documentation, and comments against the PR head and discussion. The existing CodeQL password-output thread is resolved; interactive display is intentional. The |
Co-authored-by: Henrik Brodin <90325907+hbrodin@users.noreply.github.com>
| </dev/null >"$base/editor.log" 2>&1 9>&- & | ||
| child=$! | ||
| ready=0 | ||
| while kill -0 "$child" 2>/dev/null; do |
There was a problem hiding this comment.
I reproduced a healthy code-server that never reached the host's ready state: the guest /healthz returned 200 and the transient unit was active, but this wrapper emitted no ready marker and was blocked in wait. When the 600-second host startup timeout fired, coop removed the tunnel, host password, metadata, and host temp directory, while the guest unit, descendants, sockets, password config, and /tmp/coop-we-* directory remained.
Could we avoid treating the long-lived sudo systemd-run --wait --pipe client PID as both service liveness and lease state? A systemd-native shape would be:
- start a detached transient unit with
systemd-run --collect --service-type=exec(without--wait/--pipe); - route unit output with
StandardOutput=append:...andStandardError=append:...; - keep this SSH shell in the heartbeat loop, checking
systemctl is-activeand/healthz; - have the EXIT/HUP trap synchronously
systemctl stopthe unit before removing the sockets/config.
Please also add a regression for timeout or owner death before readiness; the existing cleanup assertions only run after a URL has been published.
| [[ $password =~ ^[a-f0-9]{64}$ ]] | ||
| config="${socket%/*}/config.yaml" | ||
|
|
||
| version=4.141.0 |
There was a problem hiding this comment.
This pins and executes code-server 4.141.0, but 4.141.1 was released before this PR and explicitly updates bundled Node.js for the Brotli decoder RCE (CVE-2026-111497) and rejects compressed request bodies. Please update the version and both architecture hashes to at least 4.141.1, preferably a currently reviewed release.
Could we also prevent this hard-coded pin from silently aging again? Dependabot only discovers supported manifests, so it will not see a shell literal (supported ecosystems). A small scheduled checker would fit the current repository better than introducing Renovate just for this pin: compare against the latest non-draft/non-prerelease GitHub release, download the exact amd64/arm64 assets, compute both SHA-256 values, and open a human-reviewed PR updating these three values. It should not auto-merge or install an unreviewed release.
| rm -rf -- "$staging" | ||
| mkdir -- "$staging" | ||
| curl --fail --silent --show-error --location --proto '=https' --proto-redir '=https' \ | ||
| --connect-timeout 15 --max-time 300 --max-filesize 536870912 \ |
There was a problem hiding this comment.
The fixed five-minute total timeout makes first use deterministically fail on slower links. The ARM64 archive was 234,587,826 bytes; in my test it reached about 132 MB after 230 seconds, then hit this timeout. The EXIT cleanup and the next invocation's rm -rf "$staging" discard the partial archive, so every retry starts from zero.
Could we retain the private, install-locked partial file and resume safely (with the final pinned SHA-256 still mandatory), or otherwise choose a timeout appropriate to the artifact size? This should also emit bounded phase/download progress so a multi-minute first run does not look hung.
| .process_group(0) | ||
| .stdin(Stdio::piped()) | ||
| .stdout(Stdio::piped()) | ||
| .stderr(Stdio::null()); |
There was a problem hiding this comment.
Discarding SSH stderr loses the only concrete startup failure. When the first-use curl timed out, coop reported “Editor SSH session exited unexpectedly” and directed me to guest editor.log, but that file did not exist because code-server had never started.
Rather than forwarding arbitrary guest stderr, could stdout carry a bounded structured startup protocol such as installing, download progress, starting, ready, and failed {stage, reason}? That would preserve the distinction between download, SSH-forward, systemd-launch, and code-server runtime failures and make the host diagnostic actionable.
| #[arg(long)] | ||
| clean: bool, | ||
| }, | ||
| /// Serve a browser editor inside a running VM (installs code-server on first use) |
There was a problem hiding this comment.
After testing this, I don't think browser editing warrants another top-level command alongside the existing editor/vscode commands. Could this be exposed as coop editor [NAME] --web [--port PORT] instead? --web would conflict with --editor and --clean, --port would require --web, and --project could remain meaningful for both modes. This keeps the user goal and instance-resolution behavior under one discoverable command.
There was a problem hiding this comment.
Also, we should probably retire vscode at some point.
| administration aliases, and pull does not sanitize repositories created by an | ||
| older vulnerable release. | ||
|
|
||
| ### `web-editor` |
There was a problem hiding this comment.
If this remains a top-level command, please also update the exhaustive command mappings: docs/multi-instance.md omits web-editor from the commands using optional instance-name resolution, and docs/shell-completion.md omits it from the commands completing running instances even though the clap argument uses running_instance_candidates.
| reducing exposure through the native host-editor integrations used by `coop editor`. | ||
| Browser vulnerabilities remain part of the attack surface. | ||
|
|
||
| Code-server's password login restricts access to the editor, and the host |
There was a problem hiding this comment.
This added line has trailing whitespace, and git diff --check 34a24da...463aab7 fails on it. Please remove the trailing space after “host”.
|
Hands-on follow-up review at Reproduction: built the release binary, created a disposable Lima VM, cloned a public project, and exercised the browser editor end to end. One first-use download timed out after five minutes and discarded its partial archive. After disabling the VPN, code-server installed and I reviewed correctness, lifecycle, design, conventions, security, external API/tool usage, tests, documentation, and comments. Existing review threads about a conflicting attached The new outbound GitHub release URL and loopback listener are explicit stop-and-confirm surfaces under the repository trust model. Their boundaries otherwise looked appropriate: loopback-only forwarding, a private guest Unix socket, password delivery over stdin, private host/guest storage, HTTPS-only redirects, and a pinned SHA-256. |
Add
coop web-editorto install and run code-server (Visual Studio Code) inside an existing VM, and print a local URL and session password for the user to open in their browser. Editing, extensions, and terminals run in the guest so this brings a stronger isolation thancoop editor. This was only tested on a macOS host.CI failure should be fixed after #598 lands.