Files
clawmates/crates/bins/clawmates-node/Cargo.toml
T
Omar SobhandClaude Opus 5 2d04c5e257 feat(fleet): B2 — vm_* node ops for Firecracker microVMs
create / inject / exec / collect / destroy / list, riding the node's
existing frame dispatch ({t, id, …} -> {t:"result", id, ok, output}), so
no protocol change was needed. Control is length-prefixed JSON over
vsock; the serial console stays a log, because feeding a guest over stdin
races its startup and arrives half-consumed.

DEVIATION FROM THE PLAN, deliberately: this does NOT implement
cm_sandbox::SandboxDriver. That trait is container-shaped —
attach_pty/resize_pty/argv exec — while missions need
create -> inject -> run -> collect -> destroy. Conforming would mean
building PTY-over-vsock and window-resize semantics that no mission path
calls, purely to satisfy a signature. We give up automatic RemoteDriver
marshalling; orphan reaping is a label/id sweep either way.

Three traps from the B0 spike are handled in code rather than remembered:

  - Firecracker does NOT unlink its vsock UDS on exit, so destroy unlinks
    it explicitly, and the selftest ASSERTS it is gone. Assuming the VM
    tidies up after itself is how the mission checkout accumulated four
    uid bugs.
  - firecracker is spawned via setsid and killed as a process GROUP, so a
    background child cannot outlive the VM holding its workdir open.
  - create does not return until the guest agent has answered a ping. A
    VM that booted but serves nothing is worse than one that failed, so a
    half-created VM is destroyed rather than left registered.

A vm id becomes a path component, so ids are restricted to [A-Za-z0-9_-]
and REJECTED rather than sanitised — a caller that sent `../../etc`
wanted something we should not guess at.

Verified on tank through the real Rust path, as the daemon user, with no
sudo: `clawmates-node --vm-selftest` -> 8/8, create in 986ms, and the
host left with zero firecracker processes and zero VM directories. The
selftest asserts every step, including that a destroyed VM can no longer
be exec'd; a test that only reports the steps it completed cannot
distinguish "passed" from "stopped early".

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-05 07:35:13 -07:00

32 lines
858 B
TOML

[package]
name = "clawmates-node"
version = "0.4.0"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
publish.workspace = true
[[bin]]
name = "clawmates-node"
path = "src/main.rs"
[dependencies]
tokio = { workspace = true, features = ["process"] }
tokio-tungstenite = { version = "0.26", features = ["rustls-tls-webpki-roots"] }
futures = "0.3"
serde = { workspace = true }
serde_json = { workspace = true }
sysinfo = "0.33"
portable-pty = "0.8"
base64 = "0.22"
tar = { workspace = true }
cm-sandbox = { path = "../../cm-sandbox" }
# Linking cm-sandbox (bollard) brings a second rustls provider into the graph, so
# rustls can't auto-pick one — we install `ring` explicitly at startup.
rustls = { version = "0.23", default-features = false, features = ["ring"] }
webrtc = "0.17.1"
bytes = "1.12.0"
[lints]
workspace = true