# arcbox-vm
Guest-side sandbox orchestration: runs **inside** the ArcBox System VM and
manages nested [Firecracker](https://firecracker-microvm.github.io/)
microVMs — one per sandbox — through
[`fc-sdk`](https://crates.io/crates/fc-sdk).
Do not confuse it with `arcbox-vmm`, which is the **host** VMM that boots
the System VM itself on Virtualization.framework or KVM. Different layer,
different machine.
```
host (macOS) System VM (Linux) sandbox (Firecracker)
arcbox-daemon ──vsock──► arcbox-agent ──vsock──► vm-agent (PID 1)
arcbox-vmm arcbox-vm workload
fc-sdk
```
`arcbox-agent` is this crate's only consumer: it owns the `sandbox.v1`
surface and the vsock transport, and calls `SandboxManager` underneath.
There are no service implementations, no tonic, and no daemon here.
The crate also ships the **`vm-agent`** binary — a separate, much smaller
program that becomes PID 1 *inside* each sandbox. It imports only the
protocol leaves (`boot_proto`, `file_io::proto`, `file_watch`, `vsock`
constants, `listen_table`, `user_spec`), never the manager;
`arcbox-agent`'s `rootfs_builder` stages it into every sandbox rootfs.
## Usage
```rust
use std::sync::Arc;
use arcbox_vm::{SandboxManager, SandboxSpec, VmmConfig};
let manager = Arc::new(SandboxManager::new(VmmConfig::default())?);
let (id, ip) = manager
.create_sandbox(SandboxSpec {
vcpus: 1,
memory_mib: 512,
..Default::default()
})
.await?;
```
`VmmConfig` loads from TOML (`VmmConfig::from_file`) or is built
programmatically; `[firecracker]`, `[network]`, and `[defaults]` are the
sections that matter, and `[firecracker.jailer]` opts into jailer mode.
See `config.rs` for the fields.
## Build and test
```bash
cargo test -p arcbox-vm # unit + integration, no Firecracker needed
cargo clippy -p arcbox-vm -- -D warnings
# vm-agent, as the release ships it (aarch64 musl; brew install FiloSottile/musl-cross/musl-cross)
cargo build -p arcbox-vm --bin vm-agent --target aarch64-unknown-linux-musl --release
```
Everything real — TAP creation, boot, checkpoint — needs Linux with
`CAP_NET_ADMIN`, a `firecracker` binary, and (for jailer mode) root.
`examples/sandbox-smoke.rs` is the end-to-end walkthrough;
`.github/workflows/test-vm-linux.yml` runs the whole ladder on real KVM.
## Jailer mode: every path the FC API sees is chroot-relative
Firecracker `pivot_root`s before it processes any API request, so a
host-absolute path handed to the API resolves inside the chroot and
returns `ENOENT`. Kernel and rootfs are therefore *staged into* the
chroot before boot (`{chroot}/vmlinux`, `{chroot}/rootfs.ext4`) and the
API is given `/vmlinux` and `/rootfs.ext4`. Checkpoints write to
`{chroot}/snapshots/{id}/` and are moved out to the catalog afterwards;
restore copies them back into the *new* sandbox's chroot first.
The same rule is what keeps vsock sockets from colliding: the vmstate
records the UDS as `/run/firecracker.vsock`, which is a different host
path in every sandbox's chroot.
## Data layout
```
{data_dir}/ # default /var/lib/firecracker-vmm
├── kernels/vmlinux
├── images/*.ext4
├── sandboxes/{id}/ # firecracker.sock, .vsock, .log, .metrics
└── snapshots/{sandbox-id}/{snapshot-id}/
# vmstate, mem, meta.json
```
Under jailer mode the live files instead sit beneath
`{chroot_base_dir}/firecracker/{sandbox-id}/root/` (default
`/srv/jailer`), and the snapshot catalog above is still the durable home.
## License
MIT OR Apache-2.0, inherited from the workspace root.