shuru run — data flow

Tap any stage to expand details

CLI
Config
VM
Proxy
Storage
Execution
1. Entry point
shuru-cli/src/main.rs · fn main()
What happens

The binary starts, clap parses argv into a typed Cli struct, and dispatches to the Commands::Run branch.

Key types
struct Cli { command: Commands, } enum Commands { Run { vm: VmArgs, // --cpus, --memory … from: Option<String>, // checkpoint name console: bool, stdio: bool, command: Vec<String>, // the actual cmd }, … }
Cli::parse()
📄
2. Config loading
shuru-cli/src/config.rs · load_config()
What happens

Reads shuru.json (or the path from --config) from the current directory. Falls back to an empty default config if the file doesn't exist.

ShuruConfig fields
cpus, memory, disk_size allow_net: bool allow_host_writes: bool ports: Vec<PortMapping> mounts: Vec<MountConfig> command: Option<Vec<String>> secrets: Vec<String> network: Option<NetworkConfig> expose_host: Vec<String>
Merge priority

CLI flags win over config file. If no command is provided anywhere, defaults to /bin/sh.

ShuruConfig
🔧
3. VM preparation
shuru-cli/src/vm.rs · prepare_vm()
What happens
  1. Merge CLI + config flags (cpus, memory, disk_size, networking)
  2. Parse port and mount specs from both sources
  3. Auto-download kernel / rootfs assets if missing
  4. Find source rootfs — checkpoint .idx (CAS) or .ext4, else base image
  5. Create instance dir at ~/.local/share/shuru/instances/{pid}/
  6. Clone source → working copy via clone_file() (reflink on macOS)
  7. Extend disk image to requested disk_size
PreparedVm output
struct PreparedVm { instance_dir: String, work_rootfs: String, // writable copy cas_index: Option<String>, kernel_path: String, initrd_path: Option<String>, cpus, memory, disk_size, proxy_config: Option<ProxyConfig>, forwards: Vec<PortMapping>, mounts: Vec<MountConfig>, }
PreparedVm
Optional — run in parallel if needed
🌐
4a. Proxy --allow-net
shuru-proxy/src/lib.rs
What happens
  1. Create Unix socketpair (vm_fd / host_fd)
  2. Spawn netstack thread: NetworkStack
  3. Spawn proxy thread: tokio ProxyEngine
  4. Generate placeholder tokens for secrets
  5. Return CA cert PEM + token map
Output
ProxyHandle { placeholders: HashMap, ca_cert_pem: String, }
💾
4b. NBD store checkpoint
shuru-store/src/lib.rs
What happens
  1. Check SHURU_STORAGE=direct env to bypass
  2. Create CAS backend with lazy block ingestion
  3. Spawn NBD server on Unix socket
  4. Return handle with .uri() for the VM
Output
NbdHandle { fn uri() -> String, fn save_checkpoint(path), }
ProxyHandle? + NbdHandle?
🏗️
5. Build sandbox
shuru-vm/src/sandbox.rs · VmConfigBuilder
What happens

Builder pattern assembles the full VM config and constructs a Sandbox backed by a platform-specific VirtualMachine (Darwin/Linux).

  1. Set kernel, rootfs (or NBD URI), cpus, memory
  2. Configure console mode and verbosity
  3. Register virtio-fs mounts
  4. Attach network socket fd (if proxy)
  5. Call .build()Sandbox
Sandbox struct
struct Sandbox { vm: Arc<VirtualMachine>, mounts: Mutex<Vec<MountRequest>>, }
Sandbox
🚀
6. VM start & port forwarding
sandbox.rs · sandbox.start()
What happens
  1. sandbox.start() boots the guest kernel
  2. If port forwards configured, start_port_forwarding() binds TCP listeners on 127.0.0.1:{host_port}
  3. Each accepted TCP connection spawns a thread that establishes a vsock connection to guest port 26262 and relays bytes bidirectionally
Proxy cert injection (if --allow-net)
  1. Write CA cert to /usr/local/share/ca-certificates/shuru-proxy.crt
  2. Run update-ca-certificates --fresh in guest
  3. Build env map with secret placeholder tokens
running VM
⚙️
7. Execute command
sandbox.rs — 3 paths
Shell (TTY)
Exec (pipe)
Stdio (RPC)
  1. Detect terminal with isatty(stdin)
  2. Connect vsock → guest port 26261 (retry ×50)
  3. Send mount requests
  4. Send ExecRequest { tty: true, rows, cols }
  5. Enter raw terminal mode
  6. Spawn stdin relay thread (kqueue + SIGWINCH)
  7. Spawn vsock reader thread (BufReader batching)
  8. Read STDOUT/STDERR/RESIZE/EXIT frames
  9. Restore terminal on drop
  1. Detected: stdin is a pipe (non-interactive)
  2. Connect vsock → guest port 26261
  3. Send mount requests
  4. Send ExecRequest { tty: false }
  5. Loop reading frames: STDOUT → stdout, STDERR → stderr
  6. EXIT frame → parse exit code, break
  7. ERROR frame → write message, exit 1

Enabled with --stdio. Speaks JSON-RPC 2.0 on stdin/stdout.

exec, spawn, kill, input watch, read_file, write_file checkpoint, mkdir, read_dir stat, remove, rename, copy, chmod

Each method dispatches to a background thread; results and notifications stream back as JSON.

vsock frames
🔗
8. vsock frame protocol
shuru-proto · port 26261
Host
shuru-cli process
↕ vsock CID 3 : 26261
Guest
init / agent
Frame types (host ↔ guest)
ExecRequest → STDIN → RESIZE → ← STDOUT ← STDERR ← EXIT ← ERROR
ExecRequest payload
argv: Vec<String> env: HashMap<String, String> tty: Option<bool> rows, cols: Option<u16> cwd: Option<String>
EXIT frame
🧹
9. Cleanup & exit
vm.rs · RunResult
What happens
  1. Stop VM: sandbox.stop()
  2. If checkpoint (NBD): nbd_handle.save_checkpoint(path) writes .idx
  3. Remove instance dir: ~/.local/share/shuru/instances/{pid}/
  4. Proxy & NBD threads drop automatically
  5. Process exits with guest's exit code
RunResult
struct RunResult { exit_code: i32, nbd_handle: Option<NbdHandle>, }