# Image Configuration Reference

> Complete reference for properties.yml configuration and image settings

---

LLMS index: [llms.txt](/llms.txt) | Full content: [llms-full.txt](/llms-full.txt)

---

Complete reference for configuring container images via `properties.yml`.

## properties.yml Overview

Each image can have custom settings via a `properties.yml` file. This reference documents all
available configuration options.

**Minimal example:**

```yaml
---
rpm_packages:
  all:
    - nginx
main_package: nginx
tags:
  - value: latest
```

**Complete structure:**

```yaml
---
# Distro configuration (see Image Variants section)
# distros: [hummingbird]  # Override default distros (use when package unavailable in some distros)

# Image variants (see Image Variants section)
# Use additional_variants to extend defaults, or variants to override completely
additional_variants: [fpm, fpm-builder]  # Adds to default variants
# variants: [default, builder]  # Alternative: completely replaces defaults

# Container runtime configuration (see Container Configuration section)
user: default  # Optional: 'default' for default_user (65532), or literal user ID/name

# Package management (see Package Management section)
rpm_packages:
  build-deps: [...]
  all:
    - package-name                   # All architectures
    - name: arch-specific-package    # Arch-specific (see Package Management)
      arches:
        only: x86_64
  <distro>: [...]
  <variant>: [...]
additional_repos: [...]
allow_fedora_repos: true  # Optional: allow Fedora repos for hummingbird images (temporary workaround)

# Build configuration (see Build Configuration section)
hermetic: true
# Exceptional legacy source builds only; do not use for new images:
# build_from_source: true
# prefetch_gomod_path: upstream-submodule

# Versioning (see Versioning section)
main_package: package-name
# version_package:            # Optional: override package for version lookup per variant/distro
#   fips: package-fips-name   # Example: variant-specific package name

# Testing configuration (see Testing Configuration section)
reverse_dependency_tests: false  # Optional: disable for images like curl

# Release configuration (see Release Configuration section)
repository: image-name           # Required: Quay.io repository name
# <reasoning for stream choice>
stream: "latest"                 # Required: version series identity for the image catalog
application_category: "Storage"  # Required: Pyxis application category
# support_level: community        # Optional: community or experimental (default: Red Hat supported)
# deprecated: true                # Optional: mark the image stream as deprecated

# Image metadata (see Image Metadata section)
summary: "Short one-liner description"   # Required when adding labels
description: >-                          # Required when adding labels
  Short paragraph describing the image.
  1-2 sentences about key features.
url: "https://upstream-project.org"      # Required when adding labels

tags:
  - value: latest
  - value: '{{ package_major_version("package-name") }}'
    label: io.hummingbird-project.major-version

# Compliance scanning (enabled by default, see Compliance Scanning section)
# oscap:
#   enabled: false         # Optional: set to false to opt out
#   profiles:              # Optional: override which profiles run per variant
#     cis: true
#     stig:
#       variants: ["*fips*"]
#   exclude_rules:         # Optional: exclude specific rules (concatenated with globals)
#     - id: xccdf_org.ssgproject.content_rule_example
#       reason: "Reason for exclusion"
#       variants: ["*fips*"]   # Optional: glob patterns
#       profiles: [cis]        # Optional: limit to specific profiles
```

## Image Metadata

Images can declare metadata fields for container image labels:

### summary

- **Type**: String
- **Required**: When adding image labels (currently caddy only, expanding to all)
- **Length**: ~40-80 characters (one-liner)

Short phrase describing what the image is. Used in table/list views and catalog cards.

Do not start with the image name -- the name is always visible from context
(UI headings, `name` label, inspect output).

**Example**: `"Web server with automatic HTTPS"`

### description

- **Type**: String (use `>-` YAML scalar for multi-line readability)
- **Required**: When adding image labels
- **Length**: ~100-250 characters (1-2 sentences)

Short paragraph for card views, `podman inspect`, and Kubernetes UIs
(`io.k8s.description`). Describes what the software is and its key
distinguishing features.

Use `>-` (folded scalar) in YAML to keep the
value readable across multiple lines -- it folds to a single-line string. Do not
use `|` (literal block) as embedded newlines break label syntax.

Avoid embedded double quotes and backslashes -- values are emitted as-is in
Containerfile LABEL instructions with no escaping.

