33. Aegis OS Image Build and Release Workflow¶
Date: 2026-10-17
Status¶
Accepted. Implements issue #46 unit 2, fulfilling the executor contract anticipated in ADR-0020 and ADR-0024.
Context¶
Aegis milestone M11 requires an automated executor in cordanaLLM/imago that accepts the product-input manifest aegis.p01.product-input.v1, builds the unified operating system image using mkosi, boots the built image once under KVM acceleration to collect boot evidence, signs the artifacts keylessly via Cosign, and publishes an immutable GitHub pre-release carrying imago.p01.product-result.v1.
Several architectural and environmental constraints govern this execution:
- Host Virtualisation vs Emulation: GitHub-hosted runners (
ubuntu-26.04) provide hardware-assisted virtualisation when/dev/kvmis made accessible. Because the Aegis OS boot contract requires testing under production hypervisor conditions, software emulation (TCG) is unacceptable. Execution must fail closed if/dev/kvmis missing or not readable/writable. - Commit Lineage vs Definitions:
aegis/build/product-input.jsonin Aegis-OS specifiesrevision: "61f2fe16bab7889e007b2fd1474b025023906e91". Commit61f2fe1is the reviewed-definitions baseline; at that commit,build/mkosi.confdid not yet exist in the repository tree. The build executor checks out the targetaegis-revisiontree (wheremkosi.confexists) and must formally prove thatrequest.revisionis an ancestor ofaegis-revision. - Firmware and Console Redirection: Aegis produces Unified Kernel Images (UKIs) without hardcoded serial console arguments (
console=ttyS0). The boot validator must inject console redirection without mutating the signed image or guest filesystems. - Artifact Contract & Integrity: Forge releases impose a hard limit of 2 GiB per asset. Every release artifact must be accounted for in a single signed
SHA256SUMSfile before publishing. Releases must be strictly immutable pre-releases to prevent clobbering or unintended deployment.
Decision¶
- Workflow Trigger & Runner Environment: The executor is defined in
.github/workflows/aegis-build.ymland triggers exclusively onworkflow_dispatch. Its first step refuses any ref other thanrefs/heads/main, so a pre-release is only ever built from reviewed workflow code, andgh release create --target "${GITHUB_SHA}"points the new tag at that commit. It runs on GitHub-hostedubuntu-26.04(accepted byactionlint). Top-level permissions are strictly empty (permissions: {}); the build job receives exactlycontents: write(to publish pre-releases and upload assets) andid-token: write(for OIDC keyless Sigstore Cosign signing). - Input Validation and Lineage Verification: The dispatch inputs are
aegis-revision(40 lowercase hexadecimal characters) andrequest-sha256(64 lowercase hexadecimal characters). Inputs are passed to steps via environment variables, never expanded directly insiderun:blocks. A dedicated verification tool (tools/aegis/check_request.py) performs preflight checks: - Validates the hex syntax of both inputs;
- Verifies the SHA-256 of
aegis/build/product-input.jsonagainstrequest-sha256; - Validates the manifest syntax and schema via
imago aegis validate; - Confirms that
request.revision(61f2fe1...) is a git ancestor ofaegis-revisionusinggit -C aegis merge-base --is-ancestor. - KVM Fail-Closed Enforcement: mkosi is provisioned via the upstream GitHub Action
systemd/mkosipinned to v27.1 (ecf7f5702bb2052b7a69cab3fe7d37bc3b9ad77b). Inspection ofaction.yamlat that commit confirms that it configures udev rules (/etc/udev/rules.d/99-kvm4all.rules) granting mode0666access to/dev/kvm,/dev/vhost-vsock, and/dev/vhost-net, triggers kernel module loading (modprobe kvm), and enables unprivileged user namespaces via sysctl. The action installspacman-package-managerandmakepkgonly when the runner's apt sources carry them and skips them silently otherwise; the workflow therefore checks thatmkosiandpacmanare onPATHand fails if either is missing. There is no fallback installation of mkosi from another source. A subsequent workflow step verifies read and write access to/dev/kvmand exits immediately with code 1 upon any failure. No TCG emulation fallback is permitted. - mkosi Build Parameters & Artifact Contracts: mkosi is executed with
mkosi -C aegis/build --tools-tree=yes --tools-tree-distribution=arch --output-directory <dir> build. The first dispatch (run 39067484406) failed withCould not find 'systemd-repart' which is required to build disk images: Ubuntu 26.04 shipssystemd-repartandsystemd-ukifyas separate universe packages the runner image does not carry, and the Aegis partition set also needsmkfs.erofs,mkfs.btrfs, vfat tools and dm-verity support. mkosi(1) at v27.1 (sectionsToolsTree=and TOOLS TREES) recommends a default tools tree; its Arch package list (mkosi/resources/mkosi-tools/mkosi.conf.d/arch.conf) carriessystemd,systemd-ukify,erofs-utils,btrfs-progs,dosfstoolsandmtools, andToolsTreeSnapshot=defaults to the image snapshot when the tools tree distribution matches the image, so the build tools are pinned toSnapshot=2026/09/13with the image. No host package is installed for the build. Readingmkosi documentationusage for version 27.1 confirms that-Cdesignates the root configuration search directory, while-O, --output-directorydirects output artifacts without modifying the source tree. Based onmkosi.conf(Format=disk,ImageId=aegis-os,SplitArtifacts=uki,kernel,initrd,roothash), the build must produce five exact artifacts: aegis-os.raw: raw GPT disk image;aegis-os.efi: Unified Kernel Image (UKI);aegis-os.vmlinuz: extracted kernel binary;aegis-os.initrd: extracted initramfs;aegis-os.roothash: dm-verity root hash. The step verifies that all five artifacts exist and that no single artifact exceeds the 2 GiB forge limit.- Single-Boot Verification and SMBIOS Type 11 Console:
tools/aegis/boot_once.pyperforms an isolated boot test: - Executes QEMU (
q35,accel=kvm,cpu=host,m=2048,smp=2); - Configures non-Secure-Boot OVMF code (
/usr/share/OVMF/OVMF_CODE_4M.fd) and copies the OVMF NVRAM vars template to a throwaway file; - Initialises an isolated software TPM 2.0 daemon via
swtpm socketcommunicating over a UNIX domain socket withtpm-tis; - Creates a temporary
qcow2overlay backed byaegis-os.raw, ensuring the underlying raw image is never modified; - Redirects guest serial to
serial.log; - Injects the kernel command line argument
console=ttyS0via SMBIOS Type 11 string:-smbios type=11,value=io.systemd.stub.kernel-cmdline-extra=console=ttyS0. As documented insystemd-stub(7)(section "SMBIOS Type 11 strings", string added in systemd 254; read from the systemd 262 man page),systemd-stubreadsio.systemd.stub.kernel-cmdline-extraunless it detects a confidential computing environment, measures the value into PCR12, and adds it to the kernel command line. Secure Boot state does not gate this string; the hosted runner is not a confidential guest; - Polls for the systemd target success marker (matching
Reached target multi-user.target,Reached target Multi-User System, orStartup finished in) within a hard 600-second deadline; - Ensures all spawned process groups (QEMU, swtpm) are cleanly terminated on every exit path;
- Generates
boot-evidence.jsonmatching schemaimago.aegis.boot-evidence.v1; - On a failed boot, writes
boot-failure.json(imago.aegis.boot-failure.v1, a separate schema so a failure record never passes for boot evidence), and the workflow keeps it withserial.logas a three-day run artifact underif: failure(). Run 39075280378 timed out after 600 seconds, and its serial log was lost with the runner. - Keyless Signing & Evidence Packaging:
SHA256SUMSis computed over all released assets:aegis-os.raw,aegis-os.efi,aegis-os.vmlinuz,aegis-os.initrd,aegis-os.roothash,boot-evidence.json, andserial.log.- Sigstore Cosign (
sigstore/cosign-installerpinned tod7d6bc7722e3daa8354c50bcb52f4837da5e9b6a # v3.8.1) signsSHA256SUMSkeylessly to produceSHA256SUMS.bundle. - The signature is verified locally using
cosign verify-blobagainst--certificate-identity "https://github.com/${GITHUB_WORKFLOW_REF}"and--certificate-oidc-issuer https://token.actions.githubusercontent.com. - Product-Result Emission & Reference Forms:
imago aegis result emitproducesresult.json(imago.p01.product-result.v1): signature-ref:https://github.com/<repo>/releases/download/aegis-<cid>/SHA256SUMS.bundle(meets ADR-0024 allow-list syntax);boot-evidence-ref:https://github.com/<repo>/releases/download/aegis-<cid>/boot-evidence.json. This form satisfiesvalidateReference(non-empty, $\le 512$ bytes, no control characters or backslashes) and passesResult.Validate.- Pre-Release Publication: The pre-release is published using
gh release create aegis-<cid> --prerelease --latest=false: - Enforces that the tag starts with
aegis-; - Queries
gh release viewandgit ls-remoteto refuse execution if the tag already exists; - Never uses
--clobberand never sets latest; - Uploads only signed artifacts and evidence: the five mkosi outputs,
boot-evidence.json,serial.log,SHA256SUMS,SHA256SUMS.bundle, andresult.json.
Facts measured on the first dispatch¶
The following are unmeasured when this record is accepted and are recorded on issue #46 after the first dispatch: whether the hosted runner exposes /dev/kvm, whether swtpm starts under the runner's AppArmor state, whether the Arch Linux keyring is usable by pacman on Ubuntu, the exact file names mkosi writes for the split artifacts, disk headroom, artifact sizes against the 2 GiB limit, and wall time. First dispatch, run 39067484406 on ubuntu-26.04 image 20261012.163.1: /dev/kvm read-write passed, the request check passed, mkosi 27.1 and pacman were on PATH, pacman installed the Arch keyring and the default initrd built (61.1 MB zstd cpio); the disk build then failed on the missing host systemd-repart, fixed by the tools tree above. A measured failure of KVM, swtpm or disk is the trigger for the self-hosted VM runner fallback the operator approved; any other failure is fixed in this workflow.
Consequences¶
- Positive: Imago provides a fully automated, verifiable, and fail-closed build and release pipeline for Aegis OS.
- Positive: Git lineage validation cleanly bridges the reviewed baseline commit (
61f2fe1) with the runnable tree ataegis-revision. - Positive: SMBIOS Type 11 injection provides serial visibility during boot testing without modifying UKI binaries or root partition contents.
- Positive: All published artifacts are cryptographically bound to the GitHub Actions workflow identity through keyless Cosign signatures.
- Neutral: The pre-release tag
aegis-<correlation-id>is immutable. Re-running a failed or aborted correlation ID requires generating a new correlation ID in Aegis.
References¶
- Issue #46: Aegis product-input execution, signing, and product-result emission.
- ADR-0020: Aegis product-input contract acceptance.
- ADR-0024: Product-result emitter and signature reference form.
- ADR-0032: Local Packer executor.
- Upstream
systemd/mkosiAction: commitecf7f5702bb2052b7a69cab3fe7d37bc3b9ad77b(v27.1). - Systemd documentation:
systemd-stub(7)andsmbios-type-11(7). - Tools:
tools/aegis/check_request.py,tools/aegis/boot_once.py. - Workflow:
.github/workflows/aegis-build.yml.