OXYGENHUB
◆ main oxygenflow / docs / workspaces.md
3.8 KiB · Markdown · last changed docs/workspaces.md: paused until the promised work is delivered 18 days ago renderedsource blame history raw

Workspaces: VS Code in a microVM, the runner side

Status: paused (Andy, 2026-10-17): too large to take on, and to keep up, before the work already promised is delivered. Nothing below is built; the plan stands for when it is picked up again.

oxygenhub#69, hub spec §13.3. Written 2026-10-17 with Andy's decisions: build it, runner side first; files lazy from the start; editor traffic over a WebSocket with multiplexing. This is the plan for the runner; the hub's part (the workspaces table, the button, the proxy, commits) follows it.

What is there today

  • Jobs run to their exit under a hard time limit, with a job token that lasts at most two hours (hub JOB_TOKEN_MAX_AGE_MS).
  • A microVM has no network device. Its only traffic is guest-initiated: the egress proxy over vsock (flow-init/src/egress.rs).
  • The runner speaks to the hub in short HTTPS calls; nothing long-lived.
  • flow-casfs mounts a state lazily with FUSE, on the host. Firecracker has no virtio-fs, so a FUSE mount on the host cannot be handed to a guest.
  • The pinned guest kernel (6.1.155) has overlayfs and vsock, but no FUSE.

The shape

browser ──HTTPS/WS──► hub /{owner}/{name}/workspace/{id}/ ──┐
                                                            │ one WebSocket per workspace,
runner ◄────────────────────────────────────────────────────┘ opened by the runner (yamux streams)
  │ vsock (host connects in)
  ▼
microVM: flow-init ─► openvscode-server on 127.0.0.1:3000
         /workspace = overlay(lower: casfs over vsock, upper: scratch disk)

Steps

Each is an issue of its own, and each lands working and tested.

  1. A guest kernel with FUSE. The same 6.1 kernel and Firecracker's guest config, with CONFIG_FUSE_FS=y; pinned by BLAKE3 in flow.toml on earth-1, plex-1 and mars-1. Pipeline jobs are unaffected.
  2. casfs over vsock. flow-casfs gains a server half: on the host it answers lookups, listings and reads for one state over a vsock port of the workspace's VM, from the same graph-key cache it uses today. The guest half is a small FUSE client in the workspace rootfs. /workspace is an overlay: the state read-only below, the VM's scratch disk above, so edits are plain files. Opening a workspace costs a listing, not an export; the target is under a second on the Linux tree.
  3. The workspace rootfs. openvscode-server (pinned release, its checksum checked), oxy, the Oxygen VCS extension, a non-root user; built and imported like the rust image (images/workspace).
  4. The workspace job kind. Claim says kind: "workspace". The runner keeps the VM until it is told to stop, or the hub's idle limit passes, instead of waiting for an exit; it reports liveness and open connections every 30 s, and the hub renews the job token while the workspace lives. A runner advertises whether it takes workspaces.
  5. The tunnel. flow-init accepts host connections on a vsock port and forwards each to openvscode-server. The runner opens one WebSocket to the hub per workspace (/api/v1/flow/workspaces/{id}/tunnel, its runner token) and carries each browser connection as a yamux stream to that port. Works for runners behind NAT and through Cloudflare.

Then the hub (hub spec §13.3): the workspaces table and limits, "Open in workspace" behind a site setting (off), the owner-only reverse proxy for HTTP and WebSocket into the tunnel, and commits pushed to ws/{account}/{id} and signed by the hub as the account's delegate.

Downloads this needs

Linux 6.1 sources (kernel.org) and Firecracker's guest config for step 1, and an openvscode-server release (GitHub, Gitpod) for step 3: each pinned and checked against its published checksum, as cosign was.