CLI Configuration File (AICRConfig)

August 17, 2026 · View on GitHub

AICRConfig is a Kubernetes-style YAML/JSON document that captures the inputs to the five workflow commands — aicr snapshot, aicr recipe, aicr bundle, aicr validate, and aicr verify — so an end-to-end run version-controls as a single file instead of a shell script full of flags. Each command accepts it through the same --config flag:

aicr snapshot --config aicr-config.yaml
aicr recipe   --config aicr-config.yaml
aicr bundle   --config aicr-config.yaml
aicr validate --config aicr-config.yaml
aicr verify   ./my-bundle --config aicr-config.yaml   # bundle dir is positional

The first four are the producer pipeline; spec.verify is the consumer side, so one document can carry both how an artifact is built and the trust floor a downstream consumer enforces against it.

This page documents the complete document schema in one place. The CLI Reference shows per-command usage in its Snapshot, Recipe, Validate, Bundle, and Verify config-file-mode sections. The schema's source of truth is pkg/config.

Document Envelope

kind: AICRConfig               # required, exactly this value
apiVersion: aicr.run/v1alpha2  # required, exactly this value
metadata:
  name: gke-h100-training      # optional, identifying only
spec:
  snapshot: {}                 # each section optional —
  recipe: {}                   # at least ONE must be present
  bundle: {}
  validate: {}
  verify: {}

Each spec.* section is optional and each command reads only its own section, so a file may carry just one section or any combination. A document with none of the five sections is rejected.

Loading, Precedence, and Secrets

Sources. --config accepts a local file path or an HTTP/HTTPS URL (format detected from the extension; fetches are timeout- and size-bounded). ConfigMap cm:// URIs are intentionally rejected — extract the data with kubectl and pass the resulting file.

Precedence. A CLI flag always wins over the matching config field. For slice and map fields (tolerations, selectors, --set), a flag given on the command line replaces the file's value; it does not append.

Nil vs. empty. For agent selectors and tolerations, omitting the field entirely (nil) inherits the compiled-in defaults (tolerations defaults to tolerate all taints), while an explicit empty value ({} / []) clears the default. Several booleans are tri-state for the same reason: absent means "inherit the CLI default", an explicit false is an opt-out (spec.validate.execution.failOnError, failFast, spec.snapshot.execution.privileged, spec.recipe.criteriaStrict, spec.validate.evidence.*).

Secrets are never part of the schema. The cosign OIDC identity token used by attestation and evidence push is deliberately absent — supply it via the COSIGN_IDENTITY_TOKEN environment variable or the --identity-token flag.

Complete Example

kind: AICRConfig
apiVersion: aicr.run/v1alpha2
metadata:
  name: eks-h100-training
