Solution Licensing And Redistribution

SkillDatabases & data

Use when adding, onboarding, or repackaging any solution, tool, container image, model, weights, dataset, or runtime cache; classify each artifact separately, keep restricted weights out of images, design safe runtime caching, and record redistribution decisions where guards enforce them.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Solution Licensing And Redistribution skill

What this skill tells your AI

The instructions your AI receives, as published by nebius/nebius-physical-ai in skills/atomic/solution-licensing/SKILL.md and read by ahel’s review.

Use this skill whenever work adds something new to what NPA ships: a workbench tool, a BYOF/OSS solution, a container image, a base-image swap, a model, or a dataset. It answers one question:

We are about to bundle someone else's software. Who may we hand the result to, and in what form?

"It's open source" does not answer that. The whole point of this skill is that the answer usually depends on components the top-level license does not cover.

This is engineering guidance for classifying and recording a decision, not legal advice. When a license is novel, ambiguous, or the vendor's own sources disagree, record the finding and escalate to a human rather than picking the convenient reading.

When To Use

  • Onboarding an OSS or partner solution (pairs with skills/workflows/oss-solution-registry-onboard/SKILL.md, whose License admission gate this skill implements)
  • Adding a workbench image or changing a Dockerfile's FROM
  • Adding a model, checkpoint, or dataset to a workflow
  • Deciding whether something may be published to a public registry
  • Reviewing a PR that does any of the above

Before any provisioning, build, download, or submission that depends on a third-party EULA, also load skills/atomic/third-party-eula-preflight/SKILL.md; licensing classification does not itself establish operator consent or upstream asset access.

When the classification says an artifact may be used but not baked or redistributed, load skills/workflows/runtime-fetch-onboard/SKILL.md. It turns that decision into a bootstrap-container, runtime-delivery, cache, and validation contract so a restricted weight or SDK does not unnecessarily block the rest of the onboarding.

The Six Artifact Boundaries

Classify each boundary separately. A permissive answer at one boundary says nothing about the others.

BoundaryWhat it isTypical trap
SourceThe project's own codeApache/MIT badge on a repo whose build pulls proprietary parts
Baked runtimeEverything the image carries: base image, wheels, SDKs, binaries, assets, fonts, texturesFree to use, not to redistribute
WeightsModel checkpoints, adapters, tokenizers, and auxiliary model filesTreating registry access control as permission to redistribute baked bytes
DatasetsTraining, evaluation, calibration, and example dataAssuming a model license also covers its data
Runtime cachesDownloaded weights, SDKs, compiled kernels, and mutable runtime stateAssuming runtime fetch is durable, or persisting credentials beside cached bytes
OutputsWhat the model generates when an operator runs itTerms that restrict what the generated artifacts may be used for, and that therefore bind pipeline stages downstream of the model

The decisive layer is normally the baked runtime, because publishing an image distributes every byte in it to whoever pulls it.

Outputs is the boundary people forget, because it is the one that is not about redistribution at all. Ship an image containing none of the vendor's bytes and every other boundary goes quiet — while a term like "you may not use the Outputs to train another model" stays fully in force and lands squarely on the next stage of the workflow. See the LTX-2.5 precedent below.

Procedure

1. Enumerate what the artifact actually ships

Do not read the README. Read the Dockerfile and the lockfile.

grep -nE '^(FROM|ARG .*(BASE|IMAGE|VERSION))' npa/docker/workbench/<tool>/Dockerfile
grep -nE 'pip install|apt-get install|curl|wget|COPY --from' npa/docker/workbench/<tool>/Dockerfile

List: base image, every SDK/wheel installed from a vendor index (for example pypi.nvidia.com, nvcr.io), anything downloaded at build time, baked weights, datasets, and every cache path populated or mounted at runtime.

2. Find each component's real license

Prefer the vendor's own licensing page or the package's own metadata over a repo badge or a summary. Useful checks:

pip download --no-deps --no-binary :all: <pkg>   # then read the sdist metadata
npa/.venv/bin/python -c "import importlib.metadata as m; print(m.metadata('<pkg>')['License'])"

