# Usage Guide A practical guide for common ICT workflows. For the complete command reference, see the [CLI Specification](../architecture/image-composer-tool-cli-specification.md). ## Table of Contents - [Binary Location](#binary-location) - [Commands Overview](#commands-overview) - [Building an Image](#building-an-image) - [Build Output](#build-output) - [Building an Image in a Docker Container](#building-an-image-in-a-docker-container) - [Comparing Overlay Outputs](#comparing-overlay-outputs) - [Baseline RAW vs Overlay RAW](#baseline-raw-vs-overlay-raw) - [SBOM Comparison (Complete SBOM)](#sbom-comparison-complete-sbom) - [Delta SBOM: Optional Quick-Change View](#delta-sbom-optional-quick-change-view) - [Validating a Template](#validating-a-template) - [Resolving a Template](#resolving-a-template) - [Debugging an Extends Chain](#debugging-an-extends-chain) - [Configuration](#configuration) - [Operations Requiring Sudo](#operations-requiring-sudo) - [Running the Web UI](#running-the-web-ui) - [Running the Web UI With Docker Compose](#running-the-web-ui-with-docker-compose) - [Shell Completion](#shell-completion) - [Template Examples](#template-examples) - [Minimal Edge Device](#minimal-edge-device) - [Development Environment](#development-environment) - [Edge Microvisor Toolkit](#edge-microvisor-toolkit) - [Related Documentation](#related-documentation) --- ## Binary Location The path to the `image-composer-tool` binary depends on how you built or installed it: | Build method | Binary path | |-------------|-------------| | `go build ./cmd/image-composer-tool` | `./image-composer-tool` | | `earthly +build` | `./build/image-composer-tool` | | Debian package | `image-composer-tool` (installed to `/usr/local/bin/`) | The examples below use `./image-composer-tool` (the `go build` location). Substitute the path that matches your setup. ## Commands Overview ```bash image-composer-tool build # Build an image from a template image-composer-tool validate # Validate a template without building image-composer-tool resolve # Print the merged template YAML for debugging image-composer-tool inspect # Inspect a raw image's structure image-composer-tool compare # Compare two images image-composer-tool ai # AI-powered template generation (RAG) image-composer-tool serve # Run the web UI backend API server image-composer-tool cache clean # Manage cached artifacts image-composer-tool config # Manage configuration (init, show) image-composer-tool version # Display version info image-composer-tool --help # Show all commands and options ``` For the full details on every command — including `inspect`, `compare`, and `cache` — see the [CLI Specification](../architecture/image-composer-tool-cli-specification.md#commands). ## Building an Image > **ISO images require the `live-installer` binary.** Build it before starting > an ISO build: > > ```bash > go build -buildmode=pie -o ./build/live-installer ./cmd/live-installer > ``` > > If you use `earthly +build`, both binaries are built automatically. See the > [Installation Guide](./installation.md) for details. ```bash # go build — binary is in the repo root sudo -E ./image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml # earthly +build — binary is in ./build/ sudo -E ./build/image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml # Debian package — binary is on PATH sudo image-composer-tool build /usr/share/image-composer-tool/examples/azl3-x86_64-edge-raw.yml # Override config settings with flags sudo -E ./image-composer-tool build --workers 16 --cache-dir /tmp/cache image-templates/azl3/azl3-x86_64-edge-raw.yml # Build from scratch in throwaway cache/workspace dirs (removed after the build) sudo -E ./image-composer-tool build --no-cache image-templates/azl3/azl3-x86_64-edge-raw.yml # Override composition settings for one build (disk strategy, hostname, proxy, SSH keys) sudo -E ./image-composer-tool build --disk-strategy largest --hostname edge-01 \ --http-proxy http://proxy.example.com:3128 --ssh-authorized-key admin=$HOME/.ssh/id_ed25519.pub \ image-templates/ubuntu24/ubuntu24-x86_64-edgepack-unattended-iso.yml ``` Common flags: `--workers`, `--cache-dir`, `--work-dir`, `--no-cache`, `--verbose`, `--dotfile`, `--config`, `--log-level`. Composition overrides: `--disk-strategy`, `--hostname`, `--http-proxy`, `--https-proxy`, `--ftp-proxy`, `--no-proxy`, `--ssh-authorized-key`. See the full [build flag reference](../architecture/image-composer-tool-cli-specification.md#build-command) for descriptions and additional flags like `--system-packages-only`. ### Build Output After the image finishes building, the output is placed under the configured `work_dir`. The full path follows this pattern: ``` /--/imagebuild// ``` The default `work_dir` depends on how you installed the tool: | Install method | Default `work_dir` | Example output path | |----------------|-------------------|---------------------| | Cloned repo | `./workspace` (relative to repo root) | `./workspace/azure-linux-azl3-x86_64/imagebuild/edge/` | | Debian package | `/tmp/image-composer-tool` | `/tmp/image-composer-tool/azure-linux-azl3-x86_64/imagebuild/edge/` | You can override it with `--work-dir` or by setting `work_dir` in your configuration file. An ISO build also leaves the SPDX SBOM of the installed system (`spdx_manifest_*.json`), a composition manifest (`-.composition.json`), and the merged template (`template-dump.yaml`) in this directory. See the [Unattended ISO Installer Tutorial](./unattended-iso-provisioning.md). ## Building an Image in a Docker Container Instead of installing the tool and running builds with `sudo`, you can build inside the ICT container image, which carries `image-composer-tool` and every program a build runs: ```bash docker build -f build/ict/Dockerfile --target ict -t ict . scripts/build-in-container.sh my-template.yml ./out ``` The OS image and its SBOM land in `./out//`, owned by you. The container runs privileged with the host's `/dev` mounted, so it is root-equivalent on the host. The [CLI Quick Start (Docker)](./cli-docker-quick-start.md) covers writing a template, the mounts, and every option of the script. The script wraps one `docker run`, which runs `/opt/ict/scripts/container-build.sh` inside the container. To run that yourself, mount the template's directory and your output directory, and pass the output's mount point as `--output`; it must be a mounted volume, or the files would disappear with the container. Create the output directory first, so it is not owned by root: ```bash mkdir -p out docker run --rm --init --privileged -v /dev:/dev \ -v "$PWD:/input:ro" -v "$PWD/out:/artifacts" \ ict /opt/ict/scripts/container-build.sh \ --template /input/my-template.yml --output /artifacts --owner "$(id -u):$(id -g)" ``` ## Comparing Overlay Outputs An [overlay build](../architecture/image-composer-tool-templates.md#baseline) layers packages onto an existing baseline RAW image and emits, alongside the result, SPDX SBOM sidecars (see [SBOM generation (overlay mode)](../architecture/image-composer-tool-templates.md#sbom-generation-overlay-mode)): | Artifact | Name | |----------|------| | Overlay RAW image | `-.raw` | | Delta SBOM (overlay changes only) | `-.delta.spdx.json` | | Complete SBOM (full final inventory) | `-.complete.spdx.json` (only when a base SBOM is available; otherwise skipped) | The existing `compare` command works directly against these outputs — no overlay-specific flags are needed. The examples below assume the baseline is at `baseline.raw` and the overlay build produced `myimage-1.0.*` under the build output directory. ### Baseline RAW vs Overlay RAW Compare the structural/binary differences between the baseline and the overlay result. Add `--hash-images` to compute SHA256 and get a binary-identity verdict: ```bash # Human-readable structural diff (partition table, filesystems, bootloader, SBOM metadata) ./image-composer-tool compare baseline.raw myimage-1.0.raw # High-level counts as JSON, suitable for CI ./image-composer-tool compare --format=json --mode=summary baseline.raw myimage-1.0.raw # Binary-identity check (slower — hashes both images) ./image-composer-tool compare --hash-images baseline.raw myimage-1.0.raw ``` Expected output pattern (text): a top `Equality:` verdict followed by the changed sections. Because the overlay grows the disk and rewrites the embedded SBOM, the verdict is `different`, and the summary reports `sbomChanged` and a size change: ``` Equality: different (meaningful diffs: ...) Image: Size: 4.0 GiB -> 6.0 GiB SBOM: PackageCount: 120 -> 123 ``` The equality classes are `binary_identical` (only with `--hash-images`, when the SHA256s match), `semantically_identical` / `semantically_identical_unverified` (no meaningful diffs), or `different`. > **Note:** In the default image modes the compare tool reports SBOM *metadata* > (package count, canonical hash) only. For a package-by-package breakdown, use > the SBOM comparison below. ### SBOM Comparison (Complete SBOM) Use `--mode=spdx` for an accurate package-level diff. The complete SBOM represents the full final image inventory, so comparing the baseline's SBOM against the overlay complete SBOM reports exactly which packages were added or upgraded: ```bash # Compare the baseline SBOM against the overlay complete SBOM ./image-composer-tool compare --mode=spdx baseline.spdx.json myimage-1.0.complete.spdx.json # Either argument may also be an image — its embedded SBOM (/usr/share/sbom) is # extracted automatically, so you can compare the baseline RAW directly: ./image-composer-tool compare --mode=spdx baseline.raw myimage-1.0.complete.spdx.json # JSON for tooling ./image-composer-tool compare --format=json --mode=spdx baseline.spdx.json myimage-1.0.complete.spdx.json ``` Expected output pattern (text): added, removed, and upgraded package lists. A same-name version bump is reported as a single upgrade (`~`), not a remove + add: ``` SPDX Compare ============ Equal: false Packages: 120 -> 123 Added packages: + tree|2.1.1|... Upgraded packages: ~ curl: 8.5.0 -> 8.6.0 ``` Overlay builds are additive by default, so you will typically see additions and upgrades. Removals appear only when [`overlayPolicy.allowPackageRemoval`](../architecture/image-composer-tool-templates.md#overlaypolicy) is enabled (a conflict-driven baseline package removal). ### Delta SBOM: Optional Quick-Change View The delta SBOM lists only the overlay-contributed packages (what changed). It is an **optional** convenience for a quick "what did this overlay add?" view without diffing against the baseline — you can inspect it directly or diff it against an empty document: ```bash # The delta SBOM is a plain SPDX JSON file — read it directly jq '.packages[].name' myimage-1.0.delta.spdx.json # Or diff it against an empty SBOM to render the same added-package list via compare echo '{"packages":[]}' > empty.spdx.json ./image-composer-tool compare --mode=spdx empty.spdx.json myimage-1.0.delta.spdx.json ``` For an authoritative before/after picture of the image, prefer the **complete** SBOM comparison above; the delta is a shortcut, not a replacement. ## Validating a Template Check a template for errors before starting a build: ```bash ./image-composer-tool validate image-templates/azl3/azl3-x86_64-edge-raw.yml ``` ## Resolving a Template Print the merged template YAML to stdout — useful for debugging templates that use `extends:` or for previewing exactly what the tool will build. Sensitive fields (user passwords, `systemConfig.users[*].hash_algo`, the secure boot signing paths `systemConfig.immutability.secureBootDBKey`, `systemConfig.immutability.secureBootDBCrt`, and `systemConfig.immutability.secureBootDBCer`, and the DKMS secure boot signing paths `systemConfig.dkms.secureBoot.signingKeyPath`, `systemConfig.dkms.secureBoot.signingCertPath`, `systemConfig.dkms.secureBoot.targetKeyPath`, and `systemConfig.dkms.secureBoot.targetCertPath`) are always redacted in the output: ```bash # Chain-merge only, without OS defaults ./image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml # Full build-time view: extends chain + OS defaults ./image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full ``` ### Debugging an Extends Chain When a template uses `extends:` to inherit from a parent, `resolve` folds the chain and prints the effective template so you can verify what the leaf actually inherits before running a build: ```bash # Show what an extends child inherits from its parent ./image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml ``` The output includes the union of the parent's and child's `systemConfig.packages`, plus every other field folded through the chain's per-section merge rules. OS defaults are not applied unless you pass `--full`, so the output reflects only what the templates in the chain declared — useful for spotting an accidentally-overridden field. For the complete inheritance semantics (single-parent chains, cycle detection, target-match rules, and the depth-warning threshold), see [Template Extends (Inheritance)](../architecture/image-composer-tool-templates.md#template-extends-inheritance). ## Configuration The tool uses a layered configuration: config file values are overridden by command-line flags. A config file is auto-discovered from several standard locations (current directory, home directory, `/etc/`), or you can specify one explicitly with `--config`. ```bash # Create a default configuration file ./image-composer-tool config init # Show the active configuration ./image-composer-tool config show # Use a specific configuration file ./image-composer-tool --config /path/to/config.yaml build template.yml ``` Key settings: | Setting | Default (cloned repo) | Default (Debian pkg) | |---------|----------------------|----------------------| | `workers` | 8 | 8 | | `cache_dir` | `./cache` | `/var/cache/image-composer-tool` | | `work_dir` | `./workspace` | `/tmp/image-composer-tool` | For the complete search order and all configuration fields, see [Configuration Files](../architecture/image-composer-tool-cli-specification.md#configuration-files) in the CLI Specification. ## Operations Requiring Sudo The `build` command requires `sudo` because it performs system-level operations: creating loop devices, mounting filesystems, setting up chroot environments, installing packages, and configuring bootloaders. Always run builds with `sudo -E` to preserve your environment variables (such as `$PATH` and proxy settings). ## Running the Web UI `serve` starts the HTTP API behind the web UI and serves the frontend bundle embedded in the binary. Run it from the repository root — it resolves `--templates-dir`, `--work-dir`, and the configuration file relative to its working directory: ```bash # Start on http://127.0.0.1:8080 ./image-composer-tool serve # Pick a different port ./image-composer-tool serve --port 9090 ``` Builds still need root. Run the server as root on an isolated build host, or run it as an unprivileged user with `--sudo` plus the scoped sudoers rules that let it cancel builds and read root-owned artifacts. Generate and install those rules rather than writing them by hand: ```bash # Generate, visudo-validate, and install in one step scripts/install-sudoers.sh # Then run builds under sudo -n ./image-composer-tool serve --sudo ``` The server binds to localhost by default and has no authentication of its own. Because the API can start privileged builds, exposing it with `--host 0.0.0.0` should be a deliberate decision, not a convenience. The OS manifest, the repository catalog, and the Edge Pack catalog are embedded at build time. To edit any of them and see the change after a restart instead of a rebuild, point the matching flag at a file on disk: ```bash ./image-composer-tool serve \ --package-repos ./internal/api/service/data/package-repos.yaml \ --edge-pack ./internal/api/service/data/edge-pack.yaml ``` A malformed or missing override file stops the server at startup rather than falling back to the embedded copy. For every flag, the sudoers rules in full, and their security implications, see the [Serve Command](../architecture/image-composer-tool-cli-specification.md#serve-command) reference. For building the frontend bundle and the hot-reload development loop, see [`web/README.md`](../../../web/README.md). ### Running the Web UI With Docker Compose Instead of building the binary and installing sudoers rules, you can run the web UI in a container. The host needs only Docker with the Compose plugin: the ICT container image, which Docker builds from your checkout, carries the binary, the web UI, and every program a build runs. From the root of a checkout: ```bash docker compose up --build ``` Then open . The [Web UI Quick Start](./web-ui-quick-start.md) walks through this step by step, including where to find the OS images you build. > **Warning: the container is privileged, and therefore root-equivalent on the > host.** Building an OS image creates loop devices, mounts filesystems, and > chroots into them, so the container runs with `privileged: true` and the > host's `/dev` mounted. Every build runs as root with access to all of the > host's devices, and the web UI has no authentication of its own: anyone who > can reach the port can start a build. The port is therefore published on the > host's `127.0.0.1` only. Do not change that binding to expose the UI on a > network, and run the container only on a machine where you would trust > everyone who can reach the UI with root. What `docker-compose.yml` sets up: - **The container image.** The first `docker compose up --build` builds the ICT container image from `build/ict/Dockerfile`, which takes several minutes; later runs reuse Docker's cache. Keep passing `--build` after you pull new commits, so the image picks them up. - **Two named volumes.** Docker stores them outside your checkout and keeps them across `docker compose down` and `docker compose up`. `docker compose down -v` deletes both, and with them every build's history and images. | Volume | Mounted at | Holds | |--------|------------|-------| | `webui-workspace` | `/opt/ict/webui-workspace` | Every build the UI starts: its history entry, log, work directory, downloaded packages, and finished OS image | | `cache` | `/opt/ict/cache` | Downloaded packages for `image-composer-tool build` commands you run inside the container with `docker compose exec` | - **A clean stop.** `docker compose down`, or Ctrl+C, asks the server to stop. It cancels a running build and waits for the build to unmount its filesystems and detach its loop devices; the compose file allows 60 seconds for that. As with an interrupted build outside a container, a build killed without that cleanup, for example by `docker kill`, can leave mounts and loop devices behind on the host. - **Your proxy.** If your shell sets `http_proxy`, `https_proxy`, or `no_proxy` (or their uppercase forms), Compose passes them to the container image build and to the server, which passes them on to every build. The container also has the `image-composer-tool` CLI. Its working directory, `/opt/ict`, is laid out like a checkout of this repository, so paths such as `image-templates/` work as they do in your checkout: ```bash docker compose exec ict image-composer-tool validate image-templates/ubuntu24/ubuntu24-x86_64-minimal-raw.yml ``` To build OS images from the command line without the web UI, see [Building an Image in a Docker Container](#building-an-image-in-a-docker-container). To build the container image without Compose, run `docker build -f build/ict/Dockerfile --target ict -t ict .`, or `earthly +ict-image`, which also records the version and git commit in it (`--VERSION=` sets the version and the image tag, `--ICT_IMAGE_NAME=` the image name). `earthly +ict-image-preflight` also checks that every program ICT may run is present in the image. ## Shell Completion ```bash # Auto-detect shell and install completion ./image-composer-tool install-completion # Or specify a shell: bash, zsh, fish, powershell ./image-composer-tool install-completion --shell bash ``` After installing, reload your shell configuration (e.g., `source ~/.bashrc`). For per-shell activation steps and manual completion script generation, see the [Install-Completion Command](../architecture/image-composer-tool-cli-specification.md#install-completion-command) reference. ## Template Examples Templates are YAML files that define the requirements for an image build. For the full template system documentation, see [Creating and Reusing Image Templates](../architecture/image-composer-tool-templates.md). The `image-templates/` directory contains ready-to-use examples for all supported distributions and image types. ### Minimal Edge Device ```yaml image: name: minimal-edge version: "1.0.0" target: os: azure-linux dist: azl3 arch: x86_64 imageType: raw systemConfig: name: minimal description: Minimal edge device configuration packages: - openssh-server - ca-certificates kernel: version: "6.12" cmdline: "quiet" ``` ### Development Environment ```yaml image: name: dev-environment version: "1.0.0" target: os: azure-linux dist: azl3 arch: x86_64 imageType: raw systemConfig: name: development description: Development environment with tools packages: - openssh-server - git - docker-ce - vim - curl - wget - python3 kernel: version: "6.12" cmdline: "quiet splash" ``` ### Edge Microvisor Toolkit ```yaml image: name: emt-edge-device version: "1.0.0" target: os: edge-microvisor-toolkit dist: emt3 arch: x86_64 imageType: raw systemConfig: name: edge description: Edge Microvisor Toolkit configuration packages: - cloud-init - rsyslog kernel: version: "6.12" cmdline: "console=ttyS0,115200 console=tty0 loglevel=7" ``` --- ## Related Documentation - [AI-Powered Template Generation](./ai-template-generation.md) - [CLI Specification and Reference](../architecture/image-composer-tool-cli-specification.md) - [Image Templates](../architecture/image-composer-tool-templates.md) - [Build Process](../architecture/image-composer-tool-build-process.md) - [Installation Guide](./installation.md) - [Edge Microvisor Toolkit](https://docs.openedgeplatform.intel.com/2026.2/edge-microvisor-toolkit/index.html)