Skip to content

Commit 5c3cd46

Browse files
SashaMITcursoragent
andcommitted
feat(elastos-vz, elastos-server): port substrate-agnostic prefix to Mac arm (Phase 3 Day 2)
Day 1 shipped the bare seam: `elastos start <microvm>` on macOS reached `VzProvider::load_with_vm_config` instead of bailing, but with a *minimal* `VmConfig` — no session token, no command payload, no capsule args, no Carrier bridge listener, no rootfs overlay. The Day-1 audit (`docs/vz-backend/PHASE_3_DAY_1_PORT_PLAN.md`) classified each missing operation as **AG** (substrate-agnostic), meaning the Linux code could be ported into the Mac arm with no semantic change. Day 2 ports every AG row. What lands: - `Supervisor::build_vm_config_for_mac` (new helper, Mac-gated): vsock CID + handle alloc; launch_config normalisation; `VzVmConfig::from_manifest` + `elastos.data_dir=/opt/elastos`; TERM/winsize for interactive VMs (`TIOCGWINSZ` works on Darwin); session token injection (shell vs Capsule, NAT-only path); command payload base64; capsule args base64; provider_port boot arg; Carrier socket under `crosvm_config.socket_dir`; `elastos.carrier_path=/dev/hvc1` (Mac-specific delta — Vz kernel console lives at hvc0); rootfs overlay under `crosvm_config.rootfs_cache_dir/overlays/`. - `Supervisor::start_capsule_vm_macos` now takes the launch `config: serde_json::Value`, fails closed before any VM work if `manifest.permissions.guest_network == true` (typed message naming `com.apple.vm.networking` + Phase 3 Day 4+), spawns the microVM Carrier bridge on the socket the helper returned, then calls `VzProvider::load_with_vm_config + start`. Still exits with a typed Day-3-pending error after a clean provider drop. - `VzProvider::set_session_for_vm` + `set_network_for_vm` retired. Both now return typed migration errors in the same style as Day-1's `append_boot_args_for_vm`, pointing operators at `VmConfig::with_session` / `load_with_vm_config` and naming the entitlement requirement for bridged-mode networking. Tests (all 492 green locally on Mac; Linux-untouched gate green): - elastos-server: 4 Mac-gated tests - existing seam test updated for the new signature; - new bake test asserts every Day-2 boot arg lands and the Mac-specific `carrier_path=/dev/hvc1` overrides hvc0; - new TAP-rejection test asserts the typed entitlement error; - new overlay test asserts the file is created under `rootfs_cache_dir/overlays/`, bytes round-trip, source rootfs untouched. - elastos-vz: 2 new migration-error tests; old combined stub test retired. Apple constraint that shaped this: `VZVirtualMachineConfiguration` is frozen the moment `VZVirtualMachine::initWithConfiguration:queue:` is invoked, so any "mutate after load" API shape is unsupportable. Day 2 finishes retiring those shapes — the supervisor now bakes everything into `VmConfig` before `load_with_vm_config` runs, mirroring what the Linux arm has always done. Out of scope (Day 3+): `CapsuleBackend::VzVm` enum extension + `RunningCapsule` insertion (so `elastos ps` / `elastos stop` work on Mac); real socketpair attachment for the Vz Carrier console (bytes don't yet flow even though the listener exists); real vsock host listener bridging; TAP via Vz bridged mode (entitlement-gated). Linux path is byte-identical to the pre-Day-1 commit. Verified via `scripts/check-linux-untouched.sh bcf5a0a`. See docs/vz-backend/PHASE_3_DAY_2_NOTES.md for the full outcome log + Day 3 handoff. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent b633e40 commit 5c3cd46

6 files changed

Lines changed: 807 additions & 128 deletions

File tree

