Build¶
Status: rewritten 2026-07-05 against ADR-0031/0038/0039 (T98). The pre-pivot page (pure-Go pipeline, publish flows, backend auto-detection) is superseded; command-level detail lives in the buildctl CLI overview, conformance in the App contract, distribution in the Index contract.
What building is¶
buildctl build turns a project — package.yaml plus a Dockerfile,
mkosi.conf, or build.sh — into a conformant, signed, single-file DDI
(ADR-0038). One motion; there is no separate sign step and no way to
produce an unsigned image (ADR-0031). buildctl builds DDIs and does nothing
else (ADR-0039): no index verbs, no transport, no network distribution I/O.
package.yaml + backend source
│ backend produces a rootfs tree
▼
conformance layer ← the part that is ours
│ validate + normalize (app contract):
│ identity files, mount-point dirs, embedded manifest,
│ no User=/Group=, no host mounts, root:root,
│ unit validation (T104), additivity vs base (layered)
▼
assembly + signing ← delegated to systemd-ecosystem tooling
│ squashfs/erofs → verity → PKCS7 (SHA-256) → GPT
│ (Linux; runs in a container/VM on macOS)
▼
<name>_<version>_<arch>.raw ← the package. One file.
Backends¶
| Backend | Source | Notes |
|---|---|---|
docker |
Dockerfile |
cross-arch via buildx/binfmt |
mkosi |
mkosi.conf |
systemd-native; in-container on macOS |
shell |
build.sh |
the escape hatch — subsumes make (call it from the script) |
The backend is never guessed: declared in package.yaml or passed as
--backend. Marker-file auto-detection is dead — a Docker project with an
incidental Makefile must never surprise-build the wrong way.
The two package shapes¶
- Fat app — the backend produces a complete userland.
- Layered app — the backend builds against the base's published build
image and produces only the app's own files; the conformance layer
generates the
extension-release.d/file from thebase:declaration and fails the build on any file overlap with the base (additivity gate, ADR-0034 §20). One base, two layers, no language layer.
Keys and signing¶
The build key signs every image (roothash → PKCS7, SHA-256 digest — OpenSSL 3.x rejects SHA-1) into the verity-sig partition. It lives on the author's machine, never anywhere else. Trust semantics: Security model. Key roles carry no cert markings — roles are positional in index manifests (Q-B5).
Configuration¶
Per-user config file (vendor-defaults + /etc override + .d/ drop-ins,
per the Configuration Files convention, ADR-0034): default build key, and
index aliases for the hand-off to the distribute tool. Precedence is
strict: flag > config > loud error — buildctl never guesses.
After the build¶
Publishing is not buildctl's job (ADR-0039). Hand the .raw to the
distribute tool (index add) on whatever host holds the index — moving
the file there is your transport (scp, rsync, a stick).