spec:
  snapshot:
    output:
      path: snapshot.yaml            # same shape as -o
      format: yaml                   # yaml | json | table
      template: ""                   # optional Go template path
    agent:                           # in-cluster snapshot Job pod
      namespace: aicr-validation
      image: ""                      # default: ghcr.io/nvidia/aicr:latest
      imagePullSecrets: []
      jobName: aicr
      serviceAccountName: aicr
      nodeSelector:
        nodeGroup: gpu-worker
      tolerations:
        - dedicated=gpu-workload:NoSchedule
      requireGpu: false
      runtimeClassName: ""           # mutually exclusive with requireGpu
      os: ""                         # ubuntu | rhel | cos | amazonlinux | ol | talos
      requests: ""                   # "cpu=500m,memory=1Gi"
      limits: ""
    execution:
      timeout: 5m
      noCleanup: false
      privileged: true               # false for PSS-restricted namespaces
      maxNodesPerEntry: 0            # 0 = unlimited topology entries

  recipe:
    criteria:                        # mutually exclusive with input.snapshot
      service: eks
      accelerator: h100
      intent: training
      os: ubuntu
      platform: kubeflow
      nodes: 2
    profile: ""                      # optional name=value; empty uses the declared default
    # configuration:                 # typed desired-state inputs (not matched on)
    #   slurm:                       # only valid when the resolved recipe platform is slurm
    #     accounting:
    #       mode: disabled           # disabled | customer-managed | aicr-provided
    # input:
    #   snapshot: snapshot.yaml      # derive criteria from a snapshot instead
    output:
      path: recipe.yaml
      format: yaml                   # yaml | json | table
    data: ""                         # optional data-overlay dir/archive
    criteriaStrict: false            # reject criteria outside the embedded catalog

  bundle:
    input:
      recipe: recipe.yaml            # must match recipe.output.path when both set
    output:
      target: ./bundles              # local dir or oci:// URI
      imageRefs: ""                  # external digest file; OCI output only
    deployment:
      deployer: helmfile             # helm | helmfile | argocd | argocd-helm | flux | ...
      repo: ""
      set: []                        # value overrides, "key:path=value"
      dynamic: []
      vendorCharts: false
      appName: ""                    # argocd parent Application name override
    scheduling:
      systemNodeSelector: {}
      systemNodeTolerations: []
      acceleratedNodeSelector:
        nodeGroup: gpu-worker
      acceleratedNodeTolerations:
        - nvidia.com/gpu=present:NoSchedule
      workloadGate: ""
      workloadSelector: {}
      nodes: 2
      storageClass: ""
      sharedStorageClass: ""          # RWX class for opt-in shared filesystems
    attestation:
      enabled: false
      certificateIdentityRegexp: ""
      oidcDeviceFlow: false
      fulcioURL: ""                  # private Sigstore overrides; empty = public good
      rekorURL: ""
      signingKey: ""                 # KMS key ref (awskms:// | gcpkms:// | ...); empty = keyless OIDC
    registry:                        # OCI transport for oci:// push
      insecureTLS: false
      plainHTTP: false

  validate:
    input:
      recipe: recipe.yaml
      snapshot: snapshot.yaml
    agent:                           # same nil-vs-empty semantics as snapshot.agent
      namespace: aicr-validation
      image: ""
      imagePullSecrets: []
      jobName: aicr
      serviceAccountName: aicr
      nodeSelector:
        nodeGroup: gpu-worker
      tolerations:
        - nvidia.com/gpu=present:NoSchedule
      requireGpu: false
    execution:
      phases: [deployment, conformance, performance]
      failOnError: true              # tri-state; absent = CLI default (true)
      failFast: false
      noCluster: false
      noCleanup: false
      timeout: 40m
    evidence:
      cncf:                          # CNCF AI Conformance markdown
        dir: ./evidence
        cncfSubmission: false        # requires dir
        features: []                 # empty = all features
      attestation:                   # recipe-evidence bundle (ADR-007)
        out: evidence-result.json    # setting this enables the path
        bom: ""
        push: ""                     # OCI ref to push the signed bundle
        plainHTTP: false
        insecureTLS: false
  verify:                            # consumer side: policy for `aicr verify`
    policy:                          # assertions checked after verification
      minTrustLevel: verified        # unknown | unverified | attested | verified | max
      requireCreator: ci@myorg.example.com
      cliVersionConstraint: ">= 0.16.0"
    trust:                           # material verification runs against
      certificateIdentityRegexp: ""  # when set, must BEGIN with https://github.com/NVIDIA/aicr/
      key: ""                        # KMS URI or local PEM public-key path
      trustRoot: ""                  # private Sigstore trusted_root.json

Field Reference

spec.snapshot

Inputs to aicr snapshot. There is no input section — the snapshot is produced from the live cluster.