docs/MAC.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ to first-class support.
3131
|---|---|---|
3232
| `type: wasm` (e.g. `home`, `system`, `chat-room`) | wasmtime, capability tokens | Same as Linux |
3333
| `type: data` (e.g. `documents`, `library`, `inbox`, `gba-emulator`) | static assets, served by gateway | Same as Linux |
34-
| `type: microvm` (e.g. `chat`, `agent`, `localhost-provider`, `did-provider`, `shell`, `webspace-provider`, `ipfs-provider`, `tunnel-provider`, ...) | KVM + crosvm on Linux; Apple Vz on macOS *(in progress — see below)* | **Day-5 boot proved a real Linux guest boots under Vz.** Phase 3 Day 1 shipped the supervisor → `VzProvider` seam, so `elastos start <microvm>` no longer bails before reaching `VzProvider::load_with_vm_config`. The supervisor's `RunningCapsule` registration (so `elastos ps` / `elastos stop` see Mac VMs) is **pending Phase 3 Day 2**, hence the typed fail-closed message on `start`. See [`vz-backend/PHASE_3_DAY_1_PORT_PLAN.md`](vz-backend/PHASE_3_DAY_1_PORT_PLAN.md). |
34+
| `type: microvm` (e.g. `chat`, `agent`, `localhost-provider`, `did-provider`, `shell`, `webspace-provider`, `ipfs-provider`, `tunnel-provider`, ...) | KVM + crosvm on Linux; Apple Vz on macOS *(in progress — see below)* | **Day-5 boot proved a real Linux guest boots under Vz.** Phase 3 Day 1 shipped the supervisor → `VzProvider` seam. **Phase 3 Day 2 ported the full substrate-agnostic launch prefix** (session token, command payload, capsule args, provider_port, Carrier bridge listener, rootfs overlay, `elastos.carrier_path=/dev/hvc1`) into the Mac arm. TAP networking fails closed with a typed entitlement-required message (no silent NAT downgrade). The supervisor's `RunningCapsule` registration (so `elastos ps` / `elastos stop` see Mac VMs) is **pending Phase 3 Day 3**, hence the typed fail-closed message on `start`. See [`vz-backend/PHASE_3_DAY_2_NOTES.md`](vz-backend/PHASE_3_DAY_2_NOTES.md). |
3535