The same `description` value is used when generating the image README: the
`readme_description()` macro in `README.md.j2` templates renders it as the
introductory paragraph. Do not duplicate this text in the template; use
`{{ readme_description() }}`.

**Example**:

```yaml
description: >-
  Extensible server platform with automatic HTTPS by default.
  Provides HTTP/3, reverse proxying, load balancing, and static
  file serving with minimal configuration.
```

### url

- **Type**: String (URL)
- **Required**: When adding image labels

URL for the upstream project homepage. Used in
`org.opencontainers.image.url` and the Conforma `url` label.

**Example**: `"https://caddyserver.com"`

### deprecated

- **Type**: Boolean
- **Default**: Omitted, which means non-deprecated
- **Description**: Marks the image stream as deprecated. The generated image carries
  the `io.hummingbird-project.deprecated: true` label so the image catalog can mark the
  stream as deprecated after the final release.
- **Example**: `deprecated: true`

The field applies to the stream represented by the image directory. When all streams
sharing a `repository` are deprecated, the catalog can mark the repository as deprecated.
When only some streams are deprecated, the repository remains visible as a mixed-status
repository.

## Image Variants

Images can have multiple variants (e.g., `default` and `builder`):

- **default**: Minimal runtime environment
- **builder**: Includes development tools and build dependencies
- **custom**: Define custom variants in `properties.yml`

Each variant gets its own:

- Containerfile in `images/<name>/<variant>/Containerfile`
- RPM lockfiles in `images/<name>/<variant>/rpms`
- Build pipeline and release tags (non-default variants get `-<variant>` suffix)

### variants

- **Type**: Array of strings
- **Default**: `[default, builder]`
- **Description**: List of image variants to generate. Completely overrides the default variants.
  Each variant gets its own Containerfile, lockfiles, build pipeline, and release tags. Non-default
  variants get a `-<variant>` suffix in their image tags.
- **Example**: `[default]` (CI images that only need the default variant)
- **Note**: Prefer `additional_variants` when extending defaults; use `variants` only when you need
  to exclude default variants.

### additional_variants

- **Type**: Array of strings or objects
- **Default**: None
- **Description**: Additional variants to add to the base variants. The base is `variants` if
  specified, otherwise the default variants. Use this when you want to extend without repeating.

**Simple format (string array):**

```yaml
additional_variants: [fpm, fpm-builder]  # Adds FPM variants to base variants for all distros
```

**Object format with distro restrictions:**

Each variant can be an object with `name` and optional `distros` fields to restrict which distros
the variant is built for:

```yaml
additional_variants:
  - name: fips
    distros: [hummingbird]  # Only build fips variant for hummingbird distro
  - name: fpm               # Simple string still works in array
  - name: fpm-builder
    distros: [hummingbird, rawhide]  # Build for specific distros
```

The `distros` field supports glob patterns:

```yaml
additional_variants:
  - name: fips
    distros: ["*bird"]      # Matches hummingbird
  - name: special
    distros: ["raw*"]       # Matches rawhide
```

**When to use distro restrictions:**

Use distro restrictions when a variant requires packages or features only available in specific
distros. For example, FIPS variants may only be available for Hummingbird where FIPS-validated
packages are present

### variant_descriptions

- **Type**: Object (string-to-string mapping)
- **Required**: When image has `description` and defines `additional_variants` with non-default
  base specializations
- **Description**: Maps base variant names to human-readable descriptions. These descriptions are
  emitted as the `io.hummingbird-project.variant.description` label on each container image. The
  global description for `default` is defined in `images/variables.yml`; image-specific entries
  add descriptions for bases like `fpm`, `runtime`, or `openssl`. Modifier display (builder, fips)
  is handled by catalog consumers using the boolean variant labels, not by these descriptions.

**Writing guidelines:**

- Describe the variant **holistically** — what the image variant *is*, not just what packages it
  adds on top of `default`. Someone reading the description with no surrounding context (e.g., via
  `podman inspect`) should understand the variant's identity.
