OXYGENHUB
◆ v0.2.0 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
1
# Workspaces: VS Code in a microVM, the runner side
2
3
**Status: paused** (Andy, 2026-10-17): too large to take on, and to keep up,
4
before the work already promised is delivered. Nothing below is built; the
5
plan stands for when it is picked up again.
6
7
oxygenhub#69, hub spec §13.3. Written 2026-10-17 with Andy's decisions:
8
build it, **runner side first**; files **lazy from the start**; editor
9
traffic over a **WebSocket with multiplexing**. This is the plan for the
10
runner; the hub's part (the workspaces table, the button, the proxy,
11
commits) follows it.
12
13
## What is there today
14
15
- Jobs run to their exit under a hard time limit, with a job token that
16
  lasts at most two hours (hub `JOB_TOKEN_MAX_AGE_MS`).
17
- A microVM has no network device. Its only traffic is guest-initiated:
18
  the egress proxy over vsock (`flow-init/src/egress.rs`).
19
- The runner speaks to the hub in short HTTPS calls; nothing long-lived.
20
- flow-casfs mounts a state lazily with FUSE, on the host. Firecracker has
21
  no virtio-fs, so a FUSE mount on the host cannot be handed to a guest.
22
- The pinned guest kernel (6.1.155) has overlayfs and vsock, but no FUSE.
23
24
## The shape
25
26
```
27
browser ──HTTPS/WS──► hub /{owner}/{name}/workspace/{id}/ ──┐
28
                                                            │ one WebSocket per workspace,
29
runner ◄────────────────────────────────────────────────────┘ opened by the runner (yamux streams)
30
  │ vsock (host connects in)
31
  ▼
32
microVM: flow-init ─► openvscode-server on 127.0.0.1:3000
33
         /workspace = overlay(lower: casfs over vsock, upper: scratch disk)
34
```
35
36
## Steps
37
38
Each is an issue of its own, and each lands working and tested.
39
40
1. **A guest kernel with FUSE.** The same 6.1 kernel and Firecracker's
41
   guest config, with `CONFIG_FUSE_FS=y`; pinned by BLAKE3 in `flow.toml`
42
   on earth-1, plex-1 and mars-1. Pipeline jobs are unaffected.
43
2. **casfs over vsock.** flow-casfs gains a server half: on the host it
44
   answers lookups, listings and reads for one state over a vsock port of
45
   the workspace's VM, from the same graph-key cache it uses today. The
46
   guest half is a small FUSE client in the workspace rootfs. `/workspace`
47
   is an overlay: the state read-only below, the VM's scratch disk above,
48
   so edits are plain files. Opening a workspace costs a listing, not an
49
   export; the target is under a second on the Linux tree.
50
3. **The workspace rootfs.** openvscode-server (pinned release, its
51
   checksum checked), `oxy`, the Oxygen VCS extension, a non-root user;
52
   built and imported like the rust image (`images/workspace`).
53
4. **The `workspace` job kind.** `Claim` says `kind: "workspace"`. The
54
   runner keeps the VM until it is told to stop, or the hub's idle limit
55
   passes, instead of waiting for an exit; it reports liveness and open
56
   connections every 30 s, and the hub renews the job token while the
57
   workspace lives. A runner advertises whether it takes workspaces.
58
5. **The tunnel.** flow-init accepts host connections on a vsock port and
59
   forwards each to openvscode-server. The runner opens one WebSocket to
60
   the hub per workspace (`/api/v1/flow/workspaces/{id}/tunnel`, its
61
   runner token) and carries each browser connection as a yamux stream to
62
   that port. Works for runners behind NAT and through Cloudflare.
63
64
Then the hub (hub spec §13.3): the `workspaces` table and limits, "Open in
65
workspace" behind a site setting (off), the owner-only reverse proxy for
66
HTTP and WebSocket into the tunnel, and commits pushed to `ws/{account}/{id}`
67
and signed by the hub as the account's delegate.
68
69
## Downloads this needs
70
71
Linux 6.1 sources (kernel.org) and Firecracker's guest config for step 1,
72
and an openvscode-server release (GitHub, Gitpod) for step 3: each pinned
73
and checked against its published checksum, as cosign was.