3636
The browser-hosted Home surface
3737
(`http://127.0.0.1:8090/apps/home/`) and its child apps (System, Inbox,

docs/vz-backend/PHASE_3_DAY_1_PORT_PLAN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## Phase 3 Day 1 — supervisor → VzProvider port plan
22

3+
> **Day 2 status:** every AG row in the audit table below is now
4+
> ported (commit on the head of `sash/local-test`). The Day-2
5+
> notes live in [`PHASE_3_DAY_2_NOTES.md`](PHASE_3_DAY_2_NOTES.md);
6+
> this document is preserved as the original audit + Day-1 plan
7+
> so future days can re-verify against the same line numbers.
8+
>
39
> Audit-only document. Status as of `73cd293` on `sash/local-test`
410
> (Phase 2 Day 5 complete + the host-portable fixture fix).
511
>
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
## Phase 3 Day 2 — substrate-agnostic prefix port + stub retirement
2+
3+
> Outcome log. Status: complete. Day 3 picks up
4+
> `CapsuleBackend::VzVm` and supervisor `RunningCapsule`
5+
> registration (the last AG row in the
6+
> [Day-1 audit table](PHASE_3_DAY_1_PORT_PLAN.md#audit-table)).
7+
8+
### Goal (recap)
9+
10+
Day 1 shipped the bare supervisor → `VzProvider` seam:
11+
`elastos start <microvm>` on macOS reached
12+
`VzProvider::load_with_vm_config` instead of `bail!()`, but with
13+
a **minimal** `VmConfig` — no session token, no command payload,
14+
no capsule args, no Carrier bridge, no rootfs overlay. The Day-1
15+
audit (`PHASE_3_DAY_1_PORT_PLAN.md`) classified each missing
16+
operation as **AG** (substrate-agnostic), meaning the Linux code
17+
could be ported into the Mac arm with no semantic change.
18+
19+
Day 2 ports every AG row.
20+
21+
### What landed
22+
23+
1. **`Supervisor::build_vm_config_for_mac`** (new, Mac-gated,
24+
`elastos-server/src/supervisor.rs`). Factored helper that
25+
replicates the substrate-agnostic prefix of
26+
`launch_capsule` L997-1144 against `elastos_vz::VmConfig`:
27+
28+
- vsock CID alloc + handle (advisory CID on Mac per Phase 0 §D #5).
29+
- `_elastos_interactive` / `_elastos_capsule_args` extraction +
30+
launch_config sanitisation.
31+
- `VzVmConfig::from_manifest` + `vsock_cid` + `elastos.data_dir=/opt/elastos`.
32+
- Provider-wide initramfs default if `vm_config.initramfs_path` is `None`.
33+
- Interactive TERM/winsize injection (`TIOCGWINSZ` + `TERM` env;
34+
both POSIX, work on Darwin).
35+
- Session token injection — shell capsule gets `self.shell_token`,
36+
all others get a fresh `SessionType::Capsule` token from
37+
`self.session_registry`. Mac always follows the no-TAP branch:
38+
`boot_args += " elastos.token=<t>"` only, no `elastos.api=`
39+
(the capsule uses the microVM Carrier bridge, not HTTP).
40+
- Command payload base64 (`elastos.command=`).
41+
- Capsule args base64 (`elastos.capsule_args=`).
42+
- `elastos.provider_port=<VM_PROVIDER_PORT>` when the manifest
43+
advertises `provides`.
44+
- Carrier socket path under `self.crosvm_config.socket_dir`
45+
(OS-agnostic location reused from the Linux path).
46+
- `vm_config.boot_args += " elastos.carrier_path=/dev/hvc1"`
47+
— Mac-specific delta from the Linux flow (`/dev/hvc0` is the
48+
Vz kernel console).
49+
- Rootfs overlay under `self.crosvm_config.rootfs_cache_dir/overlays/`
50+
with bytes copied verbatim from `<capsule_dir>/rootfs.ext4`,
51+
rewiring `vm_config.rootfs_path` to the overlay.
52+
53+
2. **`Supervisor::start_capsule_vm_macos`** signature change:
54+
gained `config: serde_json::Value` so it receives the same
55+
launch payload the Linux arm does. Body now:
56+
57+
- Fails closed before any VM work if
58+
`manifest.permissions.guest_network == true`, with a typed
59+
message naming `com.apple.vm.networking` and Phase 3 Day 4+.
60+
No silent NAT downgrade.
61+
- Builds the full `VmConfig` via `build_vm_config_for_mac`.
62+
- Spawns the microVM Carrier bridge on the returned socket
63+
path (same call shape as Linux L1148-1174). The listener
64+
exists; the guest does not yet receive bytes through it
65+
because the Vz console attachment is still a placeholder
66+
(Phase 3 Day 4+).
67+
- Calls `VzProvider::load_with_vm_config + start`.
68+
- Returns the Day-3-pending typed error after a clean
69+
`provider` drop (VM stopped, no leak).
70+
71+
3. **`VzProvider::set_session_for_vm`** + **`VzProvider::set_network_for_vm`**
72+
(`elastos-vz/src/provider.rs`) retired. They previously
73+
carried `PHASE_1_STUB_MESSAGE`; they now return a typed,
74+
named migration error in the same style as
75+
`append_boot_args_for_vm` did on Day 1. The bodies explicitly
76+
point operators at `VmConfig::with_session` /
77+
`load_with_vm_config` and at the entitlement requirement for
78+
bridged-mode networking.
79+
80+
4. **Tests:**
81+
82+
| Crate | New / updated test | Purpose |
83+
|---|---|---|
84+
| `elastos-server` | `start_capsule_vm_macos_reaches_vz_provider_after_phase3_day1` (existing) | Confirms the seam is still reached after the Day-2 changes. |
85+
| `elastos-server` | `build_vm_config_for_mac_bakes_full_phase3_day2_prefix` (new) | Asserts every Day-2 boot arg (`data_dir`, `command`, `capsule_args`, `carrier_path=/dev/hvc1`) is baked; carrier-socket path embeds the handle; the wrong hvc0 path is never used. |
86+
| `elastos-server` | `start_capsule_vm_macos_fails_closed_when_manifest_requests_tap_network` (new) | Asserts TAP is rejected with a typed message naming the entitlement and the capsule name; no silent downgrade. |
87+
| `elastos-server` | `build_vm_config_for_mac_creates_rootfs_overlay_when_source_present` (new) | Asserts the overlay file is created under `rootfs_cache_dir/overlays/`, bytes round-trip, source rootfs untouched. |
88+
| `elastos-vz` | `vz_provider_set_session_for_vm_returns_typed_migration_error_after_phase3_day2` (new) | Confirms the retired stub no longer carries `PHASE_1_STUB_MESSAGE` and points at the correct new API. |
89+
| `elastos-vz` | `vz_provider_set_network_for_vm_returns_typed_migration_error_after_phase3_day2` (new) | Same shape — confirms the entitlement requirement is named for bridged-mode. |
90+
| `elastos-vz` | `vz_provider_session_and_network_still_fail_closed_with_stub` (retired) | Replaced by the two tests above; the old assertion (`contains(PHASE_1_STUB_MESSAGE)`) is now the *opposite* of correct behaviour. |
91+
92+
5. **Docs:** this file + the status header on
93+
[`PHASE_3_DAY_1_PORT_PLAN.md`](PHASE_3_DAY_1_PORT_PLAN.md);
94+
`PLAN.md` Phase 3 line; `MAC.md` capability matrix.
95+
96+
### Apple-API constraint that shaped Day 2
97+
98+
`VZVirtualMachineConfiguration` is frozen the instant
99+
`VZVirtualMachine::initWithConfiguration:queue:` is invoked. Any
100+
"mutate after load" API shape (`append_boot_args_for_vm`,
101+
`set_session_for_vm`, `set_network_for_vm`) is unsupportable on
102+
Vz. The Phase-3-Day-1 plan already named this; Day 2 finishes
103+
the cleanup by retiring the last two stubs and rerouting all
104+
boot-arg composition through `VmConfig` **before**
105+
`load_with_vm_config` runs.
106+
107+
This is also why TAP rejection runs **before** any VM work:
108+
once `VZVirtualMachine` is initialised, no networking change is
109+
possible. Failing early keeps the error close to the cause.
110+
111+
### What is still *not* working after Day 2
112+
113+
- `elastos ps` does not list the Mac capsule. The VM boots and
114+
then stops cleanly because the supervisor's `running` map
115+
cannot hold a Vz handle yet — `CapsuleBackend::Vm` wraps
116+
`Box<elastos_crosvm::vm::RunningVm>`. Day 3 adds a `VzVm`
117+
variant and the matching insertion.
118+
- `elastos stop <handle>` therefore cannot stop a Mac capsule
119+
either (Day 3).
120+
- The Carrier bridge listener exists on `<socket_dir>/<handle>-carrier.sock`,
121+
but the Vz console attachment in `ffi/console.rs::build_carrier_console_slot`
122+
is still a placeholder (does not yet forward bytes between
123+
the Unix socket and the guest's `/dev/hvc1`). Day 4 wires the
124+
real socketpair.
125+
- vsock from host → guest does not work yet. Day 5.
126+
- TAP networking (guest reachable from host LAN) is rejected
127+
with a typed error. Phase 3 Day 4+ work — needs Apple
128+
entitlement.
129+
- Booting any real first-party capsule end-to-end. Day 5 of
130+
Phase 2 already proved a synthetic Linux guest boots; Day 2
131+
does not re-prove that, and the placeholder Carrier
132+
attachment means a real capsule launched via
133+
`elastos start <name>` would boot but not be reachable.
134+
135+
### Linux-untouched evidence
136+
137+
- `scripts/check-linux-untouched.sh bcf5a0a`: green.
138+
- The Linux `start_capsule_vm` flow (`launch_capsule` L957-1213)
139+
is byte-identical to the pre-Day-1 commit. The macOS arm
140+
early-returns through `start_capsule_vm_macos`; the Linux arm
141+
is compiled but unreachable on Mac.
142+
- All `elastos-crosvm` tests pass on Linux CI without
143+
modification.
144+
145+
### Day 3 handoff
146+
147+
The next slice ([Day-1 audit table row L1197-1210](PHASE_3_DAY_1_PORT_PLAN.md#audit-table)):
148+
149+
- Add `CapsuleBackend::VzVm(Box<elastos_vz::vm::RunningVm>)` to
150+
the supervisor's `CapsuleBackend` enum (or an erasure trait).
151+
- In `start_capsule_vm_macos`, replace the Day-2 fail-closed
152+
exit with a real `RunningCapsule` insertion into `self.running`
153+
— same map the Linux arm uses, same `handle` key.
154+
- Wire `CapsuleStatus`, `WaitCapsule`, `StopCapsule` to dispatch
155+
through the new variant.
156+
- `elastos ps` / `elastos stop` start working on Mac for the
157+
first time.
158+
159+
After Day 3, the supervisor seam is complete. Days 4-5 then
160+
attach real bytes to the Carrier console + vsock paths.

docs/vz-backend/PLAN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ Risk: kernel config mismatch. Fallback: ship a small Mac-specific arm64 kernel b
252252

253253
### Phase 3 — Virtio plumbing: vsock + console + net + blk (1–2 weeks)
254254

255-
**Status as of `73cd293 + Day-1 commit`**: Phase 3 Day 1 in progress. Supervisor → `VzProvider` seam shipped (`elastos start <microvm>` on Mac now reaches `VzProvider::load_with_vm_config` instead of bailing before any work). The wrong-shape `append_boot_args_for_vm` retired in favour of `load_with_vm_config`. Full Day-1 audit in [`PHASE_3_DAY_1_PORT_PLAN.md`](PHASE_3_DAY_1_PORT_PLAN.md). Day 2+ closes the remaining substrate-agnostic prefix (session token, command payload, Carrier bridge spawn, rootfs overlay) and the `CapsuleBackend::VzVm` enum extension that lets the supervisor register Vz VMs in its running map.
255+
**Status as of `73cd293 + Day-2 commit`**: Phase 3 Day 2 complete. The substrate-agnostic prefix from `launch_capsule` L997-1144 is now ported into `Supervisor::start_capsule_vm_macos` — every boot arg the Linux flow bakes (session token, command payload, capsule args, provider_port, TERM/winsize, `elastos.data_dir`, `elastos.carrier_path=/dev/hvc1`) is baked, the Carrier bridge listener is spawned, and the rootfs overlay is created. The last two wrong-shape stubs (`set_session_for_vm`, `set_network_for_vm`) have been retired in favour of `VmConfig::with_session` + `load_with_vm_config`. TAP networking fails closed with a typed entitlement-required error (no silent NAT downgrade). Outcome log in [`PHASE_3_DAY_2_NOTES.md`](PHASE_3_DAY_2_NOTES.md). Day 3 next: extend `CapsuleBackend` with a `VzVm` variant so the supervisor can register the running Vz machine — at which point `elastos ps` / `elastos stop` start working on Mac.
256256

257257
Goal: a guest VM under Vz can speak the same wire protocols a guest under Crosvm speaks. Default networking mode requires **no Apple entitlements** so a normal Mac dev build works out of the box.
258258

0 commit comments

Comments
 (0)