- Use a **noun phrase** (2-6 words), not a full sentence.
- Do not start with the image name (the image context is already provided by the image name label).
- Avoid the word "runtime" in the `default` description (the OpenJDK `runtime` variant is a
  separate base, and OpenJDK's `default` includes the full JDK).
- Avoid expanding abbreviations redundantly (e.g., "JRE runtime" is redundant because JRE already
  means "Java Runtime Environment").
- Prefer functional descriptions (what capability the variant provides) over repeating the variant
  name as a product name.

**Example** (PHP image with FPM base):

```yaml
variant_descriptions:
  fpm: "PHP FastCGI process manager"
```

**Example** (OpenJDK image with runtime base):

```yaml
variant_descriptions:
  runtime: "Headless Java runtime"
```

### distros

- **Type**: Array of strings
- **Default**: `[rawhide, hummingbird]` (from `images/variables.yml`)
- **Description**: List of distros to build for this image. Overrides the global `default_distros`.
  Use this to disable a distro for images where a required package is not available in that distro.
- **Example**: `[hummingbird]` disables Rawhide builds for this image
- **Note**: Each enabled distro creates a full set of variants, so the final combinations are
  distros × variants.

## Container Configuration

### user

- **Type**: String, integer, or object (per-variant dict)
- **Required**: No (defaults to `default`)
- **Description**: Specifies which user the container runs as. The `USER` directive is rendered
  via the `{{ set_user() }}` macro at the end of Containerfile templates. The special (and default)
  value `default` maps to the `default_user` variable (UID 65532). For `user: default`, the
  `{{ final_stage() }}` macro sets `HOME=/tmp` and `WORKDIR /tmp` early in the image to ensure
  unprivileged users have a writable default environment. Images can override `WORKDIR` later in
  their Containerfile.j2. For builder variants with `user: default`, it additionally sets
  `ENV CONTAINER_DEFAULT_USER=65532`.

**Same user for all variants:**

```yaml
user: default
```

```yaml
user: root  # Root user
```

```yaml
user: postgres  # Literal username
```

**Per-variant configuration:**

Use when different variants need different users:

```yaml
user:
  default: root
  builder: root
  fpm: default
  fpm-builder: default
```

## Package Management

Packages are defined in `properties.yml` under `rpm_packages`:

```yaml
rpm_packages:
  build-deps:             # Build-time dependencies (installed in builder layer, not in final
                          # image). Included in lockfiles for hermetic builds.
    - golang              # Example: Go compiler for building from source
    - rpm-build           # Example: Tools for building RPMs during image construction

  all:                    # Included in all variants (Containerfiles and lockfiles)
    - coreutils-single
    - your-main-package
    - name: grub2-efi-x64           # Arch-specific: only on x86_64
      arches:
        only: x86_64
    - name: grub2-efi-aa64          # Arch-specific: only on aarch64
      arches:
        only: aarch64

  hummingbird:            # Included in all Hummingbird variants (distro-level key)
    - versioned-package-1.2

  rawhide:                # Included in all Rawhide variants (distro-level key)
    - unversioned-package

  builder:                # Included only in "builder" variant
    - compiler-packages
    - debug-tools

  custom-variant:         # Included only in "custom-variant" variant
    - variant-specific-packages
```

### Package entry format

Each entry in an `rpm_packages` list can be either a **string** (installed on all architectures) or
an **object** with architecture constraints:

**String entry** (all architectures):

```yaml
- coreutils-single
```

**Object entry** (arch-specific):

```yaml
- name: grub2-efi-x64
  arches:
    only: x86_64            # Install only on x86_64
```

```yaml
- name: some-package
  arches:
    not: aarch64             # Install on all architectures except aarch64
```

The `arches.only` and `arches.not` fields accept either a single string or a list of strings.
Supported architectures: `aarch64`, `x86_64`.

**How arch-specific packages work:**

1. **Lockfiles**: Arch-specific entries are passed to rpm-lockfile-prototype using its native
   per-arch package format. The lockfile resolver only includes matching packages for each
   architecture.
2. **Containerfiles**: Arch-specific packages are installed via a `case` statement on `TARGETARCH`,
   so the correct packages are installed during multi-arch builds.

### rpm_packages.build-deps

- **Type**: Array of strings or arch-specific objects
- **Description**: Build-time dependencies needed during image construction. Automatically installed
  in the builder layer (not in the final image) and included in lockfiles for hermetic builds. Use
  for compilers, build tools, and other packages needed only during the build process.

### rpm_packages.all

- **Type**: Array of strings or arch-specific objects
- **Description**: Packages included in all variants, in both Containerfiles and lockfiles

### rpm_packages.\<distro\>

- **Type**: Array of strings or arch-specific objects
- **Description**: Packages included in all variants for the specified distro. Useful when different
  distros ship different package names (e.g. versioned vs unversioned) and the packages should apply
  to every variant within that distro.

### rpm_packages.\<variant\>

- **Type**: Array of strings or arch-specific objects
- **Description**: Packages included only in the specified variant, regardless of distro

### additional_repos

- **Type**: Array of strings (repository filenames)
- **Default**: None
- **Description**: Additional yum repository files from `yum-repos/` to include in the RPM lockfile
  generation. These repos are added to the variant-specific repos (see [Global Variables Reference
  - default_variant_repos][default-variant-repos]). Specify only the filename, not the full path.
- **Example**: `[konflux-ci-rpm-lockfile-prototype-main-fedora-rawhide.repo]`
- **Usage**: Only needed for packages not available in standard Fedora repos or variant-specific
  repos

### allow_fedora_repos

- **Type**: Boolean
- **Default**: `false`
- **Description**: Allow Fedora repositories during lockfile generation for Hummingbird production
  images. By default, production Hummingbird images exclude Fedora repos to ensure packages come
  only from the Hummingbird repository. Set to `true` to temporarily allow Fedora repos when
  required packages are not yet available in the Hummingbird repository.
- **Example**: `true`
- **Usage**: Typically used temporarily for packages not yet built for Hummingbird (e.g., .NET
  runtime on x86_64, OpenJDK packages). Should be removed once packages are available in the
  Hummingbird repository.
- **Note**: Toolchain and similar images under `images/` (such as `hummingbird-builder`) use
  Fedora repositories for signature validation regardless of this setting.

## Hermetic Builds

For hermetic builds (enabled by default), all packages from:

- `rpm_packages.build-deps` - included in lockfiles and Containerfiles (builder layer only)
- `rpm_packages.all` - included in lockfiles and Containerfiles (final image)
- `rpm_packages.<distro>` - included in lockfiles and Containerfiles for all variants of a distro
- `rpm_packages.<variant>` - included in lockfiles and Containerfiles for matching variants
- `default_rpm_packages.builder` - automatically added for builder variants (see [Global Variables
  Reference - default_rpm_packages][default-rpm-packages])

are added to `rpms.lock.yaml` and prefetched using cachi2 for offline builds.

**Build-time vs Runtime Packages:**

- **`rpm_packages.build-deps`**: Installed in the builder layer only (not in final image)
- **`rpm_packages.all`**: Installed in `${NEWROOT}` (the final image)
- **`rpm_packages.<distro>`**: Installed in `${NEWROOT}` for all variants of the specified distro
- **`rpm_packages.<variant>`**: Installed in `${NEWROOT}` for the specified variant

## Build Configuration

### hermetic

- **Type**: Boolean
- **Default**: `true`
- **Description**: Controls whether the Konflux build pipeline passes `HERMETIC=true` to the buildah
  task, enabling hermetic (offline, network-isolated) builds with prefetched dependencies.
- **Important**: Images with `hermetic: false` are **blocked from release** by the [Conforma
  hermetic_task policy](https://conforma.dev/docs/policy/packages/release_hermetic_task.html#hermetic_task__hermetic).
  `hermetic: false` should not be used for any image in the containers repository.

### build_from_source

- **Type**: Boolean
- **Default**: `false`
- **Description**: Controls version extraction in the `package_version()` macro. When `true`,
  extracts version from `.gitmodules` for images built from git submodules. When `false`, extracts
  version from RPM package metadata. Does not control whether source code is compiled in the
  Containerfile.
- **Warning**: Do not use for new images. Software built directly from source is not represented by
  RPM package metadata and cannot be scanned for CVEs by the project's package-based scanner. This
  setting exists only for exceptional legacy images.

### prefetch_gomod_path

- **Type**: String (path relative to image directory)
- **Default**: None
- **Description**: Path to a Golang project directory (typically a git submodule) for prefetching Go
  modules via cachi2. Enables hermetic builds for Go projects without a vendor tree. The path is
  relative to the image directory.
- **Usage**: Existing exceptional source-built images only. Do not use for new images because
  source-built software cannot be scanned for CVEs by the project's package-based scanner.

## Versioning

### main_package

- **Type**: String
- **Description**: Specifies the main package for version labeling

**For RPM-based images:**

Set to the package name. Version is extracted from RPM package metadata.

```yaml
main_package: nginx
```

**For source-built images:**

Existing exceptional source-built images set this to the submodule path. Version is extracted from
the `branch` field in `.gitmodules`. Do not use this pattern for new images: source-built software
cannot be scanned for CVEs by the project's package-based scanner.

```yaml
main_package: images/minio/minio-upstream
build_from_source: true
```

### version_constraints

- **Type**: String (glob pattern)
- **Default**: None (no constraints)
- **Description**: Defines a version constraint pattern to prevent unwanted major or minor
  version updates. The constraint applies to all distros for this image. Constraints are validated
  during `make check`, and builds fail if VERSION files violate the constraint. Primarily used for
  multi-version images (e.g., python-3-11, nodejs-20) to ensure they stay on their intended version
  family.

**Constraint patterns:**

Patterns use glob-style matching:

- `3.11.*` - Match any 3.11.X version (recommended)
- `20.*` - Match any 20.X version
- `8.*` - Match any 8.X version
- `1.25.*` - Match any 1.25.X version (for 3-part versioning like Go)

The pattern matches against the content of VERSION files generated during the build process.

**Example:**

```yaml
main_package: python3.11
version_constraints: '3.11.*'  # Ensure python-3-11 stays on 3.11.X across all distros
```

```yaml
main_package: dotnet-sdk-8.0
version_constraints: '8.*'  # Applies to rawhide, hummingbird, and any other distros
```

**When to use:**

- Multi-version images where each image targets a specific version family
- Preventing automatic major/minor version bumps during Renovate updates
- Ensuring version tags remain accurate (e.g., `python:3.11` doesn't become `python:3.12`)

**Violation handling:**

When a constraint is violated, `make check` fails with a detailed error message showing:

- The VERSION file path
- The actual version found
- The constraint that was violated
- The properties.yml file where the constraint is defined

Options for resolution:

1. Create a new image for the new version (recommended for major/minor bumps)
2. Exclude the violating version via `yum-repos/*.repo` excludepkgs
3. Update the constraint to allow the new version

See [Version Constraints](../operating/version-constraints.md) for detailed documentation.

### version_package

- **Type**: Object (string-to-string mapping)
- **Default**: None
- **Description**: Override the package name used for version extraction on a per-distro, per-variant,
  or per-distro/variant basis. When a variant or distro installs a different package that provides the
  same software (e.g., a FIPS-enabled build), this field tells the version/tag macros which package
  name to look up in the lockfile. The lookup order is: `distro/variant`, then `variant`, then
  `distro`, then `main_package`.

**Per-distro override:**

Use when different distros ship the same software under different package names:

```yaml
main_package: ruby
version_package:
  hummingbird: ruby4.0  # hummingbird ships ruby4.0, rawhide ships ruby
```

**Per-variant override:**

Use when a variant installs a differently-named package:

```yaml
main_package: ruby
version_package:
  minimal: ruby-minimal  # minimal variant resolves version from ruby-minimal
```

**Per-distro/variant override:**

Use when both distro and variant affect the package name:

```yaml
main_package: ruby
version_package:
  hummingbird/fips: ruby4.0-fips
```

**Usage in templates:**

The resolved package name is available as `package_name_for_version` in Jinja2 templates:

```yaml
tags:
  - value: '{{ package_version(package_name_for_version) }}'
    label: org.opencontainers.image.version
```

```jinja2
ENV GOLANG_VERSION={{ package_version(package_name_for_version) }}
```

## Testing Configuration

### reverse_dependency_tests

- **Type**: Boolean
- **Default**: `true`
- **Description**: Controls whether this image participates in reverse dependency workflows (both
  building and testing). When `true`, changes to this image trigger rebuilds and tests of all images
  that depend on it. Dependencies are detected by searching for `TEST_IMAGES[...]` references in
  test scripts. Set to `false` for images that should not trigger reverse dependency workflows when
  changed.
- **Example**: Set to `false` for `curl` to avoid triggering unnecessary reverse dependency tests

**How it works in CI:**

When an image is tested, the CI pipeline performs two phases:

1. **Build Phase**: Builds all dependencies needed for testing
   - Uses `ci/build_images.sh --build-deps` to build:
     - Forward dependencies (images the main image's tests depend on)
     - Reverse dependencies (images that depend on the main image, filtered by `reverse_dependency_tests: true`)
     - Forward dependencies of those reverse dependencies
   - Automatically deduplicates to ensure each image is built exactly once

2. **Test Phase**: Runs tests on the main image and its reverse dependencies
   - Uses `ci/run_tests_container.sh` and `ci/run_tests_k8s.sh` with `--include-reverse-deps` to
     test:
     - The main image
     - All reverse dependencies (filtered by `reverse_dependency_tests: true`)

**Why set to `false`?**

Set `reverse_dependency_tests: false` for images like `curl` that are:

- Used pervasively across many images (too many reverse dependencies)
- Have a small, stable API that can be fully tested in their own tests
- Would cause excessive CI load if every dependent image were tested on each change

## Release Configuration

The `repository` and `stream` fields together define the release identity for an image. Both are
required and validated during CI. The `repository` specifies the Quay.io repository name, and the
`stream` specifies the version series within that repository.

### repository

- **Type**: String
- **Required**: Yes
- **Description**: The Quay.io repository name for this image. For images where the directory name
  matches the desired repository name (e.g., `images/nginx/` → `repository: nginx`), this is
  redundant but still required for explicitness. For versioned image directories, this groups
  multiple versions under one repository (e.g., `nodejs-20` and `nodejs-24` both use
  `repository: nodejs`).
- **Example**: `nodejs` (used by `nodejs-20` and `nodejs-24` directories)

### stream

- **Type**: String
- **Required**: Yes
- **Description**: The version series identity for this image within its repository. Emitted as
  the `io.hummingbird-project.stream` label on the container image, enabling the image catalog to
  provide consistent structured data about multi-stream repositories.

The stream value must reflect upstream's actual branch structure at the granularity where parallel
security-maintained branches exist. Use [endoflife.date](https://endoflife.date) to determine the
correct granularity.

**Key rules:**

- The stream is **never derived from the image directory name**. The directory name is not evidence
  for stream granularity (e.g., a directory named `foo/` for an upstream with branches `2.8`,
  `3.0`, `3.2` still requires a major.minor stream like `"3.0"`).
- The **purpose of upstream branches is irrelevant** to granularity. Whether upstream calls them
  "LTS", "stable", "mainline", or "STS" does not matter. If they are parallel branches receiving
  security updates, the stream granularity must match.
- **Match the upstream project's version naming convention.** If the project identifies branches
  as `8.0`, `9.0` (not `8`, `9`), use that format even if endoflife.date abbreviates it.
- **Sequential minor versions are not parallel branches.** If endoflife.date lists minor versions
  that each immediately supersede the previous with no support overlap, the minor is a release
  counter. Use the major version as the stream.
- Use `"latest"` only for images where no meaningful version series identity exists:
  rolling-release tools with date-based versioning (e.g., minio) or base images that track the
  distro rather than independent software (e.g., core-runtime).
- For **unversioned images** (no version in directory name, no version constraints), the stream
  value reflects the version currently shipped and must be updated when the upstream version
  changes.

See the [stream assignment guidelines](adding-images.md#choosing-a-stream-value) for the full
decision process and rationale.

**Document the reasoning** for the stream choice as a YAML comment above the `stream` field.
This ensures future maintainers understand why a particular granularity was chosen.

**Examples:**

```yaml
# endoflife.date: parallel at major (Docker Hub + Debian + Wolfi)
stream: "24"
# endoflife.date: parallel at major.minor (Docker Hub + Debian + Wolfi)
stream: "3.11"
# endoflife.date: branches are 10.1, 11.0, not 10, 11
stream: "10.1"
# rolling release with date-based versions, no version branches
stream: "latest"
```

### application_category

- **Type**: String
- **Required**: Yes
- **Description**: The Pyxis application category for the image's delivery repository. Emitted as
  the `io.hummingbird-project.application-category` label on the container image and used in the
  generated Pyxis repo config. All images sharing the same `repository` must use the same value.
  Validated during `make check` against the list in
  [`ci/application-categories.txt`](https://gitlab.com/redhat/hummingbird/containers/-/blob/main/ci/application-categories.txt).

**Common values for Hummingbird images:**

- `"Programming Languages & Runtimes"` -- language runtimes and SDKs (Go, Python, Node.js, .NET, Ruby, etc.)
- `"Web Services"` -- web servers, reverse proxies, and load balancers (Nginx, httpd, Caddy, HAProxy, Tomcat)
- `"Database & Data Management"` -- databases and caches (PostgreSQL, MariaDB, Valkey, Memcached)
- `"Developer Tools"` -- CLI utilities and build tools (git, curl, jq)
- `"Storage"` -- object storage (MinIO, MinIO Client)
- `"Operating System"` -- base runtime images (core-runtime)

**Example:**

```yaml
application_category: "Web Services"
```

### tags

- **Type**: Array of objects
- **Description**: Define image tags for Quay.io releases. Each variant gets these tags, with
  non-default variants receiving a `-<variant>` suffix.

**Tag Object Fields:**

- **`value`** (required): The tag value. Can be a literal string (e.g., `latest`) or a Jinja2
  template using helper functions.
- **`label`** (optional): If specified, the tag value is written as a `LABEL` in the Containerfile,
  and the actual rendered value from the label is used as the image tag. This ensures version tags
  match the actual package versions in the image.

**Tag Helper Functions:**

- `package_version("<package-name>")` - Full version (e.g., `1.2.3-4.fc42`)
- `package_major_version("<package-name>")` - Major version only (e.g., `1`)
- `package_major_minor_version("<package-name>")` - Major.minor version (e.g., `1.2`)

**Example:**

```yaml
tags:
  - value: latest                                        # Literal tag, no label
  - value: '{{ package_major_version("nginx") }}'        # Template tag with label
    label: io.hummingbird-project.major-version
  - value: '{{ package_major_minor_version("nginx") }}'  # Tag becomes actual version from label
    label: io.hummingbird-project.major-minor-version
  - value: '{{ package_version("nginx") }}'
    label: org.opencontainers.image.version
```

**Note**: For images with multiple versions, only set `latest` on the latest version.

### tag_suffix_aliases

- **Type**: Mapping of variant names to arrays of suffixes
- **Description**: Add aliases for every configured tag without creating another image variant.
  The suffix is appended directly to each base tag. Existing canonical tags remain unchanged.
- **Example**:

```yaml
tag_suffix_aliases:
  default:
    - jdk25
  builder:
    - jdk25-builder
```

### support_level

- **Type**: String
- **Default**: Omitted (image is Red Hat supported)
- **Values**: `community`, `experimental`
- **Description**: Controls the support level, Quay organization, and Konflux application
  assignment for hummingbird-distro images. The mapping from support level to Quay org is
  defined in `images/variables.yml` under `support_levels`; all other names (Konflux app,
  service account, RPA suffix) are derived from the Quay org name. When omitted, the image
  is Red Hat supported and assigned to `containers-hummingbird`. Rawhide images are always
  assigned to `containers-rawhide` regardless of this property.
- **Example**: `support_level: community`
- **Mapping**:

  | Value          | Quay org                | Konflux app                         | CPE | Pyxis/RPA |
  |----------------|-------------------------|-------------------------------------|-----|-----------|
  | (omitted)      | `hummingbird`           | `containers-hummingbird`            | Yes | Included  |
  | `community`    | `hummingbird-community` | `containers-community-hummingbird`  | No  | Excluded  |
  | `experimental` | `hummingbird-ci`        | `containers-ci-hummingbird`         | No  | Excluded  |

- **Usage**: Use `community` for images maintained by the community (e.g., minio, bootc-os).
  Use `experimental` for internal build infrastructure (e.g., hummingbird-builder). These
  images are built and tested normally but published to their designated Quay organization,
  not the official Red Hat registry.

**How Labels Work:**

1. Template is rendered and written to Containerfile: `LABEL org.opencontainers.image.version=1.27.3-1.fc42`
2. Build system extracts the actual value from the image label
3. Image is tagged with the extracted value: `quay.io/hummingbird/nginx:1.27.3-1.fc42`

This ensures tags always match the actual package versions built into the image.

## Compliance Scanning

OpenSCAP compliance scanning verifies CIS and STIG compliance during the
build. Scanning runs against the container rootfs using `oscap-chroot` and
fails the build if any enabled rules are violated.

Scanning is enabled by default for all images. It only runs for the
Hummingbird distro and is skipped for the `builder` base image.

### oscap.enabled

- **Type**: Boolean
- **Default**: `true` (from `images/variables.yml`)
- **Description**: Controls whether OpenSCAP compliance scanning runs for this
  image. When `true`, a `verify-compliance` step is added to the generated
  Containerfile for all Hummingbird variants. Set to `false` to opt out.

```yaml
oscap:
  enabled: false
```

### oscap.profiles

- **Type**: Object mapping profile names to enablement rules
- **Default**: `{cis: true, stig: {variants: ["*fips*"]}}` (from
  `images/variables.yml`)
- **Description**: Controls which compliance profiles run for each variant.
  Each profile can be set to `true` (all variants), `false` (disabled), or an
  object with a `variants` list of glob patterns.

**Default behavior** (CIS for all variants, STIG only for FIPS variants):

```yaml
oscap:
  profiles:
    cis: true
    stig:
      variants:
        - "*fips*"
```

**Enable STIG for all variants:**

```yaml
oscap:
  profiles:
    stig: true
```

**Disable CIS for a specific image:**

```yaml
oscap:
  profiles:
    cis: false
```

The `variants` field supports glob patterns using `fnmatch` syntax (e.g.,
`*fips*` matches `fips`, `fips-builder`, `openssl-fips`).

### oscap.exclude_rules

- **Type**: Array of rule exclusion objects
- **Default**: Global rule exclusions from `images/variables.yml`
- **Description**: Rules to exclude from compliance scanning. Image-level
  exclusions are concatenated with global exclusions (not replaced). Each
  exclusion object has the following fields:

| Field      | Type             | Required | Description                                                                       |
|------------|------------------|----------|-----------------------------------------------------------------------------------|
| `id`       | String           | Yes      | XCCDF rule ID (e.g., `xccdf_org.ssgproject.content_rule_configure_crypto_policy`) |
| `reason`   | String           | Yes      | Explanation for excluding this rule                                               |
| `variants` | Array of strings | No       | Glob patterns for variants this exclusion applies to. Omit for all variants.      |
| `profiles` | Array of strings | No       | Profile names (`cis`, `stig`) this exclusion applies to. Omit for all profiles.   |

**Exclude a rule for all variants and profiles:**

```yaml
oscap:
  exclude_rules:
    - id: xccdf_org.ssgproject.content_rule_configure_crypto_policy
      reason: "Caddy uses Go's built-in TLS stack, not system crypto-policies"
```

**Exclude a rule only for specific variants:**

```yaml
oscap:
  exclude_rules:
    - id: xccdf_org.ssgproject.content_rule_configure_crypto_policy
      reason: "crypto-policies state files not generated in installroot"
      variants:
        - "*fips*"
```

**Exclude a rule only from a specific profile:**

```yaml
oscap:
  exclude_rules:
    - id: xccdf_org.ssgproject.content_rule_some_rule
      reason: "Not applicable to CIS for this image"
      profiles:
        - stig
```

**Combine variant and profile filters:**

```yaml
oscap:
  exclude_rules:
    - id: xccdf_org.ssgproject.content_rule_configure_crypto_policy
      reason: "crypto-policies state files not generated in installroot for FIPS variants"
      variants: ["*fips*"]
      profiles: [cis]
```

### How compliance scanning works

1. **Profile resolution**: `generate_jinja2.py` determines which profiles
   (CIS, STIG) are active for the current variant based on `oscap.profiles`.
2. **Rule filtering**: Exclusion rules are filtered by variant (glob matching)
   and profile, producing per-profile exclusion lists.
3. **Tailoring file**: If any active profile has exclusions, an XCCDF tailoring
   file (`oscap-tailoring.xml`) is generated in the variant directory with
   custom profiles extending the upstream CIS/STIG profiles.
4. **Containerfile**: The `verify-compliance` command is added with explicit
   `--cis` and/or `--stig` flags based on active profiles, plus a
   `--tailoring-file` argument if exclusions exist.
5. **Build-time scan**: `oscap-chroot` evaluates the rootfs against each active
   profile. The build fails if any non-excluded rule fails.

## Next Steps

- [Adding Images][adding-images] - Step-by-step guide for adding new images
- [Image Pipeline][image-pipeline] - How the complete pipeline works

[default-variant-repos]: https://hummingbird-project.io/l/global-variables-reference#default_variant_repos
[default-rpm-packages]: https://hummingbird-project.io/l/global-variables-reference#default_rpm_packages
[adding-images]: adding-images.md
[image-pipeline]: https://hummingbird-project.io/l/image-pipeline
