Architecture
The components, the request lifecycle, the runtime model, and the deliberate non-goals.
emberd is two binaries and a wire protocol between them: a host daemon that owns microVM lifecycle, and a guest agent that runs code inside each VM.
Components
| Component | Lives in | Role |
|---|---|---|
emberd daemon | cmd/emberd | Host-side HTTP server. Owns microVM lifecycle, holds sandbox handles, dispatches exec over vsock. |
emberd-init | cmd/emberd-init | PID 1 inside each microVM. Brings up the rootfs, serves the control protocol on vsock, runs submitted code. |
| HTTP API | pkg/api | Request/response types and routing, wired to a sandbox.Manager. |
| Manager | pkg/sandbox + pkg/sandbox/firecracker | The Manager interface and its Firecracker-backed implementation. |
| Control protocol | pkg/proto | The length-prefixed-JSON wire format shared by host and guest. |
| Guest initramfs | rootfs/build.sh | A cpio archive whose /init is a static emberd-init. |
The daemon and guest agent share exactly one package — pkg/proto — which keeps
the contract between them explicit and small.
How a request flows
Each sandbox is exactly one firecracker process holding one microVM. The daemon
keeps an in-memory handle (map[id]*vm) with the machine, its work directory, and
the vsock socket path.
The runtime model
- One microVM per sandbox. The strongest isolation and the simplest thing to reason about. A warm pool comes later (see Roadmap).
- Cold boot today. Create boots a fresh VM: kernel + custom initramfs + a read-only rootfs drive. Snapshot restore (sub-100ms) is the v0.2 target.
- vsock control plane. Host ↔ guest traffic (exec requests, output) goes over a virtio-vsock device, not a NIC. "No network" really means none — including for control.
- Read-only base + tmpfs overlay. The language pack is an immutable squashfs; each sandbox layers a per-VM tmpfs on top via overlayfs. Base pages are shared, resets are trivial (discard the tmpfs), and snapshots stay small.
See Concepts for the detail behind each of these.
Sandbox identity
A sandbox ID looks like sb_a1b2c3d4e5f6 — an sb_ prefix plus 6 random bytes
hex-encoded. Firecracker's instance --id only accepts [A-Za-z0-9-], so the
daemon passes a hyphen-sanitized form (sb-a1b2c3d4e5f6) to the VMM while keeping
the underscore form as the public/API identity. The two never need to match.
Per-sandbox state on the host
Everything for one sandbox lives under ${TMPDIR:-/tmp}/emberd/<id>/:
/tmp/emberd/sb_a1b2c3d4e5f6/
├── fc.sock # Firecracker API socket
├── vsock.sock # host end of the hybrid-vsock control channel
└── vm.log # guest console + VMM log (great for debugging boots)The directory is created on Create and removed on Delete.
Networking
- v0.1: no network access inside sandboxes. No TAP, no DNS, no host loopback.
- v0.2+: opt-in per-sandbox egress allowlist — likely a host TAP device plus iptables rules limiting destinations.
Non-goals
- Persistent storage across sandbox lifetimes — use the host.
- Cross-sandbox communication — each sandbox is sealed by design.
- Built-in auth / multi-tenancy — assume the daemon is reached only on localhost or behind your own auth layer.