Prerequisites
Feature Summary
A small, semver-tracked Go API that lets external programs use AICR's
recipe resolution, bundling, snapshotting, and validation logic
in-process — without importing AICR's internal packages directly.
Problem/Use Case
AICR ships as a CLI binary and a REST server today. Both work well
for one-shot invocations from a user or a CI pipeline.
A new class of consumer wants to call AICR's logic from inside a
long-running Kubernetes controller — for example, a Crossplane
provider that reconciles a GPUCluster custom resource and needs to
re-resolve recipes whenever the source data or the cluster's
declared intent changes. These consumers need three things AICR does
not offer today:
A stable Go API contract. Today, external programs have to
import several internal AICR packages and stitch them together
with knowledge of how AICR initializes its data provider,
metadata store, and component registry. Every refactor of those
internals breaks external builds, and consumers have nothing to
pin against except a Git SHA.
Safe concurrent use within a single process. A controller
that reconciles two GPU clusters whose recipe sources differ
needs both resolutions to run side-by-side without
cross-contaminating each other's results. AICR's current caches
are process-global and keyed implicitly to the most recently
configured data source, so two sources active in one process
silently corrupt each other.
A way to release the caches. Long-running processes that ever
touch N distinct recipe sources accumulate N caches with no
eviction path, so memory grows monotonically.
Proposed Solution
Introduce a top-level package that external consumers can import
directly. The package exposes a single Client type with methods
that mirror AICR's existing workflow stages:
Resolve a recipe from criteria
Bundle the components of a resolved recipe into deployable
Helm values and rendered manifests
Collect a snapshot of live cluster state
Validate a resolved recipe against an observed snapshot
Release the client's caches when done
Each Client owns its own data source and its own caches.
Constructing two clients against different sources is the supported
pattern for the multi-source case; the clients do not share state.
The package becomes the project's semver-tracked surface. Internal
packages remain importable, but the documentation states explicitly
that they sit below the semver line — the same pattern Go's standard
database/sql uses for driver internals.
Success Criteria
- A new package, importable as
github.com/NVIDIA/aicr, ships and is
documented under the integrator guide.
- The CLI and REST server external behavior is unchanged; their
existing tests pass without modification. Migrating their internal
implementation to consume the facade is tracked separately and is
not in scope for this issue (see "Out of Scope" below).
- Two clients constructed from different recipe sources in the same
process resolve concurrently and return results scoped to their own
source. A test exercises this directly.
- Releasing a client frees its caches; a test confirms memory does
not grow when N clients are created and released in a loop.
- A migration note documents how external callers move from the
internal packages to the new client.
make qualify is green.
Alternatives Considered
- Leaving the internal packages as the public surface and adding
more documentation. Rejected — documentation does not fix the
concurrency bug, and pinning to internal packages forces every
external consumer to absorb internal refactors.
- Forking AICR's logic into the downstream consumer. Rejected —
duplicates recipe and bundler logic, diverges from AICR's recipe
catalog over time, and forfeits the value of a curated runtime.
- A REST-only integration where the controller spawns an
aicrd
sidecar. Rejected — adds a network hop and an extra process to
every cluster, and complicates the failure modes (sidecar
liveness, connection pool, retries) for a problem that an
in-process library call solves directly.
Component
New component
Priority
Critical (blocking adoption or major use case)
Compatibility / Breaking Changes
Strictly additive at the API level. The new package is net-new;
nothing previously importable is removed. The CLI and REST surfaces are unchanged.
We'd welcome the maintainers' guidance on whether the addition
warrants a minor-version bump (e.g., v0.14.0) or whether it can
ship inside an existing planned release.
Operational Considerations
- Observability: the new package exposes the same structured-log
signals AICR already emits; no new log surface.
- Upgrades: downstream consumers track AICR via
go.mod, so a
semver-tracked surface lets them adopt patch releases without
re-validating their own integration.
- Config drift: the per-client design eliminates a class of latent
drift bug (one client's source bleeding into another) that exists
today in any process running two sources.
- Multi-tenancy: the per-client isolation is what makes
multi-tenant controllers (one client per tenant configuration)
safe.
- Blast radius: zero blast radius on the CLI and REST. The change
is contained to the new package plus the internal call sites the
CLI and REST already own.
Are you willing to contribute?
Yes, I can open a PR
Prerequisites
Feature Summary
A small, semver-tracked Go API that lets external programs use AICR's
recipe resolution, bundling, snapshotting, and validation logic
in-process — without importing AICR's internal packages directly.
Problem/Use Case
AICR ships as a CLI binary and a REST server today. Both work well
for one-shot invocations from a user or a CI pipeline.
A new class of consumer wants to call AICR's logic from inside a
long-running Kubernetes controller — for example, a Crossplane
provider that reconciles a GPUCluster custom resource and needs to
re-resolve recipes whenever the source data or the cluster's
declared intent changes. These consumers need three things AICR does
not offer today:
A stable Go API contract. Today, external programs have to
import several internal AICR packages and stitch them together
with knowledge of how AICR initializes its data provider,
metadata store, and component registry. Every refactor of those
internals breaks external builds, and consumers have nothing to
pin against except a Git SHA.
Safe concurrent use within a single process. A controller
that reconciles two GPU clusters whose recipe sources differ
needs both resolutions to run side-by-side without
cross-contaminating each other's results. AICR's current caches
are process-global and keyed implicitly to the most recently
configured data source, so two sources active in one process
silently corrupt each other.
A way to release the caches. Long-running processes that ever
touch N distinct recipe sources accumulate N caches with no
eviction path, so memory grows monotonically.
Proposed Solution
Introduce a top-level package that external consumers can import
directly. The package exposes a single Client type with methods
that mirror AICR's existing workflow stages:
Resolve a recipe from criteria
Bundle the components of a resolved recipe into deployable
Helm values and rendered manifests
Collect a snapshot of live cluster state
Validate a resolved recipe against an observed snapshot
Release the client's caches when done
Each Client owns its own data source and its own caches.
Constructing two clients against different sources is the supported
pattern for the multi-source case; the clients do not share state.
The package becomes the project's semver-tracked surface. Internal
packages remain importable, but the documentation states explicitly
that they sit below the semver line — the same pattern Go's standard
database/sql uses for driver internals.
Success Criteria
github.com/NVIDIA/aicr, ships and isdocumented under the integrator guide.
existing tests pass without modification. Migrating their internal
implementation to consume the facade is tracked separately and is
not in scope for this issue (see "Out of Scope" below).
process resolve concurrently and return results scoped to their own
source. A test exercises this directly.
not grow when N clients are created and released in a loop.
internal packages to the new client.
make qualifyis green.Alternatives Considered
more documentation. Rejected — documentation does not fix the
concurrency bug, and pinning to internal packages forces every
external consumer to absorb internal refactors.
duplicates recipe and bundler logic, diverges from AICR's recipe
catalog over time, and forfeits the value of a curated runtime.
aicrdsidecar. Rejected — adds a network hop and an extra process to
every cluster, and complicates the failure modes (sidecar
liveness, connection pool, retries) for a problem that an
in-process library call solves directly.
Component
New component
Priority
Critical (blocking adoption or major use case)
Compatibility / Breaking Changes
Strictly additive at the API level. The new package is net-new;
nothing previously importable is removed. The CLI and REST surfaces are unchanged.
We'd welcome the maintainers' guidance on whether the addition
warrants a minor-version bump (e.g., v0.14.0) or whether it can
ship inside an existing planned release.
Operational Considerations
signals AICR already emits; no new log surface.
go.mod, so asemver-tracked surface lets them adopt patch releases without
re-validating their own integration.
drift bug (one client's source bleeding into another) that exists
today in any process running two sources.
multi-tenant controllers (one client per tenant configuration)
safe.
is contained to the new package plus the internal call sites the
CLI and REST already own.
Are you willing to contribute?
Yes, I can open a PR