FieldTypeNotes
output.pathstringOutput file path (same as -o)
output.formatstringyaml | json | table
output.templatestringOptional Go template path
agent.*objectIn-cluster capture Job pod: namespace, image, imagePullSecrets, jobName, serviceAccountName, nodeSelector, tolerations, requireGpu, runtimeClassName (mutually exclusive with requireGpu), os, requests, limits. Mirrors spec.validate.agent so one file pins matching placement for both
execution.timeoutduration stringe.g. 5m
execution.noCleanupboolKeep the capture Job after completion
execution.privilegedbool (tri-state)Set false for PSS-restricted namespaces
execution.maxNodesPerEntryint0 = unlimited topology entries

spec.recipe

Inputs to aicr recipe. criteria and input.snapshot are mutually exclusive — query by criteria or derive from a snapshot, not both.

FieldTypeNotes
criteria.service / .accelerator / .intent / .os / .platformstringSame names and values as the CLI flags
criteria.nodesintTarget GPU node count
profilestringOptional configuration profile selection in name=value form. Empty applies the resolved declaration's default.
configuration.slurm.accounting.modestringSlurm accounting ownership: disabled (default) | customer-managed | aicr-provided; mirrors --slurm-accounting-mode. Only valid when the resolved recipe platform is slurm (whether from criteria.platform, a snapshot, or --platform) — an explicit mode (even disabled) on any other platform is rejected with INVALID_REQUEST
input.snapshotstringSnapshot path to derive the recipe from
output.pathstringRecipe output path
output.formatstringyaml | json | table
datastringData-overlay directory/archive (same as --data)
criteriaStrictbool (tri-state)Reject criteria values outside the embedded catalog; mirrors --criteria-strict / AICR_CRITERIA_STRICT (any of the three enables it)

spec.bundle

Inputs to aicr bundle.