A package whose License field literally reads "NVIDIA Proprietary Software" settles the question regardless of what the GitHub repo's badge says.

For compiled wheels, inspect embedded dependencies, certificate data and fonts, not only the package's top-level grant. An SBOM license expression does not deliver required copyright or permission text. Bind the exact notices and any required corresponding source to the shipped artifact/member hashes, then verify that recipients actually receive those bytes, including obligations for superseded components retained in ancestor layers.

3. Ask the redistribution question explicitly

For every component, answer these four separately — permission for one is not permission for another:

  1. May we use it internally?
  2. May we redistribute it to third parties (shipping an image counts)?
  3. May we run it as a service for third parties? Vendors often treat "install and operate it for a customer" as redistribution even though no bits change hands.
  4. Are there field-of-use limits (non-commercial, research-only, no competing service, evaluation-only)?
  5. What may the operator do with the Outputs? Restrictions here survive every packaging trick, because they attach to artifacts we never touch.
  6. Do any obligations depend on facts about the operator — revenue, headcount, entity type, whether this particular use is commercial? If so, nobody upstream of the operator can answer them, which changes what the image must ask for at run time (see below).

4. Resolve conflicting vendor sources

Vendors publish general terms and product-specific terms, and they diverge. When they do, prefer the source that is more specific to the component we actually ship, and then the more recent. Record which source you relied on and its date, because the next person will find the other one and assume we were wrong.

5. Record the decision where the build enforces it

A conclusion in a PR description is not a control. Encode it:

  • npa/docker/workbench/packaging-contract.yaml — set redistribution: public or restricted on the image entry.
  • npa/src/npa/deploy/images.py — add restricted tools to RESTRICTED_PUBLICATION_TOOLS so publicly_publishable_tools() and publish_public exclude them, and so resolving them from a public registry fails loudly. Compatibility aliases retain the original Omniverse-named API, but new code must use the general inventory because any vendor runtime can be non-redistributable.
  • npa/src/npa/deploy/images.py — add tools that are licence-eligible but lack accepted built-image scans and real capability evidence on required hardware to UNVALIDATED_PUBLICATION_TOOLS. "Restricted" and "unproven" are different answers to different questions, and conflating them is wrong in both directions: a tool here is not restricted, it simply lacks the required evidence, and it leaves the set in the same change that records its accepted digest, scans, and real capability result.
  • For a solution's weights/datasets, record the license and the runtime-fetch requirement in the capability table from the onboarding skill.
  • For an output-layer restriction, the record has to travel with the artifacts: stamp a provenance manifest next to them and put a fail-closed gate in front of every downstream stage the restriction reaches. A note in the docs cannot stop a workflow; a gate can.

The packaging-contract guards then fail the build if a Dockerfile bakes a restricted marker, or is built FROM a restricted image, while claiming public.

Before publication, resolve current state from the packaging contract and the restriction/quarantine inventories in npa/src/npa/deploy/images.py; stop if they disagree. For supported release promotion, require the accepted source/digest evidence there and in the applicable image manifests. After promotion, verify the supported release against npa/src/npa/deploy/public_release_manifest.json and anonymous registry reads. Historical examples below explain packaging decisions; they do not replace current artifact scans, real capability validation, or release verification.

Patterns That Keep Us Compliant

Three patterns do the real work. Prefer them over asking for an exception.

Default operator-responsibility policy

Use this default for a public zero-restricted-payload image whose third-party artifacts are delivered directly to the operator at runtime:

The operator initiates runtime fetch with their own credential and is responsible for using the credential and fetched artifact under the exact upstream terms. NPA verifies access to the immutable artifact, stores neither the credential nor restricted bytes in the public image, and makes no claim that the credential authorizes redistribution.

