# ICT CLI Specification ## Table of Contents - [ICT CLI Specification](#image-composer-tool-cli-specification) - [Table of Contents](#table-of-contents) - [Overview](#overview) - [CLI Flow](#cli-flow) - [Usage](#usage) - [Global Options](#global-options) - [Commands](#commands) - [Build Command](#build-command) - [Validate Command](#validate-command) - [Resolve Command](#resolve-command) - [Inspect Command](#inspect-command) - [Compare Command](#compare-command) - [Cache Command](#cache-command) - [cache clean](#cache-clean) - [Config Command](#config-command) - [config init](#config-init) - [config show](#config-show) - [Version Command](#version-command) - [Install-Completion Command](#install-completion-command) - [Serve Command](#serve-command) - [Serving privileged builds](#serving-privileged-builds) - [Data overrides](#data-overrides) - [Examples](#examples) - [Building an Image](#building-an-image) - [Managing Configuration](#managing-configuration) - [Managing Cache](#managing-cache) - [Inspecting and Comparing Images](#inspecting-and-comparing-images) - [Validating Templates](#validating-templates) - [Resolving Templates](#resolving-templates) - [Configuration Files](#configuration-files) - [Global Configuration File](#global-configuration-file) - [Image Template File](#image-template-file) - [Exit Codes](#exit-codes) - [Troubleshooting](#troubleshooting) - [Common Issues](#common-issues) - [Logging](#logging) - [Related Documentation](#related-documentation) ## Overview `image-composer-tool` is a command-line tool for generating custom images for different operating systems, including Azure Linux, Wind River eLxr, RCD, and [Edge Microvisor Toolkit](https://docs.openedgeplatform.intel.com/2026.2/edge-microvisor-toolkit/index.html). The tool provides a flexible approach to creating and configuring production-ready OS images with precise customization. ICT uses a single CLI with subcommands to deliver a consistent user experience while maintaining flexibility. The tool's architecture is built around the following files: 1. A global configuration file that defines system-wide settings like cache locations and provider configurations 2. Image template files in YAML format that define per-image build requirements The tool follows a staged build process to support package caching, image caching, and various customization options that speed up development cycles and ensure reproducible builds. ## CLI Flow The following diagram illustrates the high-level flow of the ICT CLI, the commands of which begin with `image-composer-tool`: ```mermaid flowchart TD Start([ict]) --> Config[Load Configuration] Config --> Commands{Commands} Commands -->|build| Build[Build OS Image] Build --> ReadTemplate[Read YAML Template] ReadTemplate --> BuildProcess[Run Build Pipeline] BuildProcess --> SaveImage[Save Output Image] Commands -->|validate| Validate[Validate Template File] Commands -->|config| ConfigCmd[Manage Configuration] ConfigCmd --> ConfigOps[init/show] Commands -->|cache| Cache[Manage Cache] Cache --> CacheOps[Clean Cache] Commands -->|version| Version[Show Version Info] Commands -->|install-completion| Completion[Install Shell Completion] %% Styling classDef command fill:#b5e2fa,stroke:#0077b6,stroke-width:2px; classDef process fill:#f8edeb,stroke:#333,stroke-width:1px; class Start command; class Build,Validate,ConfigCmd,Cache,Version,Completion command; class ReadTemplate,BuildProcess,SaveImage,ConfigOps,CacheOps process; ``` The primary workflow is through the `build` command, which reads an image template file and runs the build pipeline to create a new image. See also: - [Build Stages](./image-composer-tool-build-process.md#build-stages-in-detail) for the stages of the build pipeline ## Usage ```bash image-composer-tool [global options] command [command options] [arguments...] ``` ## Global Options The ICT command-line utility uses a layered configuration approach, with command-line options taking priority over the configuration file settings: | Option | Description | | ------ | ----------- | | `--config FILE` | Global configuration file. This file contains system-wide settings that apply to all image builds. If not specified, the tool searches for configuration files in standard locations. | | `--log-level LEVEL` | Log level: debug, info, warn, error (overrides config). Use debug for troubleshooting build issues. | | `--log-file PATH` | Tee logs to a specific file path (overrides `logging.file` in the configuration). | | `--help, -h` | Show help for any command or subcommand. | | `--version` | Show `image-composer-tool` version information. | ## Commands ### Build Command Build an OS image from an image template file. This is the primary command for creating custom OS images according to your requirements. ```bash image-composer-tool build [flags] TEMPLATE_FILE ``` **Arguments:** - `TEMPLATE_FILE` - Path to the YAML image template file (required) **Flags:** | Flag | Description | | ---- | ----------- | | `--workers, -w INT` | Number of concurrent download workers (overrides config). | | `--cache-dir, -d DIR` | Package cache directory (overrides config). Proper caching significantly improves build times. | | `--work-dir DIR` | Working directory for builds (overrides config). This directory is where images are constructed before being finalized. | | `--verbose, -v` | Enable verbose output (equivalent to --log-level debug). Displays detailed information about each step of the build process. | | `--dotfile, -f FILE` | Generate a dot file for the merged template dependency graph (user + defaults with resolved packages). | | `--system-packages-only` | When paired with `--dotfile`, limit the dependency graph to roots defined in `SystemConfig.Packages`. Dependencies pulled in by those roots still appear, but essentials/kernel/bootloader packages aren't drawn unless required by a system package. | | `--no-cache` | Build from scratch: create fresh, unique cache and workspace directories (ignoring any existing caches), then remove them once the build finishes. The final image is copied into the configured `work_dir`. Cannot be combined with `--cache-dir` or `--work-dir`. | | `--inspect` | Run a post-build inspection of the emitted overlay image and write the report to an artifact file (default: **off**). The report covers the partition layout, filesystem, bootloader, and SBOM of the finished image. It is written next to the emitted image in the build artifacts directory as `-.inspect.txt` (the image artifact's base name with a `.inspect.txt` extension); the console shows only a one-line pointer to that file, not the report itself. When the flag is unset, no inspection runs, no file is written, and console output is unchanged. | | `--cve-check` | Enable CVE analysis of the built image. **Not yet implemented** — passing this flag currently returns an error. | | `--baseline-image FILE` | Override `baseline.source.path` from the template (overlay mode only). CLI value takes precedence over the template. | | `--disk-strategy STRATEGY` | Override `disk.selectionPolicy.strategy`: `first`, `largest`, or `fastest`. This also clears the template's `disk.path`, which would otherwise take precedence. Create mode only. | | `--hostname NAME` | Override `systemConfig.hostname`. Create mode only. | | `--http-proxy URL`, `--https-proxy URL`, `--ftp-proxy URL` | Override `systemConfig.proxy.httpProxy`, `.httpsProxy`, `.ftpProxy`: the proxy persisted into the deployed system. A flag given an empty value (for example `--http-proxy ''`) clears the template's setting. Create mode only. | | `--no-proxy LIST` | Override `systemConfig.proxy.noProxy` (comma-separated, no spaces); `--no-proxy ''` clears it. Create mode only. | | `--ssh-authorized-key USER=FILE` | Add the public keys in `FILE` to `USER`'s `authorized_keys`. `USER` must be defined in `systemConfig.users`. Repeatable. | The composition override flags change only the fields they name; an unset flag leaves the template untouched. Overridden values are validated exactly like template values. **Example:** ```bash # Build an image with default settings sudo -E image-composer-tool build my-image-template.yml # Build with custom workers and cache directory sudo -E image-composer-tool build --workers 16 --cache-dir /tmp/cache my-image-template.yml # Build with verbose output sudo -E image-composer-tool build --verbose my-image-template.yml # Build and generate dependency graphs sudo -E image-composer-tool build --dotfile deps.dot my-image-template.yml # Limit the graph to SystemConfig.Packages roots sudo -E image-composer-tool build --dotfile system.dot --system-packages-only my-image-template.yml # Overlay build with a post-build image inspection report written to # -.inspect.txt in the build artifacts directory sudo -E image-composer-tool build --inspect overlay-template.yml # Overlay build overriding the baseline image path sudo -E image-composer-tool build --baseline-image /images/base.raw overlay-template.yml # Unattended ISO for a site with a proxy, with an extra admin key sudo -E image-composer-tool build \ --disk-strategy fastest \ --http-proxy http://proxy.example.com:3128 --https-proxy http://proxy.example.com:3128 \ --no-proxy localhost,127.0.0.1,.example.com \ --ssh-authorized-key admin=$HOME/.ssh/id_ed25519.pub \ image-templates/ubuntu24/ubuntu24-x86_64-edgepack-unattended-iso.yml ``` **ISO build artifacts:** besides the `.iso`, an ISO build writes the SPDX SBOM of the system the ISO installs (`spdx_manifest_*.json`), a composition manifest (`-.composition.json`) listing the base OS, repositories, packages with versions, kernel, and every additional file with its SHA-256, and the merged template (`template-dump.yaml`), all in the build output directory. **Note:** The build command typically requires sudo privileges for operations like creating loopback devices and mounting filesystems. **Baseline image formats (overlay mode):** `baseline.source.format` accepts `raw` (default), `qcow2`, `vhd`, and `vhdx`. Non-RAW baselines are converted to RAW with `qemu-img` before mounting, so `qemu-img` must be installed on the host when a non-RAW format is used (the build fails clearly if it is missing). The declared format is verified against the image's actual format; a mismatch aborts the build. The user-supplied baseline is never modified — conversion writes into the build workspace. **Cancellation semantics:** Sending `SIGINT` (Ctrl+C) or `SIGTERM` to a running build triggers cooperative cleanup before the tool exits: 1. The signal cancels an ambient `context.Context` that is bound to every subsequent shell subprocess. Because those subprocesses are spawned into their own process group, a single `SIGTERM` to the negative process-group id reaps `bash`, `sudo`, and the real tool (`mmdebstrap`, `apt`, `mksquashfs`, `losetup`, `mkfs.*`, `xorriso`, `dracut`, `ukify`, `sbsign`, `qemu-img`, …) together. Every ICT-spawned tool terminates on `SIGTERM`, so the group exits cooperatively. If the leader (`bash` acting as the group's `cmd.Process`) has not exited within 5 s, the Go runtime's `WaitDelay` escalation sends `SIGKILL` to that leader specifically — `WaitDelay` does not broadcast `SIGKILL` to the whole process group. The group is expected to be gone by then via the earlier `SIGTERM`; the leader-level `SIGKILL` is a backstop for a wedged pipe on the leader itself, not a group-wide kill. 2. Pure-Go HTTP work (DEB/RPM package downloads and repository metadata fetches) observes the same ambient context: `http.Client.Do` calls carry the ctx via `http.NewRequestWithContext`, in-flight requests cancel as soon as it fires, and the retry backoff aborts within one delay quantum instead of running to completion. This covers `pkgfetcher.FetchPackages`, `rpmutils.fetchURLWithRetry`, and `debutils.checkFileExists`. 3. Registered teardowns run in reverse acquisition order (loop-device detach first, then chroot unmount + gpg-agent stop) under a fresh 30 s per-entry budget so cleanup itself is not aborted by the signal that fired. Each cleanup callback rebinds the ambient shell context to the per-entry budget so its own shell calls (unmount, `losetup -d`, `swapoff`) run under the cleanup deadline rather than the already-cancelled parent. 4. `PostProcess` (which is itself part of the build's cleanup) runs under a detached 2-minute context so an already-cancelled parent does not defeat the umount escalations inside it. 5. Any resource that could not be reaped (unmount stuck, `losetup -d` refused because a partition is still busy) is reported at ERROR level with the label and error text so the operator can `mount | grep ` and `losetup -l` to reclaim it. 6. Exit code is `130` (`128 + SIGINT`) **only for user-initiated cancellation** (a signal). Internal timeouts — such as the 2-minute PostProcess cleanup budget being exceeded — surface as exit `1` so scripts can distinguish "user aborted" from "internal cleanup timed out". A **second** signal during cleanup skips the remaining teardown and exits with `130` immediately — use this if a residual umount is hanging. See also: - [Build Stages in Detail](./image-composer-tool-build-process.md#build-stages-in-detail) for information about each build stage - [Build Performance Optimization](./image-composer-tool-build-process.md#build-performance-optimization) for tips to improve build speed ### Validate Command Validate an image template file without building it. This allows checking for errors in your template before committing to a full build process. ```bash image-composer-tool validate TEMPLATE_FILE ``` **Arguments:** - `TEMPLATE_FILE` - Path to the YAML image template file to validate (required) **Description:** The validate command performs the following checks: - YAML syntax validation - Schema validation against the image template JSON schema - Required fields verification - Type checking for all fields **Example:** ```bash # Validate a template file image-composer-tool validate my-image-template.yml # Validate with verbose output image-composer-tool --log-level debug validate my-image-template.yml ``` See also: - [Template Loading and Validation](./image-composer-tool-build-process.md#1-template-loading-and-validation) for details on the validation process ### Resolve Command Resolve a template and print the merged YAML to stdout for debugging and traceability. Resolve does not build anything and never writes to disk; the merged output is computed on every invocation and is not cached. ```bash image-composer-tool resolve [flags] TEMPLATE_FILE ``` **Arguments:** | Argument | Description | |---|---| | `TEMPLATE_FILE` | Path to the image template YAML file (required, positional) | **Flags:** | Flag | Description | |---|---| | `--full` | Include OS defaults in the output, showing exactly what will be built | **Description:** By default, the resolve command walks the template's `extends:` chain (leaf towards root) and prints the chain-merged YAML **without** OS defaults. If the template does not use `extends:`, the command prints `No extends used in template, nothing to resolve` and exits successfully. When `--full` is passed, resolve additionally folds the extends chain on top of the OS default configuration — OS defaults are the base layer and the extends chain overrides them (leaf wins). This is the same merge the `build` command runs, so the output shows exactly what the tool would build. `--full` suppresses the "nothing to resolve" short-circuit, so it also works for templates that do not use `extends:`. Sensitive fields are always redacted in the output: - `systemConfig.users[*].password` - `systemConfig.users[*].hash_algo` - `systemConfig.immutability.secureBootDBKey` - `systemConfig.immutability.secureBootDBCrt` - `systemConfig.immutability.secureBootDBCer` - `systemConfig.dkms.secureBoot.signingKeyPath` - `systemConfig.dkms.secureBoot.signingCertPath` - `systemConfig.dkms.secureBoot.targetKeyPath` - `systemConfig.dkms.secureBoot.targetCertPath` The output is safe to paste into an issue or a code review. **Example:** ```bash # Show the extends-chain-merged YAML for a template that inherits from a parent image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml # Show the full build-time template (extends chain + OS defaults) image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full # Pipe the merged template into a file for offline review image-composer-tool resolve my-template.yml --full > merged.yml ``` See also: - [Validate Command](#validate-command) — checks template validity without emitting YAML. ### Inspect Command Inspects a raw image and outputs comprehensive details about the image including partition table layout, partition identity and attributes, filesystem information, bootloader details, and layout diagnostics. ```bash image-composer-tool inspect [flags] IMAGE_FILE ``` **Arguments:** - `IMAGE_FILE` - Path to the RAW image file to inspect (required) **Flags:** | Flag | Description | | ---- | ----------- | | `--format STRING` | Output format: `text`, `json`, or `yaml` (default: `text`) | | `--pretty` | Pretty-print JSON output (only for `--format=json`; default: `false`) | | `--extract-sbom FILE` | Extracts SBOM and saves the output in FILE, default filename is used if FILE is not specified | **Description:** The inspect command extracts and analyzes the following from a disk image: **Partition Table:** - Type (GPT/MBR) and sector sizes - For GPT: disk GUID and protective MBR status - Layout diagnostics: largest unallocated free span and misaligned partitions (detected against physical sector size and 1 MiB alignment) **Partitions:** - Index, name, type/GUID, start/end LBA, and size - For GPT: partition GUID and decoded attribute bits (required, legacy BIOS bootable, read-only) - Filesystem type, label, and UUID - EFI/UKI evidence (if present on ESP/VFAT partitions) **Bootloader & Secure Boot:** - EFI binaries: kind, architecture, signature status, SBAT - UKI payloads: kernel/initrd/OS-release hashes and metadata **Output Formats:** - `text`: Human-readable summary with tables and structured sections - `json`: Complete structured data suitable for automation and comparison - `yaml`: YAML representation of the image summary **Example:** ```bash # Inspect a raw image and output text (default) image-composer-tool inspect my-image.raw # Inspect and output pretty JSON image-composer-tool inspect --format=json --pretty my-image.raw # Inspect and output YAML image-composer-tool inspect --format=yaml my-image.raw # Inspect and extract SPDX data from an IMAGE image-composer-tool inspect my-image.raw --extract-sbom my-sbom.json ``` ### Compare Command Compares two disk images and outputs detailed differences in partition layout, filesystems, bootloaders, and EFI/UKI payloads. ```bash image-composer-tool compare [flags] IMAGE_FILE1 IMAGE_FILE2 ``` **Arguments:** - `IMAGE_FILE1` - Path to the first RAW image file (required) - `IMAGE_FILE2` - Path to the second RAW image file (required) - `SPDX_FILE1` - Path to the first SPDX JSON file (required if `--mode=spdx`) - `SPDX_FILE2` - Path to the second SPDX JSON file (required if `--mode=spdx`) **Flags:** | Flag | Description | | ---- | ----------- | | `--format STRING` | Output format: `text` or `json` (default: `text`) | | `--mode STRING` | Compare mode: `diff` (partition/FS changes), `summary` (high-level counts), `full` (complete image metadata) or `spdx` (compare SBOM differences). Default: `diff` for text, `full` for JSON | | `--pretty` | Pretty-print JSON output (only for `--format=json`; default: `false`) | | `--hash-images` | Perform image hashing for verifying binary identical image (default `false`) | **Description:** The compare command performs a deep structural comparison of two images and reports: **Partition Table Changes:** - Disk GUID changes (GPT) - Partition table type or sector size changes - Free space layout changes (largest unallocated extent) - Misaligned partition detection changes **Partition Changes:** - Added/removed partitions (detected by GUID for GPT, by LBA range for MBR) - Modified partitions: changes to name, GUID, LBA range, size, or GPT attribute bits - Filesystem changes: type, label, UUID modifications - Per-partition EFI binary changes (path, kind, architecture, signature status) **Global EFI/UKI Changes:** - Added/removed EFI binaries across all partitions - Modified EFI binaries: SHA256, signature status, bootloader kind - UKI payload changes: kernel, initrd, OS-release, and section SHA256s **Compare Modes:** - `diff`: Detailed changes (partitions, filesystems, EFI binaries) - `summary`: High-level counts (added, removed, modified counts) - `full`: Complete image metadata plus all diffs - `spdx`: Compares two SPDX JSON files **Output:** - Text format provides human-readable sections with tables and field-by-field diffs - JSON format includes complete structured data for scripting and tooling - Exit code is 0 if images are equal, 1 if differences found **Example:** ```bash # Compare two images and show detailed text diff image-composer-tool compare image-v1.raw image-v2.raw # Show only a summary of changes image-composer-tool compare --mode=summary image-v1.raw image-v2.raw # Compare and output pretty JSON with full metadata image-composer-tool compare --format=json --mode=full --pretty image-v1.raw image-v2.raw # Compact JSON diff suitable for CI/CD automation image-composer-tool compare --format=json --mode=diff image-v1.raw image-v2.raw # Perform comparison with image hashing enabled with details text diff image-composer-tool compare --hash-images=true image-v1.raw image-v2.raw # Perform SPDX comparison image-composer-tool compare --format=json --mode=spdx spdx-file1.json spdx-file2.json ``` **Comparing overlay build outputs:** The compare command works directly against [overlay build](image-composer-tool-templates.md#baseline) artifacts — the baseline RAW, the overlay RAW, and the emitted `*.complete.spdx.json` / `*.delta.spdx.json` SBOM sidecars. Compare the baseline RAW against the overlay RAW for a structural/binary diff, and use `--mode=spdx` with the complete SBOM for an accurate package-level (added/removed/upgraded) diff. For a full walkthrough with sample commands and expected output, see [Comparing Overlay Outputs](../get-started/usage-guide.md#comparing-overlay-outputs) in the Usage Guide. In `--mode=spdx`, either argument may be a standalone SPDX JSON file or an OS image — an image's embedded SBOM (at `/usr/share/sbom`) is extracted automatically. In the default image-compare modes (`diff`/`summary`/`full`) the SBOM diff reflects metadata only (package count, canonical hash); use `--mode=spdx` for a per-package breakdown. ### Cache Command Manage cached artifacts created during the build process. ```bash image-composer-tool cache SUBCOMMAND ``` #### cache clean Remove cached packages or workspace chroot data. ```bash image-composer-tool cache clean [flags] ``` **Flags:** | Flag | Description | | ---- | ----------- | | `--packages` | Remove cached packages (default when no scope flags are provided). | | `--workspace` | Remove cached chroot environments and chroot tarballs under the workspace directory. | | `--all` | Enable both package and workspace cleanup in a single invocation. | | `--provider-id STRING` | Restrict cleanup to a specific provider (format: `os-dist-arch`). | | `--dry-run` | Show what would be removed without deleting anything. | **Examples:** ```bash # Remove all cached packages image-composer-tool cache clean # Remove chroot caches for a single provider image-composer-tool cache clean --workspace --provider-id azure-linux-azl3-x86_64 # Preview everything that would be deleted image-composer-tool cache clean --all --dry-run ``` When no scope flag is supplied, the command defaults to `--packages`. ### Config Command Manage the global configuration file. The config command provides subcommands for initializing and viewing configuration. ```bash image-composer-tool config SUBCOMMAND ``` **Subcommands:** #### config init Initialize a new configuration file with default values. ```bash image-composer-tool config init [CONFIG_FILE] ``` **Arguments:** - `CONFIG_FILE` - Path where the configuration file should be created (optional). If not specified, creates the configuration in a standard location. **Example:** ```bash # Initialize configuration in current directory image-composer-tool config init image-composer-tool.yml # Initialize in default location image-composer-tool config init ``` #### config show Show the current configuration settings. ```bash image-composer-tool config show ``` **Example:** ```bash # Show current configuration image-composer-tool config show # Show configuration from specific file image-composer-tool --config /path/to/config.yml config show ``` ### Version Command Display the tool's version information, including build date, Git commit SHA, and organization. ```bash image-composer-tool version ``` **Example:** ```bash image-composer-tool version ``` **Output includes:** - Version number - Build date - Git commit SHA - Organization ### Install-Completion Command Install shell completion for the image-composer-tool command. Supports bash, zsh, fish, and PowerShell. ```bash image-composer-tool install-completion [flags] ``` **Flags:** | Flag | Description | | ---- | ----------- | | `--shell STRING` | Shell type (bash, zsh, fish, powershell). If not specified, auto-detects current shell. | | `--force` | Force overwrite existing completion files. | **Example:** ```bash # Auto-detect shell and install completion image-composer-tool install-completion # Install completion for specific shell image-composer-tool install-completion --shell bash # Force reinstall image-composer-tool install-completion --force ``` **Post-Installation Steps:** After installing completion, you need to reload your shell configuration: **Bash:** ```bash echo "source ~/.bash_completion.d/ict.bash" >> ~/.bashrc source ~/.bashrc ``` **Zsh:** ```zsh echo 'fpath=(~/.zsh/completion $fpath)' >> ~/.zshrc source ~/.zshrc ``` **Fish:** Fish automatically loads completions from the standard location. Just restart your terminal. **PowerShell:** ```powershell Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser . $PROFILE ``` ### Serve Command Run the HTTP API that backs the ICT web UI, together with the frontend bundle embedded in the binary. The server reads the configuration manifest, resolves pre-authored templates, and triggers image builds by invoking the `image-composer-tool` binary, streaming the build logs back to the browser. ```bash image-composer-tool serve [flags] ``` **Flags:** | Flag | Description | | ---- | ----------- | | `--host STRING` | Address to bind. Defaults to `127.0.0.1` (localhost only). Set `0.0.0.0` to expose the API on all interfaces — not recommended, because this API can trigger privileged builds. | | `--port, -p STRING` | Port to listen on. Default `8080`. | | `--templates-dir STRING` | Directory of pre-authored templates offered by the Basic tab. Default `image-templates`. | | `--ict-binary STRING` | Path to the `image-composer-tool` binary used for builds. When empty, auto-detects `./build/image-composer-tool`, then `./image-composer-tool`, then `$PATH`. | | `--work-dir STRING` | Base directory for per-build work and output directories. Default `webui-workspace`. | | `--sudo` | Run builds under `sudo -n`. ICT requires root for chroot and mount operations. Requires the scoped sudoers rules described below. | | `--print-sudoers` | Print the scoped sudoers drop-in required by `--sudo`, then exit without starting the server. | | `--manifest STRING` | Path to a manifest YAML to read from disk instead of the copy embedded at build time. | | `--package-repos STRING` | Path to a package-repository catalog YAML to read from disk instead of the copy embedded at build time. Backs the Advanced tab's repository picker. | | `--edge-pack STRING` | Path to an Edge Pack catalog YAML to read from disk instead of the copy embedded at build time. Backs the Advanced tab's Edge Pack picker. | **Description:** `serve` resolves `--templates-dir`, `--work-dir`, and the global configuration file relative to its working directory. Start it from the repository root; started from elsewhere, template selections fail with `matched template file not found on disk`. The server binds to localhost by default. The API can start privileged builds, so treat exposing it on a routable address as a deliberate decision rather than a convenience — it has no authentication of its own. The repository's `docker-compose.yml` runs `image-composer-tool serve --host 0.0.0.0` in a privileged container built from `build/ict/Dockerfile`. Inside the container, listening on every interface is what lets Docker's port forwarding reach the server; the compose file publishes the port on the host's `127.0.0.1` only, so the UI stays reachable from the host alone. Builds run as the container's root user, so `--sudo` is not used. See [Running the Web UI With Docker Compose](../get-started/usage-guide.md#running-the-web-ui-with-docker-compose). #### Serving privileged builds Builds need root for chroot and mount operations. Either run the server as root on an isolated build host, in which case no extra setup is needed, or run it as an unprivileged service user with `--sudo` and three scoped, passwordless sudoers rules — build, cancel, and read: ```text ALL=(root) NOPASSWD: /path/to/image-composer-tool build * ALL=(root) NOPASSWD: /usr/bin/kill -TERM -[0-9]* ALL=(root) NOPASSWD: /usr/bin/cat /path/to/workspace/builds/* ``` The `kill` rule lets the non-root server cancel a build by signalling the root-owned build process group; the `cat` rule lets it stream root-owned build artifacts back to the browser. Without them, cancellation and downloads fail across the `sudo` boundary. Do not grant the service blanket `sudo`. Do not hand-write the rules. `--print-sudoers` generates them for the current user and the resolved `--ict-binary` and `--work-dir`, resolving `kill` and `cat` against sudo's own `secure_path` — the same lookup sudo performs at runtime — so a locally installed `/usr/local/bin/kill` shadowing the distribution one cannot silently turn a hand-written rule into `sudo: a password is required`: ```bash # Generate and install the drop-in image-composer-tool serve --print-sudoers | sudo tee /etc/sudoers.d/image-composer-tool-webui # Or generate, visudo-validate, and install in one step scripts/install-sudoers.sh ``` **Security:** the build process group id is not known ahead of time, so the `kill` rule grants the service user root `SIGTERM` — only `TERM` — to *any* process group. Accept this deliberately, or run the server as root on an isolated build host instead. #### Data overrides The manifest, the package-repository catalog, and the Edge Pack catalog are embedded in the binary at build time. Each has a flag that reads the file from disk instead, so the data can be edited and picked up by restarting `serve` rather than rebuilding the binary: | Flag | Overrides | Drives | | ---- | --------- | ------ | | `--manifest` | the embedded configuration manifest | OS targets, image types, and architectures offered by the UI | | `--package-repos` | `internal/api/service/data/package-repos.yaml` | the Advanced tab's repository picker and cross-repository package search | | `--edge-pack` | `internal/api/service/data/edge-pack.yaml` | the Advanced tab's Edge Pack picker, which groups a subset of the catalogued packages into capability domains | An override path that does not exist, or that fails validation, is reported at startup and the server does not start — a malformed catalog is not silently replaced by the embedded one. **Example:** ```bash # Serve the web UI on localhost:8080, running builds as the current user image-composer-tool serve # Run builds under sudo -n, with the scoped rules installed image-composer-tool serve --sudo # Listen on a different port, with templates from a non-default directory image-composer-tool serve --port 9090 --templates-dir ./my-templates # Iterate on the package catalogs without rebuilding the binary image-composer-tool serve \ --package-repos ./internal/api/service/data/package-repos.yaml \ --edge-pack ./internal/api/service/data/edge-pack.yaml # Print the sudoers drop-in for a non-default binary and workspace, then exit image-composer-tool serve --print-sudoers \ --ict-binary /usr/local/bin/image-composer-tool \ --work-dir /var/lib/image-composer-tool/webui ``` See also: - [`web/README.md`](../../../web/README.md) — building the frontend bundle, embedding it, and the hot-reload development loop. ## Examples ### Building an Image ```bash # Build an image with default settings sudo -E image-composer-tool build image-templates/azl3/azl3-x86_64-edge-raw.yml # Build with custom configuration sudo -E image-composer-tool --config=/path/to/config.yaml build image-templates/azl3/azl3-x86_64-edge-raw.yml # Build with custom workers and cache sudo -E image-composer-tool build --workers 12 --cache-dir ./package-cache image-templates/azl3/azl3-x86_64-edge-raw.yml # Build with debug logging sudo -E image-composer-tool --log-level debug build image-templates/azl3/azl3-x86_64-edge-raw.yml ``` ### Managing Configuration ```bash # Initialize a new configuration file image-composer-tool config init my-config.yml # Show current configuration image-composer-tool config show # Use a specific configuration file image-composer-tool --config /etc/image-composer-tool/config.yml build template.yml ``` ### Managing Cache ```bash # Remove all cached packages image-composer-tool cache clean # Remove workspace chroot caches for a specific provider image-composer-tool cache clean --workspace --provider-id azure-linux-azl3-x86_64 # Preview both package and workspace cleanup without deleting files image-composer-tool cache clean --all --dry-run ``` ### Inspecting and Comparing Images ```bash # Inspect an image in text format image-composer-tool inspect my-image.raw # Inspect and output JSON (suitable for tooling/CI) image-composer-tool inspect --format=json --pretty my-image.raw # Compare two images with detailed diff image-composer-tool compare image-v1.raw image-v2.raw # Compare with JSON output for parsing image-composer-tool compare --format=json --mode=diff image-v1.raw image-v2.raw ``` ### Validating Templates ```bash # Validate a template image-composer-tool validate image-templates/azl3/azl3-x86_64-edge-raw.yml # Validate with debug output image-composer-tool --log-level debug validate image-templates/azl3/azl3-x86_64-edge-raw.yml ``` ### Resolving Templates ```bash # Print the extends-chain-merged YAML (without OS defaults) for a template # that inherits from a parent image-composer-tool resolve image-templates/ubuntu24/ubuntu24-x86_64-extends-example-raw.yml # Print the fully merged, build-ready YAML including OS defaults image-composer-tool resolve image-templates/azl3/azl3-x86_64-edge-raw.yml --full # Save the merged template to a file for offline review image-composer-tool resolve my-template.yml --full > merged.yml ``` ## Configuration Files ### Global Configuration File The global configuration file (YAML format) defines system-wide settings that apply to all image builds. The tool searches for configuration files in the following locations (in order): 1. Path specified with `--config` flag 2. `image-composer-tool.yml` in current directory 3. `.image-composer-tool.yml` in current directory 4. `image-composer-tool.yaml` in current directory 5. `.image-composer-tool.yaml` in current directory 6. `~/.image-composer-tool/config.yml` 7. `~/.ict/config.yaml` 8. `~/.config/image-composer-tool/config.yml` 9. `~/.config/image-composer-tool/config.yaml` 10. `/etc/image-composer-tool/config.yml` 11. `/etc/image-composer-tool/config.yaml` **Example Configuration:** ```yaml # Number of concurrent workers for package downloads workers: 8 # Directory for caching downloaded packages cache_dir: "./cache" # Working directory for build process work_dir: "./workspace" # Configuration files directory config_dir: "./config" # Temporary directory temp_dir: "/tmp" # Logging configuration logging: level: "info" # debug, info, warn, error ``` **Configuration Fields:** | Field | Type | Description | | ----- | ---- | ----------- | | `workers` | integer | Number of concurrent download workers (1-100). Default: 8 | | `cache_dir` | string | Directory for package cache. Default: "./cache" | | `work_dir` | string | Working directory for builds. Default: "./workspace" | | `config_dir` | string | Directory for configuration files. Default: "./config" | | `temp_dir` | string | Temporary directory. Default: system temp directory | | `logging.level` | string | Log level (debug/info/warn/error). Default: "info" | ### Image Template File The image template file (YAML) defines everything that goes into a custom OS image. A minimal template requires only `image` and `target`; the remaining sections are merged from the OS-specific default template at build time. **Minimal example** (only the two required sections): ```yaml image: name: edge-device-image version: "1.2.0" target: os: azure-linux dist: azl3 arch: x86_64 imageType: raw ``` **Top-level sections:** | Section | Required | Description | |---------|----------|-------------| | `metadata` | No | AI-searchable discovery metadata (ignored by build engine) | | `image` | **Yes** | Image name and version | | `target` | **Yes** | OS, distribution, architecture, image type | | `disk` | No | Disk size, partitions, output artifact formats | | `packageRepositories` | No | Additional package repositories | | `systemConfig` | No (user) / **Yes** (merged) | Packages, kernel, users, bootloader, immutability, build commands | For the complete field-by-field reference including all nested fields, valid values, validation rules, and merge behavior, see the [Image Template Reference](./image-composer-tool-templates.md). ## Exit Codes The tool provides consistent exit codes that can be used in scripting and automation: | Code | Description | | ---- | ----------- | | 0 | Success: The command completed successfully. | | 1 | General error: An unspecified error occurred during execution. | | 130 | Cancelled by signal: A `SIGINT` (Ctrl+C) or `SIGTERM` was received while a build was in progress. Cooperative cleanup (chroot unmount, loop-device detach, child-process reaping, aborting in-flight package downloads) ran before exit. This code is reserved for user-initiated cancellation; internal timeouts surface as `1`. See the [Cancellation semantics](#build-command) subsection of the Build Command for details. | ## Troubleshooting ### Common Issues 1. **Disk Space**: Building images requires significant temporary disk space. ```bash # Check free space in workspace directory df -h ./workspace # Check free space in cache directory df -h ./cache ``` 2. **Permissions**: The build command requires sudo privileges. ```bash # Run with sudo and preserve environment sudo -E image-composer-tool build template.yml ``` 3. **Configuration Issues**: Verify configuration is valid. ```bash # Show current configuration image-composer-tool config show # Initialize with defaults image-composer-tool config init ``` 4. **Template Validation Errors**: Validate templates before building. ```bash # Validate template image-composer-tool validate template.yml ``` ### Logging Use the `--log-level` flag or `--verbose` flag to get more detailed output: ```bash # Debug logging image-composer-tool --log-level debug build template.yml # Verbose output (same as debug) image-composer-tool build --verbose template.yml # Error logging only image-composer-tool --log-level error build template.yml ``` ## Related Documentation - [Build Process](./image-composer-tool-build-process.md) - Detailed information about the build stages - [Templates](./image-composer-tool-templates.md) - Template structure and usage - [Caching](./image-composer-tool-caching.md) - How caching works - [Coding Style](./image-composer-tool-coding-style.md) - Development guidelines