Firecracker guest image
The Firecracker runtime boots vmlinux with the ext4 image produced by scripts/. The guest is a Debian userland with a coding toolchain: it runs git, python3, curl, tar/gzip, and the coreutils a coding agent expects, with a real CA bundle and working DNS.
Base image and packages#
| Item | Value |
|---|---|
| Base image | debian: |
| Base digest | debian: |
| Installed packages | git ca-certificates curl python3 tar gzip coreutils util-linux hostname (--no-install-recommends, apt lists and docs stripped afterwards) |
| Guest agent | aiec-guest 0.1.0, static musl, at / |
Versions measured inside the built container (not hardcoded):
| Tool | Version |
|---|---|
| git | git version 2.39.5 (Debian 1:) |
| curl | curl 7.88.1 (x86_64-pc-linux-gnu) libcurl/ |
| python3 | Python 3.11.2 |
| tar | tar (GNU tar) 1.34 |
The base image is pinned by tag and the resolved repo digest is recorded in guest-capabilities.json; AIEC_GUEST_BASE_IMAGE overrides the tag for a controlled upgrade, and the digest in the recorded capabilities file is what identifies the result.
Artifact layout#
.aiec/ (the default output directory) contains:
| File | Contents |
|---|---|
aiec-rootfs.ext4 |
4 GiB ext4 image; ~282 MiB of Debian bookworm-slim userland with the guest agent and the baked guest secret |
vmlinux |
uncompressed kernel (provided separately, not produced by this script) |
guest-capabilities.json |
machine-readable description of the artifact (see below) |
SHA256SUMS |
sha256sum lines for the rootfs and the kernel |
manifest.json |
runtime boot contract: rootfs name, kernel name, control_port:, guest_cid: |
Inside the image:
/is a symlink toinit /;sbin/ init /is the repository-ownedsbin/ init guest/(POSIXrootfs/ sbin/ init sh, no BusyBox applets). It mounts/,proc /,sys devtmpfson/,dev devpts,tmpfson/, sets the hostname, checksrun /, exportsetc/ aiec-guest-secret AIEC_GUEST_SECRET, and supervises the guest agent in a restart loop. The agent listens on vsock port 1024 and serves/, which init guarantees exists.workspace /holds the build-time secret with mode 0600.etc/ aiec-guest-secret /is a real file withetc/ resolv.conf nameserver 1.1.1.1andnameserver 8.8.8.8(the docker-managed bind mount is never written through).- Debian bookworm is merged-usr, so
/,bin /andsbin /are symlinks intolib /; the guest agent and the repository skeleton are installed through them.usr
Rebuilding#
AIEC_GUEST_SECRET=$(openssl rand -hex 32) bash scripts/build-firecracker-guest.sh
| Input | Meaning |
|---|---|
AIEC_GUEST_SECRET |
required, at least 32 bytes, baked into the image; rotate it and rebuild to change it |
AIEC_KERNEL |
kernel to hash and describe, default .aiec/ |
AIEC_GUEST_BASE_IMAGE |
base image, default debian: |
AIEC_GUEST_ROOTFS_SIZE |
image size, default 4G |
| positional argument 1 | output directory, default .aiec/ |
Requirements: docker, the Rust toolchain with the x86_64-unknown-linux-musl target, and e2fsprogs (mke2fs, e2fsck, resize2fs, debugfs). The build needs the network to pull the base image and run apt-get. When the build user cannot reach the docker socket directly, every docker call is routed through sg docker -c '...'; the script fails with a clear error if neither path works.
Exit codes: 0 on success, 1 on a build error, 2 when the rootfs was built but AIEC_KERNEL does not exist (in that case guest-capabilities.json is written with "kernel_sha256": and SHA256SUMS/manifest.json are not rewritten).
The image is verified offline without booting it:
debugfs -R 'stat /usr/bin/git' .aiec/images/aiec-rootfs.ext4
debugfs -R 'cat /etc/resolv.conf' .aiec/images/aiec-rootfs.ext4
Capability profile#
guest-capabilities.json describes what a sandbox built on this image can do:
{"schema":1,"artifact_version":"1.0.0","base":"<base image digest>","profile":"coding",
"capabilities":["sh","coreutils","git","ca-certificates","dns","https","tar","gzip","curl","python3"],
"git_version":"<measured>","guest_agent_version":"<Cargo version>","guest_protocol_version":2,"rootfs_sha256":"<sha256>",
"kernel_sha256":"<sha256>|null","built_at":"<RFC3339 UTC>"}
profile:marks the image as a coding-capable guest rather than a minimal shell."coding" capabilitiesare stable identifiers, not versions:sh,coreutils,git,ca-certificates,dns,https,tar,gzip,curl,python3.git_versionandguest_agent_versionare the measuredgit --versionoutput and theaiec-guestcrate version from the workspace manifest.guest_protocol_versionis the wire protocol revision the baked agent implements, read fromaiec-core'sPROTOCOL_VERSIONby the build. Revision 2 adds the bounded, version-consistent artifact range reads (ReadFileChunk) that collection depends on.rootfs_sha256coversaiec-rootfs.ext4as written by the script;kernel_sha256covers the kernel and isnullwhen the build had none.
The runtime consumes this file through aiec-runtime's guest_artifact module (load_guest_artifact/verify_guest_artifact): it fails closed when the file is missing, when the rootfs is unreadable, when the recorded rootfs_sha256 does not match the image on disk, when guest_protocol_version is absent or is not the revision this runtime speaks, and it verifies kernel_sha256 whenever it is present. profile == "coding" together with git in capabilities is what marks the artifact as coding-capable; a stricter deployment can additionally require ca-certificates. schema and built_at are informational.
Networking#
The guest does not configure its own address. The runtime network attachment supplies the guest addresses: NetworkAttachment:: becomes the ip= autoconfiguration parameters on the Firecracker boot cmdline, and the kernel applies them to the virtio-net interface (CONFIG_IP_PNP and CONFIG_VIRTIO_NET in the approved vmlinux). The guest userland has no role in address assignment. The guest's only DNS responsibility is to ship a usable /, which the build writes as a plain file with the public resolvers 1.1.1.1 and 8.8.8.8. The control channel is vsock (guest CID 3, port 1024) and is unaffected by IP autoconfiguration.