Apply these rules once, consistently, instead of reopening the same question for every image:

  • A customer-owned Hugging Face or NGC credential plus a successful upstream usable-payload probe is operationally sufficient for NPA to fetch from that exact provider, repository or artifact, revision, and account. It is not legal acceptance or proof of compliance. Do not add an NPA acceptance checkbox or ask the operator to attest again.
  • Preserve the exact operator statement once under one bounded manager task/run ID. If the operator states noncommercial, record exactly noncommercial; capture the intended activity separately. Child solutions may reference that record when their exact terms are compatible, without another per-image question. The record expires with the task/run and is never global or permanent. Reopen the decision only if the operator changes scope or an exact artifact's authoritative terms require a concrete additional fact.
  • Keep entitlement provider- and artifact-scoped. An HF token says nothing about an unrelated NGC, CUDA, cuDNN, dataset, or asset endpoint; an NGC token says nothing about an unrelated HF repository. Prefer a vendor-gated runtime artifact or an operator-provided runtime when the otherwise selected endpoint provides no verifiable entitlement.
  • Classify the public image from its built bytes. A neutral image is eligible for public classification only when every baked byte has verified redistribution rights, byte-level inspection proves no gated or redistribution-restricted payload is present, and all secure-image-build publication gates pass. Runtime-fetch Ready grants no redistribution rights.
  • Do not invent output or service restrictions. Escalate only a restriction stated by authoritative terms or concrete conflicting provenance. If the applicable terms contain no output restriction, record none found and continue. Running GPL software fetched directly by the operator is not NPA redistribution; GPL source-conveyance duties arise if NPA conveys the GPL bytes.

This policy does not override an explicit vendor click-through, license key, paid/enterprise entitlement, prohibited service use, or output restriction. Use the vendor's own mechanism once and reuse its result within the exact scope.

Runtime fetch under the customer's own credentials. Never bake gated or redistribution-restricted weights merely because a token can gate image access. The image ships the downloader; the operator supplies their own HF/NGC credential at runtime and fetches an exact immutable revision when the selected asset requires authorization. Do not require a token for genuinely public, anonymous weights. For Hugging Face, the token and its actual upstream repository permission are the only local access gate: probe every required repository before provisioning, with no NPA terms boolean or model-check bypass. Token presence alone proves neither access nor acceptance. For a gated artifact, a successful provider-side payload probe is operationally sufficient for NPA to fetch that exact artifact; it is not legal acceptance, proof of compliance, or permission to redistribute. Compliant use remains the credential owner's responsibility, and NPA does not collect a duplicate attestation.

Build-your-own. For a runtime we may not redistribute, ship the Dockerfile and the build tooling, not the built image. Each operator builds into their own registry (build.sh --registry <their-registry> --push), pulling the vendor base with their own credentials and EULA acceptance. The vendor delivers to each operator under that operator's own acceptance; we ship only instructions.

Runtime fetch of the whole SDK. Build-your-own has a real cost: the customer needs vendor credentials, and we cannot publish a working image at all. Where the vendor serves the runtime from an index the customer can reach directly, the stronger move is to ship an image containing none of it and fetch on first run on the customer's runtime. The absence of proprietary bytes from published layers is the redistribution control; EULA acceptance governs runtime use separately. This is how the Isaac images became publishable; see the worked precedent. Check whether the vendor's index actually requires a credential before assuming build-your-own is the only option: pypi.nvidia.com serves Isaac Sim anonymously, so the credential was never the gate — acceptance was.

All three patterns share one idea: move the vendor's delivery to the customer, so we are never the redistributor.

Runtime Weight Cache Policy

EULA simplification changes acceptance UX only. It does not make a runtime weight cache durable. Name the cache tier in every workflow design:

Cache tierLifetime and policy
Image layerImmutable and redistributed with the image. Never use it for gated or redistribution-restricted weights, datasets, credentials, or populated runtime caches.
Node-local ephemeralReuses downloads only on the same surviving node or pod volume. Treat a reschedule, node replacement, or cleanup as a cold cache.
Shared durable PVC/object storageSurvives workers only when the workflow explicitly provisions and mounts a PVC or stages objects to configured storage. It is not implied by runtime fetch.