FieldTypeNotes
input.recipestringRecipe to bundle
output.targetstringLocal directory or oci:// URI
output.imageRefsstringOptional external image-reference output file for an OCI output.target only. Local output is rejected. Its parent must be an existing real directory, and the target must be outside and not aliased to the planned or completed bundle.
deployment.deployerstringDeployer choice (same values as --deployer)
deployment.repostringGitOps repo for repo-shaped deployers
deployment.set / .dynamic[]stringValue overrides, key:path=value
deployment.vendorChartsboolVendor charts into the bundle
deployment.appNamestringArgo CD parent Application name override (multi-bundle installs sharing a namespace)
scheduling.*objectsystemNodeSelector/Tolerations, acceleratedNodeSelector/Tolerations, workloadGate, workloadSelector, nodes, storageClass, sharedStorageClass. Selectors are YAML maps; tolerations use the CLI's key=value:effect strings
attestation.enabledboolEnable bundle attestation (signing); keyless OIDC by default, KMS-backed when signingKey is set
attestation.certificateIdentityRegexpstringExpected signer identity
attestation.oidcDeviceFlowboolDevice-code flow for headless signing
attestation.fulcioURL / .rekorURLstringPrivate Sigstore endpoints; empty = public-good defaults
attestation.signingKeystringKMS key reference for key-based signing (awskms:// | gcpkms:// | azurekms:// | hashivault://); empty = keyless OIDC. Mutually exclusive with the keyless-only inputs (oidcDeviceFlow, fulcioURL, --identity-token)
registry.insecureTLS / .plainHTTPboolOCI transport options for push

When output.imageRefs is set, AICR writes the published OCI digest through a mode-0600 temporary file and an anchored same-directory rename. The target may be absent or an existing retained regular file; directories, symlinks, other non-regular files, and bundle aliases are rejected. The final validation and rename are ordered but are not one atomic identity-conditioned filesystem operation, so no other process should mutate the target directory while the bundle command runs.

spec.validate

Inputs to aicr validate.

FieldTypeNotes
input.recipe / .snapshotstringRecipe + snapshot to validate
agent.*objectIn-cluster validation Job pod; same fields and nil-vs-empty semantics as spec.snapshot.agent (minus runtimeClassName/os/requests/limits)
execution.phases[]stringe.g. [deployment, conformance, performance]
execution.failOnErrorbool (tri-state)Absent = CLI default (true); explicit false opts out
execution.failFastbool (tri-state)Stop after the first failed phase
execution.noClusterboolTest mode: no cluster access, constraints evaluated inline
execution.noCleanupboolKeep validation Jobs after completion
execution.timeoutduration stringe.g. 40m
evidence.cncf.dirstringCNCF AI Conformance evidence directory (--evidence-dir)
evidence.cncf.cncfSubmissionbool (tri-state)Emit submission layout; requires dir
evidence.cncf.features[]stringEmpty = all features; honored only with cncfSubmission
evidence.attestation.outstringRecipe-evidence result path (v1 for unprofiled recipes, v2 for profiled ones) — setting it enables the attestation path
evidence.attestation.bom / .pushstringBOM input; OCI ref for the signed bundle push
evidence.attestation.plainHTTP / .insecureTLSbool (tri-state)Push transport options

spec.verify

Verification policy for aicr verify, the one consumer-side section. The two sub-sections mirror how the command consumes them: policy holds assertions checked after verification runs, trust holds the material it verifies against.

FieldTypeNotes
policy.minTrustLevelstringunknown | unverified | attested | verified, or max (the CLI default) to auto-detect the highest level the bundle can reach
policy.requireCreatorstringPins the OIDC identity in the bundle attestation's signing certificate
policy.cliVersionConstraintstringConstrains the aicr version in the attestation predicate; supports >=, >, <=, <, ==, !=, and a bare version means >=
trust.certificateIdentityRegexpstringCertificate identity pattern for binary attestation verification; must begin with https://github.com/NVIDIA/aicr/ (leading ^ allowed) and must not use top-level alternation, so it stays confined to the repository
trust.keystringKMS key URI (awskms:// | gcpkms:// | azurekms:// | hashivault://) or local PEM public-key path; the verify counterpart to spec.bundle.attestation.signingKey
trust.trustRootstringPath to a private Sigstore trusted_root.json, additive to the built-in public-good root

policy.minTrustLevel sets operator policy, not an org-enforced guardrail. A committed value lowers the effective floor as readily as it raises it (unknown makes the trust check a no-op, since every level meets it), so treat it as a reviewable choice rather than a control that cannot be relaxed.

A lowered floor admits any bundle whose actual trust level reaches it, which is broader than unsigned bundles. It also covers chains that legitimately degraded: an attested bundle whose binary attestation is absent, or one carrying external --data, both report attested against a verified maximum, so the default max rejects them while a lowered floor does not.

What no policy value can wave through: checksum failures, and attestations that are present but fail verification. Those are rejected regardless of the floor.

aicr verify logs the floor at INFO when config is what supplies it: that is, when --min-trust-level is absent and the configured value is anything other than max. An explicit flag takes precedence instead, and that override is logged separately by the flag-precedence path.

Every field is a durable, non-secret reference or policy value; no private key material is part of the schema. Three aicr verify flags are deliberately excluded: the bundle directory (a positional argument), --format (presentation, not policy), and --insecure-ignore-tlog, which weakens the trust floor and so stays command-line-only rather than something a committed file can silently enable. It still composes with a config-supplied trust.key.

Cross-Section Rules

  • At least one of the five spec.* sections must be present.
  • spec.recipe.criteria and spec.recipe.input.snapshot are mutually exclusive.
  • When both spec.recipe.output.path and spec.bundle.input.recipe are set, they must reference the same file (compared after filepath.Clean; mixing absolute and relative forms is rejected). Mismatched paths in a workflow file are almost always a typo, so the loader fails up-front.
  • Enum-valued fields (criteria.*, output formats, phases) are validated with the same parsers the CLI flags use, so error messages match the CLI's.

See Also

  • CLI Reference — per-command flags and config-file-mode examples
  • Validation — validation phases and evidence workflow
  • Bundling — deployers and bundle outputs