Using AICR as a Go library
AICR ships as both a CLI and a Go library. External projects that need to resolve validated recipes, generate bundles, or collect observed state can import AICR directly. This page is for those consumers.
Which package to import
Import the github.com/NVIDIA/aicr/pkg/client/v1 package. This is the
compatibility-reviewed facade and the surface AICR intends to stabilize at
v1.0.
The facade provides a single Client type with constructors for the
supported recipe sources. Internally it delegates to the functional
packages under pkg/*.
You may also import pkg/* subpackages directly, but their APIs are
not covered by the same stability guarantees — see the public API
surface for the details.
Installing
For reproducibility in downstream projects, pin a specific tag:
Quick start
Snapshotting and validation
Beyond recipe resolution, the facade exposes the rest of the Snapshot → Validate workflow. Both methods are stateless w.r.t. the Client’s recipe source; they are surfaced through the Client only to keep the facade uniform and leave room for future per-Client telemetry hooks.
When WithValidationKubeconfig is omitted or passed an empty string,
ValidateState uses the shared default Kubernetes client and its standard
discovery chain: KUBECONFIG, ~/.kube/config, then in-cluster configuration.
When an explicit path is provided, the SDK reloads that kubeconfig and creates a
fresh client for each validation run. The run reuses that client for all of its
Kubernetes operations.
The recipe argument to ValidateState MUST be the *RecipeResult
returned by the same Client’s ResolveRecipe (or LoadRecipe) call —
the unexported internal recipe state is required for constraint
evaluation.
To restrict the run to specific phases, pass WithValidationPhases in
the order you want them executed:
Valid phase values are PhaseDeployment, PhaseConformance, and
PhasePerformance (canonical execution order). An unrecognized phase is rejected with
ErrCodeInvalidRequest before any cluster work, so a typo cannot
silently degrade to an empty run.
Loading an existing recipe
When a recipe has already been resolved and persisted (for example a
recipe file checked into a GitOps repo, or a cm:// ConfigMap URI), load
it back through the same Client with LoadRecipe instead of re-resolving
from criteria:
LoadRecipe hydrates overlay inputs (kind: RecipeMetadata) against the
Client’s own data provider and returns a Client-owned *RecipeResult
ready for ValidateState / BundleComponents — it passes the same
ownership check as a ResolveRecipe result. An already-hydrated
RecipeResult file is returned with its provider bound to the Client. For a
profile-bearing overlay, the effective declaration resolved from that provider
must structurally match the file’s declaration after JSON normalization;
otherwise loading fails rather than returning a recipe selected from a
different profile contract.
Note that bundle generation runs blocking preflight validations (for
example CheckDriverOwnershipCoherence, which rejects a recipe whose
snapshot recorded gpuDriverState: absent under a preinstalled-driver
profile). For recipes carrying metadata.selectedProfile (the AKS
family), the remedy is out-of-band: fix or recreate the GPU pools,
recapture the snapshot, and regenerate — the driver-ownership paths are
profile-owned, so --set overrides diverging from the selected value
are rejected. Only legacy pre-profile artifacts are remedied through
--set override flags, whose SDK surface is MakeBundle with
BundleOptions.Config — BundleComponents takes no overrides, so a
blocked legacy recipe must be bundled through MakeBundle (or
regenerated) rather than retried on the same call.
The kubeconfig argument (third parameter) is only needed when the recipe
path (first argument) is a cm:// ConfigMap URI.
For unit tests that exercise the facade surface without a live
cluster, pass aicr.WithValidationNoCluster(true): every check
reports as “skipped - no-cluster mode” and no Kubernetes resources
are created. Other facade options
(WithValidationNamespace, WithValidationRunID,
WithValidationCleanup, WithValidationImagePullSecrets,
WithValidationTolerations, WithValidationNodeSelector,
WithValidationKubeconfig) cover the production-controller knobs.
Recipe sources
AICR exposes one production recipe source today; pick it via
aicr.WithRecipeSource:
EmbeddedSource resolves against the recipe data compiled into the
AICR binary — no filesystem path required. Use it when you want AICR’s
bundled recipe data and no local overrides. FilesystemSource
layers an external directory over that same embedded data, so files in
the directory override their embedded equivalents.
Client options
Beyond WithRecipeSource, NewClient accepts these functional options:
WithVersion(version string)stamps the given version string into resolved recipe metadata (accessible viaresult.Resolved().Metadata.Version). Typically the consuming binary’s build version.WithAllowLists(al *AllowLists)fences which criteria values the Client’s resolve path accepts. A resolve whose criteria fall outside the allowlist is rejected before the recipe is built. Passnil(or omit the option) to allow all values.ParseAllowListsFromEnv()builds anAllowListsfrom theAICR_ALLOWED_ACCELERATORS,AICR_ALLOWED_SERVICES,AICR_ALLOWED_INTENTS, andAICR_ALLOWED_OSenvironment variables. It returnsnilwhen none are set —WithAllowListstreats anilAllowListsas allow-all, so the result is always safe to pass straight toWithAllowLists.
AllowLists is a facade-owned struct whose Accelerators, Services,
Intents, and OSTypes fields are plain []string slices, so callers
can construct one directly without depending on pkg/recipe’s enum
identifiers. When you already hold a pkg/recipe.AllowLists, use
aicr.WrapAllowLists to project it onto the facade shape.
Resolving from criteria
ResolveRecipe takes the stable RecipeRequest shape and returns the
facade RecipeResult — a deliberately small struct exposing the
Name, Version, Components, and optional SelectedProfile of the
resolved recipe. Set RecipeRequest.Profile to the exact name=value
selection when the resolved composition declares a profile. Empty applies
the declaration’s required default; a nonempty selection against an
unprofiled composition fails closed.
Components lists enabled (deployable) components only; disabled refs remain
visible via Resolved().ComponentRefs. When you
already hold an *aicr.Criteria value — for example, a REST handler
that parsed criteria from an incoming HTTP request and wrapped them with
aicr.WrapCriteria — use ResolveRecipeFromCriteria. Use
ResolveRecipeFromCriteriaWithProfile for an explicit selection and
ResolveRecipeFromSnapshotWithProfile for snapshot-filtered resolution.
These return the same facade *RecipeResult; call result.Resolved() when you need the
complete underlying *pkg/recipe.RecipeResult (constraints, deployment
order, validation config, metadata):
For a per-resolution Slurm accounting mode, use
ResolveRecipeFromCriteriaWithOptions or
ResolveRecipeFromSnapshotWithOptions with
aicr.WithAccountingMode("customer-managed"). The original criteria and
snapshot method signatures remain unchanged for source compatibility.
The returned *RecipeResult carries:
Name,Version,TranslatedAt— stable identityComponents—[]ComponentRef(Name, Kind, Version, Source, Chart, Namespace)SelectedProfile— selected name/value and declaration-wideOwnedPaths; nil for legacy recipesResolved()— the upstream*pkg/recipe.RecipeResultfor callers that need constraints, deployment order, validation config, or metadata (e.g., evidence emission). Do not mutate; do not retain past the facadeRecipeResult’s lifetime — marshal first if persistence is needed.
Criteria is a facade-owned struct whose enum-typed fields project to
plain strings, decoupling the public surface from pkg/recipe’s enum
identifiers. Construct one directly or wrap an upstream
*pkg/recipe.Criteria via aicr.WrapCriteria. Allowlist enforcement
(WithAllowLists) applies here just as it does on ResolveRecipe; a
nil Client, nil context, or nil criteria each return
ErrCodeInvalidRequest, and the same facade-level timeout bounds the
resolve.
ListCatalog projects the effective inherited profile declaration on each
entry as CatalogEntry.Profile. The summary contains its name, description,
required default, and sorted value names; it is nil when the composition is
unprofiled.
To extract a single value from a resolved recipe, use
SelectFromRecipeWithContext with a dot-path selector. It hydrates the
recipe’s component values and returns the value at the path; an empty
selector returns the entire hydrated structure, and a nil *RecipeResult
returns ErrCodeInvalidRequest. Hydration reads values files through the
recipe’s DataProvider, so the context bounds real I/O — cancel it and the
hydration aborts. This is the same call the aicr query CLI command and the
REST query handler run:
SelectFromRecipe is the context-less form, kept for source compatibility.
It derives a defaults.FileReadTimeout-bounded context internally, so the
reads stay bounded but the caller cannot cancel them. Prefer the
context-aware form wherever a context.Context is available.
The outermost structured code distinguishes the two failure stages, so a
caller can shape a response without reimplementing hydrate-then-select:
ErrCodeNotFound means the selector path does not exist, and any other code
(ErrCodeInternal, ErrCodeTimeout, …) means hydration failed. Match with
errors.As on the outermost error rather than errors.Is — Is walks the
wrap chain and would match an ErrCodeNotFound cause nested inside a
hydration failure.
Delivering a collected snapshot
snapshotter.DeliverSnapshot(ctx, raw, snapshotter.SnapshotDelivery{...})
writes captured bytes to a destination independent of where the agent staged
them:
A cm:// destination is written, not assumed — including when it differs from
the AgentConfig.Output used at collection time. Set TemplatePath to render
through a Go template instead of copying bytes; Output then names the
rendered report.
WrapResolved turns a *pkg/recipe.RecipeResult — typically one taken from
RecipeResult.Resolved() and then projected by the caller — back into a
facade *RecipeResult that SelectFromRecipeWithContext accepts. The result
is queryable only: it carries no owning Client, so MakeBundle,
BundleComponents, and ValidateState reject it. Use Client.AdoptRecipe
when you need a bundle-able result.
Errors
All errors returned by the facade are *pkg/errors.StructuredError
values carrying an ErrorCode. Use errors.As to inspect:
Context handling
ResolveRecipe (and every other context-aware facade method) honours
context cancellation. Each facade entry point unconditionally wraps the
caller’s context with context.WithTimeout against its per-operation
cap. The effective deadline is the smaller of the caller’s deadline
and the facade cap, per context.WithTimeout semantics — a caller
passing a tighter deadline keeps it; a caller passing
context.Background() gets the facade cap.
Per-operation caps:
ResolveRecipe/BundleComponents:defaults.RecipeOperationTimeoutCollectSnapshot: caller-controlled viaAgentConfig.Timeout(falling back todefaults.SnapshotOperationTimeoutwhen unset), plusdefaults.SnapshotOperationGrace. The grace exists becauseAgentConfig.Timeoutbudgets Job completion only — deployment and result retrieval sit outside it, so a bare cap would silently shrink the completion budget you asked for.ValidateState:defaults.ValidationOperationTimeoutMakeBundle: opt-in viaBundleOptions.Timeout. When unset (0) the caller’s context governs unchanged — large bundles,--vendor-charts, and attestation/signing can exceed any fixed cap. The REST/v1/bundlehandler sets it todefaults.BundleHandlerTimeout; the CLIbundlecommand leaves it0.
Passing a nil context.Context returns ErrCodeInvalidRequest. Use
context.Background() (or a deadline-bounded child) for unbounded callers.
Compatibility
Today AICR is pre-1.0. Under Go module versioning, a v0 minor release may
contain breaking API changes. The project mechanically detects and explicitly
records incompatible changes to the facade, but consumers must pin a patch
version in go.mod and audit upgrades.
Starting with v1.0, the facade’s exported API follows Semantic Versioning:
- Major bumps may rename, remove, or change the shape of exported types and function signatures.
- Minor bumps may add new exported types, fields, or methods.
- Patch bumps contain compatible bug fixes.
See also
- Public API surface — stability matrix per package
- Automation guide — CI integration patterns
- Recipe development — authoring recipes