The workbench ships that third tier: npa/src/npa/workbench/model_cache.py is the one place that decides where downloaded weights land, and it is wired into the SkyPilot renderer, the sim2real sibling GPU Jobs, Serverless Job envs, and VM Docker deploys. It stays inert until the operator supplies storage (NPA_MODEL_CACHE_PVC, NPA_MODEL_CACHE_HOST_PATH, or an explicit NPA_MODEL_CACHE_DIR), then redirects the whole cache family — HF_*, TORCH_HOME, NPA_COSMOS3_CACHE, NPA_COSMOS_CURATE_WEIGHTS_DIR, the LeRobot, Wan and LTX caches — into it. Reach for the claim in npa/docker/workbench/common/model-weight-cache.yaml and docs/workbench/model-weight-cache.md before designing a per-workflow cache; a new one-off cache path is how the family fragments and one stage silently re-downloads.

For a durable cache:

  1. Wire the PVC or object-storage location explicitly; do not rely on an image path or an ambient host directory.
  2. Key cache identity by provider, repository/artifact, exact immutable revision or digest, and relevant format/version. Never let mutable latest aliases overwrite an existing identity.
  3. Populate safely under concurrency: download to a unique temporary location, verify expected files/checksums, then atomically publish a ready marker or immutable prefix. Use a lock or single-writer warm stage where the backend needs one.
  4. Inject HF/NGC credentials through runtime secret plumbing only. Never write tokens into cache metadata, manifests, logs, object keys, or image layers.
  5. Reuse without redistribution: run an explicit warm/fetch stage with the operator's credential, then mount the immutable cache read-only in consumer stages or pass its durable URI and verified identity. Never COPY that cache into a later Docker build context or publish it as a derived image.

Document the selected asset license, immutable revision/digest, cache tier, storage wiring, population protocol, and consumer mount/URI in the workflow or capability record. If those are absent, describe the cache as ephemeral.

CUDA And cuDNN Are Separate Payload Boundaries

A public CUDA base tag does not establish redistribution rights for every bundled SDK. During cuRobo packaging, the inspected cudnn-devel base layer contained cuDNN development headers. The current official cuDNN supplement identified runtime .so and .dll files as distributable, while the inspected wheel's older embedded supplement also allowed .h files. Record that difference; do not claim the embedded grant excludes headers. cuRobo selects runtime-only bytes that satisfy both grants. CUDA's Linux-specific grant is not a substitute for cuDNN's separate terms. Inspect both inherited image layers and the exact cuDNN wheel closure. Use an appropriate base and remove any non-distributable install payload before its layer commits; deletion from a later layer cannot remove bytes from an ancestor. Retain license notices and recheck the built layers before publication. Runtime use consent and permission to redistribute are separate decisions.

Worked Precedent: Isaac Sim / Omniverse Kit

The canonical case in this repo, and the best template for reasoning — because the first two answers it produced were both wrong, and the third one changed the product rather than the argument.

The layers.

  • Source: Isaac Sim's GitHub source is Apache-2.0. Isaac Lab's GitHub repo (isaac-sim/IsaacLab) is BSD-3-Clause. Both freely redistributable.
  • Baked runtime: the shipped binary bundles NVIDIA-owned components (Omniverse Kit SDK, models, textures) under the NVIDIA Isaac Sim Additional Software and Materials License. Redistributing Isaac Sim with Omniverse Kit to third parties, or delivering it to them as a service, requires NVIDIA AI Enterprise. Internal R&D is free with no seat limit.
  • Weights and data: already handled by runtime fetch (Cosmos, GR00T N1, Cosmos-Reason) at runtime. Public Hugging Face assets work anonymously; gated assets use the operator's token and must pass a real upstream access probe before provisioning. There is no second NPA acceptance switch or bypass; NGC credentials apply only to NGC-hosted pulls.

Wrong answer #1: "the source is Apache-2.0, so the image is fine." The decisive layer is the baked runtime, and publishing an image distributes every byte in it.

Wrong answer #2: "Isaac Lab's repo is BSD-3, so we can bake that half and only runtime-fetch Isaac Sim." This is the trap worth memorising, because it looks like diligence. Read the package metadata, not the repo badge:

$ curl -sL https://pypi.nvidia.com/isaaclab/isaaclab-2.3.2.post1-cp311-none-manylinux_2_35_x86_64.whl -o w.whl
$ npa/.venv/bin/python -c "import zipfile; z=zipfile.ZipFile('w.whl'); print(z.read([n for n in z.namelist() if n.endswith('METADATA')][0]).decode()[:400])"
Name: isaaclab
License: NVIDIA Proprietary Software
Classifier: License :: Other/Proprietary License

and isaaclab/__init__.py opens with "distribution of this software … without an express license agreement from NVIDIA CORPORATION is strictly prohibited". The wheel is a differently-licensed repackaging of the BSD-3 repo. Same project, same version, two licences, and only one of them is on the artefact you would ship.

Also wrong: "gate the image behind a runtime token." A token gates a download. If the bytes are already in the layers, a token protects nothing — you have just added a speed bump in front of a redistribution you have already performed. Any proposal of the form "we keep baking it but add an access control" is answering the wrong question.

The answer that worked: move the vendor's delivery to the customer — for the whole SDK, not just the weights. The images were re-architected to contain no NVIDIA Isaac bytes at all. On first run they download Isaac Sim and Isaac Lab from https://pypi.nvidia.com into a cache volume. NPA defaults NVIDIA's documented ACCEPT_EULA=Y for these non-interactive workloads and preserves an explicit opt-out. NVIDIA still delivers the runtime directly to each operator; we redistribute no Isaac bytes, so the redistribution conclusion does not depend on the EULA UX default. The clean runtime-fetch isaac-lab, sonic, and groot images may therefore be classified redistribution: public. Historical SONIC L40S and inherited MuJoCo artifacts contain restricted payload; replacing them does not make those old bytes redistributable. The replacement MuJoCo design used an independent digest-pinned public Python base to remove that inherited runtime. Its accepted 0.2.0-runtime digest passed exact-layer scans and a real B200 Unitree G1 rollout, as recorded in npa/src/npa/deploy/sonic_image_manifest.json. That evidence applies only to the replacement digest; historical variants remain quarantined. Every new digest must pass its own exact-layer scans and real GPU validation before release acceptance; use the current inventories and accepted manifests for its state.

Three things made that verdict defensible rather than merely plausible, and a new solution should expect to produce all three:

  1. Default acceptance and explicit opt-out are tested features. Unset acceptance must run non-interactively. Empty, N, NO, 0, and FALSE must refuse before downloading; Y, YES, 1, and TRUE normalize to acceptance; unrecognized values fail separately as invalid. The public-image control remains the verified absence of Isaac bytes.
  2. The absence is verified on the artefact. npa/scripts/scan_image_omniverse_payload.py streams the built image's filesystem and layer history and fails on Kit payload signatures. Reading the Dockerfile is not evidence — the claim is about bytes in layers, so check bytes in layers.
  3. The guard was redesigned, not relaxed. The old check flagged any Dockerfile mentioning isaacsim; the new images legitimately mention it in bootstrap plumbing, so the check now distinguishes baked at build time from referenced for run time, and is mutation-tested in both directions. When a guard blocks a change that is genuinely fine, the fix is to make the guard encode the real distinction — never to widen its exceptions.

The other trap, still worth knowing. As of May 2026 NVIDIA announced that Omniverse is free for development, production and redistribution. Read alone that looks like it lifts the restriction. It does not: the Isaac Sim Additional Software and Materials License is the product-specific licence, and the Isaac Sim 6.0 documentation — GA'd 4 June 2026, i.e. after that announcement — still requires AI Enterprise for third-party redistribution. More specific and more recent wins. Note that the reclassification above did not rely on that announcement at all; it rests on the images containing none of the licensed material, which is a much stronger position than a favourable reading of a vendor's marketing page. Prefer arguments that survive the vendor changing their mind.

Useful carve-outs (unchanged): selling simulation outputs (datasets, videos, reports), or selling custom code and USD assets that the customer runs on their own Isaac Sim, do not require a licence.

Shortened here. Read the whole file on GitHub.

Signals

GitHub stars
29
Forks
16
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
solution-licensing
Source
github.com/nebius/nebius-physical-ai